From 9cd6a203fec9cafe65808a8d7d315841a361fe3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 27 May 2025 00:05:42 +0200 Subject: [PATCH] Fixed: #2460 SVG export - incorrect caching colorTransform and ratio for the same tag --- CHANGELOG.md | 2 + .../exporters/commonshape/SVGExporter.java | 47 +++++++++++- .../decompiler/flash/timeline/Timeline.java | 20 ++--- .../flash/types/ColorTransform.java | 76 +++++++++++++++++++ 4 files changed, 135 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8998a8c49..945ff1e9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ All notable changes to this project will be documented in this file. - [#2459] AS1/2 StoreRegister improper declaration position - AS2 Class names not showing in Folder list view - AS1/2 - Incorrect DefineFunction2 parameter names when parameter name is empty +- [#2460] SVG export - incorrect caching colorTransform and ratio for the same tag ## [23.0.1] - 2025-05-16 ### Fixed @@ -3831,6 +3832,7 @@ Major version of SWF to XML export changed to 2. [#1682]: https://www.free-decompiler.com/flash/issues/1682 [#2456]: https://www.free-decompiler.com/flash/issues/2456 [#2459]: https://www.free-decompiler.com/flash/issues/2459 +[#2460]: https://www.free-decompiler.com/flash/issues/2460 [#2427]: https://www.free-decompiler.com/flash/issues/2427 [#1826]: https://www.free-decompiler.com/flash/issues/1826 [#2448]: https://www.free-decompiler.com/flash/issues/2448 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index 8de88edf7..57efead9c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.BlendMode; +import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.filters.FILTER; @@ -32,6 +33,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; @@ -78,7 +80,7 @@ public class SVGExporter { protected int lastClipId; - public Map exportedTags = new HashMap<>(); + public Map exportedTags = new HashMap<>(); public Map> exportedChars = new HashMap<>(); @@ -88,6 +90,49 @@ public class SVGExporter { public boolean useTextTag = Configuration.textExportExportFontFace.get(); + public static class ExportKey { + + private final Tag tag; + public final ColorTransform colorTransform; + public final int ratio; + + public ExportKey(Tag tag, ColorTransform colorTransform, int ratio) { + this.tag = tag; + this.colorTransform = colorTransform; + this.ratio = ratio; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 43 * hash + Objects.hashCode(this.tag); + hash = 43 * hash + Objects.hashCode(this.colorTransform); + hash = 43 * hash + this.ratio; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ExportKey other = (ExportKey) obj; + if (this.ratio != other.ratio) { + return false; + } + if (!Objects.equals(this.tag, other.tag)) { + return false; + } + return Objects.equals(this.colorTransform, other.colorTransform); + } + } + public SVGExporter(ExportRectangle bounds, double zoom, String objectType) { this(bounds, zoom, objectType, null); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 7ddf4221f..8f5a23776 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -145,7 +145,7 @@ public class Timeline { * Map of depth to maximum frame. */ private final Map depthMaxFrame = new HashMap<>(); - + /** * Map of depth to maximum frame including buttons */ @@ -289,7 +289,7 @@ public class Timeline { ensureInitialized(); return depthMaxFrame; } - + /** * Gets map of depth to max frame including buttons * @@ -746,23 +746,23 @@ public class Timeline { for (int d = 0; d <= maxDepth; d++) { for (int f = frames.size() - 1; f >= 0; f--) { if (frames.get(f).layers.get(d) != null) { - depthMaxFrame.put(d, f); + depthMaxFrame.put(d, f); break; } } } - + if (timelined instanceof ButtonTag) { ButtonTag button = (ButtonTag) timelined; Set emptyFrames = button.getEmptyFrames(); - + for (int d = 0; d <= maxDepth; d++) { for (int f = frames.size() - 1; f >= 0; f--) { if (frames.get(f).layers.get(d) != null) { if (!emptyFrames.contains(f)) { depthMaxFrameButtons.put(d, f); break; - } + } } } } @@ -1657,11 +1657,13 @@ public class Timeline { Tag drawableTag = (Tag) drawable; RECT boundRect = drawable.getRect(); boolean createNew = false; - if (exporter.exportedTags.containsKey(drawableTag)) { - assetName = exporter.exportedTags.get(drawableTag); + SVGExporter.ExportKey exportKey = new SVGExporter.ExportKey(drawableTag, clrTrans, layer.ratio); + + if (exporter.exportedTags.containsKey(exportKey)) { + assetName = exporter.exportedTags.get(exportKey); } else { assetName = getTagIdPrefix(drawableTag, exporter); - exporter.exportedTags.put(drawableTag, assetName); + exporter.exportedTags.put(exportKey, assetName); createNew = true; } ExportRectangle rect = new ExportRectangle(boundRect); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java index a063dbfdb..5a96e29c5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java @@ -28,6 +28,15 @@ import java.awt.image.RescaleOp; */ public class ColorTransform implements Cloneable { + private int redAdd; + private int greenAdd; + private int blueAdd; + private int alphaAdd; + private int redMulti; + private int greenMulti; + private int blueMulti; + private int alphaMulti; + /** * Constructor. */ @@ -37,6 +46,7 @@ public class ColorTransform implements Cloneable { /** * Converts this color transform to RescaleOp. + * * @return RescaleOp */ public RescaleOp toRescaleOp() { @@ -46,6 +56,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to the given image. + * * @param src Source image * @return Transformed image */ @@ -55,6 +66,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to the given color. + * * @param color Color * @return Transformed color */ @@ -64,6 +76,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to the given color. + * * @param color Color * @return Transformed color */ @@ -76,6 +89,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to the given color. + * * @param color Color * @return Transformed color */ @@ -88,6 +102,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to the given color. + * * @param color Color * @return Transformed color */ @@ -100,6 +115,7 @@ public class ColorTransform implements Cloneable { /** * Applies this color transform to gradient records. + * * @param gradRecords Gradient records * @return Transformed gradient records */ @@ -118,6 +134,7 @@ public class ColorTransform implements Cloneable { /** * Gets red addition. + * * @return Red addition */ public int getRedAdd() { @@ -126,6 +143,7 @@ public class ColorTransform implements Cloneable { /** * Gets green addition. + * * @return Green addition */ public int getGreenAdd() { @@ -134,6 +152,7 @@ public class ColorTransform implements Cloneable { /** * Gets blue addition. + * * @return Blue addition */ public int getBlueAdd() { @@ -142,6 +161,7 @@ public class ColorTransform implements Cloneable { /** * Gets alpha addition. + * * @return Alpha addition */ public int getAlphaAdd() { @@ -150,6 +170,7 @@ public class ColorTransform implements Cloneable { /** * Gets red multiplier. + * * @return Red multiplier */ public int getRedMulti() { @@ -158,6 +179,7 @@ public class ColorTransform implements Cloneable { /** * Gets green multiplier. + * * @return Green multiplier */ public int getGreenMulti() { @@ -166,6 +188,7 @@ public class ColorTransform implements Cloneable { /** * Gets blue multiplier. + * * @return Blue multiplier */ public int getBlueMulti() { @@ -174,6 +197,7 @@ public class ColorTransform implements Cloneable { /** * Gets alpha multiplier. + * * @return Alpha multiplier */ public int getAlphaMulti() { @@ -182,6 +206,7 @@ public class ColorTransform implements Cloneable { /** * Merges this color transform with another one. + * * @param c Another color transform * @return Merged color transform */ @@ -245,4 +270,55 @@ public class ColorTransform implements Cloneable { throw new RuntimeException(); } } + + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + this.getRedAdd(); + hash = 97 * hash + this.getGreenAdd(); + hash = 97 * hash + this.getBlueAdd(); + hash = 97 * hash + this.getAlphaAdd(); + hash = 97 * hash + this.getRedMulti(); + hash = 97 * hash + this.getGreenMulti(); + hash = 97 * hash + this.getBlueMulti(); + hash = 97 * hash + this.getAlphaMulti(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ColorTransform)) { + return false; + } + final ColorTransform other = (ColorTransform) obj; + if (this.getRedAdd() != other.getRedAdd()) { + return false; + } + if (this.getGreenAdd() != other.getGreenAdd()) { + return false; + } + if (this.getBlueAdd() != other.getBlueAdd()) { + return false; + } + if (this.getAlphaAdd() != other.getAlphaAdd()) { + return false; + } + if (this.getRedMulti() != other.getRedMulti()) { + return false; + } + if (this.getGreenMulti() != other.getGreenMulti()) { + return false; + } + if (this.getBlueMulti() != other.getBlueMulti()) { + return false; + } + return this.getAlphaMulti() == other.getAlphaMulti(); + } + }