diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf18fe53..3ef4d9731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ All notable changes to this project will be documented in this file. - Generic tag editor - morphshape fill - show bitmapId for repeating bitmap fill, gradient matrix for focal gradient - Morphshape SVG export - focalPoint animation - Do not display lines with zero width +- Not updating Morphshape end bounds +- SVG import - linear gradients ### 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/importers/morphshape/ShapeForMorphExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/ShapeForMorphExporter.java index 90d5cc87d..5d67c8f1e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/ShapeForMorphExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/ShapeForMorphExporter.java @@ -309,7 +309,7 @@ public class ShapeForMorphExporter extends ShapeExporterBase { currentLineStyle = lineStyles.size(); LINESTYLE2 lineStyle = new LINESTYLE2(); lineStyle.width = (int) thickness; - lineStyle.color = new RGBA(color); + lineStyle.color = color == null ? null : new RGBA(color); lineStyle.pixelHintingFlag = pixelHinting; switch (scaleMode) { case "NONE": 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 37627c8a6..a86602222 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 @@ -1700,6 +1700,15 @@ public class SvgImporter { //svgTest("types-basicDOM-01-b"); } + private double angle(double x1, double y1, double x2, double y2) { + double dx = x2 - x1; + double dy = y2 - y1; + if (dx == 0 && dy == 0) { + return 0; + } + return Math.atan2(dy, dx); + } + private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) { if (fill == null || fillStyle == null) { return; @@ -1718,14 +1727,16 @@ public class SvgImporter { double x2 = parseCoordinate(lgfill.x2, 1); double y2 = parseCoordinate(lgfill.y2, 1); - Matrix xyMatrix = new Matrix(); - xyMatrix.scaleX = (x2 - x1) * SWF.unitDivisor; - xyMatrix.rotateSkew0 = (y2 - y1) * SWF.unitDivisor; - xyMatrix.rotateSkew1 = -xyMatrix.rotateSkew0; - xyMatrix.scaleY = xyMatrix.scaleX; - - Matrix gmatrix = new Matrix(); + Matrix tMatrix = new Matrix(); if (lgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) { + Matrix xyMatrix = new Matrix(); + xyMatrix.scaleX = (x2 - x1) * SWF.unitDivisor; + xyMatrix.rotateSkew0 = (y2 - y1) * SWF.unitDivisor; + xyMatrix.rotateSkew1 = -xyMatrix.rotateSkew0; + xyMatrix.scaleY = xyMatrix.scaleX; + + Matrix gmatrix = new Matrix(); + gmatrix.scaleX = (bounds.Xmax - bounds.Xmin) / SWF.unitDivisor; gmatrix.rotateSkew0 = 0; gmatrix.rotateSkew1 = 0; @@ -1734,23 +1745,53 @@ public class SvgImporter { gmatrix.translateY = bounds.Ymin; x1 *= bounds.getWidth(); y1 *= bounds.getHeight(); - } else { - gmatrix = new Matrix(fillStyle.gradientMatrix); - x1 *= SWF.unitDivisor; - y1 *= SWF.unitDivisor; - } + + Matrix zeroStartMatrix = Matrix.getTranslateInstance(0.5, 0); + Matrix scaleMatrix = Matrix.getScaleInstance(1 / 16384.0 / 2); + Matrix transMatrix = Matrix.getTranslateInstance(x1, y1); - Matrix zeroStartMatrix = Matrix.getTranslateInstance(0.5, 0); - Matrix scaleMatrix = Matrix.getScaleInstance(1 / 16384.0 / 2); - Matrix transMatrix = Matrix.getTranslateInstance(x1, y1); - - Matrix tMatrix = new Matrix(); - tMatrix = tMatrix - .concatenate(transMatrix) + + tMatrix = tMatrix.concatenate(transMatrix) .concatenate(gmatrix) .concatenate(xyMatrix) .concatenate(zeroStartMatrix) - .concatenate(scaleMatrix); + .concatenate(scaleMatrix); + } else { + Matrix gmatrix = new Matrix(fillStyle.gradientMatrix); + x1 *= SWF.unitDivisor; + y1 *= SWF.unitDivisor; + x2 *= SWF.unitDivisor; + y2 *= SWF.unitDivisor; + + Point a = new Point(-16384.0, 0.0); + Point b = new Point(16384.0, 0.0); + Point c = new Point(x1, y1); + Point d = new Point(x2, y2); + + if (!(a.equals(c) && b.equals(d))) { + double AdeltaX = b.x - a.x; + double AdeltaY = b.y - a.y; + + double BdeltaX = d.x - c.x; + double BdeltaY = d.y - c.y; + + double lenAB = Math.sqrt(AdeltaX * AdeltaX + AdeltaY * AdeltaY); + double lenCD = Math.sqrt(BdeltaX * BdeltaX + BdeltaY * BdeltaY); + + double rotation = angle(c.x, c.y, d.x, d.y) - angle(a.x, a.y, b.x, b.y); + + double scale = lenCD / lenAB; + + tMatrix = tMatrix + .concatenate(Matrix.getTranslateInstance(c.x, c.y)) + .concatenate(Matrix.getRotateInstance(rotation * 180 / Math.PI)) + .concatenate(Matrix.getScaleInstance(scale)) + .concatenate(Matrix.getTranslateInstance(-a.x, -a.y)) + ; + } + + tMatrix = tMatrix.concatenate(gmatrix); + } fillStyle.gradientMatrix = tMatrix.toMATRIX(); } else if (fill instanceof SvgRadialGradient) { SvgRadialGradient rgfill = (SvgRadialGradient) fill; 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 91d4b64c4..86ea7a0cd 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 @@ -371,7 +371,7 @@ public abstract class MorphShapeTag extends DrawableTag { } public void updateEndBounds() { - startBounds = SHAPERECORD.getBounds(endEdges.shapeRecords, morphLineStyles.getEndLineStyles(getShapeNum()), getShapeNum() == 2 ? 4 : 3, false); + endBounds = SHAPERECORD.getBounds(endEdges.shapeRecords, morphLineStyles.getEndLineStyles(getShapeNum()), getShapeNum() == 2 ? 4 : 3, false); } public void updateBounds() { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index e7eab8839..35d731d72 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -4430,19 +4430,26 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se replace(items, false); } - public boolean replaceMorphShape(MorphShapeTag morphShape, boolean create) { + public boolean replaceMorphShape(MorphShapeTag morphShape, boolean create, boolean fill) { File fileStart = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true, AppStrings.translate("dialog.morphshape.startShape")); if (fileStart == null) { return false; } - - - + DefineShape4Tag shapeStart = new DefineShape4Tag(morphShape.getSwf()); SWF.addTagBefore(shapeStart, morphShape); DefineShape4Tag shapeEnd = new DefineShape4Tag(morphShape.getSwf()); SWF.addTagBefore(shapeEnd, morphShape); + + shapeStart.shapeBounds = Helper.deepCopy(morphShape.startBounds); + shapeEnd.shapeBounds = Helper.deepCopy(morphShape.endBounds); + + if (morphShape instanceof DefineMorphShape2Tag) { + DefineMorphShape2Tag ms2 = (DefineMorphShape2Tag) morphShape; + shapeStart.edgeBounds = Helper.deepCopy(ms2.startEdgeBounds); + shapeEnd.edgeBounds = Helper.deepCopy(ms2.endEdgeBounds); + } File selfileStart = Helper.fixDialogFile(fileStart); byte[] dataStart = null; @@ -4460,9 +4467,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se try { Tag newStartTag; if (svgTextStart != null) { - newStartTag = new SvgImporter().importSvg(shapeStart, shapeEnd, svgTextStart, false); + newStartTag = new SvgImporter().importSvg(shapeStart, shapeEnd, svgTextStart, fill); } else { - newStartTag = new ShapeImporter().importImage(shapeStart, dataStart, 0, false); + newStartTag = new ShapeImporter().importImage(shapeStart, dataStart, 0, fill); } newStartTag.getTimelined().removeTag(newStartTag); @@ -4487,9 +4494,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Tag newEndTag; if (svgTextEnd != null) { - newEndTag = new SvgImporter().importSvg(shapeEnd, svgTextEnd, false); + newEndTag = new SvgImporter().importSvg(shapeEnd, svgTextEnd, fill); } else { - newEndTag = new ShapeImporter().importImage(shapeEnd, dataEnd, 0, false); + newEndTag = new ShapeImporter().importImage(shapeEnd, dataEnd, 0, fill); } newEndTag.getTimelined().removeTag(newEndTag); } @@ -4544,7 +4551,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true); } if (ti0 instanceof MorphShapeTag) { - return replaceMorphShape((MorphShapeTag) ti0, create); + return replaceMorphShape((MorphShapeTag) ti0, create, true); } if (ti0 instanceof DefineVideoStreamTag) { file = showImportFileChooser("filter.movies|*.flv", false); @@ -4741,8 +4748,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return false; } - if ((item instanceof ShapeTag) || (item instanceof MorphShapeTag)) { - Tag st = (Tag) item; + if (item instanceof MorphShapeTag) { + return replaceMorphShape((MorphShapeTag) item, false, false); + } + if (item instanceof ShapeTag) { + ShapeTag st = (ShapeTag) item; String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; File selectedFile = showImportFileChooser(filter, true); if (selectedFile != null) { @@ -4757,20 +4767,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } try { 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); - } - } + if (svgText != null) { + newTag = new SvgImporter().importSvg(st, svgText, false); + } else { + newTag = new ShapeImporter().importImage(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 b571e9059..3604f19a0 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -192,6 +192,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private JButton replaceShapeButton; private JButton replaceMorphShapeButton; + + private JButton replaceMorphShapeUpdateBoundsButton; private JButton replaceShapeUpdateBoundsButton; @@ -1310,10 +1312,20 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceMorphShapeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - mainPanel.replaceMorphShape((MorphShapeTag) mainPanel.getCurrentTree().getCurrentTreeItem(), false); + mainPanel.replaceMorphShape((MorphShapeTag) mainPanel.getCurrentTree().getCurrentTreeItem(), false, true); } }); replaceMorphShapeButton.setVisible(false); + + replaceMorphShapeUpdateBoundsButton = new JButton(mainPanel.translate("button.replaceNoFill"), View.getIcon("importmorphshape16")); + replaceMorphShapeUpdateBoundsButton.setMargin(new Insets(3, 3, 3, 10)); + replaceMorphShapeUpdateBoundsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + mainPanel.replaceMorphShape((MorphShapeTag) mainPanel.getCurrentTree().getCurrentTreeItem(), false, false); + } + }); + replaceMorphShapeUpdateBoundsButton.setVisible(false); replaceShapeUpdateBoundsButton = new JButton(mainPanel.translate("button.replaceNoFill"), View.getIcon("importshape16")); replaceShapeUpdateBoundsButton.setMargin(new Insets(3, 3, 3, 10)); @@ -1375,8 +1387,9 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditButtonsPanel.add(displayEditEditPointsButton); //displayEditButtonsPanel.add(fixPathsButton); displayEditButtonsPanel.add(replaceShapeButton); - displayEditButtonsPanel.add(replaceMorphShapeButton); displayEditButtonsPanel.add(replaceShapeUpdateBoundsButton); + displayEditButtonsPanel.add(replaceMorphShapeButton); + displayEditButtonsPanel.add(replaceMorphShapeUpdateBoundsButton); displayEditButtonsPanel.add(morphShowPanel); return displayEditButtonsPanel; } @@ -1737,10 +1750,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceImageAlphaButton.setVisible(showAlpha); replaceSpriteButton.setVisible(showSprite); replaceShapeButton.setVisible(showShape); - replaceMorphShapeButton.setVisible(showMorphShape); + replaceMorphShapeButton.setVisible(showMorphShape); morphShowPanel.setVisible(showMorphShape); displayEditEditPointsButton.setVisible(showShape || showMorphShape); replaceShapeUpdateBoundsButton.setVisible(showShape); + replaceMorphShapeUpdateBoundsButton.setVisible(showMorphShape); replaceSoundButton.setVisible(showSound); replaceMovieButton.setVisible(showMovie); prevFontsButton.setVisible(false); @@ -2333,6 +2347,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (displayEditTag instanceof MorphShapeTag) { replaceMorphShapeButton.setVisible(true); + replaceMorphShapeUpdateBoundsButton.setVisible(true); displayEditEditPointsButton.setVisible(true); } @@ -2384,8 +2399,9 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditSaveButton.setVisible(true); displayEditCancelButton.setVisible(true); replaceShapeButton.setVisible(false); - replaceMorphShapeButton.setVisible(false); + replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); + replaceMorphShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); mainPanel.setEditingStatus(); } @@ -2418,6 +2434,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceShapeButton.setVisible(false); replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); + replaceMorphShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); if ((displayEditTag instanceof MorphShapeTag) && (morphDisplayMode == MORPH_ANIMATE)) { @@ -2519,6 +2536,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceShapeButton.setVisible(false); replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); + replaceMorphShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); morphShowPanel.setVisible(false); @@ -2815,6 +2833,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (displayEditTag instanceof MorphShapeTag) { morphShowPanel.setVisible(true); replaceMorphShapeButton.setVisible(true); + replaceMorphShapeUpdateBoundsButton.setVisible(true); displayEditEditPointsButton.setVisible(true); } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index c775bb7ea..cf8f41f74 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -353,7 +353,7 @@ public class TagTreeContextMenu extends JPopupMenu { replaceNoFillMenuItem = new JMenuItem(mainPanel.translate("button.replaceNoFill")); replaceNoFillMenuItem.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent e) { mainPanel.replaceNoFillButtonActionPerformed(getCurrentItem()); } }); @@ -1046,6 +1046,7 @@ public class TagTreeContextMenu extends JPopupMenu { if (canReplace.test(it -> it instanceof MorphShapeTag)) { replaceMenuItem.setVisible(true); + replaceNoFillMenuItem.setVisible(true); } if (canReplace.test(it -> it instanceof DefineBinaryDataTag)) { @@ -3830,7 +3831,7 @@ public class TagTreeContextMenu extends JPopupMenu { remove = !mainPanel.replaceNoFill(tag); break; case MORPH_SHAPE: - remove = !mainPanel.replaceMorphShape((MorphShapeTag) tag, true); + remove = !mainPanel.replaceMorphShape((MorphShapeTag) tag, true, false); break; case FONT: remove = !mainPanel.fontEmbed(tag, true);