diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc2b6917..866820fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ All notable changes to this project will be documented in this file. - [#2105] GFX - Basic tag info - 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) +- Replacing morphshapes ### Fixed - [#1306], [#1768] Maximizing window on other than main monitor @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. - [#2104] Empty texts import - Centered start playing triangle (Playing on demand) - miterLimitFactor is FIXED8 value in MORPHLINESTYLE2 +- Display of morphshape end shape to be exactly at 65535 ratio ### 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/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index ddc340207..a550ce110 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 @@ -966,12 +966,7 @@ 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; + public static ConfigurationItem displayAs3PCodePanel = null; private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CurvedEdge.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CurvedEdge.java index a6d0ec54e..dceb09558 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CurvedEdge.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CurvedEdge.java @@ -45,9 +45,19 @@ public class CurvedEdge extends StraightEdge implements IEdge { return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, newFillStyleIdx); } + @Override + public IEdge sameWithNewFillStyle(int newFillStyleIdx) { + return new CurvedEdge(fromX, fromY, controlX, controlY, toX, toY, lineStyleIdx, newFillStyleIdx); + } + + @Override public String toString() { return "curved[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + " control:" + controlX / 20f + "," + controlY / 20f + "]"; } + @Override + public IEdge reverse() { + return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, getFillStyleIdx()); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/IEdge.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/IEdge.java index 1540275c4..829784bac 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/IEdge.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/IEdge.java @@ -35,4 +35,8 @@ public interface IEdge { public int getFillStyleIdx(); public IEdge reverseWithNewFillStyle(int newFillStyleIdx); + + public IEdge reverse(); + + public IEdge sameWithNewFillStyle(int newFillStyleIdx); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java index d04d40b62..cb48b1158 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java @@ -64,10 +64,8 @@ public abstract class ShapeExporterBase implements IShapeExporter { private final ColorTransform colorTransform; private boolean canUseSmoothing = true; - + protected int windingRule; - - public ShapeExporterBase(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { this.shape = shape; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/StraightEdge.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/StraightEdge.java index 21817d5d2..7ce32ee24 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/StraightEdge.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/StraightEdge.java @@ -83,4 +83,15 @@ public class StraightEdge implements IEdge { return "straight[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + "]"; } + @Override + public IEdge sameWithNewFillStyle(int newFillStyleIdx) { + return new StraightEdge(fromX, fromY, toX, toY, lineStyleIdx, newFillStyleIdx); + } + + @Override + public IEdge reverse() { + return new StraightEdge(toX, toY, fromX, fromY, lineStyleIdx, getFillStyleIdx()); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/MorphShapeGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/MorphShapeGenerator.java new file mode 100644 index 000000000..0ab99e816 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/MorphShapeGenerator.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2010-2023 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.morphshape; + +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.math.BezierEdge; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Reference; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class MorphShapeGenerator { + + public void generate(DefineMorphShape2Tag morphShape, ShapeTag startShape, ShapeTag endShape) throws StyleMismatchException { + ShapeForMorphExporter startExport = new ShapeForMorphExporter(startShape); + startExport.export(); + ShapeForMorphExporter endExport = new ShapeForMorphExporter(endShape); + endExport.export(); + + List> startBeziers = new ArrayList<>(); + List> endBeziers = new ArrayList<>(); + List startFillStyles = new ArrayList<>(); + List startFillstyleIndices = new ArrayList<>(); + List startLineStyles = new ArrayList<>(); + List startLineStyleIndices = new ArrayList<>(); + + List endFillStyles = new ArrayList<>(); + List endLineStyles = new ArrayList<>(); + + Set usedBs = new HashSet<>(); + + List startShapeIndices = new ArrayList<>(); + List endShapeIndices = new ArrayList<>(); + + for (int a = 0; a < startExport.shapes.size(); a++) { + double minDistance = Double.MAX_VALUE; + double minDistanceNoUsed = Double.MAX_VALUE; + int selectedB = -1; + int selectedBNoUsed = -1; + + int startFillStyleIndex = startExport.fillStyleIndices.get(a); + FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex); + int startLineStyleIndex = startExport.lineStyleIndices.get(a); + LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex); + + for (int b = 0; b < endExport.shapes.size(); b++) { + int endFillStyleIndex = endExport.fillStyleIndices.get(b); + FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex); + int endLineStyleIndex = endExport.lineStyleIndices.get(b); + LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex); + + if (((endFillStyle == null && startFillStyle == null) + || (startFillStyle != null + && endFillStyle != null + && startFillStyle.isCompatibleFillStyle(endFillStyle))) + && ((endLineStyle == null && startLineStyle == null) + || (startLineStyle != null + && endLineStyle != null + && startLineStyle.isCompatibleLineStyle(endLineStyle)))) { + double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b)); + if (distance < minDistance) { + minDistance = distance; + selectedB = b; + } + + if (distance < minDistanceNoUsed && !usedBs.contains(b)) { + minDistanceNoUsed = distance; + selectedBNoUsed = b; + } + } + } + if (selectedB == -1) { + throw new StyleMismatchException(); + } + if (selectedBNoUsed != -1) { + selectedB = selectedBNoUsed; + } + startShapeIndices.add(a); + endShapeIndices.add(selectedB); + usedBs.add(selectedB); + } + + if (usedBs.size() < endExport.shapes.size()) { + for (int b = 0; b < endExport.shapes.size(); b++) { + if (!usedBs.contains(b)) { + + double minDistance = Double.MAX_VALUE; + int selectedA = -1; + + int endFillStyleIndex = endExport.fillStyleIndices.get(b); + FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex); + int endLineStyleIndex = endExport.lineStyleIndices.get(b); + LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex); + + for (int a = 0; a < startExport.shapes.size(); a++) { + int startFillStyleIndex = startExport.fillStyleIndices.get(a); + FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex); + int startLineStyleIndex = startExport.lineStyleIndices.get(a); + LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex); + + if (((endFillStyle == null && startFillStyle == null) + || (startFillStyle != null + && endFillStyle != null + && startFillStyle.isCompatibleFillStyle(endFillStyle))) + && ((endLineStyle == null && startLineStyle == null) + || (startLineStyle != null + && endLineStyle != null + && startLineStyle.isCompatibleLineStyle(endLineStyle)))) { + double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b)); + if (distance < minDistance) { + minDistance = distance; + selectedA = a; + } + } + } + if (selectedA == -1) { + throw new StyleMismatchException(); + } + + startShapeIndices.add(selectedA); + endShapeIndices.add(b); + } + } + } + + for (int i = 0; i < startShapeIndices.size(); i++) { + int a = startShapeIndices.get(i); + int b = endShapeIndices.get(i); + + List shapeStart = Helper.deepCopy(startExport.shapes.get(a)); + split(shapeStart, startExport.pointsPosPercent.get(a), endExport.pointsPosPercent.get(b)); + List shapeEnd = Helper.deepCopy(endExport.shapes.get(b)); + split(shapeEnd, endExport.pointsPosPercent.get(b), startExport.pointsPosPercent.get(a)); + + startBeziers.add(shapeStart); + endBeziers.add(shapeEnd); + + if (startExport.fillStyleIndices.get(a) != -1) { + startFillStyles.add(startExport.fillStyles.get(startExport.fillStyleIndices.get(a))); + } + startFillstyleIndices.add(startExport.fillStyleIndices.get(a)); + if (startExport.lineStyleIndices.get(a) != -1) { + startLineStyles.add(startExport.lineStyles.get(startExport.lineStyleIndices.get(a))); + } + startLineStyleIndices.add(startExport.lineStyleIndices.get(a)); + + if (endExport.fillStyleIndices.get(b) != -1) { + endFillStyles.add(endExport.fillStyles.get(endExport.fillStyleIndices.get(b))); + } + if (endExport.lineStyleIndices.get(b) != -1) { + endLineStyles.add(endExport.lineStyles.get(endExport.lineStyleIndices.get(b))); + } + } + + + List startRecords = new ArrayList<>(); + List endRecords = new ArrayList<>(); + + MORPHFILLSTYLEARRAY morphFillStyleArray = new MORPHFILLSTYLEARRAY(); + morphFillStyleArray.fillStyles = new MORPHFILLSTYLE[startFillStyles.size()]; + + MORPHLINESTYLEARRAY morphLineStyleArray = new MORPHLINESTYLEARRAY(); + morphLineStyleArray.lineStyles2 = new MORPHLINESTYLE2[startLineStyles.size()]; + + for (int i = 0; i < startFillStyles.size(); i++) { + FILLSTYLE fsStart = startFillStyles.get(i); + FILLSTYLE fsEnd = endFillStyles.get(i); + MORPHFILLSTYLE morphFillStyle = fsStart.toMorphStyle(fsEnd); + if (morphFillStyle == null) { + throw new StyleMismatchException(); + } + morphFillStyleArray.fillStyles[i] = morphFillStyle; + } + + for (int i = 0; i < startLineStyles.size(); i++) { + LINESTYLE2 lsStart = startLineStyles.get(i); + LINESTYLE2 lsEnd = endLineStyles.get(i); + MORPHLINESTYLE2 morphLineStyle = lsStart.toMorphLineStyle2(lsEnd); + if (morphLineStyle == null) { + throw new StyleMismatchException(); + } + morphLineStyleArray.lineStyles2[i] = morphLineStyle; + if (morphLineStyle.noHScaleFlag || morphLineStyle.noVScaleFlag) { + morphShape.usesNonScalingStrokes = true; + } + if (!morphLineStyle.noHScaleFlag && !morphLineStyle.noVScaleFlag) { + morphShape.usesScalingStrokes = true; + } + } + + for (int i = 0; i < startBeziers.size(); i++) { + List beList = startBeziers.get(i); + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateFillStyle0 = true; + if (startFillstyleIndices.get(i) != -1) { + scr.fillStyle0 = startFillstyleIndices.get(i) + 1; + } else { + scr.fillStyle0 = 0; + } + scr.stateLineStyle = true; + if (startLineStyleIndices.get(i) != -1) { + scr.lineStyle = startLineStyleIndices.get(i) + 1; + } else { + scr.lineStyle = 0; + } + startRecords.add(scr); + + BezierEdge firstBe = beList.get(0); + StyleChangeRecord scrMove = new StyleChangeRecord(); + scrMove.stateMoveTo = true; + scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX()); + scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY()); + startRecords.add(scrMove); + + for (BezierEdge be : beList) { + SHAPERECORD rec = bezierToRecord(be); + startRecords.add(rec); + } + } + startRecords.add(new EndShapeRecord()); + + for (int i = 0; i < endBeziers.size(); i++) { + List beList = endBeziers.get(i); + BezierEdge firstBe = beList.get(0); + StyleChangeRecord scrMove = new StyleChangeRecord(); + scrMove.stateMoveTo = true; + scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX()); + scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY()); + endRecords.add(scrMove); + for (BezierEdge be : beList) { + SHAPERECORD rec = bezierToRecord(be); + endRecords.add(rec); + } + } + endRecords.add(new EndShapeRecord()); + + morphShape.morphFillStyles = morphFillStyleArray; + morphShape.morphLineStyles = morphLineStyleArray; + morphShape.startEdges = new SHAPE(); + morphShape.startEdges.shapeRecords = startRecords; + morphShape.startEdges.numFillBits = SWFOutputStream.getNeededBitsU(morphFillStyleArray.fillStyles.length); + morphShape.startEdges.numLineBits = SWFOutputStream.getNeededBitsU(morphLineStyleArray.lineStyles2.length); + + morphShape.endEdges = new SHAPE(); + morphShape.endEdges.numFillBits = 0; + morphShape.endEdges.numLineBits = 0; + morphShape.endEdges.shapeRecords = endRecords; + + morphShape.setModified(true); + morphShape.updateBounds(); + } + + private void split(List shape, List originalPointsPosPercent, List newPointPosPercent) { + List pointPointsPercent = new ArrayList<>(originalPointsPosPercent); + + int nppPos = 0; + for (int i = 0; i < shape.size() && nppPos < newPointPosPercent.size(); i++) { + BezierEdge be = shape.get(i); + double pointPosPct = pointPointsPercent.get(i); + double pointPosNextPct = pointPointsPercent.get(i + 1); + + double pct = newPointPosPercent.get(nppPos); + if (pct > pointPosPct && pct < pointPosNextPct) { + double deltaPct = pointPosNextPct - pointPosPct; + double newPct = pct - pointPosPct; + double insidePct = newPct / deltaPct; + Reference leftRef = new Reference<>(null); + Reference rightRef = new Reference<>(null); + be.split(insidePct, leftRef, rightRef); + shape.remove(i); + shape.add(i, leftRef.getVal()); + shape.add(i + 1, rightRef.getVal()); + pointPointsPercent.add(i + 1, pct); + nppPos++; + } else if (pct == pointPosPct) { + nppPos++; + i--; + } + } + } + + private SHAPERECORD bezierToRecord(BezierEdge be) { + if (be.points.size() == 2) { + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.deltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX()); + ser.deltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY()); + ser.generalLineFlag = true; + ser.simplify(); + return ser; + } + if (be.points.size() == 3) { + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX()); + cer.controlDeltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY()); + cer.anchorDeltaX = (int) Math.round(be.points.get(2).getX() - be.points.get(1).getX()); + cer.anchorDeltaY = (int) Math.round(be.points.get(2).getY() - be.points.get(1).getY()); + return cer; + } + return null; + } +} 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 new file mode 100644 index 000000000..ee051af45 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/ShapeForMorphExporter.java @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2010-2023 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.morphshape; + +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.shape.ShapeExporterBase; +import com.jpexs.decompiler.flash.math.BezierEdge; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Reference; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ShapeForMorphExporter extends ShapeExporterBase { + + public List> shapes = new ArrayList<>(); + public List fillStyleIndices = new ArrayList<>(); + public List lineStyleIndices = new ArrayList<>(); + public List> pointsPosPercent = new ArrayList<>(); + public List centralPos = new ArrayList<>(); + private final List pointsSum = new ArrayList<>(); + private final List segmentCount = new ArrayList<>(); + private final List> bezierLengths = new ArrayList<>(); + + private List currentShape = new ArrayList<>(); + private List currentBezierLengths = new ArrayList<>(); + private Point2D.Double currentPointsSum = new Point2D.Double(); + private int currentSegmentCount = 0; + private int currentLineStyle = -1; + private int currentFillStyle = -1; + + double currentShapeLen = 0; + + public List fillStyles = new ArrayList<>(); + public List lineStyles = new ArrayList<>(); + + private double lastX = 0; + private double lastY = 0; + + public ShapeForMorphExporter(ShapeTag shape) { + super(ShapeTag.WIND_EVEN_ODD, shape.getShapeNum(), shape.getSwf(), shape.shapes, new ColorTransform()); + } + + @Override + public void beginShape() { + shapes = new ArrayList<>(); + } + + @Override + public void endShape() { + endCurrent(); + + for (int i = 0; i < segmentCount.size(); i++) { + Point2D.Double center = new Point2D.Double(); + center.x = pointsSum.get(i).x / (double) segmentCount.get(i); + center.y = pointsSum.get(i).y / (double) segmentCount.get(i); + centralPos.add(center); + } + + for (int i = 0; i < shapes.size(); i++) { + List shape = shapes.get(i); + List points = new ArrayList<>(); + BezierEdge be = null; + for (int j = 0; j < shape.size(); j++) { + be = shape.get(j); + points.add(new Point2D.Double( + be.getBeginPoint().getX() - centralPos.get(i).getX(), + be.getBeginPoint().getY() - centralPos.get(i).getY() + )); + } + points.add(new Point2D.Double( + be.getEndPoint().getX() - centralPos.get(i).getX(), + be.getEndPoint().getY() - centralPos.get(i).getY() + )); + + double w = 0; + for (int j = 0; j < points.size(); j++) { + int secondPoint = j + 1 == points.size() ? 0 : j + 1; + double x = points.get(j).getX(); + double xp1 = points.get(secondPoint).getX(); + double y = points.get(j).getY(); + double yp1 = points.get(secondPoint).getY(); + + if (y * yp1 < 0) { + double r = x + ((y * (xp1 - x)) / (y - yp1)); + if (r > 0) { + if (y < 0) { + w = w + 1; + } else { + w = w - 1; + } + } + } else if ((y == 0) && (x > 0)) { + if (yp1 > 0) { + w = w + .5; + } else { + w = w - .5; + } + } else if ((yp1 == 0) && (xp1 > 0)) { + if (y < 0) { + w = w + .5; + } else { + w = w - .5; + } + } + } + + if (w > 0) { + //clockwise + } else { + //counter clock wise + List newShape = new ArrayList<>(); + List newBeLength = new ArrayList<>(); + for (int j = shape.size() - 1; j >= 0; j--) { + newShape.add(shape.get(j).reverse()); + newBeLength.add(bezierLengths.get(i).get(j)); + } + shape.clear(); + shape.addAll(newShape); + bezierLengths.get(i).clear(); + bezierLengths.get(i).addAll(newBeLength); + } + } + + for (int i = 0; i < shapes.size(); i++) { + List shape = shapes.get(i); + //closed shape + if (shape.get(0).getBeginPoint().equals(shape.get(shape.size() - 1).getEndPoint())) { + + //Find most top left point + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + + for (int j = 0; j < shape.size(); j++) { + if (shape.get(j).getBeginPoint().getX() < minX) { + minX = shape.get(j).getBeginPoint().getX(); + } + if (shape.get(j).getBeginPoint().getY() < minY) { + minY = shape.get(j).getBeginPoint().getY(); + } + } + double minDist = Double.MAX_VALUE; + int minPos = -1; + for (int j = 0; j < shape.size(); j++) { + double dist = shape.get(j).getBeginPoint().distance(minX, minY); + if (dist < minDist) { + minDist = dist; + minPos = j; + } + } + if (minPos > -1) { + //Rearange shape to start with the top left point + for (int j = 0; j < minPos; j++) { + shape.add(shape.remove(0)); + bezierLengths.get(i).add(bezierLengths.get(i).remove(0)); + } + } + } + } + + for (List pp : bezierLengths) { + List ppPercent = new ArrayList<>(); + double len = 0.0; + for (double bLength : pp) { + len += bLength; + } + double pos = 0; + for (double bLength : pp) { + double pct = roundPct(pos / len); + pos += bLength; + ppPercent.add(pct); + } + ppPercent.add(1.0); + pointsPosPercent.add(ppPercent); + } + } + + @Override + public void beginAliasedFills() { + + } + + @Override + public void beginFills() { + + } + + @Override + public void endFills() { + endCurrent(); + } + + @Override + public void beginLines() { + currentShape = null; + } + + @Override + public void endLines(boolean close) { + endCurrent(); + } + + @Override + public void beginFill(RGB color) { + endCurrent(); + currentShape = new ArrayList<>(); + currentFillStyle = fillStyles.size(); + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.fillStyleType = FILLSTYLE.SOLID; + fillStyle.color = color; + fillStyles.add(fillStyle); + } + + @Override + public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + endCurrent(); + currentShape = new ArrayList<>(); + currentFillStyle = fillStyles.size(); + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.fillStyleType = type; + fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT(); + fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords); + fillStyle.gradientMatrix = matrix.toMATRIX(); + fillStyle.gradient.spreadMode = spreadMethod; + fillStyle.gradient.interpolationMode = interpolationMethod; + if (focalPointRatio != 0) { + ((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio; + } + fillStyles.add(fillStyle); + } + + @Override + public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) { + endCurrent(); + currentShape = new ArrayList<>(); + currentFillStyle = fillStyles.size(); + FILLSTYLE fillStyle = new FILLSTYLE(); + if (repeat) { + if (smooth) { + fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP; + } else { + fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP; + } + } else { + if (smooth) { + fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP; + } else { + fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP; + } + } + fillStyle.bitmapMatrix = matrix.toMATRIX(); + fillStyle.bitmapId = bitmapId; + fillStyles.add(fillStyle); + } + + @Override + public void endFill() { + endCurrent(); + } + + private void endCurrent() { + if (currentShape != null && !currentShape.isEmpty()) { + shapes.add(currentShape); + bezierLengths.add(currentBezierLengths); + pointsSum.add(currentPointsSum); + segmentCount.add(currentSegmentCount); + fillStyleIndices.add(currentFillStyle); + lineStyleIndices.add(currentLineStyle); + } + currentShapeLen = 0; + currentShape = new ArrayList<>(); + currentBezierLengths = new ArrayList<>(); + currentPointsSum = new Point2D.Double(); + currentSegmentCount = 0; + currentFillStyle = -1; + currentLineStyle = -1; + } + + @Override + public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) { + endCurrent(); + currentLineStyle = lineStyles.size(); + LINESTYLE2 lineStyle = new LINESTYLE2(); + lineStyle.width = (int) thickness; + lineStyle.color = new RGBA(color); + lineStyle.pixelHintingFlag = pixelHinting; + switch (scaleMode) { + case "NONE": + lineStyle.noHScaleFlag = true; + lineStyle.noVScaleFlag = true; + break; + case "VERTICAL": + lineStyle.noHScaleFlag = true; + break; + case "HORIZONTAL": + lineStyle.noVScaleFlag = true; + break; + } + lineStyle.startCapStyle = startCaps; + lineStyle.endCapStyle = endCaps; + lineStyle.joinStyle = joints; + lineStyle.miterLimitFactor = miterLimit; + lineStyles.add(lineStyle); + } + + @Override + public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1); + lineStyle.hasFillFlag = true; + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.fillStyleType = type; + fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT(); + fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords); + fillStyle.gradientMatrix = matrix.toMATRIX(); + fillStyle.gradient.spreadMode = spreadMethod; + fillStyle.gradient.interpolationMode = interpolationMethod; + if (focalPointRatio != 0) { + ((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio; + } + lineStyle.fillType = fillStyle; + } + + @Override + public void lineBitmapStyle(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) { + LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1); + lineStyle.hasFillFlag = true; + FILLSTYLE fillStyle = new FILLSTYLE(); + if (repeat) { + if (smooth) { + fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP; + } else { + fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP; + } + } else { + if (smooth) { + fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP; + } else { + fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP; + } + } + fillStyle.bitmapMatrix = matrix.toMATRIX(); + fillStyle.bitmapId = bitmapId; + lineStyle.fillType = fillStyle; + } + + @Override + public void moveTo(double x, double y) { + int backupFillStyle = currentFillStyle; + int backupLineStyle = currentLineStyle; + endCurrent(); + currentFillStyle = backupFillStyle; + currentLineStyle = backupLineStyle; + lastX = (int) x; + lastY = (int) y; + //currentPointsPos.add(0.0); + } + + @Override + public void lineTo(double x, double y) { + if (x == lastX && y == lastY) { + return; + } + BezierEdge be = new BezierEdge(lastX, lastY, x, y); + + currentPointsSum.x += x; // - lastX; + currentPointsSum.y += y; // - lastY; + + currentShape.add(be); + lastX = x; + lastY = y; + currentBezierLengths.add(be.length()); + currentShapeLen += be.length(); + + currentSegmentCount++; + } + + @Override + public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { + + if (anchorX == lastX && anchorY == lastY) { + return; + } + + BezierEdge be = new BezierEdge(lastX, lastY, controlX, controlY, anchorX, anchorY); + + currentShape.add(be); + + currentPointsSum.x += anchorX; // - lastX; + currentPointsSum.y += anchorY; // - lastY; + + lastX = anchorX; + lastY = anchorY; + currentBezierLengths.add(be.length()); + currentShapeLen += be.length(); + + currentSegmentCount++; + } + + private double roundPct(double pct) { + double precision = 1000000d; + return Math.round(pct * precision) / precision; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/StyleMismatchException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/StyleMismatchException.java new file mode 100644 index 000000000..01fbfe264 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/morphshape/StyleMismatchException.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2023 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.morphshape; + +/** + * + * @author JPEXS + */ +public class StyleMismatchException extends Exception { + +} 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 5e7035988..924761ad4 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 @@ -226,6 +226,11 @@ public class SvgImporter { transform = Matrix.getScaleInstance(ratioX, ratioY); transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY); } + + transform = transform.preConcatenate(Matrix.getTranslateInstance(-viewBox.x, -viewBox.y)); + if (viewBox.height != 0 && viewBox.width != 0) { + transform = transform.preConcatenate(Matrix.getScaleInstance(width / viewBox.width, height / viewBox.height)); + } processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape); } catch (SAXException | IOException | ParserConfigurationException ex) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java index c5683b702..125d4acdf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java @@ -19,14 +19,16 @@ package com.jpexs.decompiler.flash.math; import com.jpexs.helpers.Reference; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Stack; /** * * @author JPEXS */ -public class BezierEdge { +public class BezierEdge implements Serializable { public List points = new ArrayList<>(); @@ -45,6 +47,14 @@ public class BezierEdge { points.add(new Point2D.Double(x1, y1)); } + public Point2D getBeginPoint() { + return points.get(0); + } + + public Point2D getEndPoint() { + return points.get(points.size() - 1); + } + public Point2D pointAt(double t) { if (points.size() == 2) { double x = (1 - t) * points.get(0).getX() + t * points.get(1).getX(); @@ -189,6 +199,30 @@ public class BezierEdge { } return ok; } + + + public double length() { + double distance = 0; + double epsilon = 1; + + Stack parts = new Stack(); + parts.push(this); + + while (!parts.isEmpty()) { + BezierEdge curve = parts.pop(); + double d = curve.points.get(0).distance(curve.points.get(curve.points.size() - 1)); + if (d < epsilon) { + distance += d; + } else { + Reference leftRef = new Reference<>(null); + Reference rightRef = new Reference<>(null); + curve.split(0.5, leftRef, rightRef); + parts.add(leftRef.getVal()); + parts.add(rightRef.getVal()); + } + } + return distance; + } @Override public String toString() { @@ -207,6 +241,14 @@ public class BezierEdge { left.setVal(new BezierEdge(leftPoints)); right.setVal(new BezierEdge(rightPoints)); } + + public BezierEdge reverse() { + List revPoints = new ArrayList<>(); + for (int i = points.size() - 1; i >= 0; i--) { + revPoints.add(points.get(i)); + } + return new BezierEdge(revPoints); + } public static void main(String[] args) { List t1 = new ArrayList<>(); @@ -245,6 +287,10 @@ public class BezierEdge { ); System.out.println("Intersection point: " + c); + + BezierEdge be = new BezierEdge(0, 0, 100, 50, 0, 100); + + System.out.println("be5.dist: " + be.length()); //Rectangle2D out = new Rectangle2D.Double(); //rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java index 003448a66..6cb811a9c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java @@ -84,7 +84,7 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { public abstract int getShapeNum(); - public SHAPEWITHSTYLE getShapes() { + public synchronized SHAPEWITHSTYLE getShapes() { if (shapes == null && shapeData != null) { try { SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength()); 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 99bb5a55a..00418939f 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 @@ -151,6 +151,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ gradient = g; } } + } + + public boolean isCompatibleFillStyle(FILLSTYLE otherFillStyle) { + if (fillStyleType != otherFillStyle.fillStyleType) { + return false; + } + switch (fillStyleType) { + case CLIPPED_BITMAP: + case NON_SMOOTHED_CLIPPED_BITMAP: + case NON_SMOOTHED_REPEATING_BITMAP: + case REPEATING_BITMAP: + if (bitmapId != otherFillStyle.bitmapId) { + return false; + } + break; + case LINEAR_GRADIENT: + case RADIAL_GRADIENT: + case FOCAL_RADIAL_GRADIENT: + if (!gradient.isCompatibleGradient(otherFillStyle.gradient)) { + return false; + } + break; + } + return true; } public MORPHFILLSTYLE toMorphStyle() { @@ -171,4 +195,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ return morphFillStyle; } + + public MORPHFILLSTYLE toMorphStyle(FILLSTYLE endFillStyle) { + if (!isCompatibleFillStyle(endFillStyle)) { + return null; + } + MORPHFILLSTYLE morphFillStyle = new MORPHFILLSTYLE(); + morphFillStyle.bitmapId = bitmapId; + if (bitmapMatrix != null) { + morphFillStyle.startBitmapMatrix = new MATRIX(bitmapMatrix); + } + if (endFillStyle.bitmapMatrix != null) { + morphFillStyle.endBitmapMatrix = new MATRIX(endFillStyle.bitmapMatrix); + } + if (color != null) { + morphFillStyle.startColor = new RGBA(color); + } + if (endFillStyle.color != null) { + morphFillStyle.endColor = new RGBA(endFillStyle.color); + } + morphFillStyle.fillStyleType = fillStyleType; + if (gradient != null) { + morphFillStyle.gradient = gradient.toMorphGradient(endFillStyle.gradient); + } + + return morphFillStyle; + } } 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 814a9bb2b..f4b290658 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 @@ -61,7 +61,20 @@ public class GRADIENT implements Serializable { public static final int INTERPOLATION_RESERVED2 = 3; @SWFArray(value = "record") - public GRADRECORD[] gradientRecords = new GRADRECORD[0]; + public GRADRECORD[] gradientRecords = new GRADRECORD[0]; + + public boolean isCompatibleGradient(GRADIENT otherGradient) { + if (interpolationMode != otherGradient.interpolationMode) { + return false; + } + if (spreadMode != otherGradient.spreadMode) { + return false; + } + if (gradientRecords.length != otherGradient.gradientRecords.length) { + return false; + } + return true; + } public MORPHGRADIENT toMorphGradient() { MORPHGRADIENT morphGradient = new MORPHGRADIENT(); @@ -73,4 +86,18 @@ public class GRADIENT implements Serializable { } return morphGradient; } + + public MORPHGRADIENT toMorphGradient(GRADIENT endGradient) { + if (!isCompatibleGradient(endGradient)) { + return null; + } + 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 947d02a06..7d48b9a87 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 @@ -46,4 +46,13 @@ public class GRADRECORD implements Serializable { morphGradRecord.endRatio = ratio; return morphGradRecord; } + + public MORPHGRADRECORD toMorphGradRecord(GRADRECORD endGradRecord) { + MORPHGRADRECORD morphGradRecord = new MORPHGRADRECORD(); + morphGradRecord.startColor = new RGBA(color); + morphGradRecord.endColor = new RGBA(endGradRecord.color); + morphGradRecord.startRatio = ratio; + morphGradRecord.endRatio = endGradRecord.ratio; + return morphGradRecord; + } } 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 b5574e614..1b1a4df57 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 @@ -141,6 +141,44 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE { @Override public void setWidth(int width) { this.width = width; + } + + public boolean isCompatibleLineStyle(LINESTYLE2 otherLineStyle) { + if (startCapStyle != otherLineStyle.startCapStyle) { + return false; + } + if (endCapStyle != otherLineStyle.endCapStyle) { + return false; + } + + if (joinStyle != otherLineStyle.joinStyle) { + return false; + } + + if (hasFillFlag != otherLineStyle.hasFillFlag) { + return false; + } + + if (noVScaleFlag != otherLineStyle.noVScaleFlag) { + return false; + } + if (pixelHintingFlag != otherLineStyle.pixelHintingFlag) { + return false; + } + if (noClose != otherLineStyle.noClose) { + return false; + } + if (miterLimitFactor != otherLineStyle.miterLimitFactor) { + return false; + } + + if (hasFillFlag) { + if (!fillType.isCompatibleFillStyle(otherLineStyle.fillType)) { + return false; + } + } + + return true; } public MORPHLINESTYLE2 toMorphLineStyle2() { @@ -165,4 +203,33 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE { } return morphLineStyle2; } + + public MORPHLINESTYLE2 toMorphLineStyle2(LINESTYLE2 endLineStyle) { + if (!isCompatibleLineStyle(endLineStyle)) { + return null; + } + + MORPHLINESTYLE2 morphLineStyle2 = new MORPHLINESTYLE2(); + morphLineStyle2.startWidth = width; + morphLineStyle2.endWidth = endLineStyle.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); + } + if (endLineStyle.color != null) { + morphLineStyle2.endColor = new RGBA(endLineStyle.color); + } + if (hasFillFlag) { + morphLineStyle2.fillType = fillType.toMorphStyle(endLineStyle.fillType); + } + return morphLineStyle2; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 0a919c411..b649b8c68 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -132,6 +132,8 @@ import com.jpexs.decompiler.flash.importers.SpriteImporter; import com.jpexs.decompiler.flash.importers.SwfXmlImporter; import com.jpexs.decompiler.flash.importers.SymbolClassImporter; import com.jpexs.decompiler.flash.importers.TextImporter; +import com.jpexs.decompiler.flash.importers.morphshape.MorphShapeGenerator; +import com.jpexs.decompiler.flash.importers.morphshape.StyleMismatchException; import com.jpexs.decompiler.flash.importers.svg.SvgImporter; import com.jpexs.decompiler.flash.search.ABCSearchResult; import com.jpexs.decompiler.flash.search.ActionSearchResult; @@ -142,7 +144,10 @@ import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; import com.jpexs.decompiler.flash.tags.DefineBitsTag; import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; import com.jpexs.decompiler.flash.tags.DoActionTag; @@ -371,8 +376,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private static final int DETAILCARDTAGINFO = 0; private static final int DETAILCARDEMPTYPANEL = -1; - - private static final int DETAILCARDDEBUGSTACKFRAME = 2; + + private static final int DETAILCARDDEBUGSTACKFRAME = 2; private static final String SPLIT_PANE1 = "SPLITPANE1"; @@ -413,7 +418,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private final PreviewPanel dumpPreviewPanel; private final TagInfoPanel tagInfoPanel; - + private final DebugStackPanel debugStackPanel; private TreePanelMode treePanelMode; @@ -1028,7 +1033,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public String translate(String key) { return mainFrame.translate(key); } - + public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel, FlashPlayerPanel previewFlashPanel) { super(); @@ -1050,9 +1055,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se whitePanel.setBackground(Color.white); } detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); -*/ + */ tagInfoPanel = new TagInfoPanel(this); - + debugStackPanel = new DebugStackPanel(); Main.getDebugHandler().addBreakListener(new DebuggerHandler.BreakListener() { @Override @@ -1061,8 +1066,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public void run() { showDetail(DETAILCARDDEBUGSTACKFRAME); - } - }); + } + }); } @Override @@ -1071,14 +1076,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public void run() { showDetail(DETAILCARDEMPTYPANEL); - } - }); - } + } + }); + } }); Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { @Override public void connected() { - + } @Override @@ -1087,11 +1092,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public void run() { reload(true); - } + } }); - } + } }); - + UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); tagTree = new TagTree(null, this); tagTree.addTreeSelectionListener(this); @@ -1454,7 +1459,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public ABCPanel getABCPanel() { if (abcPanel == null) { abcPanel = new ABCPanel(this); - displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); + displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); } return abcPanel; @@ -1854,19 +1859,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (constants.getMultiname(multiNameIndex).name_index > 0) { oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); } - - String scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString(); + + String scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString(); String newName = ViewMessages.showInputDialog(this, translate("rename.enternew"), oldName); if (newName != null) { if (!oldName.equals(newName)) { - + if (oldName.equals(abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().className)) { scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().packageStr.add(newName, "").toPrintableString(true); } - + final String fScriptName = scriptName; - + int mulCount = 0; for (ABCContainerTag cnt : abcList) { ABC abc = cnt.getABC(); @@ -1941,17 +1946,17 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Set usedOpenables = new HashSet<>(); Set usedOpenableLists = new HashSet<>(); - + Map> usedSwfIdsInBundles = new HashMap<>(); - + for (TreeItem d : sel) { Openable selectedNodeOpenable = d.getOpenable(); usedOpenables.add(selectedNodeOpenable); - + OpenableList list = selectedNodeOpenable.getOpenableList(); if (list != null) { usedOpenableLists.add(list); - } + } } Map usedSwfsIds = new HashMap<>(); @@ -2074,13 +2079,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } String selFile2; - if (usedOpenables.size() > 1) { + if (usedOpenables.size() > 1) { if (usedOpenableLists.size() > 1 && openable.getOpenableList() != null && openable.getOpenableList().isBundle()) { if (!usedSwfIdsInBundles.containsKey(openable.getOpenableList())) { usedSwfIdsInBundles.put(openable.getOpenableList(), new HashMap<>()); } - selFile2 = selFile + File.separator + openable.getOpenableList().name + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfIdsInBundles.get(openable.getOpenableList())); - } else { + selFile2 = selFile + File.separator + openable.getOpenableList().name + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfIdsInBundles.get(openable.getOpenableList())); + } else { selFile2 = selFile + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfsIds); } } else { @@ -2547,16 +2552,16 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } abcPanel.detailPanel.methodTraitPanel.methodCodePanel.gotoInstrLine(line); - } else { + } else { abcPanel.decompiledTextArea.gotoLine(line); } scrollPosStorage.saveScrollPos(oldItem); refreshBreakPoints(); - } - }); - } + } + }); + } }); - + } else if (actionPanel != null && !swf.isAS3()) { actionPanel.addScriptListener(new Runnable() { @Override @@ -2571,11 +2576,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se actionPanel.decompiledEditor.gotoLine(line); } scrollPosStorage.saveScrollPos(oldItem); - refreshBreakPoints(); + refreshBreakPoints(); } }); - } - }); + } + }); } gotoScriptName(swf, scriptName); } @@ -2627,12 +2632,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se String rawScriptName = scriptName; if (rawScriptName.startsWith("#PCODE ")) { rawScriptName = rawScriptName.substring("#PCODE ".length()); - } + } Map asms = swf.getASMs(true); if (asms.containsKey(rawScriptName)) { oldItem = null; getCurrentTree().setSelectionPath(null); - setTagTreeSelectedNode(getCurrentTree(), asms.get(rawScriptName)); + setTagTreeSelectedNode(getCurrentTree(), asms.get(rawScriptName)); } /*if (actionPanel != null && asms.containsKey(rawScriptName)) { actionPanel.setSource(asms.get(rawScriptName), true); @@ -3090,7 +3095,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); clearAllScriptCache(); updateClassesList(); - reload(true); + reload(true); } public void renameColliding(final Openable openable) { @@ -3170,7 +3175,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override protected void done() { - + } }.execute(); @@ -4275,7 +4280,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se reload(true); pinsPanel.refresh(); } - + public void refreshTree() { refreshTree(new SWF[0]); } @@ -4425,7 +4430,99 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se replace(items, false); } - public boolean replace(List items, boolean create) { + public boolean replaceMorphShape(MorphShapeTag morphShape, boolean create) { + File fileStart = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true, AppStrings.translate("dialog.morphshape.startShape")); + if (fileStart == null) { + return false; + } + + File fileEnd = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true, AppStrings.translate("dialog.morphshape.endShape")); + + if (fileEnd == null) { + fileEnd = fileStart; + } + + DefineShape4Tag shapeStart = new DefineShape4Tag(morphShape.getSwf()); + SWF.addTagBefore(shapeStart, morphShape); + + DefineShape4Tag shapeEnd = new DefineShape4Tag(morphShape.getSwf()); + SWF.addTagBefore(shapeEnd, morphShape); + + File selfileStart = Helper.fixDialogFile(fileStart); + byte[] dataStart = null; + String svgTextStart = null; + boolean svgWarningShown = false; + if (".svg".equals(Path.getExtension(selfileStart))) { + svgTextStart = Helper.readTextFile(selfileStart.getAbsolutePath()); + showSvgImportWarning(); + svgWarningShown = true; + } else { + dataStart = Helper.readFile(selfileStart.getAbsolutePath()); + } + + File selfileEnd = Helper.fixDialogFile(fileEnd); + byte[] dataEnd = null; + String svgTextEnd = null; + if (".svg".equals(Path.getExtension(selfileEnd))) { + svgTextEnd = Helper.readTextFile(selfileEnd.getAbsolutePath()); + if (!svgWarningShown) { + showSvgImportWarning(); + } + } else { + dataEnd = Helper.readFile(selfileEnd.getAbsolutePath()); + } + + try { + Tag newStartTag; + if (svgTextStart != null) { + newStartTag = new SvgImporter().importSvg(shapeStart, svgTextStart, false); + } else { + newStartTag = new ShapeImporter().importImage(shapeStart, dataStart, 0, false); + } + newStartTag.getTimelined().removeTag(newStartTag); + + Tag newEndTag; + if (svgTextStart != null) { + newEndTag = new SvgImporter().importSvg(shapeEnd, svgTextEnd, false); + } else { + newEndTag = new ShapeImporter().importImage(shapeEnd, dataEnd, 0, false); + } + newEndTag.getTimelined().removeTag(newEndTag); + + if (morphShape instanceof DefineMorphShapeTag) { + DefineMorphShape2Tag morphShape2 = new DefineMorphShape2Tag(morphShape.getSwf()); + morphShape.getTimelined().replaceTag(morphShape, morphShape2); + morphShape2.setTimelined(morphShape.getTimelined()); + morphShape = morphShape2; + } + + MorphShapeGenerator gen = new MorphShapeGenerator(); + try { + gen.generate((DefineMorphShape2Tag) morphShape, shapeStart, shapeEnd); + } catch (StyleMismatchException sme) { + ViewMessages.showMessageDialog(this, AppStrings.translate("error.morphshape.incompatible"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return false; + } + + SWF swf = morphShape.getSwf(); + swf.resetTimeline(); + refreshTree(swf); + /*if (newTag != null) { + + setTagTreeSelectedNode(getCurrentTree(), newTag); + }*/ + + swf.clearImageCache(); + swf.clearShapeCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + ViewMessages.showMessageDialog(MainPanel.this, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + return true; + } + + public boolean replace(List items, boolean create) { if (items.isEmpty()) { return false; } @@ -4442,10 +4539,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) { - 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); + return replaceMorphShape((MorphShapeTag) ti0, create); } if (ti0 instanceof DefineVideoStreamTag) { file = showImportFileChooser("filter.movies|*.flv", false); @@ -4543,14 +4637,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (svgText != null) { newTag = new SvgImporter().importSvg((ShapeTag) st, svgText); } else { - newTag = new ShapeImporter().importImage((ShapeTag) st, data); + 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); + newTag = new ShapeImporter().importImage((MorphShapeTag) st, data); } } SWF swf = st.getSwf(); @@ -4628,7 +4722,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return false; } - + public void replaceSpriteWithGifButtonActionPerformed(TreeItem item) { replaceSpriteWithGif(item); } @@ -4660,14 +4754,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Tag newTag = null; if (st instanceof ShapeTag) { if (svgText != null) { - newTag = new SvgImporter().importSvg((ShapeTag) st, svgText, false); + 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); + newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText, false); } else { newTag = new ShapeImporter().importImage((MorphShapeTag) st, data, 0, false); } @@ -4757,6 +4851,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } public File showImportFileChooser(String filter, boolean imagePreview) { + return showImportFileChooser(filter, imagePreview, null); + } + + public File showImportFileChooser(String filter, boolean imagePreview, String title) { String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; JFileChooser fc = new JFileChooser(); @@ -4816,6 +4914,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se first = false; } + if (title != null) { + fc.setDialogTitle(title); + } + if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { File result = fc.getSelectedFile(); Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); @@ -4823,7 +4925,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return null; - } + } private void showDetail(int card) { if (debugStackPanel.isActive() && card != DETAILCARDDEBUGSTACKFRAME) { @@ -4832,9 +4934,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se detailPanel.removeAll(); int pos = 0; - + if (card == DETAILCARDTAGINFO) { - detailPanel.addTab(AppStrings.translate("taginfo.header"), tagInfoPanel); + detailPanel.addTab(AppStrings.translate("taginfo.header"), tagInfoPanel); if (card == DETAILCARDTAGINFO) { detailPanel.setSelectedIndex(pos); } @@ -4845,15 +4947,15 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se detailPanel.addTab(AppStrings.translate("traits"), abcPanel.navigatorPanel); if (card == DETAILCARDAS3NAVIGATOR) { detailPanel.setSelectedIndex(pos); - } + } pos++; - } + } } if (debugStackPanel.isActive()) { detailPanel.addTab(AppStrings.translate("callStack.header"), debugStackPanel); if (card == DETAILCARDDEBUGSTACKFRAME) { detailPanel.setSelectedIndex(pos); - } + } pos++; } if (currentView != VIEW_DUMP) { @@ -5121,10 +5223,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se showTreePanelCard(DUMP_VIEW); treePanelMode = TreePanelMode.DUMP_TREE; //showDetail(DETAILCARDEMPTYPANEL); - showDetail(DETAILCARDEMPTYPANEL); + showDetail(DETAILCARDEMPTYPANEL); reload(true); updateUiWithCurrentOpenable(); - detailPanel.setVisible(false); + detailPanel.setVisible(false); return true; case VIEW_RESOURCES: pinsPanel.setVisible(true); @@ -5181,7 +5283,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } showTreePanelCard(TAGLIST_VIEW); treePanelMode = TreePanelMode.TAGLIST_TREE; - detailPanel.setVisible(true); + detailPanel.setVisible(true); refreshPins(); reload(true); updateUiWithCurrentOpenable(); @@ -5971,7 +6073,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return new Timelined() { private Timeline tim; - + @Override public Timeline getTimeline() { if (tim == null) { @@ -5991,6 +6093,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } + + private void initTimeline(Timeline timeline) { if (tag instanceof MorphShapeTag) { timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE; @@ -6005,6 +6109,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se f.layersChanged = true; timeline.addFrame(f); } + Frame f = new Frame(timeline, framesCnt); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + ds.ratio = 65535; + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); } else if (tag instanceof FontTag) { int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); int frame = fontFrameNum; @@ -6129,7 +6241,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se setDropTarget(null); disposeInner(this); Helper.emptyObject(this); - } + } public void updateMissingNeededCharacters() { if (calculateMissingNeededThread != null) { @@ -6179,7 +6291,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se }*/ } } - + private void calculateMissingNeededCharacters() { Map> missingNeededCharacters = new WeakHashMap<>(); Map> neededCharacters = new WeakHashMap<>(); @@ -6304,7 +6416,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return dialog; } - + public boolean fontEmbed(TreeItem item, boolean create) { return previewPanel.getFontPanel().fontEmbed(item, create); } diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 23306f6ae..b571e9059 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -190,6 +190,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel // Image tag buttons private JButton replaceShapeButton; + + private JButton replaceMorphShapeButton; private JButton replaceShapeUpdateBoundsButton; @@ -1302,6 +1304,16 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } }); replaceShapeButton.setVisible(false); + + replaceMorphShapeButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("importmorphshape16")); + replaceMorphShapeButton.setMargin(new Insets(3, 3, 3, 10)); + replaceMorphShapeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + mainPanel.replaceMorphShape((MorphShapeTag) mainPanel.getCurrentTree().getCurrentTreeItem(), false); + } + }); + replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton = new JButton(mainPanel.translate("button.replaceNoFill"), View.getIcon("importshape16")); replaceShapeUpdateBoundsButton.setMargin(new Insets(3, 3, 3, 10)); @@ -1363,6 +1375,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditButtonsPanel.add(displayEditEditPointsButton); //displayEditButtonsPanel.add(fixPathsButton); displayEditButtonsPanel.add(replaceShapeButton); + displayEditButtonsPanel.add(replaceMorphShapeButton); displayEditButtonsPanel.add(replaceShapeUpdateBoundsButton); displayEditButtonsPanel.add(morphShowPanel); return displayEditButtonsPanel; @@ -1723,10 +1736,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceImageButton.setVisible(showImage); replaceImageAlphaButton.setVisible(showAlpha); replaceSpriteButton.setVisible(showSprite); - replaceShapeButton.setVisible(showShape || showMorphShape); + replaceShapeButton.setVisible(showShape); + replaceMorphShapeButton.setVisible(showMorphShape); morphShowPanel.setVisible(showMorphShape); displayEditEditPointsButton.setVisible(showShape || showMorphShape); - replaceShapeUpdateBoundsButton.setVisible(showShape || showMorphShape); + replaceShapeUpdateBoundsButton.setVisible(showShape); replaceSoundButton.setVisible(showSound); replaceMovieButton.setVisible(showMovie); prevFontsButton.setVisible(false); @@ -2316,6 +2330,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceShapeUpdateBoundsButton.setVisible(true); displayEditEditPointsButton.setVisible(true); } + + if (displayEditTag instanceof MorphShapeTag) { + replaceMorphShapeButton.setVisible(true); + displayEditEditPointsButton.setVisible(true); + } if (displayEditTag instanceof DefineSpriteTag) { replaceSpriteButton.setVisible(true); @@ -2365,6 +2384,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditSaveButton.setVisible(true); displayEditCancelButton.setVisible(true); replaceShapeButton.setVisible(false); + replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); mainPanel.setEditingStatus(); @@ -2396,6 +2416,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditSaveButton.setVisible(true); displayEditCancelButton.setVisible(true); replaceShapeButton.setVisible(false); + replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); @@ -2496,6 +2517,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditCancelButton.setVisible(true); replaceShapeButton.setVisible(false); + replaceMorphShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); displayEditEditPointsButton.setVisible(false); @@ -2792,6 +2814,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (displayEditTag instanceof MorphShapeTag) { morphShowPanel.setVisible(true); + replaceMorphShapeButton.setVisible(true); displayEditEditPointsButton.setVisible(true); } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index e5026cd7f..251ce3c38 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -740,6 +740,3 @@ config.description.displayAs12PCodePanel = Show panel with disassembled P-code a config.name.displayAs3PCodePanel = Show AS3 P-code panel config.description.displayAs3PCodePanel = Show panel with disassembled P-code instructions for ActionScript 3 - -config.name.warning.replace.morphshape = Warn on replace morphshape -config.description.warning.replace.morphshape = Show warning on replacing morphshape about same start and end shape diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties index e1249c89c..0c7dbfc37 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties @@ -730,6 +730,3 @@ config.description.displayAs12PCodePanel = Zobrazit panel s akcemi disassemblova config.name.displayAs3PCodePanel = Zobrazit AS3 panel s P-k\u00f3dem config.description.displayAs3PCodePanel = Zobrazit panel s instrukcemi disassemblovan\u00e9ho P-k\u00f3du pro ActionScript 3 - -config.name.warning.replace.morphshape = Varovat p\u0159i nahrazov\u00e1n\u00ed morphshape -config.description.warning.replace.morphshape = Zobrazovat varov\u00e1n\u00ed p\u0159i nahrazov\u00e1n\u00ed morphshape o tom, \u017ee bude startov\u00e1n\u00ed a koncov\u00fd tvar stejn\u00fd diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index d29a8f8a5..4e3052553 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1204,8 +1204,9 @@ tag.movie.create = Create movie from file... tag.sound.create = Create sound from file... tag.font.create = Create font using dialog... tag.binaryData.create = Create binary data from file... -tag.morphshape.create = Create morphshape from shape file... +tag.morphshape.create = Create morphshape from shape files... -warning.replace.morphshape = In the following dialog you select a shape file.\r\n \ - That shape would be set as start and also the end shape of the morphshape.\r\n\ - You won't see any morphing animation until you edit start/end shape to be different. \ No newline at end of file +dialog.morphshape.startShape = Select start shape. +dialog.morphshape.endShape = Select end shape. Click cancel to make end shape same as the start shape. + +error.morphshape.incompatible = Cannot create morphshape: Start and end shape have incompatible fill/line styles. \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 5f96d7ea7..600ffc4b1 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1181,8 +1181,9 @@ tag.movie.create = Vytvo\u0159it video ze souboru... tag.sound.create = Vytvo\u0159it zvuk ze souboru... tag.font.create = Vytvo\u0159it p\u00edsmo pomoc\u00ed dialogu... tag.binaryData.create = Vytvo\u0159it bin\u00e1rn\u00ed data ze souboru... -tag.morphshape.create = Vytvo\u0159it morphshape ze souboru tvaru... +tag.morphshape.create = Vytvo\u0159it morphshape ze soubor\u016f tvar\u016f... -warning.replace.morphshape = V n\u00e1sleduj\u00edc\u00edm dialogu vyberete soubor s tvarem.\r\n \ - Ten tvar bude nastaven jako po\u010d\u00e1te\u010dn\u00ed a tak\u00e9 koncov\u00fd tvar tohot morphshape.\r\n\ - Neuvid\u00edte \u017e\u00e1dnou morfuj\u00edc\u00ed animaci do doby ne\u017e uprav\u00edte po\u010d\u00e1te\u010dn\u00ed/koncov\u00fd tvar tak, aby byly odli\u0161n\u00e9. \ No newline at end of file +dialog.morphshape.startShape = Vyberte po\u010d\u00e1te\u010dn\u00ed tvar. +dialog.morphshape.endShape = Vyberte koncov\u00fd tvar. Klikn\u011bte na Storno pro nastaven\u00ed stejn\u00e9ho jako je po\u010d\u00e1te\u010dn\u00ed. + +error.morphshape.incompatible = Nelze vytvo\u0159it morphshape: Po\u010d\u00e1te\u010dn\u00ed a koncov\u00fd tvar maj\u00ed nekompatibiln\u00ed styl v\u00fdpln\u011b/ohrani\u010den\u00ed. \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index ce15587b4..c775bb7ea 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -1039,11 +1039,15 @@ public class TagTreeContextMenu extends JPopupMenu { replaceMenuItem.setVisible(true); } - if (canReplace.test(it -> (it instanceof ShapeTag) || (it instanceof MorphShapeTag))) { + if (canReplace.test(it -> it instanceof ShapeTag)) { replaceMenuItem.setVisible(true); replaceNoFillMenuItem.setVisible(true); } - + + if (canReplace.test(it -> it instanceof MorphShapeTag)) { + replaceMenuItem.setVisible(true); + } + if (canReplace.test(it -> it instanceof DefineBinaryDataTag)) { replaceMenuItem.setVisible(true); } @@ -1660,11 +1664,11 @@ public class TagTreeContextMenu extends JPopupMenu { case TagTreeModel.FOLDER_MORPHSHAPES: addTagMenu.addSeparator(); JMenuItem createMorphShapeItem = new JMenuItem(AppStrings.translate("tag.morphshape.create")); - createMorphShapeItem.setIcon(View.getIcon("importshape16")); + createMorphShapeItem.setIcon(View.getIcon("importmorphshape16")); createMorphShapeItem.addActionListener((ActionEvent ae) -> { listener.call(ae, item, DefineMorphShape2Tag.class, TreeNodeType.MORPH_SHAPE); }); - addTagMenu.add(createMorphShapeItem); + addTagMenu.add(createMorphShapeItem); break; } } @@ -3822,10 +3826,12 @@ public class TagTreeContextMenu extends JPopupMenu { case SPRITE: remove = !mainPanel.replaceSpriteWithGif(tag); break; - case SHAPE: - case MORPH_SHAPE: + case SHAPE: remove = !mainPanel.replaceNoFill(tag); break; + case MORPH_SHAPE: + remove = !mainPanel.replaceMorphShape((MorphShapeTag) tag, true); + break; case FONT: remove = !mainPanel.fontEmbed(tag, true); break;