From fd5c938c48c3ea29f2345c6fea230c712568cb79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 28 Oct 2023 15:21:56 +0200 Subject: [PATCH] Added Export Morphshape as start and end shape (SVG, PNG, BMP) --- CHANGELOG.md | 1 + .../flash/exporters/MorphShapeExporter.java | 85 +++++++++++++++++++ .../exporters/modes/MorphShapeExportMode.java | 4 +- .../settings/MorphShapeExportSettings.java | 5 ++ .../flash/tags/DefineMorphShape2Tag.java | 11 +++ .../flash/tags/DefineMorphShapeTag.java | 10 +++ .../flash/tags/base/MorphShapeTag.java | 13 ++- .../flash/gui/locales/ExportDialog.properties | 3 + .../gui/locales/ExportDialog_cs.properties | 3 + 9 files changed, 133 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 300d0e3d9..e6c21dde7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file. - SVG Export - stroke-bitmapId, fill-bitmapId attribute - Morphshape SVG Export - bitmap fill strokes - SVG Export/Import - retain bitmap fill smoothed attribute +- Export Morphshape as start and end shape (SVG, PNG, BMP) ### Fixed - [#1306], [#1768] Maximizing window on other than main monitor diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index 341782c0a..a47728f78 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -23,18 +23,30 @@ import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; +import com.jpexs.decompiler.flash.helpers.BMPFile; +import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.RenderContext; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.enums.ImageFormat; +import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; +import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Graphics2D; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -92,9 +104,33 @@ public class MorphShapeExporter { } final File file = new File(outdir + File.separator + characterID + settings.getFileExtension()); + final File fileStart = new File(outdir + File.separator + characterID + ".start" + settings.getFileExtension()); + final File fileEnd = new File(outdir + File.separator + characterID + ".end" + settings.getFileExtension()); new RetryTask(() -> { MorphShapeTag mst = (MorphShapeTag) t; switch (settings.mode) { + case SVG_START_END: + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileStart))) { + ExportRectangle rect = new ExportRectangle(mst.getStartBounds()); + rect.xMax *= settings.zoom; + rect.yMax *= settings.zoom; + rect.xMin *= settings.zoom; + rect.yMin *= settings.zoom; + SVGExporter exporter = new SVGExporter(rect, settings.zoom, "shape"); + mst.getStartShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileEnd))) { + ExportRectangle rect = new ExportRectangle(mst.getStartBounds()); + rect.xMax *= settings.zoom; + rect.yMax *= settings.zoom; + rect.xMin *= settings.zoom; + rect.yMin *= settings.zoom; + SVGExporter exporter = new SVGExporter(rect, settings.zoom, "shape"); + mst.getEndShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + break; case SVG: try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { ExportRectangle rect = new ExportRectangle(mst.getRect()); @@ -107,6 +143,55 @@ public class MorphShapeExporter { fos.write(Utf8Helper.getBytes(exporter.getSVG())); } break; + case PNG_START_END: + case BMP_START_END: + double unzoom = settings.zoom; + ShapeTag st = mst.getStartShapeTag(); + RECT rect = st.getRect(); + int newWidth = (int) (rect.getWidth() * settings.zoom / SWF.unitDivisor) + 1; + int newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1; + SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE); + img.fillTransparent(); + if (settings.mode == MorphShapeExportMode.BMP_START_END) { + RGB backColor = t.getSwf().getBackgroundColor().backgroundColor; + if (backColor != null) { + Graphics2D g = (Graphics2D) img.getGraphics(); + g.setColor(backColor.toColor()); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + } + } + Matrix m = Matrix.getScaleInstance(settings.zoom); + m.translate(-rect.Xmin, -rect.Ymin); + st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true); + if (settings.mode == MorphShapeExportMode.PNG_START_END) { + ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, fileStart); + } else { + BMPFile.saveBitmap(img.getBufferedImage(), fileStart); + } + + st = mst.getEndShapeTag(); + rect = st.getRect(); + newWidth = (int) (rect.getWidth() * settings.zoom / SWF.unitDivisor) + 1; + newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1; + img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE); + img.fillTransparent(); + if (settings.mode == MorphShapeExportMode.BMP_START_END) { + RGB backColor = t.getSwf().getBackgroundColor().backgroundColor; + if (backColor != null) { + Graphics2D g = (Graphics2D) img.getGraphics(); + g.setColor(backColor.toColor()); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + } + } + m = Matrix.getScaleInstance(settings.zoom); + m.translate(-rect.Xmin, -rect.Ymin); + st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true); + if (settings.mode == MorphShapeExportMode.PNG_START_END) { + ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, fileEnd); + } else { + BMPFile.saveBitmap(img.getBufferedImage(), fileEnd); + } + break; case CANVAS: try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { int deltaX = -Math.min(mst.getStartBounds().Xmin, mst.getEndBounds().Xmin); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java index fd46bdcb6..6f387e1d9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java @@ -23,8 +23,10 @@ package com.jpexs.decompiler.flash.exporters.modes; public enum MorphShapeExportMode { //TODO: implement other morphshape export modes SVG, + SVG_START_END, CANVAS, - //PNG, + PNG_START_END, + BMP_START_END, //GIF, //AVI, SWF, diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java index 20a0b746c..48751140e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java @@ -37,7 +37,12 @@ public class MorphShapeExportSettings { public String getFileExtension() { switch (mode) { + case PNG_START_END: + return ".png"; + case BMP_START_END: + return ".bmp"; case SVG: + case SVG_START_END: return ".svg"; case CANVAS: return ".html"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java index c3b3fb594..bcc49106f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; @@ -151,4 +152,14 @@ public class DefineMorphShape2Tag extends MorphShapeTag { endEdgeBounds = SHAPERECORD.getBounds(endEdges.shapeRecords, null, 4, true); } + @Override + public ShapeTag getShapeTagAtRatio(int ratio) { + DefineShape4Tag ret = new DefineShape4Tag(swf); + ret.usesNonScalingStrokes = usesNonScalingStrokes; + ret.usesScalingStrokes = usesScalingStrokes; + ret.shapes = getShapeAtRatio(ratio); + ret.updateBounds(); + ret.setTimelined(swf); + return ret; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java index c3af75766..212cd20ea 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; @@ -110,4 +111,13 @@ public class DefineMorphShapeTag extends MorphShapeTag { public int getShapeNum() { return 1; } + + @Override + public ShapeTag getShapeTagAtRatio(int ratio) { + DefineShape3Tag ret = new DefineShape3Tag(swf); + ret.shapes = getShapeAtRatio(ratio); + ret.updateBounds(); + ret.setTimelined(swf); + return ret; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java index 86ea7a0cd..70b6ac589 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; @@ -188,8 +189,18 @@ public abstract class MorphShapeTag extends DrawableTag { public SHAPE getEndEdges() { return endEdges; + } + + public abstract ShapeTag getShapeTagAtRatio(int ratio); + + public ShapeTag getStartShapeTag() { + return getShapeTagAtRatio(0); } - + + public ShapeTag getEndShapeTag() { + return getShapeTagAtRatio(65535); + } + public SHAPEWITHSTYLE getShapeAtRatio(int ratio) { List finalRecords = new ArrayList<>(); FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio); diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 3862e0dbf..906946db7 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -61,6 +61,9 @@ morphshapes.gif = GIF morphshapes.svg = SVG morphshapes.canvas = HTML5 Canvas morphshapes.swf = SWF +morphshapes.bmp_start_end = BMP (start, end) +morphshapes.png_start_end = PNG (start, end) +morphshapes.svg_start_end = SVG (start, end) frames = Frames frames.png = PNG diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties index 78c884de4..8cba2c77e 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties @@ -61,6 +61,9 @@ morphshapes.gif = GIF morphshapes.svg = SVG morphshapes.canvas = HTML5 Canvas morphshapes.swf = SWF +morphshapes.bmp_start_end = BMP (za\u010d\u00e1tek, konec) +morphshapes.png_start_end = PNG (za\u010d\u00e1tek, konec) +morphshapes.svg_start_end = SVG (za\u010d\u00e1tek, konec) frames = Sn\u00edmky frames.png = PNG