diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 385a7cc7f..d5a8df9de 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -166,8 +166,10 @@ import com.jpexs.helpers.ProgressListener; import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; import java.awt.AlphaComposite; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; +import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; @@ -2753,7 +2755,7 @@ public final class SWF implements SWFContainerItem, Timelined { return exporter.getUniqueId("tag"); } - public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) { + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) { SWF swf = timeline.swf; String key = "frame_" + frame + "_" + time + "_" + timeline.id + "_" + swf.hashCode() + "_" + zoom; SerializableImage image; @@ -2784,9 +2786,9 @@ public final class SWF implements SWFContainerItem, Timelined { m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); m.scale(zoom); RenderContext renderContext = new RenderContext(); - renderContext.stateUnderCursor = stateUnderCursor; + renderContext.cursorPosition = cursorPosition; renderContext.mouseButton = mouseButton; - frameToImage(timeline, frame, time, renderContext, image, m, colorTransform); + frameToImage(timeline, frame, time, renderContext, image, false, m, absoluteTransformation, colorTransform); if (useCache) { swf.putToCache(key, image); } @@ -2794,7 +2796,7 @@ public final class SWF implements SWFContainerItem, Timelined { return image; } - public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, RenderContext renderContext, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, ColorTransform colorTransform, double zoom) { + public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, RenderContext renderContext, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, double zoom) { RECT rect = displayRect; for (int f = 0; f < timeline.getFrameCount(); f++) { SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, @@ -2802,12 +2804,12 @@ public final class SWF implements SWFContainerItem, Timelined { image.fillTransparent(); Matrix m = new Matrix(); m.translate(-rect.Xmin, -rect.Ymin); - frameToImage(timeline, f, 0, renderContext, image, m, colorTransform); + frameToImage(timeline, f, 0, renderContext, image, false, m, absoluteTransformation, colorTransform); ret.add(image); } } - public static void frameToImage(Timeline timeline, int frame, int time, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public static void frameToImage(Timeline timeline, int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { double unzoom = SWF.unitDivisor; if (timeline.getFrameCount() <= frame) { return; @@ -2841,8 +2843,9 @@ public final class SWF implements SWFContainerItem, Timelined { } CharacterTag character = timeline.swf.getCharacter(layer.characterId); - Matrix mat = new Matrix(layer.matrix); - mat = mat.preConcatenate(transformation); + Matrix layerMatrix = new Matrix(layer.matrix); + Matrix mat = transformation.concatenate(layerMatrix); + Matrix absMat = absoluteTransformation.concatenate(layerMatrix); if (colorTransform == null) { colorTransform = new ColorTransform(); @@ -2862,24 +2865,6 @@ public final class SWF implements SWFContainerItem, Timelined { drawableFrameCount = 1; } - int dframe; - if (timeline.fontFrameNum != -1) { - dframe = timeline.fontFrameNum; - } else { - dframe = time % drawableFrameCount; - } - - if (character instanceof ButtonTag) { - dframe = ButtonTag.FRAME_UP; - if (renderContext.stateUnderCursor == layer) { - if (renderContext.mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - } - } - RECT boundRect = drawable.getRect(); ExportRectangle rect = new ExportRectangle(boundRect); rect = mat.transform(rect); @@ -2926,11 +2911,34 @@ public final class SWF implements SWFContainerItem, Timelined { img = renderContext.shapeCache.get(cacheKey); } + int dframe; + if (timeline.fontFrameNum != -1) { + dframe = timeline.fontFrameNum; + } else { + dframe = time % drawableFrameCount; + } + + if (character instanceof ButtonTag) { + dframe = ButtonTag.FRAME_UP; + if (renderContext.cursorPosition != null) { + Shape buttonShape = drawable.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, absMat); + if (buttonShape.contains(renderContext.cursorPosition)) { + renderContext.mouseOverButton = (ButtonTag) character; + if (renderContext.mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + } + } + } + + int stateCount = renderContext.stateUnderCursor == null ? 0 : renderContext.stateUnderCursor.size(); if (img == null) { img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); img.fillTransparent(); - drawable.toImage(dframe, time, layer.ratio, renderContext, img, m, clrTrans); + drawable.toImage(dframe, time, layer.ratio, renderContext, img, isClip || layer.clipDepth > -1, m, absMat, clrTrans); if (cacheKey != null) { renderContext.shapeCache.put(cacheKey, img); @@ -3031,6 +3039,27 @@ public final class SWF implements SWFContainerItem, Timelined { g.setTransform(AffineTransform.getTranslateInstance(0, 0)); g.setClip(clip.shape); } else { + if (renderContext.cursorPosition != null) { + if (drawable instanceof DefineSpriteTag) { + if (renderContext.stateUnderCursor.size() > stateCount) { + renderContext.stateUnderCursor.add(layer); + } + } else if (absMat.transform(new ExportRectangle(boundRect)).contains(renderContext.cursorPosition)) { + Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat); + if (shape.contains(renderContext.cursorPosition)) { + renderContext.stateUnderCursor.add(layer); + } + } + } + + if (renderContext.borderImage != null) { + Graphics2D g2 = (Graphics2D) renderContext.borderImage.getGraphics(); + g2.setPaint(Color.red); + g2.setStroke(new BasicStroke(2)); + Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor))); + g2.draw(shape); + } + g.setTransform(trans); g.drawImage(img.getBufferedImage(), 0, 0, null); } @@ -3039,9 +3068,10 @@ public final class SWF implements SWFContainerItem, Timelined { } if (showPlaceholder) { - mat.translateX /= unzoom; - mat.translateY /= unzoom; - AffineTransform trans = mat.toTransform(); + Matrix mat2 = mat.clone(); + mat2.translateX /= unzoom; + mat2.translateY /= unzoom; + AffineTransform trans = mat2.toTransform(); g.setTransform(trans); BoundedTag b = (BoundedTag) character; g.setPaint(new Color(255, 255, 255, 128)); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index d7c7e14de..16acfd50f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -359,7 +359,7 @@ public class FrameExporter { } int fframe = fframes.get(pos++); - BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), new ColorTransform(), fbackgroundColor, false, settings.zoom).getBufferedImage(); + BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), new Matrix(), new ColorTransform(), fbackgroundColor, false, settings.zoom).getBufferedImage(); if (evl != null) { evl.handleExportedEvent("frame", pos, fframes.size(), tagName); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index 5a6186d48..248c9d3ca 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -122,7 +122,7 @@ public class ShapeExporter { Matrix m = new Matrix(); m.translate(-rect.Xmin, -rect.Ymin); m.scale(settings.zoom); - st.toImage(0, 0, 0, new RenderContext(), img, m, new CXFORMWITHALPHA()); + st.toImage(0, 0, 0, new RenderContext(), img, false, m, m, new CXFORMWITHALPHA()); if (settings.mode == ShapeExportMode.PNG) { ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, file); } else { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/ExportRectangle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/ExportRectangle.java index 8973b9b3b..47754be11 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/ExportRectangle.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/ExportRectangle.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.exporters.commonshape; import com.jpexs.decompiler.flash.types.RECT; +import java.awt.Point; import java.awt.geom.Rectangle2D; /** @@ -62,6 +63,12 @@ public class ExportRectangle { return yMax - yMin; } + public boolean contains(Point point) { + int x = point.x; + int y = point.y; + return xMin <= x && xMax >= x && yMin <= y && yMax >= y; + } + @Override public int hashCode() { long bits = Double.doubleToLongBits(xMin); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index 16754df5f..342bd4d21 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -916,7 +916,7 @@ public class DefineEditTextTag extends TextTag { } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { render(TextRenderMode.BITMAP, image, null, null, transformation, colorTransform, 1); } 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 9217ac647..fe7eb7c77 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 @@ -366,8 +366,8 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SWF.frameToImage(getTimeline(), frame, time, renderContext, image, transformation, colorTransform); + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { + SWF.frameToImage(getTimeline(), frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform); } @Override 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 1380ef7ef..93db4e8ab 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 @@ -92,8 +92,8 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SWF.frameToImage(getTimeline(), frame, time, renderContext, image, transformation, colorTransform); + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { + SWF.frameToImage(getTimeline(), frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform); } @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 71b1895ee..13fa82c65 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 @@ -39,7 +39,7 @@ public interface DrawableTag extends BoundedTag { public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation); - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform); + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform); public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) 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 265db2a6b..c4e8e46b0 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 @@ -323,7 +323,7 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); } 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 9d1f4e9ad..2359d4a22 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 @@ -283,7 +283,7 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { BitmapExporter.export(swf, getShape(), null, image, transformation, colorTransform); } 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 5a8802b51..4d822b8b7 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 @@ -279,7 +279,7 @@ public abstract class MorphShapeTag extends CharacterTag implements DrawableTag } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { SHAPEWITHSTYLE shape = getShapeAtRatio(ratio); // morphShape using shapeNum=3, morphShape2 using shapeNum=4 // todo: Currently the generated image is not cached, because the cache diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java index 9c1db97b0..cd2d48b80 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java @@ -18,7 +18,9 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.helpers.SerializableImage; +import java.awt.Point; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -27,9 +29,15 @@ import java.util.Map; */ public class RenderContext { - public DepthState stateUnderCursor; + public Point cursorPosition; + + public List stateUnderCursor; public int mouseButton; + public ButtonTag mouseOverButton; + public Map shapeCache = new HashMap<>(); + + public SerializableImage borderImage; } 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 0940158d7..5a74afa49 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 @@ -132,7 +132,7 @@ public abstract class ShapeTag extends CharacterTag implements DrawableTag, Lazy } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { BitmapExporter.export(swf, getShapes(), null, image, transformation, colorTransform); if (Configuration.debugMode.get()) { // show control points List paths = PathExporter.export(swf, getShapes()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java index b85639f10..310ea21cd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/StaticTextTag.java @@ -654,7 +654,7 @@ public abstract class StaticTextTag extends TextTag { } @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { staticTextToImage(swf, textRecords, getTextNum(), image, textMatrix, transformation, colorTransform); /*try { TextTag originalTag = (TextTag) getOriginalTag(); 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 681fcca6a..ae33ebdb1 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 @@ -45,6 +45,7 @@ import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.filters.FILTER; import com.jpexs.helpers.SerializableImage; +import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Area; @@ -529,8 +530,8 @@ public class Timeline { return modified; } - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SWF.frameToImage(this, frame, time, renderContext, image, transformation, colorTransform); + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) { + SWF.frameToImage(this, frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform); } public void toHtmlCanvas(StringBuilder result, double unitDivisor, List frames) { @@ -568,7 +569,7 @@ public class Timeline { } } - public void getObjectsOutlines(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation, List objs, List outlines) { + public void getObjectsOutlines(int frame, int time, int ratio, Point cursorPosition, int mouseButton, Matrix transformation, List objs, List outlines) { Frame fr = getFrame(frame); Stack clips = new Stack<>(); for (int d = maxDepth; d >= 0; d--) { @@ -592,8 +593,7 @@ public class Timeline { CharacterTag character = swf.getCharacter(layer.characterId); if (character instanceof DrawableTag) { DrawableTag drawable = (DrawableTag) character; - Matrix m = new Matrix(layer.matrix); - m = m.preConcatenate(transformation); + Matrix m = transformation.concatenate(new Matrix(layer.matrix)); int drawableFrameCount = drawable.getNumFrames(); if (drawableFrameCount == 0) { @@ -616,7 +616,7 @@ public class Timeline { } RenderContext renderContext = new RenderContext(); - renderContext.stateUnderCursor = stateUnderCursor; + renderContext.cursorPosition = cursorPosition; renderContext.mouseButton = mouseButton; Shape cshape = ((DrawableTag) character).getOutline(dframe, layer.time + time, layer.ratio, renderContext, m); @@ -634,7 +634,7 @@ public class Timeline { outlines.add(addArea); } if (character instanceof Timelined) { - ((Timelined) character).getTimeline().getObjectsOutlines(dframe, time + layer.time, layer.ratio, stateUnderCursor, mouseButton, m, objs, outlines); + ((Timelined) character).getTimeline().getObjectsOutlines(dframe, time + layer.time, layer.ratio, cursorPosition, mouseButton, m, objs, outlines); } } } @@ -672,12 +672,14 @@ public class Timeline { if (drawableFrameCount == 0) { drawableFrameCount = 1; } + int dframe = (time + layer.time) % drawableFrameCount; - if (character instanceof Timelined) { - if (character instanceof ButtonTag) { - ButtonTag bt = (ButtonTag) character; - dframe = ButtonTag.FRAME_UP; - if (renderContext.stateUnderCursor == layer) { + if (character instanceof ButtonTag) { + dframe = ButtonTag.FRAME_UP; + if (renderContext.cursorPosition != null) { + ButtonTag buttonTag = (ButtonTag) character; + Shape buttonShape = buttonTag.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m); + if (buttonShape.contains(renderContext.cursorPosition)) { if (renderContext.mouseButton > 0) { dframe = ButtonTag.FRAME_DOWN; } else { @@ -688,13 +690,13 @@ public class Timeline { } Shape cshape = ((DrawableTag) character).getOutline(dframe, time + layer.time, layer.ratio, renderContext, m); - Area addArea = new Area(cshape); if (currentClip != null) { Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); a.subtract(new Area(currentClip.shape)); addArea.subtract(a); } + if (layer.clipDepth > -1) { Clip clip = new Clip(addArea, layer.clipDepth); clips.push(clip); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index e83fcb259..dc60af652 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1824,7 +1824,7 @@ public class Graph { pos++; continue; }*/ - //if (p != defaultPart) + //if (p != defaultPart) { if (vis.contains(p)) { valueMappings.add(caseCommands.size() - 1); @@ -1849,7 +1849,7 @@ public class Graph { if (next != p) { //if (p == defaultPart && !defaultCommands.isEmpty()) { //ignore - //} else + //} else { TranslateStack s2 = (TranslateStack) stack.clone(); s2.clear(); @@ -1890,7 +1890,7 @@ public class Graph { } } } - //remove last break from last section + //remove last break from last section if (!caseCommands.isEmpty()) { List lastc = caseCommands.get(caseCommands.size() - 1); if (!lastc.isEmpty() && (lastc.get(lastc.size() - 1) instanceof BreakItem)) { diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 49ab02eb5..ba6c3ab44 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -1931,7 +1931,7 @@ public class CommandLineArgumentParser { //displayRect.Ymax *= zoom; Matrix m = new Matrix(); //m.scale(zoom); - BufferedImage img = SWF.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect, m, new ColorTransform(), Color.white, false, zoom).getBufferedImage(); + BufferedImage img = SWF.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect, m, m, new ColorTransform(), Color.white, false, zoom).getBufferedImage(); PageFormat pf = new PageFormat(); pf.setOrientation(PageFormat.PORTRAIT); Paper p = new Paper(); diff --git a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java index 50911b63c..d15ec74c8 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -308,7 +308,7 @@ public class FolderPreviewPanel extends JPanel { zoom = ratio; } } - imgSrc = SWF.frameToImageGet(swf.getTimeline(), fn.frame, fn.frame, null, 0, rect, new Matrix(), new ColorTransform(), null, true, zoom); + imgSrc = SWF.frameToImageGet(swf.getTimeline(), fn.frame, fn.frame, null, 0, rect, new Matrix(), new Matrix(), new ColorTransform(), null, true, zoom); width = imgSrc.getWidth(); height = imgSrc.getHeight(); } else if (treeItem instanceof ImageTag) { @@ -356,7 +356,7 @@ public class FolderPreviewPanel extends JPanel { image.fillTransparent(); if (imgSrc == null) { DrawableTag drawable = (DrawableTag) treeItem; - drawable.toImage(0, 0, 0, new RenderContext(), image, m, new ColorTransform()); + drawable.toImage(0, 0, 0, new RenderContext(), image, false, m, m, new ColorTransform()); } else { Graphics2D g = (Graphics2D) image.getGraphics(); g.setTransform(m.toTransform()); diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 8fd184b4f..577409fbe 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -43,7 +43,6 @@ import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.ConstantColorColorTransform; import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.SerializableImage; import java.awt.AlphaComposite; @@ -108,7 +107,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private final JLabel debugLabel = new JLabel("-"); - private DepthState stateUnderCursor = null; + private Point cursorPosition = null; private MouseEvent lastMouseEvent = null; @@ -162,9 +161,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private Rectangle rect = null; - private List dss; - - private List outlines; + private ButtonTag mouseOverButton = null; private boolean autoFit = false; @@ -220,27 +217,12 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return img.getBufferedImage(); } - public synchronized void setOutlines(List dss, List outlines) { - this.outlines = outlines; - this.dss = dss; - } - public void setImg(SerializableImage img) { this.img = img; calcRect(); repaint(); } - public synchronized List getObjectsUnderPoint(Point p) { - List ret = new ArrayList<>(); - for (int i = 0; i < outlines.size(); i++) { - if (outlines.get(i).contains(p)) { - ret.add(dss.get(i)); - } - } - return ret; - } - public Rectangle getRect() { return rect; } @@ -249,6 +231,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (img == null) { return null; } + return new Point((p.x - rect.x) * img.getWidth() / rect.width, (p.y - rect.y) * img.getHeight() / rect.height); } @@ -341,11 +324,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } private void updatePos(Timelined timelined, MouseEvent lastMouseEvent, Timer thisTimer) { - boolean handCursor = false; - DepthState newStateUnderCursor = null; if (timelined != null) { - Timeline tim = ((Timelined) timelined).getTimeline(); BoundedTag bounded = (BoundedTag) timelined; RECT rect = bounded.getRect(); int width = rect.getWidth(); @@ -358,57 +338,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay { m.scale(scale); Point p = lastMouseEvent == null ? null : lastMouseEvent.getPoint(); - List objs = new ArrayList<>(); - StringBuilder ret = new StringBuilder(); synchronized (ImagePanel.class) { if (timer == thisTimer) { - p = p == null ? null : iconPanel.toImagePoint(p); - if (p != null) { - int x = p.x; - int y = p.y; - objs = iconPanel.getObjectsUnderPoint(p); - - ret.append(" [").append(x).append(",").append(y).append("] : "); - } - } - } - - boolean first = true; - for (int i = 0; i < objs.size(); i++) { - DepthState ds = objs.get(i); - if (!first) { - ret.append(", "); - } - first = false; - CharacterTag c = tim.swf.getCharacter(ds.characterId); - if (c instanceof ButtonTag) { - newStateUnderCursor = ds; - handCursor = true; - } - ret.append(c.toString()); - if (timelined instanceof ButtonTag) { - handCursor = true; - } - } - if (first) { - ret.append(" - "); - } - - synchronized (ImagePanel.class) { - if (timer == thisTimer) { - debugLabel.setText(ret.toString()); - - if (handCursor) { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else if (iconPanel.hasAllowMove()) { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - } else { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - if (newStateUnderCursor != stateUnderCursor) { - stateUnderCursor = newStateUnderCursor; - } + cursorPosition = p; } } } @@ -458,7 +391,6 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public void mouseExited(MouseEvent e) { synchronized (ImagePanel.class) { - stateUnderCursor = null; lastMouseEvent = null; hideMouseSelection(); redraw(); @@ -471,9 +403,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay { mouseButton = e.getButton(); lastMouseEvent = e; redraw(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.getCharacter(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); + ButtonTag button = iconPanel.mouseOverButton; + if (button != null) { + DefineButtonSoundTag sounds = button.getSounds(); if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar2), timer); } @@ -487,9 +419,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay { mouseButton = 0; lastMouseEvent = e; redraw(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.getCharacter(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); + ButtonTag button = iconPanel.mouseOverButton; + if (button != null) { + DefineButtonSoundTag sounds = button.getSounds(); if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar3), timer); } @@ -504,27 +436,6 @@ public final class ImagePanel extends JPanel implements MediaDisplay { synchronized (ImagePanel.class) { lastMouseEvent = e; redraw(); - DepthState lastUnderCur = stateUnderCursor; - if (stateUnderCursor != null) { - if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { - // New mouse entered - ButtonTag b = (ButtonTag) swf.getCharacter(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp - playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar1), timer); - } - } - } - if (lastUnderCur != null) { - if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { - // Old mouse leave - ButtonTag b = (ButtonTag) swf.getCharacter(lastUnderCur.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle - playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar0), timer); - } - } - } } } @@ -714,7 +625,6 @@ public final class ImagePanel extends JPanel implements MediaDisplay { stillFrame = true; zoomAvailable = false; iconPanel.setImg(image); - iconPanel.setOutlines(new ArrayList<>(), new ArrayList<>()); drawReady = true; fireMediaDisplayStateChanged(); @@ -744,14 +654,13 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Matrix m = new Matrix(); m.translate(-rect.Xmin * zoomDouble, -rect.Ymin * zoomDouble); m.scale(zoomDouble); - textTag.toImage(0, 0, 0, new RenderContext(), image, m, new ConstantColorColorTransform(0xFFC0C0C0)); + textTag.toImage(0, 0, 0, new RenderContext(), image, false, m, m, new ConstantColorColorTransform(0xFFC0C0C0)); if (newTextTag != null) { - newTextTag.toImage(0, 0, 0, new RenderContext(), image, m, new ConstantColorColorTransform(0xFF000000)); + newTextTag.toImage(0, 0, 0, new RenderContext(), image, false, m, m, new ConstantColorColorTransform(0xFF000000)); } iconPanel.setImg(image); - iconPanel.setOutlines(new ArrayList<>(), new ArrayList<>()); drawReady = true; fireMediaDisplayStateChanged(); @@ -759,7 +668,6 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private synchronized void clearImagePanel() { iconPanel.setImg(null); - iconPanel.setOutlines(new ArrayList<>(), new ArrayList<>()); } @Override @@ -850,10 +758,13 @@ public final class ImagePanel extends JPanel implements MediaDisplay { fireMediaDisplayStateChanged(); } - private static SerializableImage getFrame(SWF swf, int frame, int time, Timelined drawable, DepthState stateUnderCursor, int mouseButton, int selectedDepth, double zoom) { + private static SerializableImage getFrame(SWF swf, int frame, int time, Timelined drawable, RenderContext renderContext, int selectedDepth, double zoom) { Timeline timeline = drawable.getTimeline(); - //String key = "drawable_" + frame + "_" + drawable.hashCode() + "_" + mouseButton + "_depth" + selectedDepth + "_" + (stateUnderCursor == null ? "out" : stateUnderCursor.hashCode()) + "_" + zoom + "_" + timeline.fontFrameNum; - SerializableImage img;// = swf.getFromCache(key); + //int mouseButton = renderContext.mouseButton; + //Point cursorPosition = renderContext.cursorPosition; + //String key = "drawable_" + frame + "_" + drawable.hashCode() + "_" + mouseButton + "_depth" + selectedDepth + "_" + (cursorPosition == null ? "out" : cursorPosition.hashCode()) + "_" + zoom + "_" + timeline.fontFrameNum; + SerializableImage img; + //SerializableImage img = swf.getFromCache(key); //if (img == null) { //boolean shouldCache = timeline.isSingleFrame(frame); RECT rect = drawable.getRect(); @@ -862,21 +773,17 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int height = (int) (rect.getHeight() * zoom); SerializableImage image = new SerializableImage((int) Math.ceil(width / SWF.unitDivisor), (int) Math.ceil(height / SWF.unitDivisor), SerializableImage.TYPE_INT_ARGB); + //renderContext.borderImage = new SerializableImage(image.getWidth(), image.getHeight(), SerializableImage.TYPE_INT_ARGB); image.fillTransparent(); Matrix m = new Matrix(); m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); m.scale(zoom); - RenderContext renderContext = new RenderContext(); - renderContext.stateUnderCursor = stateUnderCursor; - renderContext.mouseButton = mouseButton; - timeline.toImage(frame, time, frame, renderContext, image, m, new ColorTransform()); + timeline.toImage(frame, time, frame, renderContext, image, false, m, m, new ColorTransform()); Graphics2D gg = (Graphics2D) image.getGraphics(); gg.setStroke(new BasicStroke(3)); gg.setPaint(Color.green); gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - List dss = new ArrayList<>(); - List os = new ArrayList<>(); DepthState ds = null; if (timeline.getFrameCount() > frame) { ds = timeline.getFrame(frame).layers.get(selectedDepth); @@ -984,7 +891,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { MouseEvent lastMouseEvent; int frame; int time; - DepthState stateUnderCursor; + Point cursorPosition; int mouseButton; int selectedDepth; Zoom zoom; @@ -998,7 +905,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay { synchronized (ImagePanel.class) { frame = this.frame; time = this.time; - stateUnderCursor = this.stateUnderCursor; + cursorPosition = this.cursorPosition; + if (cursorPosition != null) { + cursorPosition = iconPanel.toImagePoint(cursorPosition); + } + mouseButton = this.mouseButton; selectedDepth = this.selectedDepth; zoom = this.zoom; @@ -1009,6 +920,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return; } + RenderContext renderContext = new RenderContext(); + if (cursorPosition != null) { + renderContext.cursorPosition = new Point((int) (cursorPosition.x * SWF.unitDivisor), (int) (cursorPosition.y * SWF.unitDivisor)); + } + + renderContext.mouseButton = mouseButton; + renderContext.stateUnderCursor = new ArrayList<>(); + SerializableImage img; try { Timeline timeline = timelined.getTimeline(); @@ -1017,18 +936,20 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; - getOutlines(timelined, frame, time, zoomDouble, stateUnderCursor, mouseButton, thisTimer); updatePos(timelined, lastMouseEvent, thisTimer); Matrix mat = new Matrix(); mat.translateX = swf.displayRect.Xmin; mat.translateY = swf.displayRect.Ymin; - img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton, selectedDepth, zoomDouble); + img = getFrame(swf, frame, time, timelined, renderContext, selectedDepth, zoomDouble); + if (renderContext.borderImage != null) { + img = renderContext.borderImage; + } List sounds = new ArrayList<>(); List soundClasses = new ArrayList<>(); - timeline.getSounds(frame, time, stateUnderCursor, mouseButton, sounds, soundClasses); + timeline.getSounds(frame, time, null/*stateUnderCursor*/, mouseButton, sounds, soundClasses); for (int cid : swf.getCharacters().keySet()) { CharacterTag c = swf.getCharacter(cid); for (String cls : soundClasses) { @@ -1051,15 +972,70 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return; } + StringBuilder ret = new StringBuilder(); + + if (cursorPosition != null) { + ret.append(" [").append(cursorPosition.x).append(",").append(cursorPosition.y).append("] : "); + } + + boolean handCursor = renderContext.mouseOverButton != null; + boolean first = true; + for (int i = renderContext.stateUnderCursor.size() - 1; i >= 0; i--) { + DepthState ds = renderContext.stateUnderCursor.get(i); + if (!first) { + ret.append(", "); + } + + first = false; + CharacterTag c = swf.getCharacter(ds.characterId); + ret.append(c.toString()); + } + + if (first) { + ret.append(" - "); + } + + ButtonTag lastMouseOverButton = null; synchronized (ImagePanel.class) { if (timer == thisTimer) { iconPanel.setImg(img); + lastMouseOverButton = iconPanel.mouseOverButton; + iconPanel.mouseOverButton = renderContext.mouseOverButton; + debugLabel.setText(ret.toString()); + if (handCursor) { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else if (iconPanel.hasAllowMove()) { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } else { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + drawReady = true; synchronized (delayObject) { delayObject.notify(); } } } + + if (lastMouseOverButton != renderContext.mouseOverButton) { + ButtonTag b = renderContext.mouseOverButton; + if (b != null) { + // New mouse entered + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar1 != 0) { // IdleToOverUp + playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar1), timer); + } + } + + b = lastMouseOverButton; + if (b != null) { + // Old mouse leave + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar0 != 0) { // OverUpToIdle + playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar0), timer); + } + } + } } private void playSound(SoundTag st, Timer thisTimer) { @@ -1094,29 +1070,6 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } } - private List getOutlines(Timelined timelined, int frame, int time, double zoom, DepthState stateUnderCursor, int mouseButton, Timer thisTimer) { - List objs = new ArrayList<>(); - List outlines = new ArrayList<>(); - Matrix m = new Matrix(); - Timeline timeline = timelined.getTimeline(); - RECT rect = timeline.displayRect; - m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); - m.scale(zoom); - - timeline.getObjectsOutlines(frame, time, frame, stateUnderCursor, mouseButton, m, objs, outlines); - for (int i = 0; i < outlines.size(); i++) { - outlines.set(i, SHAPERECORD.twipToPixelShape(outlines.get(i))); - } - - synchronized (ImagePanel.class) { - if (timer == thisTimer) { - iconPanel.setOutlines(objs, outlines); - } - } - - return outlines; - } - public synchronized void clearAll() { stopInternal(); clearImagePanel();