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 85c168a5f..9e6b62089 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -90,6 +90,7 @@ import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DebugIDTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; @@ -439,6 +440,21 @@ public final class SWF implements SWFContainerItem, Timelined { return characterIdTags.get(characterId); } + public CharacterIdTag getCharacterIdTag(int characterId, int tagId) { + List characterIdTags = getCharacterIdTags(characterId); + if (characterIdTags != null) { + for (CharacterIdTag t : characterIdTags) { + if (((Tag) t).getId() == tagId) { + if (t.getCharacterId() == characterId) { + return t; + } + } + } + } + + return null; + } + public Map> getDependentCharacters() { if (dependentCharacters == null) { synchronized (this) { @@ -554,6 +570,19 @@ public final class SWF implements SWFContainerItem, Timelined { return null; } + public DefineSoundTag getSound(int soundId) { + CharacterTag characterTag = getCharacters().get(soundId); + if (characterTag instanceof DefineSoundTag) { + return (DefineSoundTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a DefineSoundTag. characterId: {0}", soundId); + } + + return null; + } + public TextTag getText(int textId) { CharacterTag characterTag = getCharacters().get(textId); if (characterTag instanceof TextTag) { @@ -2497,6 +2526,12 @@ public final class SWF implements SWFContainerItem, Timelined { } } + public static void clearAllStaticCache() { + Cache.clearAll(); + Helper.clearShapeCache(); + System.gc(); + } + public void clearAllCache() { characters = null; characterIdTags = null; @@ -2505,9 +2540,7 @@ public final class SWF implements SWFContainerItem, Timelined { clearReadOnlyListCache(); clearImageCache(); clearScriptCache(); - Cache.clearAll(); - Helper.clearShapeCache(); - System.gc(); + clearAllStaticCache(); } public static void uncache(ASMSource src) { @@ -2664,7 +2697,7 @@ public final class SWF implements SWFContainerItem, Timelined { return ret; } - public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, Color backGroundColor, double zoom) { + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, double zoom) { if (timeline.getFrameCount() == 0) { return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB); } @@ -2687,7 +2720,7 @@ public final class SWF implements SWFContainerItem, Timelined { RenderContext renderContext = new RenderContext(); renderContext.cursorPosition = cursorPosition; renderContext.mouseButton = mouseButton; - timeline.toImage(frame, time, renderContext, image, false, m, transformation, absoluteTransformation, colorTransform); + timeline.toImage(frame, time, renderContext, image, false, m, transformation, m, colorTransform); return image; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java index 4eaf28ebf..8a1066d31 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java @@ -16,9 +16,11 @@ */ package com.jpexs.decompiler.flash.dumpview; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.TagStub; +import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -31,7 +33,7 @@ import java.util.logging.Logger; * * @author JPEXS */ -public class DumpInfo { +public class DumpInfo implements TreeItem { public String name; @@ -145,4 +147,18 @@ public class DumpInfo { return resolvedTag; } + @Override + public SWF getSwf() { + Tag tag = getTag(); + if (tag != null) { + return tag.getSwf(); + } + + return null; + } + + @Override + public boolean isModified() { + return false; + } } 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 0faab1a9e..5810a690c 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 @@ -196,12 +196,12 @@ public class FrameExporter { rect.yMax *= settings.zoom; rect.xMin *= settings.zoom; rect.yMin *= settings.zoom; - SVGExporter exporter = new SVGExporter(rect); + SVGExporter exporter = new SVGExporter(rect, settings.zoom); if (fbackgroundColor != null) { exporter.setBackGroundColor(fbackgroundColor); } - tim.toSVG(frame, 0, null, 0, exporter, null, 0, settings.zoom); + tim.toSVG(frame, 0, null, 0, exporter, null, 0); fos.write(Utf8Helper.getBytes(exporter.getSVG())); } ret.add(f); @@ -360,7 +360,7 @@ public class FrameExporter { } int fframe = fframes.get(pos++); - BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage(); + BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), null, fbackgroundColor, 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/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index 119cdb046..0d961850f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -96,8 +96,8 @@ public class MorphShapeExporter { rect.yMax *= settings.zoom; rect.xMin *= settings.zoom; rect.yMin *= settings.zoom; - SVGExporter exporter = new SVGExporter(rect); - mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, settings.zoom); + SVGExporter exporter = new SVGExporter(rect, settings.zoom); + mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); fos.write(Utf8Helper.getBytes(exporter.getSVG())); } break; 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 c98317b98..80d6a7ff7 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 @@ -107,8 +107,8 @@ public class ShapeExporter { rect.yMax *= settings.zoom; rect.xMin *= settings.zoom; rect.yMin *= settings.zoom; - SVGExporter exporter = new SVGExporter(rect); - st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, settings.zoom); + SVGExporter exporter = new SVGExporter(rect, settings.zoom); + st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); fos.write(Utf8Helper.getBytes(exporter.getSVG())); } break; @@ -119,8 +119,7 @@ public class ShapeExporter { int newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1; SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); img.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); + Matrix m = Matrix.getTranslateInstance(-rect.Xmin, -rect.Ymin); m.scale(settings.zoom); st.toImage(0, 0, 0, new RenderContext(), img, false, m, m, m, new CXFORMWITHALPHA()); if (settings.mode == ShapeExportMode.PNG) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java index b66ad4b77..6acb93048 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/TextExporter.java @@ -82,8 +82,8 @@ public class TextExporter { new RetryTask(() -> { try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { ExportRectangle rect = new ExportRectangle(textTag.getRect()); - SVGExporter exporter = new SVGExporter(rect); - textTag.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, settings.zoom); + SVGExporter exporter = new SVGExporter(rect, settings.zoom); + textTag.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); fos.write(Utf8Helper.getBytes(exporter.getSVG())); } }, handler).run(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java index 2415b6cd0..bef21beee 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java @@ -189,8 +189,8 @@ public final class Matrix implements Cloneable { } public void translate(double x, double y) { - translateX += x; - translateY += y; + translateX = scaleX * x + rotateSkew1 * y + translateX; + translateY = rotateSkew0 * x + scaleY * y + translateY; } public void scale(double factor) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index d2fc20cb4..974aabedd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -80,11 +80,9 @@ public class SVGExporter { private final HashSet fontFaces = new HashSet<>(); - private String clip; - public boolean useTextTag = Configuration.textExportExportFontFace.get(); - public SVGExporter(ExportRectangle bounds) { + public SVGExporter(ExportRectangle bounds, double zoom) { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); try { @@ -98,7 +96,7 @@ public class SVGExporter { if (bounds != null) { svgRoot.setAttribute("width", (bounds.getWidth() / SWF.unitDivisor) + "px"); svgRoot.setAttribute("height", (bounds.getHeight() / SWF.unitDivisor) + "px"); - createDefGroup(bounds, null); + createDefGroup(bounds, null, zoom); } } catch (ParserConfigurationException ex) { Logger.getLogger(SVGExporter.class.getName()).log(Level.SEVERE, null, ex); @@ -125,10 +123,15 @@ public class SVGExporter { } public final void createDefGroup(ExportRectangle bounds, String id) { + createDefGroup(bounds, id, 1); + } + + public final void createDefGroup(ExportRectangle bounds, String id, double zoom) { Element g = _svg.createElement("g"); if (bounds != null) { - g.setAttribute("transform", "matrix(1, 0, 0, 1, " - + roundPixels20(-bounds.xMin / (double) SWF.unitDivisor) + ", " + roundPixels20(-bounds.yMin / (double) SWF.unitDivisor) + ")"); + Matrix mat = Matrix.getTranslateInstance(-bounds.xMin, -bounds.yMin); + mat.scale(zoom); + g.setAttribute("transform", mat.getSvgTransformationString(SWF.unitDivisor, 1)); } if (id != null) { g.setAttribute("id", id); @@ -150,7 +153,10 @@ public class SVGExporter { public final Element createSubGroup(Matrix transform, String id) { Element group = createSubGroup(id, "g"); - group.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1)); + if (transform != null) { + group.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1)); + } + return group; } @@ -218,9 +224,6 @@ public class SVGExporter { image.setAttribute("id", instanceName); } image.setAttribute("xlink:href", "#" + href); - if (clip != null) { - image.setAttribute("clip-path", "url(#" + clip + ")"); - } _svgGs.peek().appendChild(image); return image; } @@ -257,14 +260,6 @@ public class SVGExporter { return prefix + lastId; } - public void setClip(String clip) { - this.clip = clip; - } - - public String getClip() { - return clip; - } - protected static double roundPixels20(double pixels) { return Math.round(pixels * 100) / 100.0; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java index 46add5969..06c6c636f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java @@ -429,12 +429,15 @@ public class CanvasShapeExporter extends ShapeExporterBase { preLineFillData.append("\tctx.fillStyle=").append(lineLastRadColor).append(";\r\n\tctx.fill(\"evenodd\");\r\n"); } - preLineFillData.append("\tctx.transform(").append(Helper.doubleStr(lineFillMatrix.scaleX / unitDivisor)) - .append(",").append(Helper.doubleStr(lineFillMatrix.rotateSkew0 / unitDivisor)) - .append(",").append(Helper.doubleStr(lineFillMatrix.rotateSkew1 / unitDivisor)) - .append(",").append(Helper.doubleStr(lineFillMatrix.scaleY / unitDivisor)) - .append(",").append(Helper.doubleStr((lineFillMatrix.translateX + deltaX) / unitDivisor)) - .append(",").append(Helper.doubleStr((lineFillMatrix.translateY + deltaY) / unitDivisor)).append(");\r\n"); + if (lineFillMatrix != null) { + preLineFillData.append("\tctx.transform(").append(Helper.doubleStr(lineFillMatrix.scaleX / unitDivisor)) + .append(",").append(Helper.doubleStr(lineFillMatrix.rotateSkew0 / unitDivisor)) + .append(",").append(Helper.doubleStr(lineFillMatrix.rotateSkew1 / unitDivisor)) + .append(",").append(Helper.doubleStr(lineFillMatrix.scaleY / unitDivisor)) + .append(",").append(Helper.doubleStr((lineFillMatrix.translateX + deltaX) / unitDivisor)) + .append(",").append(Helper.doubleStr((lineFillMatrix.translateY + deltaY) / unitDivisor)).append(");\r\n"); + } + lineFillData.insert(0, preLineFillData); lineFillData.append("\tctx.fillRect(").append(-16384 - 32768 * lineRepeatCnt).append(",").append(-16384 - 32768 * lineRepeatCnt).append(",").append(2 * 16384 + 32768 * 2 * lineRepeatCnt).append(",").append(2 * 16384 + 32768 * 2 * lineRepeatCnt).append(");\r\n"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java index 7adc8bbd3..aa60e1707 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java @@ -117,7 +117,6 @@ public class ImageImporter extends TagImporter { int alpha = (imgData[y * width + x] >> 24) & 0xff; data[y * width + x] = (byte) alpha; } - } newData = data; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java index 6f1a90470..e518fdeb2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java @@ -128,7 +128,7 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(ba)); } else { - bitmapAlphaData = ByteArrayRange.EMPTY; + bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(new byte[0])); } imageData = new ByteArrayRange(data); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java index 0003fc48f..a3e5e2570 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java @@ -133,7 +133,7 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(ba)); } else { - bitmapAlphaData = ByteArrayRange.EMPTY; + bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(new byte[0])); } imageData = new ByteArrayRange(data); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonSoundTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonSoundTag.java index d15f44c0d..37c1fc336 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonSoundTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonSoundTag.java @@ -43,22 +43,22 @@ public class DefineButtonSoundTag extends Tag implements CharacterIdTag { public int buttonId; @SWFType(BasicType.UI16) - public int buttonSoundChar0; + public int buttonSoundChar0; // OverUpToIdle public SOUNDINFO buttonSoundInfo0; @SWFType(BasicType.UI16) - public int buttonSoundChar1; + public int buttonSoundChar1; // IdleToOverUp public SOUNDINFO buttonSoundInfo1; @SWFType(BasicType.UI16) - public int buttonSoundChar2; + public int buttonSoundChar2; // OverUpToOverDown public SOUNDINFO buttonSoundInfo2; @SWFType(BasicType.UI16) - public int buttonSoundChar3; + public int buttonSoundChar3; // OverDownToOverUp public SOUNDINFO buttonSoundInfo3; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 8903f8d45..771e50995 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -233,13 +233,8 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer { @Override protected void initTimeline(Timeline timeline) { - ColorTransform clrTrans = null; - for (Tag t : swf.getTags()) { - if (t instanceof DefineButtonCxformTag) { - DefineButtonCxformTag cx = (DefineButtonCxformTag) t; - clrTrans = cx.buttonColorTransform; - } - } + DefineButtonCxformTag cxformTag = (DefineButtonCxformTag) swf.getCharacterIdTag(buttonId, DefineButtonCxformTag.ID); + ColorTransform clrTrans = cxformTag == null ? null : cxformTag.buttonColorTransform; int maxDepth = 0; Frame frameUp = new Frame(timeline, 0); Frame frameDown = new Frame(timeline, 0); 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 78c99fa1f..b9ee108bf 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 @@ -934,8 +934,8 @@ public class DefineEditTextTag extends TextTag { } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) { - render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1); } @Override 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 01446b832..6a5d128dc 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 @@ -187,11 +187,13 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli } } if (r != null) { - foundSomething = true; - ret.Xmin = Math.min(r.Xmin, ret.Xmin); - ret.Ymin = Math.min(r.Ymin, ret.Ymin); - ret.Xmax = Math.max(r.Xmax, ret.Xmax); - ret.Ymax = Math.max(r.Ymax, ret.Ymax); + if (r.Xmin < r.Xmax && r.Ymin < r.Ymax) { + foundSomething = true; + ret.Xmin = Math.min(r.Xmin, ret.Xmin); + ret.Ymin = Math.min(r.Ymin, ret.Ymin); + ret.Xmax = Math.max(r.Xmax, ret.Xmax); + ret.Ymax = Math.max(r.Ymax, ret.Ymax); + } } } if (!foundSomething) { @@ -256,8 +258,8 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli r.Ymin = (int) Math.min(Math.min(Math.min(topleft.y, topright.y), bottomleft.y), bottomright.y); r.Xmax = (int) Math.max(Math.max(Math.max(topleft.x, topright.x), bottomleft.x), bottomright.x); r.Ymax = (int) Math.max(Math.max(Math.max(topleft.y, topright.y), bottomleft.y), bottomright.y); - } + ret.Xmin = Math.min(r.Xmin, ret.Xmin); ret.Ymin = Math.min(r.Ymin, ret.Ymin); ret.Xmax = Math.max(r.Xmax, ret.Xmax); @@ -371,8 +373,8 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException { - getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1); } @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 6149da630..b28bb240e 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 @@ -97,24 +97,12 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException { - getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1); } public DefineButtonSoundTag getSounds() { - List characterIdTags = swf.getCharacterIdTags(getCharacterId()); - if (characterIdTags != null) { - for (CharacterIdTag t : characterIdTags) { - if (t instanceof DefineButtonSoundTag) { - DefineButtonSoundTag st = (DefineButtonSoundTag) t; - if (st.buttonId == getCharacterId()) { - return st; - } - } - } - } - - return null; + return (DefineButtonSoundTag) swf.getCharacterIdTag(getCharacterId(), DefineButtonSoundTag.ID); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/CharacterTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/CharacterTag.java index 173f05b30..89d3d1a0e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/CharacterTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/CharacterTag.java @@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.helpers.ByteArrayRange; -import java.util.List; /** * @@ -78,12 +77,6 @@ public abstract class CharacterTag extends Tag implements CharacterIdTag { } public DefineScalingGridTag getScalingGridTag() { - List mtags = swf.getCharacterIdTags(getCharacterId()); - for (CharacterIdTag ct : mtags) { - if (ct instanceof DefineScalingGridTag) { - return (DefineScalingGridTag) ct; - } - } - return null; + return (DefineScalingGridTag) swf.getCharacterIdTag(getCharacterId(), DefineScalingGridTag.ID); } } 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 08cc3912a..1a3fedbc5 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 @@ -41,7 +41,7 @@ public interface DrawableTag extends BoundedTag { public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform); - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException; + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException; public void toHtmlCanvas(StringBuilder result, double unitDivisor); 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 cedff89c3..089851325 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 @@ -331,7 +331,7 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) { + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { } @Override 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 739d3aa02..8955fe253 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 @@ -290,8 +290,8 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException { - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShape(), exporter, null, colorTransform, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShape(), exporter, null, colorTransform, 1); shapeExporter.export(); } 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 23c6c33cd..01d5d6a5c 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 @@ -289,15 +289,15 @@ public abstract class MorphShapeTag extends CharacterTag implements DrawableTag } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) { + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { if (ratio == -2) { SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0); SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535); - SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform, zoom); + SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform, 1); shapeExporter.export(); } else { SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform, zoom); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform, 1); shapeExporter.export(); } } 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 2fa7ead29..1f13ca32b 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 @@ -179,8 +179,8 @@ public abstract class ShapeTag extends CharacterTag implements DrawableTag, Lazy } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException { - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), exporter, null, colorTransform, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), exporter, null, colorTransform, 1); shapeExporter.export(); } 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 bc455e0f7..a9daa65e4 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 @@ -666,8 +666,8 @@ public abstract class StaticTextTag extends TextTag { } @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) { - staticTextToSVG(swf, textRecords, getTextNum(), exporter, getRect(), textMatrix, colorTransform, zoom); + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + staticTextToSVG(swf, textRecords, getTextNum(), exporter, getRect(), textMatrix, colorTransform, 1); } @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 012e3270b..fb3fec940 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 @@ -26,7 +26,6 @@ import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.helpers.FontHelper; import com.jpexs.decompiler.flash.helpers.HighlightedText; import com.jpexs.decompiler.flash.importers.TextImportResizeTextBoundsMode; import com.jpexs.decompiler.flash.tags.text.JustifyAlignGlyphEntry; @@ -451,6 +450,8 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { int x = 0; int y = 0; List glyphs = null; + Matrix mat0 = transformation.clone(); + mat0 = mat0.concatenate(new Matrix(textMatrix)); for (TEXTRECORD rec : textRecords) { if (rec.styleFlagsHasColor) { if (numText == 2) { @@ -481,13 +482,13 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { double divider = font == null ? 1 : font.getDivider(); double rat = textHeight / 1024.0 / divider; + Matrix matScale = Matrix.getScaleInstance(rat); Color textColor2 = new Color(textColor, true); for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = transformation.clone(); - mat = mat.concatenate(new Matrix(textMatrix)); - Matrix matTr = Matrix.getTranslateInstance(x, y); - mat = mat.concatenate(matTr); - mat = mat.concatenate(Matrix.getScaleInstance(rat)); + matScale.translateX = x; + matScale.translateY = y; + + Matrix mat = mat0.concatenate(matScale); SHAPE shape = null; if (entry.glyphIndex != -1 && glyphs != null) { // shapeNum: 1 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 6128909a4..606a4d153 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 @@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.FrameExporter; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.Point; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; @@ -66,7 +65,6 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -74,9 +72,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; +import org.w3c.dom.Element; /** * @@ -557,47 +553,47 @@ public class Timeline { } /*public void toImage(int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) { - ExportRectangle scalingGrid = null; - if (timelined instanceof CharacterTag) { - DefineScalingGridTag sgt = ((CharacterTag) timelined).getScalingGridTag(); - if (sgt != null) { - scalingGrid = new ExportRectangle(sgt.splitter); - } - } + ExportRectangle scalingGrid = null; + if (timelined instanceof CharacterTag) { + DefineScalingGridTag sgt = ((CharacterTag) timelined).getScalingGridTag(); + if (sgt != null) { + scalingGrid = new ExportRectangle(sgt.splitter); + } + } - if (scalingGrid == null || transformation.rotateSkew0 != 0 || transformation.rotateSkew1 != 0) { - toImage(frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform, null); - return; - } + if (scalingGrid == null || transformation.rotateSkew0 != 0 || transformation.rotateSkew1 != 0) { + toImage(frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform, null); + return; + } - //9-slice scaling using DefineScalingGrid - Matrix diffTransform = prevTransformation.inverse().preConcatenate(transformation); - transformation = diffTransform; + //9-slice scaling using DefineScalingGrid + Matrix diffTransform = prevTransformation.inverse().preConcatenate(transformation); + transformation = diffTransform; - Matrix prevScale = new Matrix(); - prevScale.scaleX = prevTransformation.scaleX; - prevScale.scaleY = prevTransformation.scaleY; + Matrix prevScale = new Matrix(); + prevScale.scaleX = prevTransformation.scaleX; + prevScale.scaleY = prevTransformation.scaleY; - ExportRectangle boundsRect = new ExportRectangle(timelined.getRect()); + ExportRectangle boundsRect = new ExportRectangle(timelined.getRect()); - 0 | 1 | 2 - ------------ - 3 | 4 | 5 - ------------ - 6 | 7 | 8 + 0 | 1 | 2 + ------------ + 3 | 4 | 5 + ------------ + 6 | 7 | 8 - ExportRectangle targetRect[] = new ExportRectangle[9]; - ExportRectangle sourceRect[] = new ExportRectangle[9]; - Matrix transforms[] = new Matrix[9]; + ExportRectangle targetRect[] = new ExportRectangle[9]; + ExportRectangle sourceRect[] = new ExportRectangle[9]; + Matrix transforms[] = new Matrix[9]; - DefineScalingGridTag.getSlices(transformation, prevScale, boundsRect, scalingGrid, sourceRect, targetRect, transforms); + DefineScalingGridTag.getSlices(transformation, prevScale, boundsRect, scalingGrid, sourceRect, targetRect, transforms); - for (int i = 0; i < targetRect.length; i++) { - toImage(frame, time, renderContext, image, isClip, transforms[i], absoluteTransformation, colorTransform, targetRect[i]); - } - }*/ - private void drawDrawable(Matrix strokeTransform, DepthState layer, Matrix layerMatrix, Graphics2D g, ColorTransform colorTransForm, int blendMode, List clips, List prevClips, Matrix transformation, boolean isClip, int clipDepth, Matrix absMat, int time, int ratio, RenderContext renderContext, SerializableImage image, DrawableTag drawable, List filters, double unzoom, ColorTransform clrTrans) { + for (int i = 0; i < targetRect.length; i++) { + toImage(frame, time, renderContext, image, isClip, transforms[i], absoluteTransformation, colorTransform, targetRect[i]); + } + }*/ + private void drawDrawable(Matrix strokeTransform, DepthState layer, Matrix layerMatrix, Graphics2D g, ColorTransform colorTransForm, int blendMode, List clips, Matrix transformation, boolean isClip, int clipDepth, Matrix absMat, int time, int ratio, RenderContext renderContext, SerializableImage image, DrawableTag drawable, List filters, double unzoom, ColorTransform clrTrans) { Matrix drawMatrix = new Matrix(); int drawableFrameCount = drawable.getNumFrames(); if (drawableFrameCount == 0) { @@ -642,7 +638,7 @@ public class Timeline { return; } - m.translate(-rect.xMin, -rect.yMin); + m = m.preConcatenate(Matrix.getTranslateInstance(-rect.xMin, -rect.yMin)); //strokeTransform = strokeTransform.clone(); //strokeTransform.translate(-rect.xMin, -rect.yMin); drawMatrix.translate(rect.xMin, rect.yMin); @@ -764,9 +760,6 @@ public class Timeline { gm.drawImage(img.getBufferedImage(), 0, 0, null); Clip clip = new Clip(Helper.imageToShape(mask), clipDepth); // Maybe we can get current outline instead converting from image (?) clips.add(clip); - prevClips.add(g.getClip()); - g.setTransform(AffineTransform.getTranslateInstance(0, 0)); - g.setClip(clip.shape); } else { if (renderContext.cursorPosition != null) { if (drawable instanceof DefineSpriteTag) { @@ -812,17 +805,44 @@ public class Timeline { g.setTransform(transformation.toTransform()); List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); int maxDepth = getMaxDepth(); + int clipCount = 0; for (int i = 1; i <= maxDepth; i++) { + boolean clipChanged = clipCount != clips.size(); for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - g.setClip(prevClips.get(c)); - prevClips.remove(c); + if (clips.get(c).depth < i) { clips.remove(c); + clipChanged = true; } } + + if (clipChanged) { + if (clips.size() > 0) { + Area clip = null; + for (Clip clip1 : clips) { + Shape shape = clip1.shape; + if (clip == null) { + clip = new Area(shape); + } else { + clip.intersect(new Area(shape)); + } + } + + g.setTransform(new AffineTransform()); + g.setClip(clip); + + // draw clip border + //g.setPaint(Color.red); + //g.setStroke(new BasicStroke(2)); + //g.draw(clip); + } else { + g.setClip(null); + } + + clipCount = clips.size(); + } + if (!frameObj.layers.containsKey(i)) { continue; } @@ -873,25 +893,25 @@ public class Timeline { Rectangle2D r = new Rectangle2D.Double(p1.xMin, p1.yMin, p1.getWidth(), p1.getHeight()); g.setClip(r); - drawDrawable(strokeTransformation.preConcatenate(layerMatrix), layer, transforms[s], g, colorTransform, layer.blendMode, clips, prevClips, transformation.clone(), isClip, layer.clipDepth, absMat, time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans); + drawDrawable(strokeTransformation.preConcatenate(layerMatrix), layer, transforms[s], g, colorTransform, layer.blendMode, clips, transformation, isClip, layer.clipDepth, absMat, time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans); } g.setClip(c); /* - for (int s = 0; s < 9; s++) { - g.setTransform(new AffineTransform()); - ExportRectangle p1 = transformation.transform(targetRect[s]); - g.setClip(c); + for (int s = 0; s < 9; s++) { + g.setTransform(new AffineTransform()); + ExportRectangle p1 = transformation.transform(targetRect[s]); + g.setClip(c); - Rectangle2D r = new Rectangle2D.Double(p1.xMin, p1.yMin, p1.getWidth(), p1.getHeight()); - g.setColor(Color.blue); - g.draw(r); + Rectangle2D r = new Rectangle2D.Double(p1.xMin, p1.yMin, p1.getWidth(), p1.getHeight()); + g.setColor(Color.blue); + g.draw(r); - }*/ + }*/ g.setTransform(origTransform); } else { - drawDrawable(strokeTransformation, layer, layerMatrix, g, colorTransform, layer.blendMode, clips, prevClips, transformation.clone(), isClip, layer.clipDepth, absMat, time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans); + drawDrawable(strokeTransformation, layer, layerMatrix, g, colorTransform, layer.blendMode, clips, transformation, isClip, layer.clipDepth, absMat, time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans); } } else if (character instanceof BoundedTag) { showPlaceholder = true; @@ -929,28 +949,44 @@ public class Timeline { } } - g.setTransform(AffineTransform.getScaleInstance(1, 1)); + g.setTransform(new AffineTransform()); g.setClip(prevClip); } - public void toSVG(int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, double zoom) throws IOException { + public void toSVG(int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level) throws IOException { if (getFrameCount() <= frame) { return; } Frame frameObj = getFrame(frame); List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); int maxDepth = getMaxDepth(); + int clipCount = 0; + Element clipGroup = null; for (int i = 1; i <= maxDepth; i++) { + boolean clipChanged = clipCount != clips.size(); for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - exporter.setClip(prevClips.get(c)); - prevClips.remove(c); + if (clips.get(c).depth < i) { clips.remove(c); + clipChanged = true; } } + + if (clipChanged) { + if (clipGroup != null) { + exporter.endGroup(); + } + + if (clips.size() > 0) { + String clip = clips.get(clips.size() - 1).shape; // todo: merge clip areas + clipGroup = exporter.createSubGroup(null, null); + clipGroup.setAttribute("clip-path", "url(#" + clip + ")"); + } + + clipCount = clips.size(); + } + if (!frameObj.layers.containsKey(i)) { continue; } @@ -981,7 +1017,7 @@ public class Timeline { assetName = getTagIdPrefix(drawableTag, exporter); exporter.exportedTags.put(drawableTag, assetName); exporter.createDefGroup(new ExportRectangle(boundRect), assetName); - drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, zoom); + drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1); exporter.endGroup(); } ExportRectangle rect = new ExportRectangle(boundRect); @@ -993,10 +1029,8 @@ public class Timeline { exporter.createClipPath(new Matrix(), clipName); SvgClip clip = new SvgClip(clipName, layer.clipDepth); clips.add(clip); - prevClips.add(exporter.getClip()); Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); exporter.addUse(mat, boundRect, assetName, layer.instanceName); - exporter.setClip(clip.shape); exporter.endGroup(); } else { Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); @@ -1005,6 +1039,9 @@ public class Timeline { } } + if (clipGroup != null) { + exporter.endGroup(); + } } private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java index 8acc9153a..66b9af310 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java @@ -28,7 +28,7 @@ import java.awt.image.RescaleOp; public class ColorTransform implements Cloneable { public RescaleOp toRescaleOp() { - return new RescaleOp(new float[]{getRedMulti() / 255f, getGreenMulti() / 255f, getBlueMulti() / 255f, getAlphaMulti() / 255f}, + return new RescaleOp(new float[]{getRedMulti() / 256f, getGreenMulti() / 256f, getBlueMulti() / 256f, getAlphaMulti() / 256f}, new float[]{getRedAdd(), getGreenAdd(), getBlueAdd(), getAlphaAdd()}, null); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index babcf1ab5..87c3d53cf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -217,9 +217,8 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali double px = x * w2 + w2 / 2 - w / 2 - minXMin * ratio; double py = y * h2 - minYMin * ratio; - Matrix transformation = new Matrix(); - transformation.translate(px, py); - transformation = transformation.concatenate(Matrix.getScaleInstance(ratio)); + Matrix transformation = Matrix.getTranslateInstance(px, py); + transformation.scale(ratio); BitmapExporter.export(swf, shape, color, image, transformation, transformation, colorTransform); // draw bounding boxes diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index 1c826041e..f98de9c5e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -35,6 +35,7 @@ import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; +import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; import com.jpexs.decompiler.flash.tags.DefineButtonTag; import com.jpexs.decompiler.flash.tags.DefineEditTextTag; import com.jpexs.decompiler.flash.tags.DefineFontNameTag; @@ -1063,13 +1064,9 @@ public class XFLConverter { } if (tag instanceof DefineButtonTag) { DefineButtonTag bt = (DefineButtonTag) tag; - for (Tag t : tags) { - if (t instanceof DefineButtonCxformTag) { - DefineButtonCxformTag bcx = (DefineButtonCxformTag) t; - if (bcx.buttonId == bt.buttonId) { - colorTransform = bcx.buttonColorTransform; - } - } + DefineButtonCxformTag bcx = (DefineButtonCxformTag) bt.getSwf().getCharacterIdTag(bt.buttonId, DefineButtonCxformTag.ID); + if (bcx != null) { + colorTransform = bcx.buttonColorTransform; } } @@ -1120,17 +1117,17 @@ public class XFLConverter { if (colorTransform != null) { writer.writeStartElement("color"); writer.writeStartElement("Color"); - if (colorTransform.getRedMulti() != 255) { - writer.writeAttribute("redMultiplier", ((float) colorTransform.getRedMulti()) / 255.0f); + if (colorTransform.getRedMulti() != 256) { + writer.writeAttribute("redMultiplier", ((float) colorTransform.getRedMulti()) / 256.0f); } - if (colorTransform.getGreenMulti() != 255) { - writer.writeAttribute("greenMultiplier", ((float) colorTransform.getGreenMulti()) / 255.0f); + if (colorTransform.getGreenMulti() != 256) { + writer.writeAttribute("greenMultiplier", ((float) colorTransform.getGreenMulti()) / 256.0f); } - if (colorTransform.getBlueMulti() != 255) { - writer.writeAttribute("blueMultiplier", ((float) colorTransform.getBlueMulti()) / 255.0f); + if (colorTransform.getBlueMulti() != 256) { + writer.writeAttribute("blueMultiplier", ((float) colorTransform.getBlueMulti()) / 256.0f); } - if (colorTransform.getAlphaMulti() != 255) { - writer.writeAttribute("alphaMultiplier", ((float) colorTransform.getAlphaMulti()) / 255.0f); + if (colorTransform.getAlphaMulti() != 256) { + writer.writeAttribute("alphaMultiplier", ((float) colorTransform.getAlphaMulti()) / 256.0f); } if (colorTransform.getRedAdd() != 0) { @@ -1275,8 +1272,21 @@ public class XFLConverter { symbolStr.writeAttribute("color", randomOutlineColor()); symbolStr.writeStartElement("frames"); int lastFrame = 0; + DefineButtonSoundTag sound = button.getSounds(); loopframes: for (int frame = 1; frame <= 4; frame++) { + if (sound != null) { + switch (frame) { + case 1: + break; + case 2: + break; + case 3: + break; + case 4: + break; + } + } for (BUTTONRECORD rec : records) { if (rec.placeDepth == i) { boolean ok = false; @@ -1765,18 +1775,11 @@ public class XFLConverter { } } - private static void convertFrame(boolean shapeTween, HashMap characters, ReadOnlyTagList tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files, XFLXmlWriter writer) throws XMLStreamException { + private static void convertFrame(boolean shapeTween, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files, XFLXmlWriter writer) throws XMLStreamException { DefineSoundTag sound = null; if (startSound != null) { - for (Tag t : tags) { - if (t instanceof DefineSoundTag) { - DefineSoundTag s = (DefineSoundTag) t; - if (s.soundId == startSound.soundId) { - sound = s; - break; - } - } - } + SWF swf = startSound.getSwf(); + sound = swf.getSound(startSound.soundId); } writer.writeStartElement("DOMFrame"); @@ -2046,7 +2049,7 @@ public class XFLConverter { frame++; String elements = elementsWriter.toString(); if (!elements.equals(lastElements) && frame > 0) { - convertFrame(lastShapeTween, characters, tags, null, null, frame - duration, duration, "", lastElements, files, writer2); + convertFrame(lastShapeTween, null, null, frame - duration, duration, "", lastElements, files, writer2); duration = 1; } else if (frame == 0) { duration = 1; @@ -2060,7 +2063,7 @@ public class XFLConverter { } if (!lastElements.isEmpty()) { frame++; - convertFrame(lastShapeTween, characters, tags, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2); + convertFrame(lastShapeTween, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2); } afterStr = "" + afterStr; @@ -2091,16 +2094,11 @@ public class XFLConverter { for (Tag t : tags) { if (t instanceof FontTag) { + SWF swf = t.getSwf(); FontTag font = (FontTag) t; int fontId = font.getFontId(); - String fontName = null; - for (Tag t2 : tags) { - if (t2 instanceof DefineFontNameTag) { - if (((DefineFontNameTag) t2).fontId == fontId) { - fontName = ((DefineFontNameTag) t2).fontName; - } - } - } + DefineFontNameTag fontNameTag = (DefineFontNameTag) swf.getCharacterIdTag(fontId, DefineFontNameTag.ID); + String fontName = fontNameTag == null ? null : fontNameTag.fontName; if (fontName == null) { fontName = font.getFontNameIntag(); } @@ -2300,65 +2298,74 @@ public class XFLConverter { return hasLabel; } - private void convertSoundLayer(int layerIndex, String backgroundColor, HashMap characters, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, HashMap files, XFLXmlWriter writer) throws XMLStreamException { + private void convertSoundLayer(int layerIndex, ReadOnlyTagList timeLineTags, HashMap files, XFLXmlWriter writer) throws XMLStreamException { + int soundLayerIndex = 0; XFLXmlWriter writer2 = new XFLXmlWriter(); - StartSoundTag lastStartSound = null; - SoundStreamHeadTypeTag lastSoundStreamHead = null; - StartSoundTag startSound = null; - SoundStreamHeadTypeTag soundStreamHead = null; - int duration = 1; + List startSounds = new ArrayList<>(); + List startSoundFrameNumbers = new ArrayList<>(); + List soundStreamHeads = new ArrayList<>(); + List soundStreamHeadFrameNumbers = new ArrayList<>(); int frame = 0; for (Tag t : timeLineTags) { if (t instanceof StartSoundTag) { - startSound = (StartSoundTag) t; - - for (Tag ta : tags) { - if (ta instanceof DefineSoundTag) { - DefineSoundTag s = (DefineSoundTag) ta; - if (s.soundId == startSound.soundId) { - if (!files.containsKey("sound" + s.soundId + "." + s.getExportFormat().toString().toLowerCase())) { //Sound was not exported - startSound = null; // ignore - } - break; - } - } + StartSoundTag startSound = (StartSoundTag) t; + SWF swf = startSound.getSwf(); + DefineSoundTag s = swf.getSound(startSound.soundId); + if (!files.containsKey("sound" + s.soundId + "." + s.getExportFormat().toString().toLowerCase())) { //Sound was not exported + startSound = null; // ignore } - } - if (t instanceof SoundStreamHeadTypeTag) { - soundStreamHead = (SoundStreamHeadTypeTag) t; + if (startSound != null) { + startSounds.add(startSound); + startSoundFrameNumbers.add(frame); + } + } else if (t instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag soundStreamHead = (SoundStreamHeadTypeTag) t; if (!files.containsKey("sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat().toString().toLowerCase())) { //Sound was not exported soundStreamHead = null; // ignore } - } - if (t instanceof ShowFrameTag) { - if (soundStreamHead != null || startSound != null) { - if (lastSoundStreamHead != null || lastStartSound != null) { - convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files, writer2); - } - frame += duration; - duration = 1; - lastSoundStreamHead = soundStreamHead; - lastStartSound = startSound; - soundStreamHead = null; - startSound = null; - } else { - duration++; + + if (soundStreamHead != null) { + soundStreamHeads.add(soundStreamHead); + soundStreamHeadFrameNumbers.add(frame); } + } else if (t instanceof ShowFrameTag) { + frame++; } } - if (lastSoundStreamHead != null || lastStartSound != null) { - if (frame < 0) { - frame = 0; - duration = 1; - } - convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files, writer2); - } - if (writer2.length() > 0) { - writer.writeStartElement("DOMLayer", new String[]{"name", "Layer " + layerIndex, "color", randomOutlineColor()}); + for (int i = 0; i < soundStreamHeads.size(); i++) { + writer.writeStartElement("DOMLayer", new String[]{"name", "Sound Layer " + (soundLayerIndex++), "color", randomOutlineColor()}); writer.writeStartElement("frames"); - writer.writeCharactersRaw(writer2.toString()); + + int startFrame = soundStreamHeadFrameNumbers.get(i); + int duration = frame - startFrame; + + if (startFrame != 0) { + // empty frames should be added + convertFrame(false, null, null, 0, startFrame, "", "", files, writer); + } + + convertFrame(false, soundStreamHeads.get(i), null, startFrame, duration, "", "", files, writer); + + writer.writeEndElement(); + writer.writeEndElement(); + } + + for (int i = 0; i < startSounds.size(); i++) { + writer.writeStartElement("DOMLayer", new String[]{"name", "Sound Layer " + (soundLayerIndex++), "color", randomOutlineColor()}); + writer.writeStartElement("frames"); + + int startFrame = startSoundFrameNumbers.get(i); + int duration = frame - startFrame; + + if (startFrame != 0) { + // empty frames should be added + convertFrame(false, null, null, 0, startFrame, "", "", files, writer); + } + + convertFrame(false, null, startSounds.get(i), startFrame, duration, "", "", files, writer); + writer.writeEndElement(); writer.writeEndElement(); } @@ -2463,7 +2470,7 @@ public class XFLConverter { int soundLayerIndex = layerCount; layerCount++; - convertSoundLayer(soundLayerIndex, backgroundColor, characters, tags, timelineTags, files, writer); + convertSoundLayer(soundLayerIndex, timelineTags, files, writer); writer.writeEndElement(); writer.writeEndElement(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java index 27c8f46f9..c260936c0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java @@ -73,7 +73,7 @@ public class Helper { public static String decompilationErrorAdd = null; - private static final Map shapeCache = new HashMap<>(); + private static final Map shapeCache = new HashMap<>(); private static final String[] hexStringCache; @@ -1180,9 +1180,8 @@ public class Helper { } } - String key = byteArrayToBase64String(bs.toByteArray()); - if (shapeCache.containsKey(key)) { - return shapeCache.get(key); + if (shapeCache.containsKey(bs)) { + return shapeCache.get(bs); } for (int x = 0; x < width; x++) { @@ -1210,7 +1209,7 @@ public class Helper { } } - shapeCache.put(key, area); + shapeCache.put(bs, area); return area; } @@ -1229,18 +1228,15 @@ public class Helper { BitSet bs = new BitSet(width * height); bs.set(type); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int idx = width * y + x; - if ((imgData[idx] >>> 24) > 0) { - bs.set(idx); - } + int pixelCount = width * height; + for (int i = 0; i < pixelCount; i++) { + if ((imgData[i] >>> 24) > 0) { + bs.set(i); } } - String key = byteArrayToBase64String(bs.toByteArray()); - if (shapeCache.containsKey(key)) { - return shapeCache.get(key); + if (shapeCache.containsKey(bs)) { + return shapeCache.get(bs); } BitSet bsArea = new BitSet(width * height); @@ -1321,7 +1317,7 @@ public class Helper { } } - shapeCache.put(key, area); + shapeCache.put(bs, area); return area; } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 6958d26b8..cdfc84c2f 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -434,7 +434,7 @@ public class CommandLineArgumentParser { if (filter == null || filter.equals("zoom")) { out.println(" " + (cnt++) + ") -zoom "); - out.println(" ...apply zoom during export (currently for FlashPaper conversion only)"); + out.println(" ...apply zoom during export"); } if (filter == null || filter.equals("replace")) { @@ -2042,11 +2042,7 @@ public class CommandLineArgumentParser { } System.out.print("Page " + page + "/" + totalPages + "..."); RECT displayRect = new RECT(ds.getTimeline().displayRect); - //displayRect.Xmax *= zoom; - //displayRect.Ymax *= zoom; - Matrix m = new Matrix(); - //m.scale(zoom); - BufferedImage img = SWF.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect, m, m, null, Color.white, zoom).getBufferedImage(); + BufferedImage img = SWF.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect, new Matrix(), null, Color.white, 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 db97f4f61..42270f9cc 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -313,7 +313,7 @@ public class FolderPreviewPanel extends JPanel { String key = "frame_" + fn.frame + "_" + timeline.id + "_" + zoom; imgSrc = swf.getFromCache(key); if (imgSrc == null) { - imgSrc = SWF.frameToImageGet(timeline, fn.frame, fn.frame, null, 0, rect, new Matrix(), new Matrix(), null, null, zoom); + imgSrc = SWF.frameToImageGet(timeline, fn.frame, fn.frame, null, 0, rect, new Matrix(), null, null, zoom); swf.putToCache(key, imgSrc); } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index a91f5d394..58940eeb8 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -50,7 +50,6 @@ import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; -import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; @@ -418,8 +417,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { /*if (width > swf.displayRect.getWidth()) { scale = (double) swf.displayRect.getWidth() / (double) width; }*/ - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); + Matrix m = Matrix.getTranslateInstance(-rect.Xmin, -rect.Ymin); m.scale(scale); Point p = lastMouseEvent == null ? null : lastMouseEvent.getPoint(); @@ -491,7 +489,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { ButtonTag button = iconPanel.mouseOverButton; if (button != null) { DefineButtonSoundTag sounds = button.getSounds(); - if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown + if (sounds != null && sounds.buttonSoundChar2 != 0) { // OverUpToOverDown playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar2), sounds.buttonSoundInfo2, timer); } } @@ -507,7 +505,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { ButtonTag button = iconPanel.mouseOverButton; if (button != null) { DefineButtonSoundTag sounds = button.getSounds(); - if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp + if (sounds != null && sounds.buttonSoundChar3 != 0) { // OverDownToOverUp playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar3), sounds.buttonSoundInfo3, timer); } } @@ -736,8 +734,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { SerializableImage image = new SerializableImage((int) (width / SWF.unitDivisor) + 1, (int) (height / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); image.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin * zoomDouble, -rect.Ymin * zoomDouble); + Matrix m = Matrix.getTranslateInstance(-rect.Xmin * zoomDouble, -rect.Ymin * zoomDouble); m.scale(zoomDouble); textTag.toImage(0, 0, 0, new RenderContext(), image, false, m, m, m, new ConstantColorColorTransform(0xFFC0C0C0)); @@ -864,7 +861,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Matrix m = new Matrix(); m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); m.scale(zoom); - timeline.toImage(frame, time, renderContext, image, false, m, new Matrix(), m, null); + timeline.toImage(frame, time, renderContext, image, false, m, m, m, null); Graphics2D gg = (Graphics2D) image.getGraphics(); gg.setStroke(new BasicStroke(3)); diff --git a/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java b/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java index c8264ff8e..6fa98cc82 100644 --- a/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java +++ b/src/com/jpexs/decompiler/flash/gui/LoadFromMemoryFrame.java @@ -288,7 +288,7 @@ public class LoadFromMemoryFrame extends AppFrame { tableRes.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 10) { //Enter pressed + if (e.getKeyCode() == KeyEvent.VK_ENTER) { //Enter pressed openSwf(); } } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index a607e5773..57bddb8d7 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -228,8 +228,7 @@ public class Main { try { proc = Runtime.getRuntime().exec(new String[]{exePath, ffile}); } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); - + logger.log(Level.SEVERE, null, ex); return null; } boolean isDebug; @@ -344,7 +343,7 @@ public class Main { try (FileInputStream fis = new FileInputStream(fTempFile)) { instrSWF = new SWF(fis, false, false); } catch (InterruptedException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); + logger.log(Level.SEVERE, null, ex); } if (instrSWF != null) { String swfFileName = fTempFile.getAbsolutePath(); @@ -373,7 +372,7 @@ public class Main { try (FileInputStream fis = new FileInputStream(toPrepareFile)) { instrSWF = new SWF(fis, toPrepareFile.getAbsolutePath(), origFile.getName(), false); } catch (InterruptedException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); + logger.log(Level.SEVERE, null, ex); } if (instrSWF != null) { for (Tag t : instrSWF.getLocalTags()) { @@ -1608,7 +1607,7 @@ public class Main { Helper.writeFile(tfile, data); openFile(new SWFSourceInfo(null, tfile, title)); } catch (IOException ex) { - Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Cannot create tempfile"); + logger.log(Level.SEVERE, "Cannot create tempfile"); } } @@ -1654,7 +1653,7 @@ public class Main { }); flashDebugger.addConnectionListener(debugHandler); } catch (IOException ex) { - Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "eeex", ex); + logger.log(Level.SEVERE, "eeex", ex); } }); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index c076a754c..fe4a1e1e9 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -675,6 +675,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } }); searchPanel.add(closeSearchButton, BorderLayout.EAST); + searchPanel.setVisible(false); treePanel = new JPanel(new CardLayout()); treePanel.add(createResourcesViewCard(), RESOURCES_VIEW); treePanel.add(createDumpViewCard(), DUMP_VIEW); @@ -2878,23 +2879,6 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public void valueChanged(TreeSelectionEvent e) { Object source = e.getSource(); - if (source == dumpTree) { - reload(false); - Object sel = e.getPath().getLastPathComponent(); - if (sel instanceof DumpInfo) { - DumpInfo di = (DumpInfo) sel; - Tag t = di.getTag(); - if (t != null) { - showPreview(t, dumpPreviewPanel); - } else { - showPreview(null, dumpPreviewPanel); - } - } else { - showPreview(null, dumpPreviewPanel); - } - return; - } - TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); if (!(treeItem instanceof SWFList)) { @@ -2914,6 +2898,17 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } reload(false); + + if (source == dumpTree) { + Object sel = e.getPath().getLastPathComponent(); + Tag t = null; + if (sel instanceof DumpInfo) { + DumpInfo di = (DumpInfo) sel; + t = di.getTag(); + } + + showPreview(t, dumpPreviewPanel); + } } public void unloadFlashPlayer() { @@ -3047,6 +3042,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } if (item instanceof Timelined) { timelineViewPanel.setTimelined((Timelined) item); + } else if (item instanceof Frame) { + timelineViewPanel.setTimelined(((Frame) item).timeline.timelined); } else { timelineViewPanel.setTimelined(swf); } @@ -3171,13 +3168,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof Frame && internalViewer) { Frame fn = (Frame) treeItem; SWF swf = fn.getSwf(); - Timelined timelined = swf; - if (fn.timeline.timelined instanceof DefineSpriteTag) { - DefineSpriteTag parentSprite = (DefineSpriteTag) fn.timeline.timelined; - timelined = parentSprite; - } - - previewPanel.showImagePanel(timelined, swf, fn.frame); + previewPanel.showImagePanel(fn.timeline.timelined, swf, fn.frame); } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); previewPanel.setImageReplaceButtonVisible(((Tag) treeItem).isReadOnly() && (treeItem instanceof DefineSoundTag), false); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java index 9b5b559be..546496f50 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.Tag; import java.awt.BorderLayout; import java.io.IOException; import java.io.StringReader; @@ -129,6 +130,8 @@ public class SlotConstTraitDetailPanel extends JPanel implements TraitDetail { Logger.getLogger(SlotConstTraitDetailPanel.class.getName()).log(Level.SEVERE, null, ex); return false; } + + ((Tag) abc.parentTag).setModified(true); return true; } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_fr.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_fr.properties index eddc1c79d..c244ddf50 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_fr.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_fr.properties @@ -347,7 +347,7 @@ config.description.lastSessionSelection = Contient la s\u00e9lection lors de la config.name.loopMedia = Rejouer les sons et les sprites config.description.loopMedia = Rejoue automatiquement les sons et les sprites -config.name.gui.timeLineSplitPane.dividerLocationPercent = (Internal) Timeline Splitter location +config.name.gui.timeLineSplitPane.dividerLocationPercent = (Internal) Position du \u00e9parateur de la fen\u00eatre du chronogramme config.description.gui.timeLineSplitPane.dividerLocationPercent = config.name.cacheImages = Images en m\u00e9moire cache diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_it.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_it.properties index 7955471e9..3d58494d1 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_it.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_it.properties @@ -219,9 +219,9 @@ config.name.formatting.indent.useTabs = Indentare con tab config.description.formatting.indent.useTabs = Utilizzare tab invece degli spazi per l'indentazione config.name.beginBlockOnNewLine = Parentesi graffa su riga successiva -config.description.beginBlockOnNewLine = Iniziare un blocco con parentesi { } dopo un a-capo +config.description.beginBlockOnNewLine = Iniziare un blocco di parentesi graffe dopo un a-capo -config.name.check.updates.delay = Intervallo tra verifiche aggiornamenti +config.name.check.updates.delay = Intervallo tra controllo aggiornamenti config.description.check.updates.delay = Tempo minimo tra controlli automatici per aggiornamenti all'avvio config.name.check.updates.stable = Cercare versioni stabili @@ -318,7 +318,7 @@ config.name.editorMode = Modalit\u00e0 editor config.description.editorMode = Rende modificabili le aree di testo in automatico alla selezione di un nodo text o script config.name.autoSaveTagModifications = Salvataggio automatico modifiche ai tag -config.description.autoSaveTagModifications = Salvare le modifiche quando si seleziona un nuovo tag nella struttura +config.description.autoSaveTagModifications = Salva le modifiche quando si seleziona un nuovo tag nella struttura config.name.saveSessionOnExit = Salva sessione in uscita config.description.saveSessionOnExit = Salva la sessione corrente e la riapre dopo il riavvio di FFDec (funziona solo con file reali) @@ -326,10 +326,10 @@ config.description.saveSessionOnExit = Salva la sessione corrente e la riapre do config.name.showDebugMenu = Mostra il menu di debug config.description.showDebugMenu = Mostra il menu di debug -config.name.allowOnlyOneInstance = Permettere una sola istanza FFDec (sotto Windows) -config.description.allowOnlyOneInstance = FFDec pu\u00f2 essere eseguito solo una volta, tutti i file aperti verranno aggiunti alla stessa finestra.\nFunziona solo sotto sistema operativo Windows +config.name.allowOnlyOneInstance = Permetti una sola istanza di FFDec (sotto Windows) +config.description.allowOnlyOneInstance = FFDec pu\u00f2 essere avviato solo una volta, tutti i file aperti verranno aggiunti alla stessa finestra.\nFunziona solo sotto sistema operativo Windows -config.name.scriptExportSingleFile = Esportare script verso un singolo file +config.name.scriptExportSingleFile = Esporta script verso un unico file config.description.scriptExportSingleFile = Esportazione script verso un unico file invece che file multipli config.name.setFFDecVersionInExportedFont = Imposta il numero di versione di FFDec nel font esportato @@ -359,8 +359,8 @@ config.description.swfSpecificConfigs = Contiene le configurazioni specifiche ag config.name.exeExportMode = Modalit\u00e0 di esportazione EXE config.description.exeExportMode = Modalit\u00e0 di esportazione EXE -config.name.ignoreCLikePackages = Ignora FlashCC / Alchimia o package simili -config.description.ignoreCLikePackages = I package FlashCC / Alchimia non possono solitamente essere decompilati correttamente.\r\n\u00c8 possibile disattivarli per velocizzare la decompilazione di altri package. +config.name.ignoreCLikePackages = Ignora FlashCC / Alchemy o package simili +config.description.ignoreCLikePackages = I package FlashCC / Alchemy non possono solitamente essere decompilati correttamente.\r\n\u00c8 possibile disattivarli per velocizzare la decompilazione di altri package. config.name.overwriteExistingFiles = Sovrascrivere i file esistenti config.description.overwriteExistingFiles = Sovrascrivere i file esistenti durante l'esportazione. Attualmente solo per script AS2/3 @@ -378,33 +378,33 @@ config.name.lastSessionFileTitles = Titoli file ultima sessione config.description.lastSessionFileTitles = Contiene i titoli dei file aperti nell'ultima sessione (ad es. quelli caricati da URL ecc.) config.group.name.paths = Percorsi -config.group.description.paths = Ubicazione dei file necessari -config.group.tip.paths = \u00c8 possibile ottenere questi file dal sito Adobe +config.group.description.paths = Ubicazione dei file richiesti +config.group.tip.paths = \ufffd possibile ottenere questi file dal sito Adobe config.group.link.paths = https://www.adobe.com/support/flashplayer/debug_downloads.html config.group.linkText.paths = [apri] config.name.playerLocation = 1) Percorso Flash Player config.description.playerLocation = Posizione eseguibile Flash Player. Utilizzato per l'azione Esegui. -config.name.playerDebugLocation = 2) Percorso Flash Player per debug +config.name.playerDebugLocation = 2) Percorso Flash Player per il debug config.description.playerDebugLocation = Posizione eseguibile Flash Player per il debug. Utilizzato per l'azione Debug. config.name.playerLibLocation = 3) Percorso PlayerGlobal (.swc) -config.description.playerLibLocation = Posizione della libreria Flash Player playerglobal.swc. Utilizzato perlopi\u00f9 per compilare AS3. +config.description.playerLibLocation = Posizione della libreria playerglobal.swc utilizzata perlopi\u00f9 per compilare AS3. -config.name.debugHalt = Blocca esecuzione all'avvio del debug +config.name.debugHalt = Pausa esecuzione all'avvio del debug config.description.debugHalt = Pausa SWF all'avvio del debug. -config.name.gui.avm2.splitPane.vars.dividerLocationPercent = (Interno) Posizione splitter del menu Debug +config.name.gui.avm2.splitPane.vars.dividerLocationPercent = (Interno) Posizione divisorio del menu Debug config.description.gui.avm2.splitPane.vars.dividerLocationPercent = tip = Suggerimento:\u0020 -config.name.gui.action.splitPane.vars.dividerLocationPercent = (Interno) Posizione splitter del menu Debug AS1/2 +config.name.gui.action.splitPane.vars.dividerLocationPercent = (Interno) Posizione divisorio del menu Debug AS1/2 config.description.gui.action.splitPane.vars.dividerLocationPercent = config.name.setMovieDelay = Ritardo in ms prima di cambiare il file SWF nel riproduttore esterno -config.description.setMovieDelay = Si sconsigliano valori sotto i 1000ms +config.description.setMovieDelay = Si sconsigliano valori inferiori a 1000ms config.name.warning.svgImport = Avvisa all'importazione SVG config.description.warning.svgImport = @@ -422,8 +422,10 @@ config.name.displayDupInstructions = Mostra istruzioni \u00a7\u00a7dup config.description.displayDupInstructions = Visualizza istruzioni \u00a7\u00a7dup nel codice. In loro assenza il codice pu\u00f2 essere facilmente compilato ma i dup con effetti collaterali potrebbero essere eseguiti due volte. config.name.useRegExprLiteral = Decompila RegExp nella forma /pattern/mod. -config.description.useRegExprLiteral = Usa la sintassi /pattern/mod nella decompilazione di espressioni regolari. Altrimenti, utilizzare new RegExp(\"pat\",\"mod\") +config.description.useRegExprLiteral = Usa la sintassi /pattern/mod nella decompilazione di espressioni regolari. Altrimenti, utilizza new RegExp(\"pat\",\"mod\") config.name.handleSkinPartsAutomatically = Gestisce metadati [SkinPart] in automatico -config.description.handleSkinPartsAutomatically = Decompila e modifica direttamente i metadati [SkinPart] in automatico. Se disattivato, l'attributo _skinParts ed il suo metodo getter sono visibili e modificabili direttamente. +config.description.handleSkinPartsAutomatically = Decompila e modifica direttamente i metadati [SkinPart] in automatico. Se disattivato, l'attributo _skinParts ed il suo metodo getter sono visibili e modificabili dall'utente. +config.name.resetLetterSpacingOnTextImport = Ripristina spaziatura lettere all'importazione di testo +config.description.resetLetterSpacingOnTextImport = Utile per i font cirillici, essendo piu ampi diff --git a/src/com/jpexs/decompiler/flash/gui/locales/LoadFromMemoryFrame_fr.properties b/src/com/jpexs/decompiler/flash/gui/locales/LoadFromMemoryFrame_fr.properties index d7ba2aa84..5d0360efd 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/LoadFromMemoryFrame_fr.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/LoadFromMemoryFrame_fr.properties @@ -29,3 +29,4 @@ column.version = Version column.fileSize = Taille du fichier column.pid = PID column.processName = Nom du processus +column.address = Addresse diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_fr.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_fr.properties index f7a2bceae..09de2a162 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_fr.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_fr.properties @@ -713,3 +713,6 @@ menu.settings.simplifyExpressions = Simplification d'expressions #after 8.0.1 menu.recentFiles.empty = La liste des fichiers r\u00e9cents est vide message.warning.outOfMemory32BitJre = M\u00e9moire insuffisante. Vous utilisez une version 32 bits de Java sur un syst\u00e8me 64 bits. Veuillez utiliser Java 64 bits. + +menu.file.reloadAll = Recharger tout +message.confirm.reloadAll = Cette action annule tous les changements qui n'ont pas \u00e9t\u00e9 enregistr\u00e9s dans les fichiers SWF et recharge tous les fichiers dans l'application.\nContinuer ? diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_it.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_it.properties index af2ec5bfe..dbfe30570 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_it.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_it.properties @@ -334,10 +334,12 @@ message.confirm.reload = Questa azione annulla tutte le modifiche non salvate e dialog.selectbkcolor.title = Scelta colore di sfondo + error.font.nocharacter = Il font sorgente selezionato non contiene il carattere "%char%". warning.initializers = I campi statici e le costanti sono spesso valorizzati negli initializer.\nLa modifica del valore qui spesso non \u00e8 sufficiente! + #after version 1.7.0u1: menu.tools.searchMemory = Cerca SWF in memoria @@ -349,7 +351,7 @@ ColorChooser.resetText = Risetta ColorChooser.previewText = Anteprima ColorChooser.swatchesNameText = Palette ColorChooser.swatchesRecentText = Recente: -ColorChooser.sampleText = Testo Campione Testo Campione +ColorChooser.sampleText=Testo Campione Testo Campione #after version 1.7.1: @@ -448,8 +450,8 @@ button.next = Successivo #after version 2.1.0 message.action.playerglobal.title = Libreria PlayerGlobal.swc mancante -message.action.playerglobal.needed = Per effetuare modifiche immediate AS3 la libreria "PlayerGlobal.swc" deve essere scaricato dal sito di Adobe.\r\n%adobehomepage%\r\nPremere OK per visitare la pagina di download. -message.action.playerglobal.place = Scarica la libreria PlayerGlobal(.swc) e posizionarla nella cartella \r\n%libpath%\r\nScegliere OK per continuare. +message.action.playerglobal.needed = Per effettuare modifiche immediate in AS3 la libreria "PlayerGlobal.swc" deve essere scaricato dal sito di Adobe.\r\n%adobehomepage%\r\nPremere OK per visitare la pagina di download. +message.action.playerglobal.place = Scarica la libreria PlayerGlobal(.swc) e posizionala nella cartella \r\n%libpath%\r\nScegliere OK per continuare. message.confirm.experimental.function = Questa funzione \u00e8 SPERIMENTALE. Ci\u00f2 significa che non \u00e8 garantito il risultato ed il file SWF pu\u00f2 essere non funzionale dopo il salvataggio. message.confirm.donotshowagain = Non mostrare pi\u00f9 il messaggio @@ -631,9 +633,9 @@ abc.traitslist.scriptinitializer = Inizializzatore script menu.settings.autoOpenLoadedSWFs = Apri SWF caricati durante la riproduzione #after version 6.1.1 -menu.file.start = Inizia +menu.file.start = Avvio menu.file.start.run = Esegui -menu.file.start.stop = Ferma +menu.file.start.stop = Arresta menu.file.start.debug = Debug menu.debugging = Debug menu.debugging.debug = Debug @@ -641,14 +643,15 @@ menu.debugging.debug.stop = Ferma menu.debugging.debug.pause = Pausa menu.debugging.debug.stepOver = Passo successivo menu.debugging.debug.stepInto = Entra nel metodo -menu.debugging.debug.stepOut = Esci dal metodo +menu.debugging.debug.stepOut = Risali dal metodo menu.debugging.debug.continue = Continua menu.debugging.debug.stack = Stack... menu.debugging.debug.watch = Nuovo osservatore... -message.playerpath.notset = Proiettore Flash Player non trovato. Si prega di configurare il suo percorso in Impostazioni avanzate / Percorsi (1). -message.playerpath.debug.notset = Proiettore Flash Player debugger di contenuto non trovato. Si prega di configurare il suo percorso in Impostazioni avanzate / Percorsi (2). -message.playerpath.lib.notset = PlayerGlobal (.swc) non trovato. Si prega di configurare il suo percorso in Impostazioni avanzate / Percorsi (3). + +message.playerpath.notset = Proiettore Flash Player non trovato. Si prega di configurarne il percorso in Impostazioni avanzate / Percorsi (1). +message.playerpath.debug.notset = Proiettore Flash Player debugger di contenuto non trovato. Si prega di configurarne il percorso in Impostazioni avanzate / Percorsi (2). +message.playerpath.lib.notset = PlayerGlobal (.swc) non trovato. Si prega di configurarne il percorso in Impostazioni avanzate / Percorsi (3). debugpanel.header = Debug @@ -699,8 +702,14 @@ menu.file.start.debugpcode = Debug P-code # after 7.1.2 button.replaceNoFill = Sostituisci - Aggiorna limiti... message.warning.svgImportExperimental = Non tutte le funzioni SVG sono supportate. Controllare il registro dopo l'importazione. -message.imported.swf = Il file SWF utilizza asset da un file SWF importato:\n%url%\nVuoi caricare gli asset da quell'indirizzo? +message.imported.swf = Il file SWF utilizza asset da un file SWF importato:\n%url%\nCaricare gli asset da quell'indirizzo? message.imported.swf.manually = Impossibile caricare SWF importato\n%url%\nFile o URL non esistente.\nSelezionare un file locale? -message.warning.hexViewNotUpToDate = Hex View non aggiornato. Si prega di salvare e ricaricare il file per aggiornare la visuale esadecimale. +message.warning.hexViewNotUpToDate = Hex View non aggiornato. Si prega di salvare e ricaricare il file per aggiornare la visuale. message.font.replace.updateTexts = Sono stati sostituiti alcuni caratteri. Vuoi aggiornare i testi esistenti? + +menu.settings.simplifyExpressions = Semplifica espressioni + +#after 8.0.1 +menu.recentFiles.empty = Lista file recenti \u00e8 vuota +message.warning.outOfMemeory32BitJre = Errore di memoria esaurita. Stai eseguendo Java a 32bit su sistema operativo a 64bit. Prova con Java a 64bit. diff --git a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java index 29e14c0eb..2f1008f65 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java +++ b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java @@ -117,6 +117,8 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { private static Font notUnderlinedFont = null; + private final int zeroCharacterWidth; + static { Font font = new JLabel().getFont(); notUnderlinedFont = font; @@ -189,9 +191,7 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { frameLabel.setVisible(false); - Dimension min = new Dimension(frameLabel.getFontMetrics(notUnderlinedFont).stringWidth("000"), frameLabel.getPreferredSize().height); - frameLabel.setMinimumSize(min); - frameLabel.setPreferredSize(min); + zeroCharacterWidth = frameLabel.getFontMetrics(notUnderlinedFont).stringWidth("0"); frameLabel.addMouseListener(new MouseAdapter() { @@ -333,6 +333,12 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { View.execInEventDispatchLater(() -> { updateZoom(); + int totalFrames = display.getTotalFrames(); + int currentFrame = display.getCurrentFrame(); + if (currentFrame >= totalFrames) { + currentFrame = totalFrames - 1; + } + float frameRate = display.getFrameRate(); Zoom zoom = display.getZoom(); zoomFitButton.setVisible(zoom != null); percentLabel.setVisible(zoom != null); @@ -342,13 +348,14 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { graphicControls.setVisible(screenAvailable); totalFrameLabel.setVisible(screenAvailable); frameLabel.setVisible(screenAvailable); - frameControls.setVisible(screenAvailable); - int totalFrames = display.getTotalFrames(); - int currentFrame = display.getCurrentFrame(); - if (currentFrame >= totalFrames) { - currentFrame = totalFrames - 1; + if (screenAvailable) { + int charCount = Math.max(Integer.toString(totalFrames).length(), 3); + Dimension min = new Dimension(zeroCharacterWidth * charCount, frameLabel.getPreferredSize().height); + frameLabel.setMinimumSize(min); + frameLabel.setPreferredSize(min); } - float frameRate = display.getFrameRate(); + + frameControls.setVisible(screenAvailable); if (totalFrames == 0) { progress.setIndeterminate(true); } else { @@ -357,8 +364,8 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { progress.setValue(currentFrame); progress.setIndeterminate(false); } - frameLabel.setText(("" + (currentFrame + 1))); - totalFrameLabel.setText("" + totalFrames); + frameLabel.setText(Integer.toString(currentFrame + 1)); + totalFrameLabel.setText(Integer.toString(totalFrames)); if (frameRate != 0) { timeLabel.setText("(" + formatMs((int) (currentFrame * 1000.0 / frameRate)) + ")"); totalTimeLabel.setText("(" + formatMs((int) (totalFrames * 1000.0 / frameRate)) + ")");