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 823f04008..13d84635e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -149,6 +149,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.EmptyStackException; import java.util.HashMap; import java.util.HashSet; @@ -861,12 +862,12 @@ public final class SWF implements SWFContainerItem, Timelined { } } - /*preload shape tags*/ - for (Tag tag : tags) { - if (tag instanceof ShapeTag) { - ((ShapeTag) tag).getShapes(); - } - } + /*preload shape tags + for (Tag tag : tags) { + if (tag instanceof ShapeTag) { + ((ShapeTag) tag).getShapes(); + } + }*/ } @Override @@ -2810,9 +2811,32 @@ public final class SWF implements SWFContainerItem, Timelined { return modified; } - public void removeTag(Tag t, boolean removeDependencies) { - Timelined timelined = t.getTimelined(); - if (t instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(t.getId())) { + public void removeTags(Collection tags, boolean removeDependencies) { + Set timelineds = new HashSet<>(); + for (Tag tag : tags) { + Timelined timelined = tag.getTimelined(); + timelineds.add(timelined); + removeTagInternal(timelined, tag, removeDependencies); + } + + for (Timelined timelined : timelineds) { + resetTimelines(timelined); + } + + updateCharacters(); + clearImageCache(); + } + + public void removeTag(Tag tag, boolean removeDependencies) { + Timelined timelined = tag.getTimelined(); + removeTagInternal(timelined, tag, removeDependencies); + resetTimelines(timelined); + updateCharacters(); + clearImageCache(); + } + + private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { + if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { List tags; if (timelined instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) timelined; @@ -2820,7 +2844,7 @@ public final class SWF implements SWFContainerItem, Timelined { } else { tags = this.tags; } - tags.remove(t); + tags.remove(tag); if (timelined instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) timelined; sprite.setModified(true); @@ -2829,18 +2853,15 @@ public final class SWF implements SWFContainerItem, Timelined { } else { // timeline should be always the swf here if (removeDependencies) { - removeTagWithDependenciesFromTimeline(t, timelined.getTimeline()); + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); if (timelined instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) timelined; sprite.setModified(true); } } else { - removeTagFromTimeline(t, timeline); + removeTagFromTimeline(tag, timeline); } } - resetTimelines(timelined); - updateCharacters(); - clearImageCache(); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java index ba2c5c23d..d6d3106ed 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.helpers.utf8.Utf8Helper; import java.awt.Component; +import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Area; @@ -65,7 +66,7 @@ public class Helper { public static String newLine = System.getProperty("line.separator"); public static String decompilationErrorAdd = null; - + private static final Map shapeCache = new HashMap<>(); /** @@ -898,7 +899,7 @@ public class Helper { return sb.toString(); } - public static Shape imageToShape(BufferedImage image) { + public static Shape imageToShapeOld(BufferedImage image) { Area area = new Area(); Rectangle rectangle = new Rectangle(); int y1, y2; @@ -909,12 +910,12 @@ public class Helper { int type = image.getType(); if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - } else{ + } else { imgData = image.getRGB(0, 0, width, height, null, 0, width); } - + BitSet bs = new BitSet(width * height); - bs.set(type); + bs.set(type); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int idx = width * y + x; @@ -923,12 +924,12 @@ public class Helper { } } } - + String key = byteArrayToBase64String(bs.toByteArray()); if (shapeCache.containsKey(key)) { return shapeCache.get(key); } - + for (int x = 0; x < width; x++) { y1 = Integer.MAX_VALUE; y2 = -1; @@ -957,7 +958,159 @@ public class Helper { shapeCache.put(key, area); return area; } - + + public static Shape imageToShape(BufferedImage image) { + Area area = new Area(); + int width = image.getWidth(); + int height = image.getHeight(); + + int[] imgData; + int type = image.getType(); + if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { + imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + } else { + imgData = image.getRGB(0, 0, width, height, null, 0, width); + } + + 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); + } + } + } + + String key = byteArrayToBase64String(bs.toByteArray()); + if (shapeCache.containsKey(key)) { + return shapeCache.get(key); + } + + BitSet bsArea = new BitSet(width * height); + boolean modified = true; + + List leftCoordsX = new ArrayList<>(); + List leftCoordsY = new ArrayList<>(); + List rightCoordsX = new ArrayList<>(); + List rightCoordsY = new ArrayList<>(); + while (modified) { + modified = false; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int idx = width * y + x; + if ((imgData[idx] >>> 24) > 0 && !bsArea.get(idx)) { + leftCoordsX.clear(); + leftCoordsY.clear(); + rightCoordsX.clear(); + rightCoordsY.clear(); + int leftX = x; + int rightX = findRight(imgData, x, y, width); + leftCoordsX.add(leftX); + leftCoordsY.add(y); + rightCoordsX.add(rightX); + rightCoordsY.add(y); + setBitSet(bsArea, leftX, rightX, y, width); + int y2 = y + 1; + while (y2 < height) { + leftCoordsX.add(leftX); + leftCoordsY.add(y2); + rightCoordsX.add(rightX); + rightCoordsY.add(y2); + + int leftX2 = findFirst(imgData, leftX, rightX, y2, width); + if (leftX2 == -1) { + break; + } + + int rightX2 = findRight(imgData, leftX2, y2, width); + + if (leftX2 != leftX) { + leftCoordsX.add(leftX2); + leftCoordsY.add(y2); + } + + if (rightX2 != rightX) { + rightCoordsX.add(rightX2); + rightCoordsY.add(y2); + } + + leftX = leftX2; + rightX = rightX2; + + setBitSet(bsArea, leftX, rightX, y2, width); + y2++; + } + + int cnt = leftCoordsX.size() + rightCoordsX.size(); + int[] xCoords = new int[cnt]; + int[] yCoords = new int[cnt]; + for (int i = 0; i < rightCoordsX.size(); i++) { + xCoords[i] = rightCoordsX.get(i); + yCoords[i] = rightCoordsY.get(i); + } + + int offset = rightCoordsX.size(); + for (int i = 0; i < leftCoordsX.size(); i++) { + int idx2 = leftCoordsX.size() - i - 1; + xCoords[i + offset] = leftCoordsX.get(idx2); + yCoords[i + offset] = leftCoordsY.get(idx2); + } + + Area area2 = new Area(new Polygon(xCoords, yCoords, xCoords.length)); + area.add(area2); + modified = true; + } + } + } + } + + shapeCache.put(key, area); + return area; + } + + private static void setBitSet(BitSet bitSet, int x1, int x2, int y, int width) { + int idx = width * y + x1; + int idx2 = width * y + x2; + for (; idx < idx2; idx++) { + bitSet.set(idx); + } + } + + private static int findFirst(int[] imgData, int x1, int x2, int y, int width) { + int idx = width * y + x1; + if ((imgData[idx] >>> 24) > 0) { + while (x1 > 0 && (imgData[idx - 1] >>> 24) > 0) { + x1--; + idx--; + } + return x1; + } + + int idx2 = width * y + x2; + for (; idx < idx2; idx++) { + if ((imgData[idx] >>> 24) > 0) { + return x1; + } + + x1++; + } + + return -1; + } + + private static int findRight(int[] imgData, int x, int y, int width) { + int result = x; + int idx = width * y + x; + while ((imgData[idx] >>> 24) > 0 && result < width) { + result++; + idx++; + } + + return result; + } + public static void clearShapeCache() { shapeCache.clear(); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index a9588884f..d21131856 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1979,13 +1979,17 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec if (swf == null) { return; } + List tags = new ArrayList<>(swf.tags); + List toRemove = new ArrayList<>(); for (Tag tag : tags) { System.out.println(tag.getClass()); if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { - swf.removeTag(tag, true); + toRemove.add(tag); } } + + swf.removeTags(toRemove, true); refreshTree(swf); } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 1d5803170..6f2004380 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -45,7 +45,9 @@ import java.awt.event.MouseEvent; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JMenu; @@ -519,8 +521,18 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener { } if (View.showConfirmDialog(this, confirmationMessage, mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + Map> tagsToRemoveBySwf = new HashMap<>(); for (Tag tag : tagsToRemove) { - tag.getSwf().removeTag(tag, removeDependencies); + SWF swf = tag.getSwf(); + if (!tagsToRemoveBySwf.containsKey(swf)) { + tagsToRemoveBySwf.put(swf, new ArrayList<>()); + } + + tagsToRemoveBySwf.get(swf).add(tag); + } + + for (SWF swf : tagsToRemoveBySwf.keySet()) { + swf.removeTags(tagsToRemoveBySwf.get(swf), removeDependencies); } mainPanel.refreshTree(null);