From a2ee9178a257e138886578692cb57be6fa9a86cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Sun, 9 Jun 2013 16:20:39 +0200 Subject: [PATCH] Improved internal flash viewer - shapes --- .../flash/types/shaperecords/SHAPERECORD.java | 252 +++++++++++++++--- 1 file changed, 220 insertions(+), 32 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index 4352e9a02..c6e957b16 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; +import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -46,6 +47,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,14 +66,30 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { return ret; } + private static String edgesToStr(List records) { + int x = 0; + int y = 0; + String ret = ""; + for (SHAPERECORD rec : records) { + x = rec.changeX(x); + y = rec.changeY(y); + ret += ("[" + x + "," + y + "],"); + } + return ret; + + } + private static class Path { + public boolean used = false; public LINESTYLE lineStyle; public LINESTYLE2 lineStyle2; public boolean useLineStyle2; public FILLSTYLE fillStyle0; public FILLSTYLE fillStyle1; public List edges = new ArrayList<>(); + public Point start; + public Point end; public void draw(int startX, int startY, Graphics2D g, int shapeNum) { AffineTransform oldAf = g.getTransform(); @@ -327,6 +345,8 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } if (ok) { GeneralPath path = toGeneralPath(startX, startY); + path.closePath(); + //path.setWindingRule(GeneralPath.WIND_EVEN_ODD); g.fill(path); } g.setTransform(oldAf); @@ -334,6 +354,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } public void drawTo(List tags, int startX, int startY, Graphics2D g, int shapeNum) { + g.setComposite(AlphaComposite.SrcOver); fill(tags, startX, startY, g, shapeNum); draw(startX, startY, g, shapeNum); } @@ -352,7 +373,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { if (scr.stateMoveTo) { nx += startX; ny += startY; - ret.moveTo(nx, ny); + if ((!wasMoveTo) || ((nx != x) || (ny != y))) { + ret.moveTo(nx, ny); + } wasMoveTo = true; } } @@ -449,10 +472,11 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } return new RECT(min_x, max_x, min_y, max_y); } + static int cnt = 0; private static List getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) { List paths = new ArrayList<>(); - Path path = new Path(); + Path path = null; int x = 0; int y = 0; int max_x = 0; @@ -460,12 +484,20 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { int min_x = Integer.MAX_VALUE; int min_y = Integer.MAX_VALUE; boolean started = false; + FILLSTYLE lastFillStyle0 = null; + FILLSTYLE lastFillStyle1 = null; + LINESTYLE lastLineStyle = null; + LINESTYLE2 lastLineStyle2 = null; for (SHAPERECORD r : records) { if (r instanceof StyleChangeRecord) { StyleChangeRecord scr = (StyleChangeRecord) r; - paths.add(path); + if (path != null) { + path.end = new Point(x, y); + paths.add(path); + } path = new Path(); + if (scr.stateNewStyles) { fillStyles = scr.fillStyles; lineStylesList = scr.lineStyles; @@ -478,14 +510,21 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } else { path.fillStyle0 = fillStyles.fillStyles[scr.fillStyle0 - 1]; } + } else { + path.fillStyle0 = lastFillStyle0; } + + lastFillStyle0 = path.fillStyle0; if (scr.stateFillStyle1) { if ((scr.fillStyle1 == 0) || (fillStyles == null)) { path.fillStyle1 = null; } else { path.fillStyle1 = fillStyles.fillStyles[scr.fillStyle1 - 1]; } + } else { + path.fillStyle1 = lastFillStyle1; } + lastFillStyle1 = path.fillStyle1; if (scr.stateLineStyle) { if (shapeNum >= 4) { path.useLineStyle2 = true; @@ -502,11 +541,32 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { path.lineStyle = lineStylesList.lineStyles[scr.lineStyle - 1]; } } + } else { + if (shapeNum >= 4) { + path.lineStyle2 = lastLineStyle2; + } else { + path.lineStyle = lastLineStyle; + } } + + lastLineStyle = path.lineStyle; + lastLineStyle2 = path.lineStyle2; + if (started && (!scr.stateMoveTo)) { + scr.stateMoveTo = true; + scr.moveDeltaX = x; + scr.moveDeltaY = y; + } + } + if (path == null) { + path = new Path(); + path.start = new Point(x, y); } path.edges.add(r); x = r.changeX(x); y = r.changeY(y); + if (path.start == null) { + path.start = new Point(x, y); + } if (r.isMove()) { started = true; } @@ -525,6 +585,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } } } + path.end = new Point(x, y); paths.add(path); List paths2 = new ArrayList<>(); for (Path p : paths) { @@ -536,55 +597,182 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { } if (p.fillStyle1 != null) { Path f = new Path(); - f.edges = new ArrayList<>(); + boolean flip = true; f.fillStyle0 = p.fillStyle1; f.lineStyle = p.lineStyle; f.lineStyle2 = p.lineStyle2; f.useLineStyle2 = p.useLineStyle2; - x = 0; - y = 0; - for (SHAPERECORD r : p.edges) { - x = r.changeX(x); - y = r.changeY(y); - SHAPERECORD r2 = null; - try { - r2 = (SHAPERECORD) r.clone(); - } catch (CloneNotSupportedException ex) { - Logger.getLogger(SHAPERECORD.class.getName()).log(Level.SEVERE, null, ex); + f.edges = new ArrayList<>(); + f.start = p.end; + f.end = p.start; + if (!flip) { + f.edges = p.edges; + } else { + x = 0; + y = 0; + boolean revstarted = false; + List chedges = new ArrayList<>(); + for (SHAPERECORD r : p.edges) { + int oldX = x; + int oldY = y; + x = r.changeX(x); + y = r.changeY(y); + if (r instanceof StyleChangeRecord) { + StyleChangeRecord mv = (StyleChangeRecord) r; + if (mv.stateMoveTo) { + StyleChangeRecord mv2 = null; + try { + mv2 = (StyleChangeRecord) mv.clone(); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(SHAPERECORD.class.getName()).log(Level.SEVERE, null, ex); + } + mv2.moveDeltaX = oldX; + mv2.moveDeltaY = oldY; + r = mv2; + if (!revstarted) { + revstarted = true; + continue; + } + } + } + chedges.add(r); + } + int finishX = x; + int finishY = y; + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateMoveTo = true; + scr.moveDeltaX = finishX; + scr.moveDeltaY = finishY; + f.edges.add(scr); + + x = finishX; + y = finishY; + for (int e = chedges.size() - 1; e >= 0; e--) { + SHAPERECORD r = chedges.get(e); + SHAPERECORD r2 = null; + try { + r2 = (SHAPERECORD) r.clone(); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(SHAPERECORD.class.getName()).log(Level.SEVERE, null, ex); + } + r2.flip(); + + x = r.changeX(x); + y = r.changeY(y); + f.edges.add(r2); } - r2.flip(); - f.edges.add(0, r2); } - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateMoveTo = true; - scr.moveDeltaX = x; - scr.moveDeltaY = y; - f.edges.add(0, scr); paths2.add(f); } } - List paths3 = new ArrayList<>(); + List> groupedPaths = new ArrayList<>(); for (Path p1 : paths2) { boolean found = false; - for (Path p2 : paths3) { - if (p1 == p2) { + for (List list : groupedPaths) { + if (list.contains(p1)) { continue; } - if (p1.sameStyle(p2)) { - p2.edges.addAll(p1.edges); + if (p1.sameStyle(list.get(0))) { + list.add(p1); found = true; break; } } if (!found) { - paths3.add(p1); + List newitem = new ArrayList<>(); + newitem.add(p1); + groupedPaths.add(newitem); + } + } + List subpath; + List edges; + Point k2; + Point k1; + + List ret = new ArrayList<>(); + + for (int e = 0; e < groupedPaths.size(); e++) { + List pathList = groupedPaths.get(e); + List> m = new ArrayList<>(); + Map> ind = new HashMap<>(); + int usedCount = 0; + for (int f = 0; f < pathList.size(); f++) { + path = pathList.get(f); + if (path.start.equals(path.end)) { + path.used = true; + usedCount++; + List psh = new ArrayList<>(); + psh.add(path); + m.add(psh); + } else { + path.used = false; + if (!ind.containsKey(path.start)) { + ind.put(path.start, new ArrayList()); + } + ind.get(path.start).add(path); + } + } + for (int f = 0; f < pathList.size(); f++) { + if (usedCount == pathList.size()) { + break; + } + path = pathList.get(f); + if (!path.used) { + subpath = new ArrayList<>(); + subpath.add(path); + path.used = true; + usedCount++; + edges = ind.get(path.start); + for (int l = 0; l < edges.size(); l++) { + if (edges.get(l) == path) { + edges.remove(l); + } + } + k1 = path.start; + k2 = path.end; + while (!k2.equals(k1)) { + if (!ind.containsKey(k2)) { + break; + } + edges = ind.get(k2); + if (edges.isEmpty()) { + break; + } + path = edges.remove(0); + subpath.add(path); + path.used = true; + usedCount++; + k2 = path.end; + } + m.add(subpath); + } + } + + if (!m.isEmpty()) { + Path onepath = null; + for (List list : m) { + for (Path p : list) { + if (onepath == null) { + onepath = new Path(); + onepath.fillStyle0 = p.fillStyle0; + } + if (onepath.start == null) { + onepath.start = p.start; + } + onepath.edges.addAll(p.edges); + onepath.end = p.end; + } + } + if (onepath != null) { + ret.add(onepath); + } } } bounds.Xmax = max_x; bounds.Ymax = max_y; bounds.Xmin = min_x; bounds.Ymin = min_y; - return paths3; + return ret; } /** @@ -619,7 +807,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { private static HashMap cache = new HashMap<>(); public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) { - return shapeToImage(tags, shapeNum, fillStyles, lineStylesList, records, Color.black); + return shapeToImage(tags, shapeNum, fillStyles, lineStylesList, records, null); } public static List shapeToPaths(List tags, int shapeNum, List records) { @@ -640,20 +828,20 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { RECT rect = new RECT(); List paths = getPaths(rect, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records); BufferedImage ret = new BufferedImage( - //(int)((rect.Xmax-rect.Xmin)/DESCALE),(int)((rect.Ymax-rect.Ymin)/DESCALE) (int) (rect.getWidth() / DESCALE + 2), (int) (rect.getHeight() / DESCALE + 2), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) ret.getGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + for (Path p : paths) { - if (p.fillStyle0 == null) { + if ((p.fillStyle0 == null) && (p.lineStyle == null) && (p.lineStyle2 == null) && (defaultColor != null)) { p.fillStyle0 = new FILLSTYLE(); p.fillStyle0.fillStyleType = FILLSTYLE.SOLID; p.fillStyle0.color = new RGB(defaultColor); p.fillStyle0.colorA = new RGBA(defaultColor); } - p.drawTo(tags, -rect.Xmin, -rect.Ymin/*-rect.Xmin, -rect.Ymin*/, g, shapeNum); + p.drawTo(tags, -rect.Xmin, -rect.Ymin, g, shapeNum); } cache.put(key, ret); return ret;