From 247b1c17a4a29ce36bbd4b389e6d9bc2bc0c6938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 22 Oct 2023 14:49:17 +0200 Subject: [PATCH] Added Replacing morphshapes (currently only same shape for start/end) Fixed miterLimitFactor is FIXED8 value in MORPHLINESTYLE2 --- CHANGELOG.md | 4 +- .../decompiler/flash/SWFInputStream.java | 2 +- .../decompiler/flash/SWFOutputStream.java | 2 +- .../flash/configuration/Configuration.java | 5 + .../flash/importers/ShapeImporter.java | 42 ++++- .../flash/importers/svg/SvgImporter.java | 178 ++++++++++++------ .../decompiler/flash/types/FILLSTYLE.java | 19 ++ .../flash/types/FILLSTYLEARRAY.java | 9 + .../decompiler/flash/types/GRADIENT.java | 11 ++ .../decompiler/flash/types/GRADRECORD.java | 9 + .../decompiler/flash/types/LINESTYLE.java | 9 + .../decompiler/flash/types/LINESTYLE2.java | 23 +++ .../flash/types/LINESTYLEARRAY.java | 17 ++ .../flash/types/MORPHLINESTYLE2.java | 4 +- .../com/jpexs/decompiler/flash/types/RGB.java | 6 + .../jpexs/decompiler/flash/types/RGBA.java | 9 + .../flash/types/SHAPEWITHSTYLE.java | 129 +++++++++++++ src/com/jpexs/decompiler/flash/gui/Main.java | 6 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 46 ++++- .../decompiler/flash/gui/PreviewPanel.java | 5 +- .../flash/gui/graphics/importmorphshape16.png | Bin 0 -> 5895 bytes .../flash/gui/graphics/importmorphshape32.png | Bin 0 -> 7849 bytes .../locales/AdvancedSettingsDialog.properties | 3 + .../AdvancedSettingsDialog_cs.properties | 3 + .../flash/gui/locales/MainFrame.properties | 19 +- .../flash/gui/locales/MainFrame_cs.properties | 19 +- .../flash/gui/tagtree/TagTreeContextMenu.java | 14 +- 27 files changed, 506 insertions(+), 87 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/importmorphshape16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/importmorphshape32.png diff --git a/CHANGELOG.md b/CHANGELOG.md index e265e03f5..8fc2b6917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,9 @@ All notable changes to this project will be documented in this file. - ActionScript Debugger - Call stack frames switching - view variables around call stack - ActionScript Debugger - Highlight lines of callstack - [#2105] GFX - Basic tag info -- Context menu items to create new tags (shape, sprite, image, movie, sound, binaryData) from files +- Context menu items to create new tags (shape, morphshape, sprite, image, movie, sound, binaryData) from files and using font embed dialog for fonts +- Replacing morphshapes (currently only same shape for start/end) ### Fixed - [#1306], [#1768] Maximizing window on other than main monitor @@ -22,6 +23,7 @@ All notable changes to this project will be documented in this file. - GFX - Fonts with stripped shapes - [#2104] Empty texts import - Centered start playing triangle (Playing on demand) +- miterLimitFactor is FIXED8 value in MORPHLINESTYLE2 ### Changed - Basic tag info panel always visible even when nothing to display (to avoid flickering) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index 1ab543905..1a3ca32bf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -3221,7 +3221,7 @@ public class SWFInputStream implements AutoCloseable { ret.noClose = (int) readUB(1, "noClose") == 1; ret.endCapStyle = (int) readUB(2, "endCapStyle"); if (ret.joinStyle == LINESTYLE2.MITER_JOIN) { - ret.miterLimitFactor = readUI16("miterLimitFactor"); + ret.miterLimitFactor = readFIXED8("miterLimitFactor"); } if (!ret.hasFillFlag) { ret.startColor = readRGBA("startColor"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index 2fc006c7f..cfc244081 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1773,7 +1773,7 @@ public class SWFOutputStream extends OutputStream { writeUB(1, value.noClose ? 1 : 0); writeUB(2, value.endCapStyle); if (value.joinStyle == LINESTYLE2.MITER_JOIN) { - writeUI16(value.miterLimitFactor); + writeFIXED8(value.miterLimitFactor); } if (!value.hasFillFlag) { writeRGBA(value.startColor); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 17f387619..ddc340207 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -967,6 +967,11 @@ public final class Configuration { @ConfigurationDefaultBoolean(true) @ConfigurationCategory("ui") public static ConfigurationItem displayAs3PCodePanel = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationName("warning.replace.morphshape") + @ConfigurationCategory("ui") + public static ConfigurationItem warningReplaceMorphShape = null; private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java index 21e85b4f1..c1248ec5d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java @@ -31,6 +31,7 @@ import com.jpexs.decompiler.flash.tags.DefineShapeTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.RECT; @@ -57,22 +58,55 @@ import java.util.logging.Logger; public class ShapeImporter { public Tag importImage(ShapeTag st, byte[] newData) throws IOException { - return importImage(st, newData, 0, true); + return importImage((Tag) st, newData, 0, true); } + public Tag importImage(MorphShapeTag mst, byte[] newData) throws IOException { + return importImage((Tag) mst, newData, 0, true); + } + + public Tag importImage(MorphShapeTag mst, byte[] newData, int tagType, boolean fill) throws IOException { + return importImage((Tag) mst, newData, tagType, fill); + } + public Tag importImage(ShapeTag st, byte[] newData, int tagType, boolean fill) throws IOException { + return importImage((Tag) st, newData, tagType, fill); + } + + private Tag importImage(Tag st, byte[] newData, int tagType, boolean fill) throws IOException { ImageTag imageTag = addImage(st, newData, tagType); st.setModified(true); - RECT rect = st.getRect(); + RECT rect = null; + int shapeNum = 0; + if (st instanceof ShapeTag) { + rect = ((ShapeTag) st).getRect(); + shapeNum = ((ShapeTag) st).getShapeNum(); + } + if (st instanceof MorphShapeTag) { + rect = ((MorphShapeTag) st).getRect(); + int morphShapeNum = ((MorphShapeTag) st).getShapeNum(); + if (morphShapeNum == 2) { + shapeNum = 4; + } else { + shapeNum = 3; + } + } if (!fill) { Dimension dimension = imageTag.getImageDimension(); rect.Xmax = rect.Xmin + (int) (SWF.unitDivisor * dimension.getWidth()); rect.Ymax = rect.Ymin + (int) (SWF.unitDivisor * dimension.getHeight()); } - SHAPEWITHSTYLE shapes = imageTag.getShape(rect, fill, st.getShapeNum()); - st.shapes = shapes; + SHAPEWITHSTYLE shapes = imageTag.getShape(rect, fill, shapeNum); + if (st instanceof ShapeTag) { + ShapeTag shapeTag = (ShapeTag) st; + shapeTag.shapes = shapes; + } + if (st instanceof MorphShapeTag) { + MorphShapeTag morphShapeTag = (MorphShapeTag) st; + shapes.updateMorphShapeTag(morphShapeTag, fill); + } return (Tag) st; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java index 9b43f0ea3..5e7035988 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java @@ -28,9 +28,11 @@ import com.jpexs.decompiler.flash.importers.ShapeImporter; import com.jpexs.decompiler.flash.importers.svg.css.CssParseException; import com.jpexs.decompiler.flash.importers.svg.css.CssParser; import com.jpexs.decompiler.flash.importers.svg.css.CssSelectorToXPath; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; import com.jpexs.decompiler.flash.tags.DefineShape4Tag; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; @@ -91,16 +93,33 @@ public class SvgImporter { private final Set shownWarnings = new HashSet<>(); - ShapeTag shapeTag; - + /** + * Shape or morphshape tag + */ + Tag shapeTag; + private Rectangle2D.Double viewBox; - + public Tag importSvg(ShapeTag st, String svgXml) { - return importSvg(st, svgXml, true); + return importSvg((Tag) st, svgXml, true); } + public Tag importSvg(MorphShapeTag mst, String svgXml) { + return importSvg((Tag) mst, svgXml, true); + } + public Tag importSvg(ShapeTag st, String svgXml, boolean fill) { + return importSvg((Tag) st, svgXml, fill); + } + + public Tag importSvg(MorphShapeTag mst, String svgXml, boolean fill) { + return importSvg((Tag) mst, svgXml, fill); + } + + private Tag importSvg(Tag st, String svgXml, boolean fill) { shapeTag = st; + + boolean morphShape = st instanceof MorphShapeTag; if (st instanceof DefineShape4Tag) { DefineShape4Tag shape4 = (DefineShape4Tag) st; @@ -115,8 +134,23 @@ public class SvgImporter { shapes.fillStyles.fillStyles = new FILLSTYLE[0]; shapes.lineStyles.lineStyles = new LINESTYLE[0]; - int shapeNum = st.getShapeNum(); - RECT rect = st.getRect(); + int shapeNum = 0; + RECT rect = null; + + if (st instanceof ShapeTag) { + shapeNum = ((ShapeTag) st).getShapeNum(); + rect = ((ShapeTag) st).getRect(); + } + if (st instanceof MorphShapeTag) { + int morphShapeNum = ((MorphShapeTag) st).getShapeNum(); + if (morphShapeNum == 2) { + shapeNum = 4; + } else { + shapeNum = 3; + } + rect = ((MorphShapeTag) st).getRect(); + } + int origXmin = rect.Xmin; int origYmin = rect.Ymin; @@ -193,17 +227,24 @@ public class SvgImporter { transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY); } - processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style); + processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape); } catch (SAXException | IOException | ParserConfigurationException ex) { Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex); } shapes.shapeRecords.add(new EndShapeRecord()); - st.shapes = shapes; - if (!fill) { - st.updateBounds(); + if (st instanceof ShapeTag) { + ShapeTag shape = (ShapeTag) st; + shape.shapes = shapes; + if (!fill) { + shape.updateBounds(); + } } + if (st instanceof MorphShapeTag) { + shapes.updateMorphShapeTag((MorphShapeTag) st, fill); + } + st.setModified(true); return (Tag) st; @@ -272,7 +313,7 @@ public class SvgImporter { } } - private void processSwitch(Element element, Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style) { + private void processSwitch(Element element, Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, boolean morphShape) { for (int i = 0; i < element.getChildNodes().getLength(); i++) { Node childNode = element.getChildNodes().item(i); if (childNode instanceof Element) { @@ -283,18 +324,18 @@ public class SvgImporter { if (childElement.hasAttribute("systemLanguage")) { String systemLanguage = childElement.getAttribute("systemLanguage"); if (systemLanguage.equals("en-us") || systemLanguage.equals("en")) { - processElement(childElement, idMap, shapeNum, shapes, transform, style); + processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape); return; } continue; } - processElement(childElement, idMap, shapeNum, shapes, transform, style); + processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape); return; } } } - private void processElement(Element element, Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style) { + private void processElement(Element element, Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, boolean morphShape) { if (element.hasAttribute("requiredExtensions") && !element.getAttribute("requiredExtensions").isEmpty()) { return; } @@ -303,25 +344,25 @@ public class SvgImporter { Matrix m = Matrix.parseSvgMatrix(element.getAttribute("transform"), 1, 1); Matrix m2 = m == null ? transform : transform.concatenate(m); if ("switch".equals(tagName)) { - processSwitch(element, idMap, shapeNum, shapes, transform, style); + processSwitch(element, idMap, shapeNum, shapes, transform, style, morphShape); } else if ("style".equals(tagName)) { processStyle(element); } else if ("g".equals(tagName)) { - processSvgObject(idMap, shapeNum, shapes, element, m2, newStyle); + processSvgObject(idMap, shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("path".equals(tagName)) { - processPath(shapeNum, shapes, element, m2, newStyle); + processPath(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("circle".equals(tagName)) { - processCircle(shapeNum, shapes, element, m2, newStyle); + processCircle(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("ellipse".equals(tagName)) { - processEllipse(shapeNum, shapes, element, m2, newStyle); + processEllipse(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("rect".equals(tagName)) { - processRect(shapeNum, shapes, element, m2, newStyle); + processRect(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("line".equals(tagName)) { - processLine(shapeNum, shapes, element, m2, newStyle); + processLine(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("polyline".equals(tagName)) { - processPolyline(shapeNum, shapes, element, m2, newStyle); + processPolyline(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("polygon".equals(tagName)) { - processPolygon(shapeNum, shapes, element, m2, newStyle); + processPolygon(shapeNum, shapes, element, m2, newStyle, morphShape); } else if ("defs".equals(tagName)) { processDefs(element); } else if ("title".equals(tagName) || "desc".equals(tagName) @@ -332,12 +373,12 @@ public class SvgImporter { } } - private void processSvgObject(Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style) { + private void processSvgObject(Map idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style, boolean morphShape) { for (int i = 0; i < element.getChildNodes().getLength(); i++) { Node childNode = element.getChildNodes().item(i); if (childNode instanceof Element) { Element childElement = (Element) childNode; - processElement(childElement, idMap, shapeNum, shapes, transform, style); + processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape); } } } @@ -372,7 +413,7 @@ public class SvgImporter { } } - private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List commands, Matrix transform, SvgStyle style) { + private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List commands, Matrix transform, SvgStyle style, boolean morphShape) { if ("nonzero".equals(style.getFillRule())) { if (shapeTag instanceof DefineShape4Tag) { @@ -387,14 +428,18 @@ public class SvgImporter { double x0 = 0; double y0 = 0; - StyleChangeRecord scrStyle = getStyleChangeRecord(shapeNum, style); - int fillStyle = scrStyle.fillStyle1; + StyleChangeRecord scrStyle = getStyleChangeRecord(shapeNum, style, morphShape); + int fillStyle = morphShape ? scrStyle.fillStyle0 : scrStyle.fillStyle1; int lineStyle = scrStyle.lineStyle; scrStyle.stateFillStyle0 = true; - scrStyle.stateFillStyle1 = true; + if (!morphShape) { + scrStyle.stateFillStyle1 = true; + } scrStyle.stateLineStyle = true; scrStyle.fillStyle0 = 0; - scrStyle.fillStyle1 = 0; + if (!morphShape) { + scrStyle.fillStyle1 = 0; + } scrStyle.lineStyle = 0; List newRecords = new ArrayList<>(); @@ -428,8 +473,13 @@ public class SvgImporter { StyleChangeRecord scr = new StyleChangeRecord(); if (fillStyle != 0) { - scr.stateFillStyle1 = true; - scr.fillStyle1 = fillStyle; + if (morphShape) { + scr.stateFillStyle0 = true; + scr.fillStyle0 = fillStyle; + } else { + scr.stateFillStyle1 = true; + scr.fillStyle1 = fillStyle; + } } if (lineStyle != 0) { scr.lineStyle = lineStyle; @@ -584,7 +634,7 @@ public class SvgImporter { shapes.shapeRecords.addAll(newRecords); } - private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { + private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { String data = childElement.getAttribute("d"); char command = 0; @@ -889,7 +939,7 @@ public class SvgImporter { // ignore remaining data as specified in SVG Specification F.2 Error processing } - processCommands(shapeNum, shapes, pathCommands, transform, style); + processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape); } private double calcAngle(double ux, double uy, double vx, double vy) { @@ -903,7 +953,7 @@ public class SvgImporter { return sign * Math.acos(ux * vx + uy * vy / (lu * lv)); } - private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { + private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { String attr = childElement.getAttribute("cx"); double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; @@ -913,10 +963,10 @@ public class SvgImporter { attr = childElement.getAttribute("r"); double r = attr.length() > 0 ? parseLength(attr, (viewBox.width + viewBox.height) / 2) : 0; - processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r); + processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r, morphShape); } - private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { + private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { String attr = childElement.getAttribute("cx"); double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; @@ -929,10 +979,10 @@ public class SvgImporter { attr = childElement.getAttribute("ry"); double ry = attr.length() > 0 ? parseLength(attr, viewBox.height) : 0; - processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry); + processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry, morphShape); } - private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry) { + private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry, boolean morphShape) { double sqrt2RXHalf = Math.sqrt(2) * rx / 2; double sqrt2Minus1RX = (Math.sqrt(2) - 1) * rx; double sqrt2RYHalf = Math.sqrt(2) * ry / 2; @@ -987,10 +1037,10 @@ public class SvgImporter { serz.command = 'Z'; pathCommands.add(serz); - processCommands(shapeNum, shapes, pathCommands, transform, style); + processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape); } - private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { + private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { String attr = childElement.getAttribute("x"); double x = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; @@ -1098,10 +1148,10 @@ public class SvgImporter { serz.command = 'Z'; pathCommands.add(serz); - processCommands(shapeNum, shapes, pathCommands, transform, style); + processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape); } - private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { + private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { String attr = childElement.getAttribute("x1"); double x1 = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0; @@ -1126,18 +1176,18 @@ public class SvgImporter { pathCommands.add(cer); - processCommands(shapeNum, shapes, pathCommands, transform, style); + processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape); } - private void processPolygon(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { - processPolyline(shapeNum, shapes, childElement, transform, style, true); + private void processPolygon(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { + processPolyline(shapeNum, shapes, childElement, transform, style, true, morphShape); } - private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) { - processPolyline(shapeNum, shapes, childElement, transform, style, false); + private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) { + processPolyline(shapeNum, shapes, childElement, transform, style, false, morphShape); } - private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean close) { + private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean close, boolean morphShape) { String data = childElement.getAttribute("points"); char command = 'M'; @@ -1187,7 +1237,7 @@ public class SvgImporter { pathCommands.add(serz); } - processCommands(shapeNum, shapes, pathCommands, transform, style); + processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape); } //Stub for w3 test. TODO: refactor and move to test directory. It's here because of easy access - compiling single file @@ -1671,12 +1721,16 @@ public class SvgImporter { } } - private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) { + private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style, boolean morphShape) { StyleChangeRecord scr = new StyleChangeRecord(); scr.stateNewStyles = true; scr.fillStyles = new FILLSTYLEARRAY(); - scr.stateFillStyle1 = true; + if (morphShape) { + scr.stateFillStyle0 = true; + } else { + scr.stateFillStyle1 = true; + } scr.stateLineStyle = true; SvgFill fill = style.getFillWithOpacity(); if (fill != null && fill != SvgTransparentFill.INSTANCE) { @@ -1690,10 +1744,18 @@ public class SvgImporter { //...apply in second step - applyStyleGradients } - scr.fillStyle1 = 1; + if (morphShape) { + scr.fillStyle0 = 1; + } else { + scr.fillStyle1 = 1; + } } else { scr.fillStyles.fillStyles = new FILLSTYLE[0]; - scr.fillStyle1 = 0; + if (morphShape) { + scr.fillStyle0 = 0; + } else { + scr.fillStyle1 = 0; + } } scr.lineStyles = new LINESTYLEARRAY(); @@ -1717,11 +1779,19 @@ public class SvgImporter { DefineShape4Tag shape4 = (DefineShape4Tag) shapeTag; shape4.usesNonScalingStrokes = true; } + if (shapeTag instanceof DefineMorphShape2Tag) { + DefineMorphShape2Tag morph2 = (DefineMorphShape2Tag) shapeTag; + morph2.usesNonScalingStrokes = true; + } } else { if (shapeTag instanceof DefineShape4Tag) { DefineShape4Tag shape4 = (DefineShape4Tag) shapeTag; shape4.usesScalingStrokes = true; } + if (shapeTag instanceof DefineMorphShape2Tag) { + DefineMorphShape2Tag morph2 = (DefineMorphShape2Tag) shapeTag; + morph2.usesScalingStrokes = true; + } } int swfCap = lineCap == SvgLineCap.BUTT ? LINESTYLE2.NO_CAP diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java index cb313dfe3..99bb5a55a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java @@ -152,4 +152,23 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ } } } + + public MORPHFILLSTYLE toMorphStyle() { + MORPHFILLSTYLE morphFillStyle = new MORPHFILLSTYLE(); + morphFillStyle.bitmapId = bitmapId; + if (bitmapMatrix != null) { + morphFillStyle.startBitmapMatrix = new MATRIX(bitmapMatrix); + morphFillStyle.endBitmapMatrix = new MATRIX(bitmapMatrix); + } + if (color != null) { + morphFillStyle.startColor = new RGBA(color); + morphFillStyle.endColor = new RGBA(color); + } + morphFillStyle.fillStyleType = fillStyleType; + if (gradient != null) { + morphFillStyle.gradient = gradient.toMorphGradient(); + } + + return morphFillStyle; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java index d634018fa..26751b13a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java @@ -55,4 +55,13 @@ public class FILLSTYLEARRAY implements NeedsCharacters, Serializable { } return modified; } + + public MORPHFILLSTYLEARRAY toMorphFillStyleArray() { + MORPHFILLSTYLEARRAY morphFillStyleArray = new MORPHFILLSTYLEARRAY(); + morphFillStyleArray.fillStyles = new MORPHFILLSTYLE[fillStyles.length]; + for (int i = 0; i < fillStyles.length; i++) { + morphFillStyleArray.fillStyles[i] = fillStyles[i].toMorphStyle(); + } + return morphFillStyleArray; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADIENT.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADIENT.java index 27d6629fa..814a9bb2b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADIENT.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADIENT.java @@ -62,4 +62,15 @@ public class GRADIENT implements Serializable { @SWFArray(value = "record") public GRADRECORD[] gradientRecords = new GRADRECORD[0]; + + public MORPHGRADIENT toMorphGradient() { + MORPHGRADIENT morphGradient = new MORPHGRADIENT(); + morphGradient.interPolationMode = interpolationMode; + morphGradient.spreadMode = spreadMode; + morphGradient.gradientRecords = new MORPHGRADRECORD[gradientRecords.length]; + for (int i = 0; i < gradientRecords.length; i++) { + morphGradient.gradientRecords[i] = gradientRecords[i].toMorphGradRecord(); + } + return morphGradient; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADRECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADRECORD.java index 5e7b85b80..947d02a06 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADRECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/GRADRECORD.java @@ -37,4 +37,13 @@ public class GRADRECORD implements Serializable { public float getRatioFloat() { return ((float) ratio) / 255.0f; } + + public MORPHGRADRECORD toMorphGradRecord() { + MORPHGRADRECORD morphGradRecord = new MORPHGRADRECORD(); + morphGradRecord.startColor = new RGBA(color); + morphGradRecord.endColor = new RGBA(color); + morphGradRecord.startRatio = ratio; + morphGradRecord.endRatio = ratio; + return morphGradRecord; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE.java index 320d762bc..1f36aba2e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE.java @@ -75,4 +75,13 @@ public class LINESTYLE implements NeedsCharacters, Serializable, ILINESTYLE { public void setWidth(int width) { this.width = width; } + + public MORPHLINESTYLE toMorphLineStyle() { + MORPHLINESTYLE morphLineStyle = new MORPHLINESTYLE(); + morphLineStyle.startColor = new RGBA(color); + morphLineStyle.endColor = new RGBA(color); + morphLineStyle.startWidth = width; + morphLineStyle.endWidth = width; + return morphLineStyle; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java index 6c0b22c73..b5574e614 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java @@ -142,4 +142,27 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE { public void setWidth(int width) { this.width = width; } + + public MORPHLINESTYLE2 toMorphLineStyle2() { + MORPHLINESTYLE2 morphLineStyle2 = new MORPHLINESTYLE2(); + morphLineStyle2.startWidth = width; + morphLineStyle2.endWidth = width; + morphLineStyle2.startCapStyle = startCapStyle; + morphLineStyle2.joinStyle = joinStyle; + morphLineStyle2.hasFillFlag = hasFillFlag; + morphLineStyle2.noHScaleFlag = noHScaleFlag; + morphLineStyle2.noVScaleFlag = noVScaleFlag; + morphLineStyle2.pixelHintingFlag = pixelHintingFlag; + morphLineStyle2.noClose = noClose; + morphLineStyle2.endCapStyle = endCapStyle; + morphLineStyle2.miterLimitFactor = miterLimitFactor; + if (color != null) { + morphLineStyle2.startColor = new RGBA(color); + morphLineStyle2.endColor = new RGBA(color); + } + if (fillType != null) { + morphLineStyle2.fillType = fillType.toMorphStyle(); + } + return morphLineStyle2; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java index f5a7d1d26..1eb209cb6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java @@ -87,4 +87,21 @@ public class LINESTYLEARRAY implements NeedsCharacters, Serializable { } return modified; } + + public MORPHLINESTYLEARRAY toMorphLineStyleArray() { + MORPHLINESTYLEARRAY morphLineStyleArray = new MORPHLINESTYLEARRAY(); + if (lineStyles != null) { + morphLineStyleArray.lineStyles = new MORPHLINESTYLE[lineStyles.length]; + for (int i = 0; i < lineStyles.length; i++) { + morphLineStyleArray.lineStyles[i] = lineStyles[i].toMorphLineStyle(); + } + } + if (lineStyles2 != null) { + morphLineStyleArray.lineStyles2 = new MORPHLINESTYLE2[lineStyles2.length]; + for (int i = 0; i < lineStyles2.length; i++) { + morphLineStyleArray.lineStyles2[i] = lineStyles2[i].toMorphLineStyle2(); + } + } + return morphLineStyleArray; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE2.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE2.java index 433e67a8d..24cf222cd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE2.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE2.java @@ -78,9 +78,9 @@ public class MORPHLINESTYLE2 implements Serializable { public static final int SQUARE_CAP = 2; - @SWFType(value = BasicType.UI16) + @SWFType(value = BasicType.FIXED8) @Conditional(value = "joinStyle", options = {MITER_JOIN}) - public int miterLimitFactor; + public float miterLimitFactor; @Conditional("!hasFillFlag") public RGBA startColor; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGB.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGB.java index ef1c3cfc4..4b0055500 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGB.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGB.java @@ -46,6 +46,12 @@ public class RGB implements Serializable { @SWFType(BasicType.UI8) public int blue; + public RGB(RGB color) { + this.red = color.red; + this.green = color.green; + this.blue = color.blue; + } + public RGB(int red, int green, int blue) { this.red = red; this.green = green; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGBA.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGBA.java index 06886ac0b..2624db49d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGBA.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/RGBA.java @@ -72,6 +72,15 @@ public class RGBA extends RGB implements Serializable { super(rgb); alpha = (rgb >> 24) & 0xFF; } + + public RGBA(RGB color) { + super(color); + if (color instanceof RGBA) { + alpha = ((RGBA) color).alpha; + } else { + alpha = 255; + } + } public RGBA() { } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java index e43209712..8c3bbbce8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java @@ -17,9 +17,13 @@ package com.jpexs.decompiler.flash.types; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -104,4 +108,129 @@ public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializab return SHAPERECORD.getBounds(shapeRecords, lineStyles, shapeNum, false); } + public void updateMorphShapeTag(MorphShapeTag morphShapeTag, boolean fill) { + morphShapeTag.startEdges.shapeRecords.clear(); + morphShapeTag.endEdges.shapeRecords.clear(); + + FILLSTYLEARRAY mergedFillStyles = new FILLSTYLEARRAY(); + LINESTYLEARRAY mergedLineStyles = new LINESTYLEARRAY(); + + List mergedFillStyleList = new ArrayList<>(); + List mergedLineStyleList = new ArrayList<>(); + List mergedLineStyle2List = new ArrayList<>(); + + int lastFillCount = fillStyles.fillStyles.length; + + for (int i = 0; i < fillStyles.fillStyles.length; i++) { + mergedFillStyleList.add(fillStyles.fillStyles[i]); + } + + int lastLineCount = 0; + + if (lineStyles.lineStyles != null) { + lastLineCount = lineStyles.lineStyles.length; + for (int i = 0; i < lineStyles.lineStyles.length; i++) { + mergedLineStyleList.add(lineStyles.lineStyles[i]); + } + } + if (lineStyles.lineStyles2 != null) { + lastLineCount = lineStyles.lineStyles2.length; + for (int i = 0; i < lineStyles.lineStyles2.length; i++) { + mergedLineStyle2List.add(lineStyles.lineStyles2[i]); + } + } + + int fillOffset = 0; + int lineOffset = 0; + List newShapeRecords = new ArrayList<>(); + for (int r = 0; r < shapeRecords.size(); r++) { + SHAPERECORD rec = shapeRecords.get(r); + rec = Helper.deepCopy(rec); + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) rec; + if (scr.stateNewStyles) { + for (int i = 0; i < scr.fillStyles.fillStyles.length; i++) { + mergedFillStyleList.add(scr.fillStyles.fillStyles[i]); + } + fillOffset += lastFillCount; + lastFillCount = scr.fillStyles.fillStyles.length; + if (scr.lineStyles.lineStyles != null) { + for (int i = 0; i < scr.lineStyles.lineStyles.length; i++) { + mergedLineStyleList.add(scr.lineStyles.lineStyles[i]); + } + lineOffset += lastLineCount; + lastLineCount = scr.lineStyles.lineStyles.length; + } + if (scr.lineStyles.lineStyles2 != null) { + for (int i = 0; i < scr.lineStyles.lineStyles2.length; i++) { + mergedLineStyle2List.add(scr.lineStyles.lineStyles2[i]); + } + lineOffset += lastLineCount; + lastLineCount = scr.lineStyles.lineStyles2.length; + } + scr.stateNewStyles = false; + } + if (scr.stateFillStyle0) { + scr.fillStyle0 += fillOffset; + } + if (scr.stateFillStyle1) { + scr.fillStyle1 += fillOffset; + } + if (scr.stateLineStyle) { + scr.lineStyle += lineOffset; + } + } + newShapeRecords.add(rec); + } + + mergedFillStyles.fillStyles = new FILLSTYLE[mergedFillStyleList.size()]; + for (int i = 0; i < mergedFillStyleList.size(); i++) { + mergedFillStyles.fillStyles[i] = mergedFillStyleList.get(i); + } + mergedLineStyles.lineStyles = new LINESTYLE[mergedLineStyleList.size()]; + for (int i = 0; i < mergedLineStyleList.size(); i++) { + mergedLineStyles.lineStyles[i] = mergedLineStyleList.get(i); + } + mergedLineStyles.lineStyles2 = new LINESTYLE2[mergedLineStyle2List.size()]; + for (int i = 0; i < mergedLineStyle2List.size(); i++) { + mergedLineStyles.lineStyles2[i] = mergedLineStyle2List.get(i); + } + + + morphShapeTag.morphFillStyles = mergedFillStyles.toMorphFillStyleArray(); + morphShapeTag.morphLineStyles = mergedLineStyles.toMorphLineStyleArray(); + SHAPE startShapes = new SHAPE(); + startShapes.numFillBits = SWFOutputStream.getNeededBitsU(mergedFillStyleList.size()); + startShapes.numLineBits = SWFOutputStream.getNeededBitsU(mergedLineStyleList.size() + mergedLineStyle2List.size()); + startShapes.shapeRecords = newShapeRecords; + morphShapeTag.startEdges = startShapes; + + SHAPE endShapes = new SHAPE(); + endShapes.numFillBits = 0; + endShapes.numLineBits = 0; + List endRecords = new ArrayList<>(); + for (int i = 0; i < newShapeRecords.size(); i++) { + SHAPERECORD rec = newShapeRecords.get(i); + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) rec; + if (scr.stateMoveTo) { + StyleChangeRecord nscr = new StyleChangeRecord(); + nscr.stateMoveTo = true; + nscr.moveDeltaX = scr.moveDeltaX; + nscr.moveDeltaY = scr.moveDeltaY; + nscr.calculateBits(); + endRecords.add(nscr); + } + } else { + endRecords.add(Helper.deepCopy(rec)); + } + } + endShapes.shapeRecords = endRecords; + morphShapeTag.endEdges = endShapes; + + if (!fill) { + morphShapeTag.updateBounds(); + } + } + } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 2f0fe453a..c87f7f766 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -826,7 +826,11 @@ public class Main { statusTimer.schedule(new TimerTask() { @Override public void run() { - if (mainFrame != null && mainFrame.getPanel().getStatusPanel().isStatusHidden()) { + if (mainFrame == null) { + return; + } + MainFrameStatusPanel sp = mainFrame.getPanel().getStatusPanel(); + if (sp != null && sp.isStatusHidden()) { long nowTime = System.currentTimeMillis(); if (nowTime > lastTimeStartWork + 5000) { mainFrame.getPanel().showOldStatus(); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 003e03c8d..0a919c411 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -4441,6 +4441,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (ti0 instanceof ShapeTag) { file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true); } + if (ti0 instanceof MorphShapeTag) { + if (ViewMessages.showConfirmDialog(this, AppStrings.translate("warning.replace.morphshape"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE, JOptionPane.WARNING_MESSAGE, Configuration.warningReplaceMorphShape, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { + return false; + } + file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true); + } if (ti0 instanceof DefineVideoStreamTag) { file = showImportFileChooser("filter.movies|*.flv", false); } @@ -4520,8 +4526,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se reload(true); } } - if (item instanceof ShapeTag) { - ShapeTag st = (ShapeTag) item; + if ((item instanceof ShapeTag) || (item instanceof MorphShapeTag)) { + Tag st = (Tag) item; File selfile = Helper.fixDialogFile(selectedFile); byte[] data = null; String svgText = null; @@ -4532,7 +4538,21 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se data = Helper.readFile(selfile.getAbsolutePath()); } try { - Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText) : new ShapeImporter().importImage(st, data); + Tag newTag = null; + if (st instanceof ShapeTag) { + if (svgText != null) { + newTag = new SvgImporter().importSvg((ShapeTag) st, svgText); + } else { + newTag = new ShapeImporter().importImage((ShapeTag) st, data); + } + } + if (st instanceof MorphShapeTag) { + if (svgText != null) { + newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText); + } else { + newTag = new ShapeImporter().importImage((MorphShapeTag) st, data); + } + } SWF swf = st.getSwf(); if (newTag != null) { refreshTree(swf); @@ -4622,8 +4642,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return false; } - if (item instanceof ShapeTag) { - ShapeTag st = (ShapeTag) item; + if ((item instanceof ShapeTag) || (item instanceof MorphShapeTag)) { + Tag st = (Tag) item; String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; File selectedFile = showImportFileChooser(filter, true); if (selectedFile != null) { @@ -4637,7 +4657,21 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se data = Helper.readFile(selfile.getAbsolutePath()); } try { - Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText, false) : new ShapeImporter().importImage(st, data, 0, false); + Tag newTag = null; + if (st instanceof ShapeTag) { + if (svgText != null) { + newTag = new SvgImporter().importSvg((ShapeTag) st, svgText, false); + } else { + newTag = new ShapeImporter().importImage((ShapeTag) st, data, 0, false); + } + } + if (st instanceof MorphShapeTag) { + if (svgText != null) { + newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText, false); + } else { + newTag = new ShapeImporter().importImage((MorphShapeTag) st, data, 0, false); + } + } SWF swf = st.getSwf(); if (newTag != null) { refreshTree(swf); diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index efe30e35d..23306f6ae 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1718,14 +1718,15 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel showSound = false; showMovie = false; showSprite = false; + showMorphShape = false; } replaceImageButton.setVisible(showImage); replaceImageAlphaButton.setVisible(showAlpha); replaceSpriteButton.setVisible(showSprite); - replaceShapeButton.setVisible(showShape); + replaceShapeButton.setVisible(showShape || showMorphShape); morphShowPanel.setVisible(showMorphShape); displayEditEditPointsButton.setVisible(showShape || showMorphShape); - replaceShapeUpdateBoundsButton.setVisible(showShape); + replaceShapeUpdateBoundsButton.setVisible(showShape || showMorphShape); replaceSoundButton.setVisible(showSound); replaceMovieButton.setVisible(showMovie); prevFontsButton.setVisible(false); diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/importmorphshape16.png b/src/com/jpexs/decompiler/flash/gui/graphics/importmorphshape16.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6e40d07c824d1ba16fa404c61176cfe88e74d4 GIT binary patch literal 5895 zcmeHKc{G%58y`uS6j8QDrlCk?pJ8S)_OZlhF{C%!V-_Z6F*6ujMX5we-cXiGdrEJM zB1t77OPlJItq{?hQ4)o>@0n?N>wMoi-|3w1`>#3YnR%Y;{$0QOx_ciRM?L`%h{~xrhM#0L5Ca#A1RQPz8!m|zvf*qgmji)F zpB`Pq-7{jYF?DxIpPFt+W=8dIMC;t1yVaYrNcB%1;iE5SrdVz%)D3xRw=rf!4;4Ou zX?9gNyqVN+(mT(-4trK&J0R>ASHkk7&-21>CfDf4UR&<>Hg833+T!I0C$kVs!Vy(o zC)Ry@ajdR6X&Y^GiAN@i7STT)slw&IQnPpyJF~p$+)zQ?q;=2p-ba639#fO~?27vH z>Frypg^kgM*Lx1kI*v^`o}#6}Q8jcl3(CQfdb19c%oH8d^zLvyq{l$*upMbKLWbt3 zb>+iK-k5aluA&c@W+Vo-)))P?w5!6zD~;c2{Hb!yK!4%6wBBj{MvsrZ_w|B`3_Q}0 z)?B{`DNw?i>erqrQ&|ezoiZmzgwv0u5iGt$9e=gRTdybTi$GLw1RXO}0aRGcoGJ{vs0rt?fIBh>v4TvrXct#iip_mpncSLEU_? z;I@{s(m|KMmfV|jW~a082;|`Ohsb4lNsw5<()5cNWXSw;Om2eqz}(W#WZrdGtg?2Y zvTN|+d`PxX3@{L{KJP`AQICEThe=WiZU?g>wYwxp44C-L(a#t#7t1cxgi>I22m8w5<;tzIW_W z`nCFT0ph*@s(B&RE<-7};3=e6Z_WtsCg16?8|^ZV^eT#<-q$Ss;MZ++veR1H?s7r> ze1lnhdd`ybCr<_W-U_>xD~dh-2z{*G*Gf~TJ^SVSd8hKAAC+Mbi_Bblry^g*tSkH7 zK!>`(h#Pe|Y^|d!In`A7nz2fERWxQV)hEA=bjx!z-K4Ah7UN~pMGx)P2}A9cn{%B) zXcxr=!}0e`&%Qj>uCAXDd4~sG%c{}1vXl9^`&h}o@Um^{#R>Ex-L1PW$zU1Nop9?U z$6wU`!P=a7WFUWeZ2|LUr29yw*C}-`E#mQQNuq?*{@O6B^G|iptJFPCW!VZ1%g;>u zjqkjV=so?nyb2^kANW=jxyfw3OfSl@_eK zjy*;!6mSWEHFP8k8;{ z%nw_&?%vSb1lq2|X#*o+g;$>Wd>m;;n|6v$|9N6jS!pv#{ew>%VN+u<$Mr$N$@`1u zxLxcmZ6dt?bIstv#!V4=?;ZuVe)8TIoKM!B*Vg=MJmDAyPuTTB6pi4G*@iUJs{h*=s zlK=Yj^Kli`isC0vWR-u5Wye=r6<;m5Q&P0A>tmhweXA{rWxB9Qztfa5(yPH|7HY}H@Dh~Q4%F)IPkS8G^ffak-VAm23>m=* z7M{e4#Soth=?t629C2~Tvw030@J-mp<&7;m12q~pwNI!C36*JYEoI`38G(xgxa(%x zcd;FRY`5MXYK1Hqv5m~#Ynn2iFKs_KQ`=4y5dC4N_&$&nx^<`L`a+VYrS3AnpakygL1q^XuaY~5&SQ>9*IwUFJ;M&WtWz05yA>{AS zXUoGUif0AC&w3ZPE)AxX*mk`pYF*aYrUs+nVfP%3`pii}ildU$?y%sF($r;pVbkj~ zrMn)g!kq$;T@cl6*TXW;KcRMZJZx>N3jJ+r3er6+YWaBj(S7js8qEb?qkeQx3RA#C z0W3iX8ztol!PhJVVq+&20?cr>1RlZ;o`S{v<}ZvAMJUo? zG12S@HV=e~!CA3COzG-QU-`{LE?@cmH>PvTTTU%qqras5#V5096SL@vL*va0*+&aBoWv|B$33#S>uQ- z76C*4j$(y~3sxl%@qJWsDi%nEx8^WOcyb65Aah7a0*Qo0lGy|zl0{-!aflcufj|Nj zR4gXNMIhn<;Bs<#Kqwn6 z9@|?2$o0fpk*&$r)?@+(kHO)riT{9n*&;Dmi*i;h28G8dM&t`a0n-7c1>}_q5-99o zHWVij8;}S@41pkmijao@mwSF2rh^-b1xNsAK*9z|F*rO0i=p6f3@m|y$5F84r5GFq z^BrEm;&NjD8(O}7;5MHZ-Hj^-=Z{sGKJO`ScI4-$&yNvY#b$!T72ARWFh8dt2BO(4 zMVuhl=OJbozz=1E{o_l)ezkM|LotMqa4ZbR${I-q$V?=GiRB;x9G-*ZSg}|v0M8*{ z*qk5P#R85b1`x3wLO~rtt-u0RXazU_vQ$fc#K(lOHHr*U)JIOI0H!iYm%SR_m^D1l~RfT)6np7?BOe~Z1lFT#YjB)Vnu-Rd;mqujyKI3qk$HX>VVKjiid*JzJRc2Fyt z>h--SOg6tWRuWW^zAQIspUFu3(y`;!f0!?H-!pkBQN^GvHt>;#y64u#yQ^BS_KFVQ z&o%0=nK-m!>Ar+)Q@kf!e7>0u-J#LaaPD4y~bO8?4P`h^v!zEOUkj z;tMKk1#e|7Z*w$vWjb?b53ktrjD>UTV~HFw*N#L*TSb#?xRKHaA)(BiP~#`NZ~4dD zZcH3>*qV|GyRky>$7ony&-2Cm;M4Bp1oNWu&S>U2Hy9pidmVSiw`ZVuVsiY#-HtXP zzF8@>>KQy~@6woTD}C<%E2hS?w{^w?N)u5)LPVM*kPu1&0hA5~kuE(A3_=o;P^E;bQk0?y(ov8is1!vI zrHB+k1(n`JR1m2u2#CCZ-g~|Celu_G%=`U!Gjnpz+521HUhCUyuQ}_yg}D(Q_kL~w z0KjKrtZ&J@1GlanoXme(P|z0ufIB6~+MaHS@dr|4iOtf;IG z6Y4d!df-vW{;6!lK@d3M_@mBRrxnfx~pL8F`8S};JR0?`%gWBhnkgobdm6Ma_J@=z^-$&~W z2oY|nZ_5>XW@5YJyI{PC^#ZYTQ8ck?C(h7NoiN@$_K237UQ#{KPrvc`A;;Pyes5a#?l3iJs{Nr}a7FZ0n|2L;Q^ z@92{>=k(lxCoIvdYAC*jj{D;}$zs}YQ-%ANEaNWO2JKUQ?POMFQ<5~1C_P}3qyR$K z#-}=v3SP^URgW58L>;+W&?tg$Wf2A9N>G@rtB51&f&bnj(^$Xw-;LMh~= z_g7W8r{y=j(pa@wq+fqc&$GHuwS;>kD;hO}CUcWk<|yjmw8sOBI?Tq>2H&fu1cwKS z6&IF{Ce(qROGR{>Ksy17wc@%?ysYBL{q7H=Gp|W`q^<7SC=fh}hjzk6vA{I{bMK;L z{UF->s*^v2Najmi!xK5654^Qw`g4a#V|j(CF}>0K2f4e7v;{({=I;-Q-#9&a{iVvX z%d*M+hQyv7N$VeVoND@Pdf|nzzS!#3s)7ss-wxXS=;*G0Nt6%5K0DHJYwiwj(!;eEj( z7|B(K-ctkFmGwWC?ue)9rD{$in@wr*_pcu)Gbw9+@_G3Z)zT`7J8DLe!?P>nU3ny~ zz1)khb;mBreM5}BsewmX-*#V|P>A5u4SW+sd>&}Mv-OK-f?a7vipnyAau+rKT(oE1 zq`u#wG)7VJ?LzSB{6k=o+Z0;%NX^&2q%8FC;n}pJu#hxTN1H%oXJ2z}tR1Y(vqr3? z_>T&~-PnPd7oA2VyulLeRyf7oJHIZFB!PlGC z^n|=>x`|Q%4>j`_x;>IN#j!m@hfO|Nyli&WQLB-3)K>eF3?Bt|=A?y|@?Jb&ioOM@ zuH_oa?3cyJYNiJBE30JDUp;^KoaDh3=d@ zVL9j{4`9Uv2TEu&?BKMPD*}QUlDN}Z`cs3mZcl0*gFl_(&LlZfH!Hq|-GBWg6B|fK zzR-5Widv#!Bx9vJxNk|uz(;(ZKUoAadEk{+pZSZrq$)znboOcsB)xq3WNKgVzDRFH z#kd~J2tuNCE0G*7ILdmcB!v=t7j)-RIVMHl=EU^9y-}QZ46dCnjT~$k?cc0(oV!61 z4vmudn6H=%5qjAA*rw9yK(3?~;2}k+X<<{C+f_6t>D(b>iPZjj)=f=6+{Yd813Bu z=XAH#**Ck{`TE38SC&c%gc`Z$+PkOKTsa1Mv9z)0!6yV@cHh%IW}ne30&96qZ+3rI zsH*5Vl9$OMedI-cYCxeHwmX2ameQta^r9}!^_7G(&wV`XJXQ4)U?_v4FlxT?L=E;Me7QmwjO57z^^}s>`Z$A@R1W7SwoS zGuvj0PNvPZg@m}Wfwc~<$UCnFd>kVf7>VJw3qBk{8GRD*hVQKGHAE7x#;!^oYb-X~oExn|4p!rupj|J#QUE@Ab}?=s9G{ON0%3%~a$lxHz4WbxyYj z*}NHe;wG}<>2R4<`%U22P8H3jlrZ)?4F#`DrroH11CH$YSI{a+AfwEG_uMO|)^*{< z!~(JP@QX@8;k+nt2?KQ3C*Fl;&@;tB_&M#hYyT86?*ytdI`N(xREZ(5Bd&7ylJM8l z;I9J_^==b*^A2<2yp>}w@*(d> z&&N{*I1#V&_2&aq<;5h2g-?(33te^qUhTE#;p1$XSQwF!c6c_ueDwU)klJ9Z)?|YF zYI9NgSAlcmq@$1JZn)eYtXK#1dQjuUG{;OO>CP1}o-+OG03OG1c@raku!TTr5_j`^ za#N?ShP^y>?%9qujqiFNmv`wUs~K}#iohb<#b4NdKNf==Wm)^&J#xeeHtfkZEt9dUbW)P;Dfon4OAt%AUxe zbDXHFm6En93V$c7Iui)nv-)!MV_MDeN~iP>J$yPrjP=>QoMFgB@rb9R)0=$VRmm^E z6i-F~04&i&Jv|E(J-t6ZPnjR3nITCi<7Qp)7FVkRONCt`0zPdPEsYUH=dM}b*&$-; zm}kS4V)iK4L@#+_Las(c{JZ^4WR1qm$8m)n5t6`ihqi^RV zfN+Ils&z5|o62}=Eg5%ODRz9%5ndnz+G*PPN@%);SF5!T6&ze2x1=~v3%lTQhzx6x z6?_gIxqVLkoQE1X>$A2`+GXkJZdaSa$x9@hr`AOU}Ay#YVGFhZe}0hM`ZR3_UL0s9N8O! z!IQBBNPstm*-rofS~>v~4DK|64#W~Xh$IwfzU~nSNW`N+cB*J)G)0eaif9}}C0GTS zTjPRG;}Cd|jyAVe0Fp`IO`v0d0p4CD8ZrO{+U7+vpSOmUK)`JX{WJ<>kG25nk*NeA z90G?ZgAD?RelUeAL6e9AyYk+pa=v)Ng1XD zgMpa{FfEWo#{_^$w1Zm|KRNUXG#r&kp%cj@;1(waOZKIsKp^Hk@DKmIDQNVc^d#CZ z6_|P`1z;#jP>8aUx3|*o7BsqnACu&lL;ul&X3cE7lq?A}vM&`!Fz_Rg=m&qNz~lb3 zr}$F6w!^{Wln7n~Zzhz+TowAaC5=qb7Jpi7N#H^Brfgd=#r_+TPIUiEtiSoTHM1Sg z?~X9d|K$A}`j6PR!AukyjnpUOe7D>)(MN%{)<@#WI3gbT^HIYc3Ri(CgRywH8W>K% zK*3lX0t3dvFzz^IJl0(ei~WttghZobNI1e46_Xr7Wb&xOaX4i-LKUo`s-nWAP$7Ud z)Z7VRRV-8)qoN9jBh(4MQ5>fdnOTYP`rWH7Dm;_QT}2sz(O~8TK}|y)49CFnULL~o1-L5V{0*q+!D9C^gT1OZvB@WRmD znFh==KqTSG{`ONPpBF~9igs{P=mu%HJ}Lfzd*JGDvg)zj2{8N?I)A#=M?S~hU7tD){kEa`^Px(KO_SNr-4wxA`oB| zth*{0j(}r+CIfp%RXGFm+^V#Z1s`t$=dBwo3l*c>hy` zt$koh28Jqw|B?(2Qu-CF(pJUzBU&w`|D}i4HsH4s!?gQ3#w=aTN~rW_Dg33^R?+z{ z{(kMle{lsS^}mb!BYpps>tDJ4kplk+{BL&sE7w0#;2(ki&9479xw!vorU)eF7mz=* zRq7||K4rF9oLDm>{q2S;46)kR!+hFFF+NEH0Cb!FFAfOiU^gk_-th-IG7vbW1m(g&)U!(Xd!$Vm>SMI#N(UF1ujB684-w=D}pE>|$r4pJ99;%oc z0LsqjYN&4jzi9DU+Jz?w7wuykHV~2X_5`(A3CeST2U}S>kWMOq0_)jj1;RIr*nMpM zCEv@n8JCuN8ya*w1^~x7MJq+wPl1~h({?hvYA)x7Q(axhmTXFG!Qd-qja@UFsjohp zEwhRiV?VEx0K;(XxXw6LJLl2<0+SCfm7+?k1XtLByq+iaHgJ`*sUHNm8fd??9903# z=r=1;8ooUdYm@VjCpmAn^B)mn6hC#E3_rs@&XqcnzT42uR%$W}WT@_N3LocqyE^#u zM(DHe_xCW)BJHde`zi=S%O5q_*w|3I86?p>)2iF+2=h$I$G0zUOm6@tV|qWgMuz#U z_O;f`YKr*!3+*{#GScRsZCT?U2yg%PBK>(HRi*wZ7a~}jPj{XcStCUBJ23MhYp+gu zu?YU)&MLu|*``hHm5~}7BK3#4+Fgy$3KU9(NulREj=a~RFh1GMW6R10lYGnA3{Ahl z3)!hgy|{Yyf!kOmqq8q}u=ViVP^Qo|x?=X6N!OeQIIfhH2C3Py3S=>|llo-@?N6^x zj(durl0CXq_FdSl5ST_6cknkXnu(bxJzr?Kw-Ky;e*>|wY7O|&N2V@t_tW6PK1-!i#MTLF(atG(^FHxD^GnAFJceL zWe-=+@#U6g>mLW7{Jv1DdTVy5!dvr*MdQj-`_u9waW}iX^T!PZN^Bz{#0*%SomWy! zeS3O)IVqaYbXjUxzgyinVEcKmVy1J2f@c@pboy! it instanceof ShapeTag)) { + if (canReplace.test(it -> (it instanceof ShapeTag) || (it instanceof MorphShapeTag))) { replaceMenuItem.setVisible(true); replaceNoFillMenuItem.setVisible(true); } @@ -1655,6 +1657,15 @@ public class TagTreeContextMenu extends JPopupMenu { }); addTagMenu.add(createBinaryDataItem); break; + case TagTreeModel.FOLDER_MORPHSHAPES: + addTagMenu.addSeparator(); + JMenuItem createMorphShapeItem = new JMenuItem(AppStrings.translate("tag.morphshape.create")); + createMorphShapeItem.setIcon(View.getIcon("importshape16")); + createMorphShapeItem.addActionListener((ActionEvent ae) -> { + listener.call(ae, item, DefineMorphShape2Tag.class, TreeNodeType.MORPH_SHAPE); + }); + addTagMenu.add(createMorphShapeItem); + break; } } @@ -3812,6 +3823,7 @@ public class TagTreeContextMenu extends JPopupMenu { remove = !mainPanel.replaceSpriteWithGif(tag); break; case SHAPE: + case MORPH_SHAPE: remove = !mainPanel.replaceNoFill(tag); break; case FONT: