diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java new file mode 100644 index 000000000..6f2cf1b28 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierEdge.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.math; + +import com.jpexs.helpers.Reference; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class BezierEdge { + + public List points = new ArrayList<>(); + + public BezierEdge(List points) { + this.points = points; + } + + public BezierEdge(double x0, double y0, double x1, double y1) { + points.add(new Point2D.Double(x0, y0)); + points.add(new Point2D.Double(x1, y1)); + } + + public BezierEdge(double x0, double y0, double cx, double cy, double x1, double y1) { + points.add(new Point2D.Double(x0, y0)); + points.add(new Point2D.Double(cx, cy)); + points.add(new Point2D.Double(x1, y1)); + } + + public Point2D pointAt(double t) { + if (points.size() == 2) { + double x = (1-t)*points.get(0).getX() + t * points.get(1).getX(); + double y = (1-t)*points.get(0).getY() + t * points.get(1).getY(); + return new Point2D.Double(x, y); + } + //points size == 3 + double x = (1 - t) * (1 - t) * points.get(0).getX() + + 2 * t * (1 - t) * points.get(1).getX() + + t * t * points.get(2).getX(); + double y = (1 - t) * (1 - t) * points.get(0).getY() + + 2 * t * (1 - t) * points.get(1).getY() + + t * t * points.get(2).getY(); + return new Point2D.Double(x, y); + } + + public Rectangle2D bbox() { + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = -Double.MAX_VALUE; + double maxY = -Double.MAX_VALUE; + //System.err.println("calculating bbox"); + for (Point2D p : points) { + //System.err.println(" point " + p); + if (p.getX() < minX) { + minX = p.getX(); + } + if (p.getX() > maxX) { + maxX = p.getX(); + } + if (p.getY() < minY) { + minY = p.getY(); + } + if (p.getY() > maxY) { + maxY = p.getY(); + } + } + + Rectangle2D b = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + /*System.err.println(" bbox = "+b); + System.err.println(" maxx = "+maxX); + System.err.println(" maxy = "+maxY);*/ + return b; + } + + + private final double MIN_SIZE = 1.0; + + public double area() { + Rectangle2D rect = bbox(); + double w = rect.getWidth(); + double h = rect.getHeight(); + + + if (w < MIN_SIZE) { + w = MIN_SIZE; + } + if (h < MIN_SIZE) { + h = MIN_SIZE; + } + return w * h; + } + + + private static boolean rectIntersection(Rectangle2D r1, Rectangle2D r2) { + double xmin = Math.max(r1.getX(), r2.getX()); + double xmax1 = r1.getX() + r1.getWidth(); + double xmax2 = r2.getX() + r2.getWidth(); + double xmax = Math.min(xmax1, xmax2); + if (Double.compare(xmax, xmin) >= 0) { + double ymin = Math.max(r1.getY(), r2.getY()); + double ymax1 = r1.getY() + r1.getHeight(); + double ymax2 = r2.getY() + r2.getHeight(); + double ymax = Math.min(ymax1, ymax2); + if (Double.compare(ymax, ymin) >= 0) { + //out.setRect(xmin, ymin, xmax-xmin, ymax-ymin); + return true; + } + } + return false; + } + + + public boolean intersects(BezierEdge b2, List t1Ref, List t2Ref) { + return intersects(b2, + 0, + 1, + 0, + 1, t1Ref, t2Ref); + } + + private boolean intersects( + BezierEdge b2, + double start1, + double end1, + double start2, + double end2, + List t1Ref, + List t2Ref) { + final double threshold = MIN_SIZE * 2.0; //? + Rectangle2D bb1 = bbox(); + Rectangle2D bb2 = b2.bbox(); + if (!rectIntersection(bb1, bb2)) { + return false; + } + double sumAreas = area()+b2.area(); + //System.err.println("sumAreas="+sumAreas); + if(Double.compare(sumAreas, threshold) <= 0) { + double t1 = (start1+end1) / 2; + double t2 = (start2+end2) / 2; + + t1Ref.add(t1); + t2Ref.add(t2); + return true; + } + + //System.err.println("subdividing "+this+ " and "+b2); + BezierUtils bu = new BezierUtils(); + List b1aPoints = new ArrayList<>(); + List b1bPoints = new ArrayList<>(); + bu.subdivide(points, 0.5, b1aPoints, b1bPoints); + + List b2aPoints = new ArrayList<>(); + List b2bPoints = new ArrayList<>(); + bu.subdivide(b2.points, 0.5, b2aPoints, b2bPoints); + BezierEdge b1a = new BezierEdge(b1aPoints); + BezierEdge b1b = new BezierEdge(b1bPoints); + BezierEdge b2a = new BezierEdge(b2aPoints); + BezierEdge b2b = new BezierEdge(b2bPoints); + + double half1 = start1 + (end1- start1) / 2.0; + double half2 = start2 + (end2- start2) / 2.0; + + boolean ok = false; + if(b1a.intersects(b2a, start1, half1, start2, half2, t1Ref, t2Ref)) { + ok = true; + } + if (b1a.intersects(b2b, start1, half1, half2, end2, t1Ref, t2Ref)) { + ok = true; + } + if (b1b.intersects(b2a, half1, end1, start2, half2, t1Ref, t2Ref)) { + ok = true; + } + if (b1b.intersects(b2b, half1, end1, half2, end2, t1Ref, t2Ref)) { + ok = true; + } + return ok; + } + + @Override + public String toString() { + List list = new ArrayList<>(); + for(Point2D p:points) { + list.add("["+p.getX()+","+p.getY()+"]"); + } + return "{"+String.join("-", list)+"}"; + } + + public void split(double t, Reference left, Reference right) { + List leftPoints = new ArrayList<>(); + List rightPoints = new ArrayList<>(); + BezierUtils bu = new BezierUtils(); + bu.subdivide(points, t, leftPoints, rightPoints); + left.setVal(new BezierEdge(leftPoints)); + right.setVal(new BezierEdge(rightPoints)); + } + + + + public static void main(String[] args) { + List t1 = new ArrayList<>(); + List t2 = new ArrayList<>(); + /*/BezierEdge be1 = new BezierEdge(0,0,100,100); + BezierEdge be2 = new BezierEdge(75,100,75,0); + + System.out.println("lines "+ be1+ " and "+be2); + + + + + System.out.println("hasIntersection = "+be1.intersects(be2, t1, t2)); + System.out.println("intersection points: "+t1+", "+t2); + + + BezierEdge be3 = new BezierEdge(0,0,100,0); + BezierEdge be4 = new BezierEdge(0,100,100,100); + + System.out.println("lines "+ be3+ " and "+be4); + + System.out.println("hasIntersection = "+be3.intersects(be4, t1, t2)); + System.out.println("intersection points: "+t1+", "+t2);*/ + + + BezierEdge be5 = new BezierEdge(25,0,25,100); + BezierEdge be6 = new BezierEdge(0,50,100,50); + + System.out.println("lines "+ be5+ " and "+be6); + + System.out.println("hasIntersection = "+be5.intersects(be6, t1, t2)); + System.out.println("intersection ts: "+t1+", "+t2); + + Point2D c = new Point2D.Double( + (1-t1.get(0))*be5.points.get(0).getX() + t1.get(0) * be5.points.get(1).getX(), + (1-t1.get(0))*be5.points.get(0).getY() + t1.get(0) * be5.points.get(1).getY() + ); + + System.out.println("Intersection point: " +c); + + //Rectangle2D out = new Rectangle2D.Double(); + //rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out); + //System.out.println("out = "+out); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/BezierUtils.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierUtils.java similarity index 95% rename from src/com/jpexs/decompiler/flash/gui/BezierUtils.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierUtils.java index 8cd6a0a0a..4b01cab09 100644 --- a/src/com/jpexs/decompiler/flash/gui/BezierUtils.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/math/BezierUtils.java @@ -1,20 +1,20 @@ /* - * Copyright (C) 2022 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. */ -package com.jpexs.decompiler.flash.gui; +package com.jpexs.decompiler.flash.math; import java.awt.geom.Point2D; import java.util.ArrayList; 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 03a87dc40..fc69da6ea 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 @@ -16,6 +16,11 @@ */ package com.jpexs.decompiler.flash.xfl; +import com.jpexs.decompiler.flash.xfl.shapefixer.CurvedEdgeRecordAdvanced; +import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeFixer; +import com.jpexs.decompiler.flash.xfl.shapefixer.StraightEdgeRecordAdvanced; +import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeRecordAdvanced; +import com.jpexs.decompiler.flash.xfl.shapefixer.StyleChangeRecordAdvanced; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; @@ -140,6 +145,7 @@ import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; import java.awt.Font; import java.awt.Point; +import java.awt.geom.Point2D; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; @@ -147,12 +153,15 @@ import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.nio.file.Paths; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; @@ -215,51 +224,67 @@ public class XFLConverter { */ private final boolean DEBUG_EXPORT_LAYER_DEPTHS = false; - private static void convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y, StringBuilder ret) { - if (record instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) record; - Point p = new Point(scr.moveDeltaX, scr.moveDeltaY); + + private static String formatEdgeDouble(double value, boolean curved) { + if (value % 1 == 0) { + return "" + (int) value; + } + DecimalFormat df = new DecimalFormat("0.##", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + df.setGroupingUsed(false); + String strValue = "" + df.format(value); + + if (curved) { + String parts[] = strValue.split("\\."); + return ("#" + Integer.toHexString(Integer.parseInt(parts[0])) + "." + Integer.toHexString(Integer.parseInt(parts[1]))).toUpperCase(Locale.ENGLISH); + } + return "" + strValue; + } + + private static void convertShapeEdge(MATRIX mat, ShapeRecordAdvanced record, double x, double y, StringBuilder ret) { + if (record instanceof StyleChangeRecordAdvanced) { + StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) record; + Point2D p = new Point2D.Double(scr.moveDeltaX, scr.moveDeltaY); if (scr.stateMoveTo) { - ret.append("! ").append(p.x).append(" ").append(p.y); + ret.append("! ").append(formatEdgeDouble(p.getX(), false)).append(" ").append(formatEdgeDouble(p.getY(), false)); } - } else if (record instanceof StraightEdgeRecord) { - StraightEdgeRecord ser = (StraightEdgeRecord) record; - if (ser.generalLineFlag || ser.vertLineFlag) { - y += ser.deltaY; - } - if (ser.generalLineFlag || (!ser.vertLineFlag)) { - x += ser.deltaX; - } - Point p = new Point(x, y); - ret.append("| ").append(p.x).append(" ").append(p.y); - } else if (record instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer = (CurvedEdgeRecord) record; - int controlX = cer.controlDeltaX + x; - int controlY = cer.controlDeltaY + y; - int anchorX = cer.anchorDeltaX + controlX; - int anchorY = cer.anchorDeltaY + controlY; - Point control = new Point(controlX, controlY); - Point anchor = new Point(anchorX, anchorY); - ret.append("[ ").append(control.x).append(" ").append(control.y).append(" ").append(anchor.x).append(" ").append(anchor.y); + } else if (record instanceof StraightEdgeRecordAdvanced) { + StraightEdgeRecordAdvanced ser = (StraightEdgeRecordAdvanced) record; + y += ser.deltaY; + x += ser.deltaX; + Point2D p = new Point2D.Double(x, y); + ret.append("| "); + ret.append(formatEdgeDouble(p.getX(), false)); + ret.append(" "); + ret.append(formatEdgeDouble(p.getY(), false)); + } else if (record instanceof CurvedEdgeRecordAdvanced) { + CurvedEdgeRecordAdvanced cer = (CurvedEdgeRecordAdvanced) record; + double controlX = cer.controlDeltaX + x; + double controlY = cer.controlDeltaY + y; + double anchorX = cer.anchorDeltaX + controlX; + double anchorY = cer.anchorDeltaY + controlY; + Point2D control = new Point2D.Double(controlX, controlY); + Point2D anchor = new Point.Double(anchorX, anchorY); + ret.append("[ ").append(formatEdgeDouble(control.getX(), true)).append(" ").append(formatEdgeDouble(control.getY(), true)).append(" ").append(formatEdgeDouble(anchor.getX(), true)).append(" ").append(formatEdgeDouble(anchor.getY(), true)); } } - private static void convertShapeEdges(int startX, int startY, MATRIX mat, List records, StringBuilder ret) { - int x = startX; - int y = startY; + private static void convertShapeEdges(double startX, double startY, MATRIX mat, List recordsAdvanced, StringBuilder ret) { + + double x = startX; + double y = startY; boolean hasMove = false; - if (!records.isEmpty()) { - if (records.get(0) instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) records.get(0); + if (!recordsAdvanced.isEmpty()) { + if (recordsAdvanced.get(0) instanceof StyleChangeRecordAdvanced) { + StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) recordsAdvanced.get(0); if (scr.stateMoveTo) { hasMove = true; } } } if (!hasMove) { - ret.append("! ").append(startX).append(" ").append(startY); + ret.append("! ").append(formatEdgeDouble(startX, false)).append(" ").append(formatEdgeDouble(startY, false)); } - for (SHAPERECORD rec : records) { + for (ShapeRecordAdvanced rec : recordsAdvanced) { convertShapeEdge(mat, rec, x, y, ret); x = rec.changeX(x); y = rec.changeY(y); @@ -678,76 +703,27 @@ public class XFLConverter { index++; } return ret; - } - - /** - * Remove bugs in shape: - * - * ... straightrecord straightrecord stylechange straightrecord (-2,0) <-- - * merge this with previous stylegchange - * - * @param shapeRecords - * @return - */ - private static List smoothShape(List shapeRecords) { - List ret = new ArrayList<>(shapeRecords.size()); - for (SHAPERECORD rec : shapeRecords) { - ret.add(rec.clone()); - } - - for (int i = 1; i < ret.size() - 1; i++) { - if (ret.get(i) instanceof StraightEdgeRecord && (ret.get(i - 1) instanceof StyleChangeRecord) && (ret.get(i + 1) instanceof StyleChangeRecord)) { - StraightEdgeRecord ser = (StraightEdgeRecord) ret.get(i); - StyleChangeRecord scr = (StyleChangeRecord) ret.get(i - 1); - StyleChangeRecord scr2 = (StyleChangeRecord) ret.get(i + 1); - if ((!scr.stateMoveTo && !scr.stateNewStyles) && Math.abs(ser.deltaX) < 5 && Math.abs(ser.deltaY) < 5) { - if (i >= 2) { - SHAPERECORD rbef = ret.get(i - 2); - if (rbef instanceof StraightEdgeRecord) { - StraightEdgeRecord ser_b = (StraightEdgeRecord) rbef; - ser_b.generalLineFlag = true; - ser_b.deltaX = ser.changeX(ser_b.deltaX); - ser_b.deltaY = ser.changeY(ser_b.deltaY); - } else if (rbef instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer_b = (CurvedEdgeRecord) rbef; - cer_b.anchorDeltaX = ser.changeX(cer_b.anchorDeltaX); - cer_b.anchorDeltaY = ser.changeY(cer_b.anchorDeltaY); - } else { - //??? - } - - ret.remove(i - 1); - ret.remove(i - 1); - if (scr.stateFillStyle0 && !scr2.stateFillStyle0) { - scr2.stateFillStyle0 = true; - scr2.fillStyle0 = scr.fillStyle0; - } - if (scr.stateFillStyle1 && !scr2.stateFillStyle1) { - scr2.stateFillStyle1 = true; - scr2.fillStyle1 = scr.fillStyle1; - } - if (scr.stateLineStyle && !scr2.stateLineStyle) { - scr2.stateLineStyle = true; - scr2.lineStyle = scr.lineStyle; - } - - i -= 2; - } - } - } - - } - return ret; - } + } private static List getShapeLayers(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape) throws XMLStreamException { if (mat == null) { mat = new MATRIX(); } - //smoothing fixes some shapes in #503, but also breaks some shapes of #1257 - //shapeRecords = smoothShape(shapeRecords); - List edges = new ArrayList<>(); + List shapeRecordsAdvanced = new ArrayList<>(); + for(SHAPERECORD rec:shapeRecords) { + ShapeRecordAdvanced arec = ShapeRecordAdvanced.createFromSHAPERECORD(rec); + if (arec != null) { + shapeRecordsAdvanced.add(arec); + } + } + + + //#1903, #503, #1257 This is not working at all :-( + //ShapeFixer fixer = new ShapeFixer(); + //shapeRecordsAdvanced = fixer.fixShape(shapeRecordsAdvanced); + + List edges = new ArrayList<>(); int lineStyleCount = 0; int fillStyle0 = -1; int fillStyle1 = -1; @@ -799,17 +775,17 @@ public class XFLConverter { currentLayer.writeStartElement("edges"); } - int x = 0; - int y = 0; - int startEdgeX = 0; - int startEdgeY = 0; + double x = 0; + double y = 0; + double startEdgeX = 0; + double startEdgeY = 0; LINESTYLEARRAY actualLinestyles = lineStyles; int strokeStyleOrig = 0; fillStyleCount = fillStyles == null ? 0 : fillStyles.fillStyles.length; - for (SHAPERECORD edge : shapeRecords) { - if (edge instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) edge; + for (ShapeRecordAdvanced edge : shapeRecordsAdvanced) { + if (edge instanceof StyleChangeRecordAdvanced) { + StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) edge; boolean styleChange = false; int lastFillStyle1 = fillStyle1; int lastFillStyle0 = fillStyle0; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/CurvedEdgeRecordAdvanced.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/CurvedEdgeRecordAdvanced.java new file mode 100644 index 000000000..80797862b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/CurvedEdgeRecordAdvanced.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.xfl.shapefixer; + +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; + +/** + * + * @author JPEXS + */ +public class CurvedEdgeRecordAdvanced extends ShapeRecordAdvanced { + public double controlDeltaX; + public double controlDeltaY; + public double anchorDeltaX; + public double anchorDeltaY; + + public CurvedEdgeRecordAdvanced() { + } + + public CurvedEdgeRecordAdvanced(int controlDeltaX, int controlDeltaY, int anchorDeltaX, int anchorDeltaY) { + this.controlDeltaX = controlDeltaX; + this.controlDeltaY = controlDeltaY; + this.anchorDeltaX = anchorDeltaX; + this.anchorDeltaY = anchorDeltaY; + } + + public CurvedEdgeRecordAdvanced(CurvedEdgeRecord cer) { + this.controlDeltaX = cer.controlDeltaX; + this.controlDeltaY = cer.controlDeltaY; + this.anchorDeltaX = cer.anchorDeltaX; + this.anchorDeltaY = cer.anchorDeltaY; + } + + @Override + public double changeX(double x) { + return x + controlDeltaX + anchorDeltaX; + } + + @Override + public double changeY(double y) { + return y + controlDeltaY + anchorDeltaY; + } + + @Override + public CurvedEdgeRecord toBasicRecord() { + CurvedEdgeRecord ret = new CurvedEdgeRecord(); + ret.controlDeltaX = (int)Math.round(controlDeltaX); + ret.controlDeltaY = (int)Math.round(controlDeltaY); + ret.anchorDeltaX = (int)Math.round(anchorDeltaX); + ret.anchorDeltaY = (int)Math.round(anchorDeltaY); + return ret; + } + + @Override + public void round() { + controlDeltaX = Math.round(controlDeltaX); + controlDeltaY = Math.round(controlDeltaY); + anchorDeltaX = Math.round(anchorDeltaX); + anchorDeltaY = Math.round(anchorDeltaY); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeFixer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeFixer.java new file mode 100644 index 000000000..9d2eb6107 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeFixer.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.xfl.shapefixer; + +import com.jpexs.decompiler.flash.math.BezierEdge; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.jpexs.helpers.Reference; +import java.awt.geom.Point2D; +import java.util.Comparator; +import java.util.LinkedHashMap; + +/** + * + * @author JPEXS + */ +public class ShapeFixer { + + private void addToEdgeMap(Map> edgeMap, Edge edge) { + if (!edgeMap.containsKey(edge.getFrom())) { + edgeMap.put(edge.getFrom(), new ArrayList<>()); + } + if (!edgeMap.containsKey(edge.getTo())) { + edgeMap.put(edge.getTo(), new ArrayList<>()); + } + edgeMap.get(edge.getFrom()).add(edge); + edgeMap.get(edge.getTo()).add(edge.invert()); + } + + private boolean fixSingleEdge(List records) { + Map> edgeMap = new LinkedHashMap<>(); + double x = 0; + double y = 0; + int fillStyle0 = 0; + int fillStyle1 = 0; + + for (int r = 0; r < records.size(); r++) { + ShapeRecordAdvanced rec = records.get(r); + if (rec instanceof StyleChangeRecordAdvanced) { + StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) rec; + if (scr.stateNewStyles) { + fillStyle0 = 0; + fillStyle1 = 0; + } + if (scr.stateFillStyle0) { + fillStyle0 = scr.fillStyle0; + } + if (scr.stateFillStyle1) { + fillStyle1 = scr.fillStyle1; + } + } + if (rec instanceof StraightEdgeRecordAdvanced) { + StraightEdgeRecordAdvanced ser = (StraightEdgeRecordAdvanced) rec; + addToEdgeMap(edgeMap, new Edge(fillStyle0, fillStyle1, r, false, x, y, x + ser.deltaX, y + ser.deltaY)); + } + if (rec instanceof CurvedEdgeRecordAdvanced) { + CurvedEdgeRecordAdvanced cer = (CurvedEdgeRecordAdvanced) rec; + addToEdgeMap(edgeMap, new Edge(fillStyle0, fillStyle1, r, false, + x, y, + x + cer.controlDeltaX, y + cer.controlDeltaY, + x + cer.controlDeltaX + cer.anchorDeltaX, y + cer.controlDeltaY + cer.anchorDeltaY + )); + } + x = rec.changeX(x); + y = rec.changeY(y); + } + + for (Point2D p : edgeMap.keySet()) { + List edges = edgeMap.get(p); + for (int i = 0; i < edges.size(); i++) { + for (int j = 0; j < edges.size(); j++) { + if (i == j) { + continue; + } + Edge edge1 = edges.get(i); + Edge edge2 = edges.get(j); + List newEdges1 = new ArrayList<>(); + List newEdges2 = new ArrayList<>(); + + if (edge1.intersection(edge2, newEdges1, newEdges2)) { + + /*System.out.println("---------------------------------------"); + System.out.println("intersection edge "+edge1 + " and " +edge2); + System.out.println("newEdges1:"); + for(Edge e:newEdges1) { + System.out.println("..."+e); + } + System.out.println("newEdges2:"); + for(Edge e:newEdges2) { + System.out.println("..."+e); + }*/ + + Point2D a = edge1.getFrom(); + Point2D b = edge1.getTo(); + Point2D c = edge2.getTo(); + + boolean edge2isRight = ((b.getX() - a.getX()) * (c.getY() - a.getY())) - ((b.getY() - a.getY()) * (c.getX() - a.getX())) > 0; + //edge2isRight = !edge2isRight; + + fillStyle0 = edge2isRight ? edge1.fillStyle0 : edge2.fillStyle0; + fillStyle1 = edge2isRight ? edge2.fillStyle1 : edge1.fillStyle1; + + StyleChangeRecordAdvanced moveCenter = new StyleChangeRecordAdvanced(); + moveCenter.stateMoveTo = true; + moveCenter.moveDeltaX = edge1.getFrom().getX(); + moveCenter.moveDeltaY = edge1.getFrom().getY(); + moveCenter.stateFillStyle0 = true; + moveCenter.stateFillStyle1 = true; + moveCenter.fillStyle0 = fillStyle0; + moveCenter.fillStyle1 = fillStyle1; + + //common line + StraightEdgeRecordAdvanced ser = new StraightEdgeRecordAdvanced(); + ser.deltaX = newEdges1.get(0).getTo().getX() - edge1.getFrom().getX(); + ser.deltaY = newEdges1.get(0).getTo().getY() - edge1.getFrom().getY(); + + StyleChangeRecordAdvanced scrStart1 = new StyleChangeRecordAdvanced(); + scrStart1.stateMoveTo = false; + scrStart1.stateFillStyle0 = true; + scrStart1.stateFillStyle1 = true; + scrStart1.fillStyle0 = newEdges1.get(1).fillStyle0; + scrStart1.fillStyle1 = newEdges1.get(1).fillStyle1; + + StyleChangeRecordAdvanced moveBack1 = new StyleChangeRecordAdvanced(); + moveBack1.stateMoveTo = true; + Edge edge1NotInverted = edge1.inverted ? edge1.invert() : edge1; + + moveBack1.moveDeltaX = edge1NotInverted.getTo().getX(); + moveBack1.moveDeltaY = edge1NotInverted.getTo().getY(); + moveBack1.stateFillStyle0 = true; + moveBack1.stateFillStyle1 = true; + moveBack1.fillStyle0 = edge1NotInverted.fillStyle0; + moveBack1.fillStyle1 = edge1NotInverted.fillStyle1; + + records.remove(edge1.recordIndex); + records.add(edge1.recordIndex, moveCenter); + records.add(edge1.recordIndex + 1, ser); + records.add(edge1.recordIndex + 2, scrStart1); + records.add(edge1.recordIndex + 3, newEdges1.get(1).toShapeRecordAdvanced()); + records.add(edge1.recordIndex + 4, moveBack1); + + if (edge2.recordIndex > edge1.recordIndex) { + edge2.recordIndex += 4; + } + + StyleChangeRecordAdvanced moveStart2 = new StyleChangeRecordAdvanced(); + moveStart2.stateMoveTo = true; + moveStart2.moveDeltaX = newEdges2.get(1).getFrom().getX(); + moveStart2.moveDeltaY = newEdges2.get(1).getFrom().getY(); + moveStart2.stateFillStyle0 = true; + moveStart2.stateFillStyle1 = true; + moveStart2.fillStyle0 = newEdges2.get(1).fillStyle0; + moveStart2.fillStyle1 = newEdges2.get(1).fillStyle1; + + StyleChangeRecordAdvanced moveBack2 = new StyleChangeRecordAdvanced(); + moveBack2.stateMoveTo = true; + Edge edge2NotInverted = edge2.inverted ? edge2.invert() : edge2; + + moveBack2.moveDeltaX = edge2NotInverted.getTo().getX(); + moveBack2.moveDeltaY = edge2NotInverted.getTo().getY(); + moveBack2.stateFillStyle0 = true; + moveBack2.stateFillStyle1 = true; + moveBack2.fillStyle0 = edge2NotInverted.fillStyle0; + moveBack2.fillStyle1 = edge2NotInverted.fillStyle1; + + records.remove(edge2.recordIndex); + records.add(edge2.recordIndex, moveStart2); + records.add(edge2.recordIndex + 1, newEdges2.get(1).toShapeRecordAdvanced()); + records.add(edge2.recordIndex + 2, moveBack2); + return true; + } + } + } + } + return false; + } + + public List fixShape(List records) { + List ret = Helper.deepCopy(records); + while(fixSingleEdge(ret)) { + //nothing + } + for(ShapeRecordAdvanced rec:ret) { + rec.round(); + } + return ret; + } +} + +class Edge { + + int recordIndex; + boolean inverted; + int fillStyle0 = 0; + int fillStyle1 = 0; + + List points = new ArrayList<>(); + + public Point2D getFrom() { + return points.get(0); + } + + public Point2D getTo() { + return points.get(points.size() - 1); + } + + public BezierEdge toBezierEdge() { + return new BezierEdge(new ArrayList<>(points)); + } + + public boolean intersection(Edge otherEdge, List newThisEdges, List newOtherEdges) { + List t1s = new ArrayList<>(); + List t2s = new ArrayList<>(); + BezierEdge be1 = this.toBezierEdge(); + BezierEdge be2 = otherEdge.toBezierEdge(); + if (!be1.intersects(be2, t1s, t2s)) { + return false; + } + Reference be1aRef = new Reference<>(null); + Reference be1bRef = new Reference<>(null); + Reference be2aRef = new Reference<>(null); + Reference be2bRef = new Reference<>(null); + + t1s.sort(new Comparator() { + @Override + public int compare(Double o1, Double o2) { + return Double.compare(o1, o2); + } + }); + + t2s.sort(new Comparator() { + @Override + public int compare(Double o1, Double o2) { + return Double.compare(o1, o2); + } + }); + + if (t1s.size() == 1) { + return false; + } + + double t1 = t1s.get(t1s.size() - 1); + double t2 = t2s.get(t2s.size() - 1); + /*System.out.println("t1 = "+t1); + System.out.println("t2 = "+t2);*/ + + be1.split(t1, be1aRef, be1bRef); + be2.split(t2, be2aRef, be2bRef); + + newThisEdges.add(new Edge(this.fillStyle0, this.fillStyle1, -1, this.inverted, be1aRef.getVal())); + newThisEdges.add(new Edge(this.fillStyle0, this.fillStyle1, -1, this.inverted, be1bRef.getVal())); + + newOtherEdges.add(new Edge(otherEdge.fillStyle0, otherEdge.fillStyle1, -1, otherEdge.inverted, be2aRef.getVal())); + newOtherEdges.add(new Edge(otherEdge.fillStyle0, otherEdge.fillStyle1, -1, otherEdge.inverted, be2bRef.getVal())); + + if (newThisEdges.get(0).isEmpty() || newOtherEdges.get(0).isEmpty()) { + newThisEdges.clear(); + newOtherEdges.clear(); + return false; + } + + return true; + } + + public Point2D pointAt(double t) { + return toBezierEdge().pointAt(t); + } + + public Edge invert() { + List newPoints = new ArrayList<>(); + for (int i = points.size() - 1; i >= 0; i--) { + newPoints.add(points.get(i)); + } + return new Edge(fillStyle1, fillStyle0, recordIndex, !inverted, newPoints); + } + + public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, BezierEdge be) { + List points2D = be.points; + List points = new ArrayList<>(); + for (Point2D p : points2D) { + points.add(new Point2D.Double(p.getX(),p.getY())); + } + this.points = points; + this.recordIndex = recordIndex; + this.inverted = inverted; + this.fillStyle0 = fillStyle0; + this.fillStyle1 = fillStyle1; + } + + public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, List points) { + this.points = points; + this.recordIndex = recordIndex; + this.inverted = inverted; + this.fillStyle0 = fillStyle0; + this.fillStyle1 = fillStyle1; + } + + public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, double fromX, double fromY, double toX, double toY) { + points.add(new Point2D.Double(fromX, fromY)); + points.add(new Point2D.Double(toX, toY)); + this.recordIndex = recordIndex; + this.inverted = inverted; + this.fillStyle0 = fillStyle0; + this.fillStyle1 = fillStyle1; + } + + public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, double fromX, double fromY, double controlX, double controlY, double toX, double toY) { + points.add(new Point2D.Double(fromX, fromY)); + points.add(new Point2D.Double(controlX, controlY)); + points.add(new Point2D.Double(toX, toY)); + this.recordIndex = recordIndex; + this.inverted = inverted; + this.fillStyle0 = fillStyle0; + this.fillStyle1 = fillStyle1; + + } + + @Override + public String toString() { + List list = new ArrayList<>(); + for (Point2D p : points) { + list.add("[" + p.getX() + "," + p.getY() + "]"); + } + return "{" + String.join("-", list) + "}"; + } + + public boolean isEmpty() { + return getFrom().equals(getTo()); + } + + public ShapeRecordAdvanced toShapeRecordAdvanced() { + if (points.size() == 3) { + CurvedEdgeRecordAdvanced cer = new CurvedEdgeRecordAdvanced(); + cer.controlDeltaX = points.get(1).getX() - points.get(0).getX(); + cer.controlDeltaY = points.get(1).getY() - points.get(0).getY(); + cer.anchorDeltaX = points.get(2).getX() - points.get(1).getX(); + cer.anchorDeltaY = points.get(2).getY() - points.get(1).getY(); + return cer; + } + StraightEdgeRecordAdvanced ser = new StraightEdgeRecordAdvanced(); + ser.deltaX = points.get(1).getX() - points.get(0).getX(); + ser.deltaY = points.get(1).getY() - points.get(0).getY(); + return ser; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeRecordAdvanced.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeRecordAdvanced.java new file mode 100644 index 000000000..95cd236c2 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/ShapeRecordAdvanced.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ + +package com.jpexs.decompiler.flash.xfl.shapefixer; + +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import java.io.Serializable; + +/** + * + * @author JPEXS + */ +public abstract class ShapeRecordAdvanced implements Serializable { + public abstract double changeX(double x); + public abstract double changeY(double y); + + public abstract SHAPERECORD toBasicRecord(); + + public static ShapeRecordAdvanced createFromSHAPERECORD(SHAPERECORD rec) { + if(rec instanceof StyleChangeRecord) { + return new StyleChangeRecordAdvanced((StyleChangeRecord)rec); + } + if(rec instanceof CurvedEdgeRecord) { + return new CurvedEdgeRecordAdvanced((CurvedEdgeRecord)rec); + } + if(rec instanceof StraightEdgeRecord) { + return new StraightEdgeRecordAdvanced((StraightEdgeRecord)rec); + } + return null; + } + + public abstract void round(); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StraightEdgeRecordAdvanced.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StraightEdgeRecordAdvanced.java new file mode 100644 index 000000000..802bf7c97 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StraightEdgeRecordAdvanced.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.xfl.shapefixer; + +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; + +/** + * + * @author JPEXS + */ +public class StraightEdgeRecordAdvanced extends ShapeRecordAdvanced { + public double deltaX; + public double deltaY; + + public StraightEdgeRecordAdvanced() { + } + + public StraightEdgeRecordAdvanced(double deltaX, double deltaY) { + this.deltaX = deltaX; + this.deltaY = deltaY; + } + + public StraightEdgeRecordAdvanced(StraightEdgeRecord ser) { + this.deltaX = ser.deltaX; + this.deltaY = ser.deltaY; + } + + @Override + public double changeX(double x) { + return x + deltaX; + } + + @Override + public double changeY(double y) { + return y + deltaY; + } + + @Override + public StraightEdgeRecord toBasicRecord() { + StraightEdgeRecord ret = new StraightEdgeRecord(); + ret.generalLineFlag = true; + ret.deltaX = (int)Math.round(deltaX); + ret.deltaY = (int)Math.round(deltaY); + ret.simplify(); + return ret; + } + + @Override + public void round() { + deltaX = Math.round(deltaX); + deltaY = Math.round(deltaY); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StyleChangeRecordAdvanced.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StyleChangeRecordAdvanced.java new file mode 100644 index 000000000..3f265f9af --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/shapefixer/StyleChangeRecordAdvanced.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.xfl.shapefixer; + +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; + +/** + * + * @author JPEXS + */ +public class StyleChangeRecordAdvanced extends ShapeRecordAdvanced{ + public boolean stateNewStyles; + + public boolean stateLineStyle; + + public boolean stateFillStyle1; + + public boolean stateFillStyle0; + + public boolean stateMoveTo; + + public double moveDeltaX; + + public double moveDeltaY; + + public int fillStyle0; + + public int fillStyle1; + + public int lineStyle; + + public FILLSTYLEARRAY fillStyles; + + public LINESTYLEARRAY lineStyles; + + public StyleChangeRecordAdvanced() { + + } + public StyleChangeRecordAdvanced(StyleChangeRecord scr) { + this.stateNewStyles = scr.stateNewStyles; + this.stateLineStyle = scr.stateLineStyle; + this.stateFillStyle0 = scr.stateFillStyle0; + this.stateFillStyle1 = scr.stateFillStyle1; + this.stateMoveTo = scr.stateMoveTo; + this.moveDeltaX = scr.moveDeltaX; + this.moveDeltaY = scr.moveDeltaY; + this.fillStyle0 = scr.fillStyle0; + this.fillStyle1 = scr.fillStyle1; + this.lineStyle = scr.lineStyle; + this.fillStyles = scr.fillStyles; + this.lineStyles = scr.lineStyles; + } + + + @Override + public StyleChangeRecord toBasicRecord() { + StyleChangeRecord ret = new StyleChangeRecord(); + ret.stateNewStyles = this.stateNewStyles; + ret.stateLineStyle = this.stateLineStyle; + ret.stateFillStyle0 = this.stateFillStyle0; + ret.stateFillStyle1 = this.stateFillStyle1; + ret.stateMoveTo = this.stateMoveTo; + ret.moveDeltaX = (int)Math.round(this.moveDeltaX); + ret.moveDeltaY = (int)Math.round(this.moveDeltaY); + ret.fillStyle0 = this.fillStyle0; + ret.fillStyle1 = this.fillStyle1; + ret.lineStyle = this.lineStyle; + ret.fillStyles = this.fillStyles; + ret.lineStyles = this.lineStyles; + return ret; + } + + @Override + public double changeX(double x) { + if (stateMoveTo) { + return moveDeltaX; + } + return x; + } + + @Override + public double changeY(double y) { + if (stateMoveTo) { + return moveDeltaY; + } + return y; + } + + @Override + public void round() { + if (stateMoveTo) { + moveDeltaX = Math.round(moveDeltaX); + moveDeltaY = Math.round(moveDeltaY); + } + } + + + +} diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 4aed40d1c..b9b25a39e 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.math.BezierUtils; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.Action; @@ -296,6 +297,16 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private JScrollBar verticalScrollBar; private boolean updatingScrollBars = false; private final int SCROLL_SPACE_BEFORE = (int) SWF.unitDivisor * 500; + + private List showPoints1 = new ArrayList<>(); + + private List showPoints2 = new ArrayList<>(); + + + public void setShowPoints(List showPoints1, List showPoints2) { + this.showPoints1 = showPoints1; + this.showPoints2 = showPoints2; + } private static String formatDouble(double value) { return DECIMAL_FORMAT.format(value); @@ -919,6 +930,26 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } }*/ } + + for (int i = 0; i < showPoints1.size(); i++) { + int xt = showPoints1.get(i).x; + int pointSize = 3; + int yt = showPoints1.get(i).y; + Shape pointShape; + pointShape = new Ellipse2D.Double(xt - pointSize, yt - pointSize, pointSize * 2, pointSize * 2); + g2.setPaint(Color.blue); + g2.fill(pointShape); + } + + for (int i = 0; i < showPoints2.size(); i++) { + int xt = showPoints2.get(i).x; + int pointSize = 3; + int yt = showPoints2.get(i).y; + Shape pointShape; + pointShape = new Ellipse2D.Double(xt - pointSize, yt - pointSize, pointSize * 2, pointSize * 2); + g2.setPaint(Color.red); + g2.fill(pointShape); + } g2.setTransform(oldTransform); } if (!(timelined instanceof SWF) && (freeTransformDepth > -1 || hilightedPoints != null)) { @@ -3910,9 +3941,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; if (lowQuality) { zoomDouble /= LQ_FACTOR; - } - RECT timRect = timelined.getRect(); - + } double rx = point.getX() * zoomDouble / SWF.unitDivisor + offsetPoint.getX(); // + offsetXRef.getVal(); double ry = point.getY() * zoomDouble / SWF.unitDivisor + offsetPoint.getY(); // + offsetYRef.getVal(); diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 284376e07..531a2a73b 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.math.BezierUtils; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFHeader; import com.jpexs.decompiler.flash.action.parser.ActionParseException; @@ -70,6 +71,8 @@ import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeFixer; +import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeRecordAdvanced; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Reference; import com.jpexs.helpers.SerializableImage; @@ -1240,7 +1243,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditEditPointsButton = new JButton(mainPanel.translate("button.edit.points"), View.getIcon("pointsedit16")); displayEditEditPointsButton.setMargin(new Insets(3, 3, 3, 10)); displayEditEditPointsButton.addActionListener(this::editPointsDisplayEditTagButtonActionPerformed); - + + displayEditShowAnimationButton = new JToggleButton(mainPanel.translate("button.morph.animation")); displayEditShowAnimationButton.setMargin(new Insets(3, 3, 3, 10)); displayEditShowAnimationButton.addActionListener(this::showAnimationDisplayEditTagButtonActionPerformed); @@ -1300,6 +1304,38 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditSaveButton.setVisible(false); displayEditCancelButton.setVisible(false); } + + + /*JButton fixPathsButton = new JButton("Fix paths"); + fixPathsButton.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent e) { + ShapeTag shape = (ShapeTag) displayEditTag; + ShapeFixer fixer = new ShapeFixer(); + List newPoints1 = new ArrayList<>(); + List newPoints2 = new ArrayList<>(); + List shapeRecordsAdvanced = new ArrayList<>(); + for (SHAPERECORD rec:shape.shapes.shapeRecords) { + ShapeRecordAdvanced arec = ShapeRecordAdvanced.createFromSHAPERECORD(rec); + if (arec != null) { + shapeRecordsAdvanced.add(arec); + } + } + List fixed = fixer.fixShape(shapeRecordsAdvanced); + + List newRecords=new ArrayList<>(); + + for (ShapeRecordAdvanced arec:fixed) { + newRecords.add(arec.toBasicRecord()); + } + newRecords.add(new EndShapeRecord()); + shape.shapes.shapeRecords = newRecords; + //displayEditImagePanel.setShowPoints(newPoints1, newPoints2); + displayEditTag.getSwf().clearShapeCache(); + displayEditImagePanel.repaint(); + refreshHilightedPoints(); + } + });*/ ButtonsPanel displayEditButtonsPanel = new ButtonsPanel(); displayEditButtonsPanel.add(displayEditTransformButton); @@ -1307,6 +1343,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditButtonsPanel.add(displayEditSaveButton); displayEditButtonsPanel.add(displayEditCancelButton); displayEditButtonsPanel.add(displayEditEditPointsButton); + //displayEditButtonsPanel.add(fixPathsButton); displayEditButtonsPanel.add(replaceShapeButton); displayEditButtonsPanel.add(replaceShapeUpdateBoundsButton); displayEditButtonsPanel.add(morphShowPanel);