diff --git a/CHANGELOG.md b/CHANGELOG.md index 563399ba5..7ee6e9398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - AS3 Metadata in P-code formatting - AS3 Metadata single value (null item key) - [#1981] AS3 star import collisions +- [#1982] Slow calculation of large shape outlines - now use only rectangles for large shapes ### Changed - AS1/2/3 P-code - format Number values with EcmaScript toString function @@ -2983,6 +2984,7 @@ All notable changes to this project will be documented in this file. [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 [#1981]: https://www.free-decompiler.com/flash/issues/1981 +[#1982]: https://www.free-decompiler.com/flash/issues/1982 [#1970]: https://www.free-decompiler.com/flash/issues/1970 [#1972]: https://www.free-decompiler.com/flash/issues/1972 [#1973]: https://www.free-decompiler.com/flash/issues/1973 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineScalingGridTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineScalingGridTag.java index 943fc826e..c5e83e098 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineScalingGridTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineScalingGridTag.java @@ -116,7 +116,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag { } public RECT getRect() { - Shape s = getOutline(0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true, new ExportRectangle(0, 0, 1, 1)/*?*/, 1); + Shape s = getOutline(true, 0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true, new ExportRectangle(0, 0, 1, 1)/*?*/, 1); if (s == null) { return null; } @@ -124,7 +124,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag { return new RECT(r.x, r.x + r.width, r.y, r.y + r.height); } - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked, ExportRectangle viewRect, double unzoom) { + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked, ExportRectangle viewRect, double unzoom) { CharacterTag ct = swf.getCharacter(characterId); if (ct == null) { return null; @@ -135,7 +135,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag { double[] coords = new double[6]; DrawableTag dt = (DrawableTag) ct; - Shape path = dt.getOutline(frame, time, ratio, renderContext, transformation, stroked, viewRect, unzoom); + Shape path = dt.getOutline(fast, frame, time, ratio, renderContext, transformation, stroked, viewRect, unzoom); PathIterator iterator = path.getPathIterator(new AffineTransform()); GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD); ExportRectangle boundsRect = new ExportRectangle(dt.getRect()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index f83f1093c..76186b9c9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -400,8 +400,8 @@ public class DefineSpriteTag extends DrawableTag implements Timelined { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { - return getTimeline().getOutline(frame, time, renderContext, transformation, stroked, viewRect, unzoom); + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + return getTimeline().getOutline(fast, frame, time, renderContext, transformation, stroked, viewRect, unzoom); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineVideoStreamTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineVideoStreamTag.java index c957e1dcd..00c149576 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineVideoStreamTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineVideoStreamTag.java @@ -325,7 +325,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { return transformation.toTransform().createTransformedShape(new Rectangle2D.Double(0, 0, width * SWF.unitDivisor, height * SWF.unitDivisor)); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java index 070763fa0..ab71df9e4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java @@ -88,8 +88,8 @@ public abstract class ButtonTag extends DrawableTag implements Timelined { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { - return getTimeline().getOutline(frame, time, renderContext, transformation, stroked, viewRect, unzoom); + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + return getTimeline().getOutline(fast, frame, time, renderContext, transformation, stroked, viewRect, unzoom); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java index b5992c410..a4e7f7f05 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java @@ -44,8 +44,21 @@ public abstract class DrawableTag extends CharacterTag implements BoundedTag { public abstract int getUsedParameters(); - public abstract Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom); - + /** + * Calculates drawable outline. + * @param fast When the structure is large, can approximate to rectangles instead of being slow. + * @param frame + * @param time + * @param ratio + * @param renderContext + * @param transformation + * @param stroked + * @param viewRect + * @param unzoom + * @return + */ + public abstract Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom); + public abstract void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing); public abstract void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index f1b0e647c..3bedf5f2f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -427,7 +427,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { RECT r = getRect(); return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight())); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java index 808aea280..e5f07fbdc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java @@ -249,8 +249,8 @@ public abstract class ImageTag extends DrawableTag { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { - return transformation.toTransform().createTransformedShape(getShape(1).getOutline(1, swf, stroked)); + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + return transformation.toTransform().createTransformedShape(getShape(1).getOutline(fast, 1, swf, stroked)); } @Override 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 254329053..3d84a7444 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 @@ -349,8 +349,8 @@ public abstract class MorphShapeTag extends DrawableTag { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { - return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(getShapeNum() == 2 ? 4 : 1, swf, stroked)); + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(fast, getShapeNum() == 2 ? 4 : 1, swf, stroked)); } @Override 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 c7bdaa4dd..9b2914ab8 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 @@ -183,8 +183,8 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { - return transformation.toTransform().createTransformedShape(getShapes().getOutline(getShapeNum(), swf, stroked)); + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + return transformation.toTransform().createTransformedShape(getShapes().getOutline(fast, getShapeNum(), swf, stroked)); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index e205bba82..42083fc0c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -787,7 +787,7 @@ public abstract class TextTag extends DrawableTag { } @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { RECT r = getBounds(); Shape shp = new Rectangle.Double(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()); return transformation.toTransform().createTransformedShape(shp); //TODO: match character shapes (?) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 4840f642b..88add8809 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -813,7 +813,7 @@ public class Timeline { int dy = (int) (viewRect.yMin * unzoom); Point cursorPositionInView = new Point((int) Math.round(renderContext.cursorPosition.x * unzoom) - dx, (int) Math.round(renderContext.cursorPosition.y * unzoom) - dy); - Shape buttonShape = drawable.getOutline(ButtonTag.FRAME_HITTEST, dtime, ratio, renderContext, absMat, true, viewRect, unzoom); + Shape buttonShape = ((ButtonTag) drawable).getOutline(true, ButtonTag.FRAME_HITTEST, dtime, ratio, renderContext, absMat, true, viewRect, unzoom); if (buttonShape.contains(cursorPositionInView)) { renderContext.mouseOverButton = (ButtonTag) drawable; if (renderContext.mouseButton > 0) { @@ -980,7 +980,7 @@ public class Timeline { renderContext.stateUnderCursor.add(layer); } } else if (absMat.transform(new ExportRectangle(boundRect)).contains(cursorPositionInView)) { - Shape shape = drawable.getOutline(dframe, dtime, layer.ratio, renderContext, absMat, true, viewRect, unzoom); + Shape shape = drawable.getOutline(true, dframe, dtime, layer.ratio, renderContext, absMat, true, viewRect, unzoom); if (shape.contains(cursorPositionInView)) { renderContext.stateUnderCursor.add(layer); } @@ -991,7 +991,7 @@ public class Timeline { Graphics2D g2 = (Graphics2D) renderContext.borderImage.getGraphics(); g2.setPaint(Color.red); g2.setStroke(new BasicStroke(2)); - Shape shape = drawable.getOutline(dframe, dtime, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)), true, viewRect, unzoom); + Shape shape = drawable.getOutline(true, dframe, dtime, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)), true, viewRect, unzoom); g2.draw(shape); } if (!(sameImage && canUseSameImage)) { @@ -1341,7 +1341,7 @@ public class Timeline { } } - public Shape getOutline(int frame, int time, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { + public Shape getOutline(boolean fast, int frame, int time, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) { Frame fr = getFrame(frame); Area area = new Area(); Stack clips = new Stack<>(); @@ -1378,7 +1378,7 @@ public class Timeline { dframe = ButtonTag.FRAME_UP; if (renderContext.cursorPosition != null) { ButtonTag buttonTag = (ButtonTag) character; - Shape buttonShape = buttonTag.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom); + Shape buttonShape = buttonTag.getOutline(fast, ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom); int dx = (int) Math.round(viewRect.xMin * unzoom); int dy = (int) Math.round(viewRect.yMin * unzoom); Point cursorPositionInView = new Point((int) Math.round(renderContext.cursorPosition.x * unzoom) - dx, (int) Math.round(renderContext.cursorPosition.y * unzoom) - dy); @@ -1392,7 +1392,7 @@ public class Timeline { } } - Shape cshape = ((DrawableTag) character).getOutline(dframe, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom); + Shape cshape = ((DrawableTag) character).getOutline(fast, dframe, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom); Area addArea = new Area(cshape); if (currentClip != null) { Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java index a6b8b074a..f400f5f11 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java @@ -47,6 +47,7 @@ public class SHAPE implements NeedsCharacters, Serializable { public List shapeRecords; private Shape cachedOutline; + private Shape fastCachedOutline; @Override public void getNeededCharacters(Set needed) { @@ -86,27 +87,50 @@ public class SHAPE implements NeedsCharacters, Serializable { public void clearCachedOutline() { cachedOutline = null; + fastCachedOutline = null; } - public Shape getOutline(int shapeNum, SWF swf, boolean stroked) { + /** + * + * @param fast When the shape is large, can approximate to rectangles instead of being slow. + * @param shapeNum Version of DefineShape, 2 for DefineShape2 etc. + * @param swf + * @param stroked + * @return + */ + public Shape getOutline(boolean fast, int shapeNum, SWF swf, boolean stroked) { if (cachedOutline != null) { return cachedOutline; } + if (fast && fastCachedOutline != null) { + return fastCachedOutline; + } List strokes = new ArrayList<>(); List paths = PathExporter.export(shapeNum, swf, this, strokes); - Area area = new Area(); + boolean large = shapeRecords.size() > 500; + + if (!large) { + fast = false; + } + + Area area = new Area(); for (GeneralPath path : paths) { - area.add(new Area(path)); + area.add(new Area(fast ? path.getBounds2D() : path)); } if (stroked) { for (GeneralPath path : strokes) { - area.add(new Area(path)); + area.add(new Area(fast ? path.getBounds2D() : path)); } - } + } - cachedOutline = area; + if (fast) { + fastCachedOutline = area; + } else { + fastCachedOutline = null; + cachedOutline = area; + } return area; } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index dc809a20c..a4074263a 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -2978,7 +2978,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } int dframe = time % drawableFrameCount; - Shape outline = dt.getOutline(dframe, time, ds.ratio, renderContext, Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))), true, viewRect, zoom); + Matrix transformation = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))); + RECT dtRect = dt.getRect(); + Rectangle2D dtRect2D = new Rectangle2D.Double(dtRect.Xmin, dtRect.Ymin, dtRect.getWidth(), dtRect.getHeight()); + Shape outline = transformation.toTransform().createTransformedShape(dtRect2D); + //dt.getOutline(dframe, time, ds.ratio, renderContext, Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))), true, viewRect, zoom); Rectangle bounds = outline.getBounds(); gg.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT, @@ -3007,10 +3011,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int dframe = time % drawableFrameCount; //Matrix finalMatrix = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m).concatenate(new Matrix(ds.matrix)); - Shape outline = dt.getOutline(dframe, time, ds.ratio, renderContext, transform, true, viewRect, zoom); + Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, transform, true, viewRect, zoom); if (temporaryMatrix != null) { - Shape tempOutline = dt.getOutline(dframe, time, ds.ratio, renderContext, temporaryMatrix, true, viewRect, zoom); + Shape tempOutline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, temporaryMatrix, true, viewRect, zoom); gg.setStroke(new BasicStroke(1)); gg.setPaint(Color.black); gg.draw(tempOutline); @@ -3995,7 +3999,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (lowQuality) { zoomDouble /= LQ_FACTOR; } - Shape outline = dt.getOutline(dframe, time, ds.ratio, renderContext, b.concatenate(new Matrix(ds.matrix)), true, _viewRect, zoomDouble); + Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, b.concatenate(new Matrix(ds.matrix)), true, _viewRect, zoomDouble); return outline.getBounds2D(); } }