mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-26 22:30:45 +00:00
Fixed #2123 FLA export - morphshapes fixer
More FLA export progress info
This commit is contained in:
@@ -17,6 +17,10 @@
|
||||
package com.jpexs.decompiler.flash.math;
|
||||
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.Serializable;
|
||||
@@ -489,12 +493,68 @@ public class BezierEdge implements Serializable {
|
||||
BezierEdge qm = new BezierEdge(-1957, 2676, -1957, 2676.5, -1957.5, 2676.5);
|
||||
BezierEdge qn = new BezierEdge(-1957, 2675.5, -1957, 2676, -1957, 2676);
|
||||
|
||||
List<Point2D> ps = new ArrayList<>();
|
||||
/*List<Point2D> ps = new ArrayList<>();
|
||||
qm.intersects(qn, t1, t2, ps);
|
||||
System.err.println("t1 is " + t1);
|
||||
System.err.println("t2 is " + t2);
|
||||
System.err.println("intersections is " + ps);
|
||||
*/
|
||||
/*Shape r1 = new Rectangle2D.Double(0, 0, 200, 100);
|
||||
GeneralPath r2 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
r2.moveTo(0, 0);
|
||||
r2.lineTo(100, 0);
|
||||
r2.lineTo(150, 0);
|
||||
r2.lineTo(200, 0);
|
||||
r2.lineTo(200, 100);
|
||||
r2.lineTo(0, 100);
|
||||
r2.closePath();
|
||||
|
||||
GeneralPath sh1 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
sh1.moveTo(0, 0);
|
||||
sh1.quadTo(100, 100, 0, 100);
|
||||
sh1.lineTo(0, 0);
|
||||
sh1.closePath();
|
||||
|
||||
BezierEdge bex = new BezierEdge(0, 0, 100, 100, 0, 100);
|
||||
Reference<BezierEdge> bex1Ref = new Reference<>(null);
|
||||
Reference<BezierEdge> bex2Ref = new Reference<>(null);
|
||||
bex.split(0.2, bex1Ref, bex2Ref);
|
||||
BezierEdge bex1 = bex1Ref.getVal();
|
||||
BezierEdge bex2 = bex2Ref.getVal();
|
||||
GeneralPath sh2 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
sh2.moveTo(bex1.getBeginPoint().getX(), bex1.getBeginPoint().getY());
|
||||
sh2.quadTo(10+bex1.points.get(1).getX(), bex1.points.get(1).getY(), bex1.getEndPoint().getX(), bex1.getEndPoint().getY());
|
||||
sh2.quadTo(bex2.points.get(1).getX(), bex2.points.get(1).getY(), bex2.getEndPoint().getX(), bex2.getEndPoint().getY());
|
||||
sh2.lineTo(0, 0);
|
||||
sh2.closePath();
|
||||
|
||||
Area a1 = new Area(r1);
|
||||
Area a2 = new Area(r2);
|
||||
//a1.exclusiveOr(a2);
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*GeneralPath p1 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
p1.moveTo(0, 0);
|
||||
p1.lineTo(200, 0);
|
||||
p1.lineTo(200, 200);
|
||||
p1.lineTo(0, 200);
|
||||
p1.lineTo(0, 0);
|
||||
p1.closePath();
|
||||
p1.moveTo(50, 50);
|
||||
p1.lineTo(150, 50);
|
||||
p1.lineTo(150, 150);
|
||||
p1.lineTo(50, 150);
|
||||
p1.lineTo(50, 50);
|
||||
p1.closePath();
|
||||
|
||||
System.err.println("cont:" + p1.contains(100, 100));
|
||||
System.err.println("cont:" + p1.contains(150, 150)); */
|
||||
|
||||
//System.err.println("minDist = " + minDist+", maxDist = "+ maxDist);
|
||||
|
||||
//System.err.println("eArea = " + Areas.calcArea(a1));
|
||||
|
||||
/*Point2D c = new Point2D.Double(
|
||||
(1 - t1.get(0)) * be5.points.get(0).getX() + t1.get(0) * be5.points.get(1).getX(),
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2023 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 java.awt.geom.Area;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Distances {
|
||||
public static double getBatchDistance(List<BezierEdge> batch1, List<BezierEdge> batch2) {
|
||||
Area a1 = batchToArea(batch1);
|
||||
Area a2 = batchToArea(batch2);
|
||||
return areaDist(a1, a2);
|
||||
}
|
||||
|
||||
private static Area batchToArea(List<BezierEdge> batch) {
|
||||
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
if (batch.isEmpty()) {
|
||||
return new Area();
|
||||
}
|
||||
path.moveTo(batch.get(0).getBeginPoint().getX(), batch.get(0).getBeginPoint().getY());
|
||||
for (BezierEdge be : batch) {
|
||||
if (be.points.size() == 3) {
|
||||
path.quadTo(be.points.get(1).getX(),
|
||||
be.points.get(1).getY(),
|
||||
be.points.get(2).getX(),
|
||||
be.points.get(2).getY());
|
||||
} else {
|
||||
path.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
}
|
||||
}
|
||||
path.closePath();
|
||||
return new Area(path);
|
||||
}
|
||||
|
||||
private static double areaDist(Area a1, Area a2) {
|
||||
List<Point2D> points1 = getAreaPoints(a1);
|
||||
List<Point2D> points2 = getAreaPoints(a2);
|
||||
double minDist = Double.MAX_VALUE;
|
||||
double maxDist = 0;
|
||||
for (Point2D p : points1) {
|
||||
double dist = Double.MAX_VALUE;
|
||||
for (Point2D p2 : points2) {
|
||||
double d = p.distance(p2);
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
}
|
||||
if (dist > maxDist) {
|
||||
maxDist = dist;
|
||||
}
|
||||
}
|
||||
return maxDist;
|
||||
}
|
||||
|
||||
private static List<Point2D> getAreaPoints(Area area) {
|
||||
double F = 1.0;
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
PathIterator pi = area.getPathIterator(null, 0.1);
|
||||
double[] coords = new double[6];
|
||||
double xPrev = 0;
|
||||
double yPrev = 0;
|
||||
double xBegin = 0;
|
||||
double yBegin = 0;
|
||||
while (!pi.isDone()) {
|
||||
int code = pi.currentSegment(coords);
|
||||
switch (code) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
xBegin = coords[0];
|
||||
yBegin = coords[1];
|
||||
points.add(new Point2D.Double(xBegin, yBegin));
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
//coords[0], coords[1]
|
||||
//Point2D np = new Point2D.Double(coords[0], coords[1]);
|
||||
double dx = coords[0] - xPrev;
|
||||
double dy = coords[1] - yPrev;
|
||||
double dz = Math.sqrt(dx * dx + dy * dy);
|
||||
double divisor;
|
||||
/*if (dy > dx) {
|
||||
divisor = dy / F;
|
||||
} else {
|
||||
divisor = dx / F;
|
||||
}*/
|
||||
divisor = dz / F;
|
||||
for (int d = 1; d <= divisor; d++) {
|
||||
Point2D p2 = new Point2D.Double(xPrev + d * dx / divisor, yPrev + d * dy / divisor);
|
||||
points.add(p2);
|
||||
}
|
||||
//points.add(np);
|
||||
//System.err.println("dx, dy: "+dx+", "+dy);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Curved edge not expected");
|
||||
}
|
||||
xPrev = coords[0];
|
||||
yPrev = coords[1];
|
||||
//System.err.println("pos: "+xPrev+", "+ yPrev);
|
||||
pi.next();
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -248,4 +249,52 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
|
||||
|
||||
return morphFillStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 23 * hash + this.fillStyleType;
|
||||
hash = 23 * hash + (this.inShape3 ? 1 : 0);
|
||||
hash = 23 * hash + Objects.hashCode(this.color);
|
||||
hash = 23 * hash + Objects.hashCode(this.gradientMatrix);
|
||||
hash = 23 * hash + Objects.hashCode(this.gradient);
|
||||
hash = 23 * hash + this.bitmapId;
|
||||
hash = 23 * hash + Objects.hashCode(this.bitmapMatrix);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final FILLSTYLE other = (FILLSTYLE) obj;
|
||||
if (this.fillStyleType != other.fillStyleType) {
|
||||
return false;
|
||||
}
|
||||
if (this.inShape3 != other.inShape3) {
|
||||
return false;
|
||||
}
|
||||
if (this.bitmapId != other.bitmapId) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.color, other.color)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.gradientMatrix, other.gradientMatrix)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.gradient, other.gradient)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.bitmapMatrix, other.bitmapMatrix);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.types.annotations.EnumValue;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -109,4 +110,36 @@ public class GRADIENT implements Serializable {
|
||||
}
|
||||
return morphGradient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 97 * hash + this.spreadMode;
|
||||
hash = 97 * hash + this.interpolationMode;
|
||||
hash = 97 * hash + Arrays.deepHashCode(this.gradientRecords);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final GRADIENT other = (GRADIENT) obj;
|
||||
if (this.spreadMode != other.spreadMode) {
|
||||
return false;
|
||||
}
|
||||
if (this.interpolationMode != other.interpolationMode) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.deepEquals(this.gradientRecords, other.gradientRecords);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.types;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Internal;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -55,4 +56,36 @@ public class GRADRECORD implements Serializable {
|
||||
morphGradRecord.endRatio = endGradRecord.ratio;
|
||||
return morphGradRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 19 * hash + this.ratio;
|
||||
hash = 19 * hash + (this.inShape3 ? 1 : 0);
|
||||
hash = 19 * hash + Objects.hashCode(this.color);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final GRADRECORD other = (GRADRECORD) obj;
|
||||
if (this.ratio != other.ratio) {
|
||||
return false;
|
||||
}
|
||||
if (this.inShape3 != other.inShape3) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.color, other.color);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
|
||||
import com.jpexs.decompiler.flash.types.annotations.ConditionalType;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -84,4 +85,32 @@ public class LINESTYLE implements NeedsCharacters, Serializable, ILINESTYLE {
|
||||
morphLineStyle.endWidth = width;
|
||||
return morphLineStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 53 * hash + this.width;
|
||||
hash = 53 * hash + Objects.hashCode(this.color);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final LINESTYLE other = (LINESTYLE) obj;
|
||||
if (this.width != other.width) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.color, other.color);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.types.annotations.EnumValue;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Reserved;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -232,4 +233,76 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
|
||||
}
|
||||
return morphLineStyle2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 83 * hash + this.width;
|
||||
hash = 83 * hash + this.startCapStyle;
|
||||
hash = 83 * hash + this.joinStyle;
|
||||
hash = 83 * hash + (this.hasFillFlag ? 1 : 0);
|
||||
hash = 83 * hash + (this.noHScaleFlag ? 1 : 0);
|
||||
hash = 83 * hash + (this.noVScaleFlag ? 1 : 0);
|
||||
hash = 83 * hash + (this.pixelHintingFlag ? 1 : 0);
|
||||
hash = 83 * hash + this.reserved;
|
||||
hash = 83 * hash + (this.noClose ? 1 : 0);
|
||||
hash = 83 * hash + this.endCapStyle;
|
||||
hash = 83 * hash + Float.floatToIntBits(this.miterLimitFactor);
|
||||
hash = 83 * hash + Objects.hashCode(this.color);
|
||||
hash = 83 * hash + Objects.hashCode(this.fillType);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final LINESTYLE2 other = (LINESTYLE2) obj;
|
||||
if (this.width != other.width) {
|
||||
return false;
|
||||
}
|
||||
if (this.startCapStyle != other.startCapStyle) {
|
||||
return false;
|
||||
}
|
||||
if (this.joinStyle != other.joinStyle) {
|
||||
return false;
|
||||
}
|
||||
if (this.hasFillFlag != other.hasFillFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.noHScaleFlag != other.noHScaleFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.noVScaleFlag != other.noVScaleFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.pixelHintingFlag != other.pixelHintingFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.reserved != other.reserved) {
|
||||
return false;
|
||||
}
|
||||
if (this.noClose != other.noClose) {
|
||||
return false;
|
||||
}
|
||||
if (this.endCapStyle != other.endCapStyle) {
|
||||
return false;
|
||||
}
|
||||
if (Float.floatToIntBits(this.miterLimitFactor) != Float.floatToIntBits(other.miterLimitFactor)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.color, other.color)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.fillType, other.fillType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -112,4 +112,36 @@ public class RGB implements Serializable {
|
||||
return "[RGB red:" + red + ", green:" + green + ", blue:" + blue + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 97 * hash + this.red;
|
||||
hash = 97 * hash + this.green;
|
||||
hash = 97 * hash + this.blue;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final RGB other = (RGB) obj;
|
||||
if (this.red != other.red) {
|
||||
return false;
|
||||
}
|
||||
if (this.green != other.green) {
|
||||
return false;
|
||||
}
|
||||
return this.blue == other.blue;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -110,4 +110,54 @@ public class RGBA extends RGB implements Serializable {
|
||||
return "[RGB red:" + red + ", green:" + green + ", blue:" + blue + ", alpha:" + alpha + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 97 * hash + this.red;
|
||||
hash = 97 * hash + this.green;
|
||||
hash = 97 * hash + this.blue;
|
||||
hash = 97 * hash + this.alpha;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof RGB) {
|
||||
final RGB other = (RGBA) obj;
|
||||
if (this.alpha != 255) {
|
||||
return false;
|
||||
}
|
||||
if (this.red != other.red) {
|
||||
return false;
|
||||
}
|
||||
if (this.green != other.green) {
|
||||
return false;
|
||||
}
|
||||
if (this.blue != other.blue) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final RGBA other = (RGBA) obj;
|
||||
if (this.red != other.red) {
|
||||
return false;
|
||||
}
|
||||
if (this.green != other.green) {
|
||||
return false;
|
||||
}
|
||||
if (this.blue != other.blue) {
|
||||
return false;
|
||||
}
|
||||
return this.alpha == other.alpha;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +241,34 @@ public class XFLConverter {
|
||||
EDGE_DECIMAL_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
|
||||
|
||||
private static class StatusStack {
|
||||
private Stack<String> statuses = new Stack<>();
|
||||
private ProgressListener progressListener;
|
||||
|
||||
public StatusStack(ProgressListener progressListener) {
|
||||
this.progressListener = progressListener;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if (progressListener == null) {
|
||||
return;
|
||||
}
|
||||
String status = String.join(", ", statuses);
|
||||
progressListener.status(status);
|
||||
}
|
||||
|
||||
public void pushStatus(String status) {
|
||||
statuses.push(status);
|
||||
update();
|
||||
}
|
||||
|
||||
public void popStatus() {
|
||||
statuses.pop();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatEdgeDouble(double value, boolean curved, boolean morphshape) {
|
||||
if (value % 1 == 0) {
|
||||
return "" + (int) value;
|
||||
@@ -783,9 +811,10 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
List<ShapeRecordAdvanced> shapeRecordsAdvanced;
|
||||
useFixer = true;
|
||||
if (useFixer) {
|
||||
ShapeFixer fixer = new ShapeFixer();
|
||||
shapeRecordsAdvanced = fixer.fix(shapeRecords);
|
||||
shapeRecordsAdvanced = fixer.fix(shapeRecords, morphshape, shapeNum, fillStyles, lineStyles);
|
||||
} else {
|
||||
shapeRecordsAdvanced = new ArrayList<>();
|
||||
for (SHAPERECORD rec : shapeRecords) {
|
||||
@@ -1029,6 +1058,10 @@ public class XFLConverter {
|
||||
* @return
|
||||
*/
|
||||
private static String removeOnlyStrokeEdgesBeforeSameFilled(String layer) {
|
||||
if (true) {
|
||||
//This is moved to ShapeFixer
|
||||
return layer;
|
||||
}
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
|
||||
dbf.setNamespaceAware(false);
|
||||
@@ -1061,6 +1094,16 @@ public class XFLConverter {
|
||||
String fillStyle0 = fillStyle0Node != null ? fillStyle0Node.getNodeValue() : null;
|
||||
String fillStyle1 = fillStyle1Node != null ? fillStyle1Node.getNodeValue() : null;
|
||||
|
||||
if ("0".equals(fillStyle0)) {
|
||||
fillStyle0 = null;
|
||||
}
|
||||
if ("0".equals(fillStyle1)) {
|
||||
fillStyle1 = null;
|
||||
}
|
||||
if ("0".equals(strokeStyle)) {
|
||||
strokeStyle = null;
|
||||
}
|
||||
|
||||
if (prevStrokeOnly != null
|
||||
&& strokeStyle != null
|
||||
&& strokeStyle.equals(prevStrokeOnly)
|
||||
@@ -1631,29 +1674,33 @@ public class XFLConverter {
|
||||
return date.getTime() / 1000;
|
||||
}
|
||||
|
||||
private void convertLibrary(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, Map<Integer, ScriptPack> characterScriptPacks, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, ProgressListener progressListener) throws XMLStreamException {
|
||||
|
||||
private void convertLibrary(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, Map<Integer, ScriptPack> characterScriptPacks, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
|
||||
//TODO: Imported assets
|
||||
//linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf"
|
||||
convertMedia(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, progressListener);
|
||||
convertSymbols(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, progressListener);
|
||||
statusStack.pushStatus("media");
|
||||
convertMedia(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, statusStack);
|
||||
statusStack.popStatus();
|
||||
statusStack.pushStatus("symbols");
|
||||
convertSymbols(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
statusStack.popStatus();
|
||||
}
|
||||
|
||||
private void convertSymbols(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, Map<Integer, ScriptPack> characterScriptPacks, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, ProgressListener progressListener) throws XMLStreamException {
|
||||
private void convertSymbols(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, Map<Integer, ScriptPack> characterScriptPacks, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
|
||||
//boolean hasSymbol = false;
|
||||
Reference<Integer> nextClipId = new Reference<>(-1);
|
||||
writer.writeStartElement("symbols");
|
||||
for (int ch : characters.keySet()) {
|
||||
CharacterTag symbol = characters.get(ch);
|
||||
if (progressListener != null && symbol != null) {
|
||||
progressListener.status(symbol.toString());
|
||||
}
|
||||
if (symbol == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((symbol instanceof ShapeTag) && nonLibraryShapes.contains(symbol.getCharacterId())) {
|
||||
continue; //shapes with 1 ocurrence and single layer are not added to library
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((symbol instanceof ShapeTag) || (symbol instanceof DefineSpriteTag) || (symbol instanceof ButtonTag)) {
|
||||
statusStack.pushStatus(symbol.toString());
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
|
||||
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
|
||||
@@ -1848,13 +1895,14 @@ public class XFLConverter {
|
||||
} else if (symbol instanceof DefineSpriteTag) {
|
||||
DefineSpriteTag sprite = (DefineSpriteTag) symbol;
|
||||
if (sprite.getTags().isEmpty()) { //probably AS2 class
|
||||
statusStack.popStatus();
|
||||
continue;
|
||||
}
|
||||
final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite.spriteId) ? characterScriptPacks.get(sprite.spriteId) : null;
|
||||
|
||||
extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
convertTimeline(swf, swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
|
||||
} else if (symbol instanceof ShapeTag) {
|
||||
symbolStr.writeStartElement("timeline");
|
||||
@@ -1888,19 +1936,25 @@ public class XFLConverter {
|
||||
//TODO: itemID="518de416-00000341"
|
||||
}
|
||||
writer.writeEndElement();
|
||||
//hasSymbol = true;
|
||||
}
|
||||
//hasSymbol = true;
|
||||
statusStack.popStatus();
|
||||
}
|
||||
}
|
||||
|
||||
extractMultilevelClips(swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
extractMultiUsageMorphShapes(writer, swf, nonLibraryShapes, backgroundColor, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
statusStack.pushStatus("extracting multilevel clips");
|
||||
extractMultilevelClips(swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
statusStack.popStatus();
|
||||
|
||||
statusStack.pushStatus("converting multiusage morphshapes");
|
||||
extractMultiUsageMorphShapes(writer, swf, nonLibraryShapes, backgroundColor, characters, flaVersion, files, multiUsageMorphShapes, statusStack);
|
||||
statusStack.popStatus();
|
||||
/*if (hasSymbol) {
|
||||
|
||||
}*/
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private void convertMedia(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, ProgressListener progressListener) throws XMLStreamException {
|
||||
private void convertMedia(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
|
||||
boolean hasMedia = false;
|
||||
for (int ch : characters.keySet()) {
|
||||
CharacterTag symbol = characters.get(ch);
|
||||
@@ -1923,10 +1977,7 @@ public class XFLConverter {
|
||||
CharacterTag symbol = characters.get(ch);
|
||||
if (symbol instanceof ImageTag) {
|
||||
|
||||
if (progressListener != null) {
|
||||
progressListener.status(symbol.toString());
|
||||
}
|
||||
|
||||
statusStack.pushStatus(symbol.toString());
|
||||
ImageTag imageTag = (ImageTag) symbol;
|
||||
boolean allowSmoothing = false;
|
||||
|
||||
@@ -2013,7 +2064,9 @@ public class XFLConverter {
|
||||
writer.writeAttribute("frameBottom", image.getHeight());
|
||||
writer.writeEndElement();
|
||||
mediaCount++;
|
||||
statusStack.popStatus();
|
||||
} else if (/*(symbol instanceof SoundStreamHeadTypeTag) || FIXME */(symbol instanceof DefineSoundTag)) {
|
||||
statusStack.pushStatus(symbol.toString());
|
||||
int soundFormat = 0;
|
||||
int soundRate = 0;
|
||||
boolean soundType = false;
|
||||
@@ -2180,7 +2233,9 @@ public class XFLConverter {
|
||||
|
||||
writer.writeEndElement();
|
||||
mediaCount++;
|
||||
statusStack.popStatus();
|
||||
} else if (symbol instanceof DefineVideoStreamTag) {
|
||||
statusStack.pushStatus(symbol.toString());
|
||||
DefineVideoStreamTag video = (DefineVideoStreamTag) symbol;
|
||||
String videoType = "no media";
|
||||
switch (video.codecID) {
|
||||
@@ -2260,6 +2315,7 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
mediaCount++;
|
||||
statusStack.popStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2446,7 +2502,7 @@ public class XFLConverter {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private static void convertFrames(SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
private static void convertFrames(SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
|
||||
boolean lastIn = true;
|
||||
XFLXmlWriter writer2 = new XFLXmlWriter();
|
||||
prevStr += "<frames>";
|
||||
@@ -2648,8 +2704,10 @@ public class XFLConverter {
|
||||
} else {
|
||||
if (lastCharacter == m && Objects.equals(matrix, lastMatrix)) {
|
||||
elementsWriter.writeCharactersRaw(lastElements);
|
||||
} else {
|
||||
} else {
|
||||
statusStack.pushStatus(m.toString());
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter, false);
|
||||
statusStack.popStatus();
|
||||
}
|
||||
|
||||
lastCharacter = m;
|
||||
@@ -2659,7 +2717,9 @@ public class XFLConverter {
|
||||
} else {
|
||||
shapeTween = false;
|
||||
if (character instanceof TextTag) {
|
||||
statusStack.pushStatus(character.toString());
|
||||
convertText(instanceName, (TextTag) character, matrix, filters, clipActions, elementsWriter);
|
||||
statusStack.popStatus();
|
||||
} else if (character instanceof DefineVideoStreamTag) {
|
||||
convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions, elementsWriter);
|
||||
} else if (character instanceof ImageTag) {
|
||||
@@ -2724,7 +2784,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private static void convertFonts(Map<Integer, String> characterClasses, ReadOnlyTagList tags, XFLXmlWriter writer, ProgressListener progressListener) throws XMLStreamException {
|
||||
private static void convertFonts(Map<Integer, String> characterClasses, ReadOnlyTagList tags, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
|
||||
boolean hasFont = false;
|
||||
for (Tag t : tags) {
|
||||
if (t instanceof FontTag) {
|
||||
@@ -2744,9 +2804,7 @@ public class XFLConverter {
|
||||
|
||||
for (Tag t : tags) {
|
||||
if (t instanceof FontTag) {
|
||||
if (progressListener != null) {
|
||||
progressListener.status(t.toString());
|
||||
}
|
||||
statusStack.pushStatus(t.toString());
|
||||
SWF swf = t.getSwf();
|
||||
FontTag font = (FontTag) t;
|
||||
int fontId = font.getFontId();
|
||||
@@ -2767,6 +2825,7 @@ public class XFLConverter {
|
||||
|
||||
String fontChars = font.getCharacters();
|
||||
if ("".equals(fontChars)) {
|
||||
statusStack.popStatus();
|
||||
continue;
|
||||
}
|
||||
String embeddedCharacters = fontChars;
|
||||
@@ -2827,6 +2886,7 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
statusStack.popStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3214,7 +3274,8 @@ public class XFLConverter {
|
||||
FLAVersion flaVersion,
|
||||
HashMap<String, byte[]> files,
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
List<Integer> multiUsageMorphShapes,
|
||||
StatusStack statusStack
|
||||
) throws XMLStreamException {
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
|
||||
@@ -3233,7 +3294,7 @@ public class XFLConverter {
|
||||
"lastModified", Long.toString(getTimestamp(swf))});
|
||||
symbolStr.writeAttribute("symbolType", "graphic");
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
|
||||
symbolStr.writeEndElement(); // DOMSymbolItem
|
||||
String symbolStr2 = prettyFormatXML(symbolStr.toString());
|
||||
@@ -3249,7 +3310,7 @@ public class XFLConverter {
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
||||
extractMultilevelClips(timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
extractMultilevelClips(timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
}
|
||||
|
||||
private String generateMaskedSymbolName(int symbolId) {
|
||||
@@ -3320,10 +3381,13 @@ public class XFLConverter {
|
||||
HashMap<Integer, CharacterTag> characters,
|
||||
FLAVersion flaVersion,
|
||||
HashMap<String, byte[]> files,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
List<Integer> multiUsageMorphShapes,
|
||||
StatusStack statusStack
|
||||
) throws XMLStreamException {
|
||||
|
||||
for (int objectId : multiUsageMorphShapes) {
|
||||
statusStack.pushStatus(characters.get(objectId).toString());
|
||||
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
|
||||
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
|
||||
@@ -3348,7 +3412,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>());
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>(), statusStack);
|
||||
|
||||
symbolStr.writeEndElement(); // DOMSymbolItem
|
||||
String symbolStr2 = prettyFormatXML(symbolStr.toString());
|
||||
@@ -3363,6 +3427,7 @@ public class XFLConverter {
|
||||
//TODO: itemID="518de416-00000341"
|
||||
}
|
||||
writer.writeEndElement();
|
||||
statusStack.popStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3376,7 +3441,8 @@ public class XFLConverter {
|
||||
FLAVersion flaVersion,
|
||||
HashMap<String, byte[]> files,
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
List<Integer> multiUsageMorphShapes,
|
||||
StatusStack statusStack
|
||||
) throws XMLStreamException {
|
||||
int f = 0;
|
||||
|
||||
@@ -3642,14 +3708,14 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
System.err.println("clip repeats in " + found);*/
|
||||
addExtractedClip(new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
addExtractedClip(new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
placeToMaskedSymbol.put(secondPlace, new MultiLevelClip(secondPlace, nextClipId.getVal(), numFrames));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertTimeline(SWF swf, AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, String name, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
private void convertTimeline(SWF swf, AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, String name, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
|
||||
|
||||
List<String> classNames = new ArrayList<>();
|
||||
//Searches for Object.registerClass("linkageIdentifier",mypkg.MyClass);
|
||||
@@ -3918,7 +3984,7 @@ public class XFLConverter {
|
||||
"color", randomOutlineColor(),
|
||||
"layerType", "mask",
|
||||
"locked", "true"});
|
||||
convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes);
|
||||
convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes, statusStack);
|
||||
writer.writeEndElement();
|
||||
|
||||
int parentIndex = index;
|
||||
@@ -4007,7 +4073,7 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) {
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack);
|
||||
for (int i = clipFrame; i <= lastFrame; i++) {
|
||||
depthToFramesList.get(nd).remove((Integer) i);
|
||||
}
|
||||
@@ -4021,7 +4087,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack);
|
||||
if (nonEmpty) {
|
||||
index++;
|
||||
}
|
||||
@@ -4059,8 +4125,9 @@ public class XFLConverter {
|
||||
writer.writeEndElement(); //DOMLayer
|
||||
}
|
||||
|
||||
private boolean writeLayer(SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
private boolean writeLayer(SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
|
||||
XFLXmlWriter layerPrev = new XFLXmlWriter();
|
||||
statusStack.pushStatus("layer " + (index + 1));
|
||||
//System.err.println("- writing layer " + (index + 1) + (startFrame == 0 && endFrame == Integer.MAX_VALUE ? ", all frames": ", frame " + startFrame + " to " + endFrame));
|
||||
layerPrev.writeStartElement("DOMLayer", new String[]{
|
||||
"name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + d + ")" : ""),
|
||||
@@ -4077,7 +4144,8 @@ public class XFLConverter {
|
||||
layerPrev.writeCharacters(""); // todo honfika: hack to close start tag
|
||||
String layerAfter = "</DOMLayer>";
|
||||
int prevLength = writer.length();
|
||||
convertFrames(swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes);
|
||||
convertFrames(swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes, statusStack);
|
||||
statusStack.popStatus();
|
||||
return writer.length() != prevLength;
|
||||
}
|
||||
|
||||
@@ -4608,16 +4676,17 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol = new HashMap<>();
|
||||
|
||||
StatusStack statusStack = new StatusStack(progressListener);
|
||||
|
||||
convertFonts(characterClasses, swf.getTags(), domDocument, progressListener);
|
||||
convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, progressListener);
|
||||
convertFonts(characterClasses, swf.getTags(), domDocument, statusStack);
|
||||
convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
|
||||
//domDocument.writeStartElement("timelines");
|
||||
ScriptPack documentScriptPack = characterScriptPacks.containsKey(0) ? characterScriptPacks.get(0) : null;
|
||||
if (progressListener != null) {
|
||||
progressListener.status("main timeline");
|
||||
}
|
||||
convertTimeline(swf, swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
statusStack.pushStatus("main timeline");
|
||||
convertTimeline(swf, swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
|
||||
statusStack.popStatus();
|
||||
//domDocument.writeEndElement();
|
||||
|
||||
if (hasAmfMetadata) {
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.math.BezierEdge;
|
||||
import com.jpexs.decompiler.flash.math.Distances;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLE;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE2;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
|
||||
@@ -25,18 +29,26 @@ 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.helpers.Reference;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Shape fixer.
|
||||
* This will walk a shape and split crossed edges so FLA editor can properly load it.
|
||||
* Shape fixer. This will walk a shape and split crossed edges so FLA editor can
|
||||
* properly load it. It also fixes morphshape - removes duplicated paths, calculate holes.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ShapeFixer {
|
||||
|
||||
|
||||
boolean DEBUG_PRINT = false;
|
||||
|
||||
private class BezierPair {
|
||||
@@ -47,7 +59,7 @@ public class ShapeFixer {
|
||||
|
||||
public BezierPair(BezierEdge be1, BezierEdge be2) {
|
||||
this.be1 = be1;
|
||||
this.be2 = be2;
|
||||
this.be2 = be2;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +72,7 @@ public class ShapeFixer {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
//simplifyed to be fast, not so neccessarily accurate
|
||||
//simplified to be fast, not so neccessarily accurate
|
||||
return hashCode() == obj.hashCode();
|
||||
/*if (this == obj) {
|
||||
return true;
|
||||
@@ -81,10 +93,22 @@ public class ShapeFixer {
|
||||
|
||||
}
|
||||
|
||||
public List<ShapeRecordAdvanced> fix(
|
||||
List<SHAPERECORD> records
|
||||
) {
|
||||
private boolean isEmptyBatch(List<BezierEdge> batch) {
|
||||
for (BezierEdge be : batch) {
|
||||
if (!be.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ShapeRecordAdvanced> fix(
|
||||
List<SHAPERECORD> records,
|
||||
boolean morphshape,
|
||||
int shapeNum,
|
||||
FILLSTYLEARRAY baseFillStyles,
|
||||
LINESTYLEARRAY baseLineStyles
|
||||
) {
|
||||
List<List<BezierEdge>> shapes = new ArrayList<>();
|
||||
List<BezierEdge> currentShape = new ArrayList<>();
|
||||
List<Integer> fillStyles0 = new ArrayList<>();
|
||||
@@ -164,6 +188,416 @@ public class ShapeFixer {
|
||||
y = rec.changeY(y);
|
||||
}
|
||||
|
||||
if (morphshape) {
|
||||
|
||||
//Remove empty edges
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<List<BezierEdge>> closedShapes = new ArrayList<>();
|
||||
List<Integer> closedFillStyle = new ArrayList<>();
|
||||
List<Integer> closedLineStyle = new ArrayList<>();
|
||||
List<Integer> closedLayers = new ArrayList<>();
|
||||
List<Integer> closedShapesI = new ArrayList<>();
|
||||
List<Integer> closedShapesJ = new ArrayList<>();
|
||||
|
||||
Map<Integer, Integer> replacements = new LinkedHashMap<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedLineStyle.add(lineStyles.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedLayers.add(layers.get(i));
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> removedShapes = new HashSet<>();
|
||||
for (int i1 = 0; i1 < closedShapes.size(); i1++) {
|
||||
if (replacements.containsKey(i1)) {
|
||||
continue;
|
||||
}
|
||||
for (int i2 = 0; i2 < closedShapes.size(); i2++) {
|
||||
if (i1 == i2) {
|
||||
continue;
|
||||
}
|
||||
if (replacements.containsKey(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedLayers.get(i1) != closedLayers.get(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedFillStyle.get(i1) > 0 && closedFillStyle.get(i2) > 0) {
|
||||
if (closedFillStyle.get(i1) != closedFillStyle.get(i2)) {
|
||||
FILLSTYLEARRAY fa = closedLayers.get(i1) == -1 ? baseFillStyles : fillStyleLayers.get(closedLayers.get(i1));
|
||||
FILLSTYLE fs1 = fa.fillStyles[closedFillStyle.get(i1) - 1];
|
||||
FILLSTYLE fs2 = fa.fillStyles[closedFillStyle.get(i2) - 1];
|
||||
if (!fs1.equals(fs2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) > 0) {
|
||||
if (closedLineStyle.get(i1) != closedLineStyle.get(i2)) {
|
||||
LINESTYLEARRAY lsa = closedLayers.get(i1) == -1 ? baseLineStyles : lineStyleLayers.get(closedLayers.get(i1));
|
||||
if (shapeNum <= 3) {
|
||||
LINESTYLE ls1 = lsa.lineStyles[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE ls2 = lsa.lineStyles[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
LINESTYLE2 ls1 = lsa.lineStyles2[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE2 ls2 = lsa.lineStyles2[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closedShapes.get(i1).size() <= 1 || closedShapes.get(i2).size() <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEmptyBatch(closedShapes.get(i1)) || isEmptyBatch(closedShapes.get(i2))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double dist = Distances.getBatchDistance(closedShapes.get(i1), closedShapes.get(i2));
|
||||
|
||||
if (dist <= 10) { //magic
|
||||
/*System.err.println("dist = " + dist);
|
||||
System.err.println("removed");
|
||||
System.err.println("removed shape["+closedShapesI.get(i2)+"]["+closedShapesJ.get(i2)+"], fs "+ closedFillStyle.get(i2) + ", ls " + closedLineStyle.get(i2));
|
||||
System.err.println("left shape["+closedShapesI.get(i1)+"]["+closedShapesJ.get(i1)+"], fs "+ closedFillStyle.get(i1)+ ", ls " + closedLineStyle.get(i1));
|
||||
*/
|
||||
replacements.put(i2, i1);
|
||||
removedShapes.add(i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int repI = replacements.containsKey(i) ? replacements.get(i) : i;
|
||||
List<BezierEdge> listI = closedShapes.get(repI);
|
||||
|
||||
for (int j = i + 1; j < closedShapes.size(); j++) {
|
||||
/*if (i == j) {
|
||||
break;
|
||||
}*/
|
||||
if (removedShapes.contains(j) && !replacements.containsKey(j)) {
|
||||
continue;
|
||||
}
|
||||
int repJ = replacements.containsKey(j) ? replacements.get(j) : j;
|
||||
List<BezierEdge> listJ = closedShapes.get(repJ);
|
||||
|
||||
if (closedFillStyle.get(i) != closedFillStyle.get(j)
|
||||
|| closedLineStyle.get(i) != closedLineStyle.get(j)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listI.equals(listJ)) {
|
||||
replacements.remove(j);
|
||||
removedShapes.add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
if (removedShapes.contains(i)) {
|
||||
//System.err.println("removing shape["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
if (replacements.containsKey(i)) {
|
||||
list.addAll(from, closedShapes.get(replacements.get(i)));
|
||||
}
|
||||
|
||||
if (!removedShapes.contains(i)) {
|
||||
for (int j = to; j >= from; j--) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This will remove a stroked path with no fill which has same
|
||||
* stroke as subsequent path (or is its prefix). This happens in the
|
||||
* morphshape edges. This needs to be cleaned up before exporting to FLA.
|
||||
*/
|
||||
List<BezierEdge> prevList = null;
|
||||
int prevI = -1;
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
|
||||
int prevFillStyle0 = fillStyles0.get(i - 1);
|
||||
int prevFillStyle1 = fillStyles1.get(i - 1);
|
||||
int prevLineStyle = lineStyles.get(i - 1);
|
||||
lineStyle = lineStyles.get(i);
|
||||
fillStyle0 = fillStyles0.get(i);
|
||||
fillStyle1 = fillStyles1.get(i);
|
||||
|
||||
if (fillStyle0 == 0 && fillStyle1 == 0 && lineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
if (prevList.size() >= list.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(i).clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (prevFillStyle0 == 0 && prevFillStyle1 == 0 && prevLineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
//list startswitch prevList
|
||||
if (list.size() >= prevList.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < prevList.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(prevI).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
|
||||
//Clear obvious duplicates
|
||||
//System.err.println("Clear obvious duplicates...");
|
||||
prevList = null;
|
||||
prevI = -1;
|
||||
//if (false)
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
if (fillStyles0.get(i) == fillStyles0.get(prevI)
|
||||
&& lineStyles.get(i) == lineStyles.get(prevI)) {
|
||||
if (list.equals(prevList)) {
|
||||
System.err.println("clearing " + i);
|
||||
prevList.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
|
||||
//----------------------------FIND "holes" = apply wind even odd -------------
|
||||
closedShapes = new ArrayList<>();
|
||||
closedFillStyle = new ArrayList<>();
|
||||
closedLineStyle = new ArrayList<>();
|
||||
closedLayers = new ArrayList<>();
|
||||
closedShapesI = new ArrayList<>();
|
||||
closedShapesJ = new ArrayList<>();
|
||||
Set<Integer> closedHolesI = new LinkedHashSet<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedLineStyle.add(lineStyles.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedLayers.add(layers.get(i));
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
//reversing anti-clockwise
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
List<BezierEdge> list = closedShapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
points.add(list.get(0).getBeginPoint());
|
||||
for (BezierEdge be : list) {
|
||||
if (be.points.size() == 3) {
|
||||
points.add(be.points.get(1));
|
||||
}
|
||||
points.add(be.getEndPoint());
|
||||
}
|
||||
double sum = 0;
|
||||
for (int j = 0; j < points.size(); j++) {
|
||||
Point2D p1 = points.get(j);
|
||||
Point2D p2 = points.get((j + 1) % points.size());
|
||||
sum += (p1.getX() * p2.getY() - p2.getX() * p1.getY());
|
||||
}
|
||||
|
||||
if (sum < 0) { //anti clockwise
|
||||
//reverse the list
|
||||
List<BezierEdge> rev = new ArrayList<>();
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
rev.add(list.get(list.size() - 1 - j).reverse());
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
int shapeJ = closedShapesJ.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
shapes.get(shapeI).set(shapeJ + j, rev.get(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> fillStyleToClosed = new LinkedHashMap<>();
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int fs = closedFillStyle.get(i);
|
||||
if (fs != 0) {
|
||||
if (!fillStyleToClosed.containsKey(fs)) {
|
||||
fillStyleToClosed.put(fs, new ArrayList<>());
|
||||
}
|
||||
fillStyleToClosed.get(fs).add(i);
|
||||
}
|
||||
}
|
||||
for (int fs : fillStyleToClosed.keySet()) {
|
||||
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
Map<Integer, GeneralPath> closedPaths = new LinkedHashMap<>();
|
||||
for (int i : fillStyleToClosed.get(fs)) {
|
||||
List<BezierEdge> closed = closedShapes.get(i);
|
||||
if (closed.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
GeneralPath closedPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
|
||||
closedPath.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
path.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
boolean isEmpty = true;
|
||||
for (BezierEdge be : closed) {
|
||||
if (be.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
isEmpty = false;
|
||||
if (be.points.size() == 3) {
|
||||
closedPath.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
} else {
|
||||
closedPath.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
}
|
||||
}
|
||||
closedPath.closePath();
|
||||
path.closePath();
|
||||
if (!isEmpty) {
|
||||
closedPaths.put(i, closedPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : closedPaths.keySet()) {
|
||||
GeneralPath region = closedPaths.get(i);
|
||||
Rectangle r = region.getBounds();
|
||||
double px;
|
||||
double py;
|
||||
do {
|
||||
px = r.getX() + r.getWidth() * Math.random();
|
||||
py = r.getY() + r.getHeight() * Math.random();
|
||||
} while (!region.contains(px, py));
|
||||
|
||||
if (!path.contains(px, py)) {
|
||||
closedHolesI.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
if (closedHolesI.contains(i)) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
//System.err.println("removing hole["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
shapes.add(shapeI + 1, closedShapes.get(i));
|
||||
closedShapes.set(i, new ArrayList<>());
|
||||
fillStyles0.add(shapeI + 1, 0);
|
||||
fillStyles1.add(shapeI + 1, fillStyles0.get(shapeI));
|
||||
lineStyles.add(shapeI + 1, lineStyles.get(shapeI));
|
||||
layers.add(shapeI + 1, layers.get(shapeI));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------- detecting overlapping edges --------------------
|
||||
List<BezierPair> splittedPairs = new ArrayList<>();
|
||||
|
||||
loopi1:
|
||||
@@ -173,193 +607,182 @@ public class ShapeFixer {
|
||||
for (int j1 = 0; j1 < shapes.get(i1).size(); j1++) {
|
||||
BezierEdge be1 = shapes.get(i1).get(j1);
|
||||
for (int i2 = 0; i2 < shapes.size(); i2++) {
|
||||
if (layers.get(i2) == layer) {
|
||||
loopj2:
|
||||
for (int j2 = 0; j2 < shapes.get(i2).size(); j2++) {
|
||||
BezierEdge be2 = shapes.get(i2).get(j2);
|
||||
if (layers.get(i2) != layer) {
|
||||
continue;
|
||||
}
|
||||
loopj2:
|
||||
for (int j2 = 0; j2 < shapes.get(i2).size(); j2++) {
|
||||
BezierEdge be2 = shapes.get(i2).get(j2);
|
||||
|
||||
if (i1 == i2 && j1 == j2) {
|
||||
continue;
|
||||
if (i1 == i2 && j1 == j2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (be1.isEmpty()) {
|
||||
shapes.get(i1).remove(j1);
|
||||
if (i1 == i2 && j2 > j1) {
|
||||
j2--;
|
||||
}
|
||||
|
||||
if (be1.isEmpty()) {
|
||||
shapes.get(i1).remove(j1);
|
||||
if (i1 == i2 && j2 > j1) {
|
||||
j2--;
|
||||
}
|
||||
j1--;
|
||||
continue loopj1;
|
||||
}
|
||||
if (be2.isEmpty()) {
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2 && j1 > j2) {
|
||||
j1--;
|
||||
continue loopj1;
|
||||
}
|
||||
if (be2.isEmpty()) {
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2 && j1 > j2) {
|
||||
j1--;
|
||||
}
|
||||
j2--;
|
||||
continue loopj2;
|
||||
}
|
||||
j2--;
|
||||
continue loopj2;
|
||||
}
|
||||
|
||||
//duplicated edge
|
||||
if (be1.equals(be2) || be1.equals(be2.reverse())) {
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2 && j1 > j2) {
|
||||
j1--;
|
||||
}
|
||||
j2--;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("removing duplicate " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
//duplicated edge
|
||||
if (be1.equals(be2) || be1.equals(be2.reverse())) {
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2 && j1 > j2) {
|
||||
j1--;
|
||||
}
|
||||
j2--;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("removing duplicate " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
BezierPair pair = new BezierPair(be1, be2);
|
||||
if (splittedPairs.contains(pair)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Double> t1Ref = new ArrayList<>();
|
||||
List<Double> t2Ref = new ArrayList<>();
|
||||
List<Point2D> intPoints = new ArrayList<>();
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
//System.err.print("checking shape[" + i1 + "][" + j1 + "] to shape[" + i2 + "][" + j2 + "] : " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
|
||||
boolean isint = be1.intersects(be2, t1Ref, t2Ref, intPoints);
|
||||
if (DEBUG_PRINT) {
|
||||
//System.err.println(" " + isint);
|
||||
}
|
||||
|
||||
if (!t1Ref.isEmpty()) {
|
||||
|
||||
if ((be1.getBeginPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getBeginPoint().equals(be2.getEndPoint())
|
||||
|| be1.getEndPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getEndPoint().equals(be2.getEndPoint())) && (t1Ref.size() == 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BezierPair pair = new BezierPair(be1, be2);
|
||||
if (splittedPairs.contains(pair)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Double> t1Ref = new ArrayList<>();
|
||||
List<Double> t2Ref = new ArrayList<>();
|
||||
List<Point2D> intPoints = new ArrayList<>();
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.print("checking shape[" + i1 + "][" + j1 + "] to shape[" + i2 + "][" + j2 + "] : " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
System.err.println("intersects " + be1.toSvg() + " " + be2.toSvg());
|
||||
System.err.println(" fillstyle0: " + fillStyles0.get(i1) + " , " + fillStyles0.get(i2));
|
||||
System.err.println(" fillstyle1: " + fillStyles1.get(i1) + " , " + fillStyles1.get(i2));
|
||||
System.err.println(" linestyle: " + lineStyles.get(i1) + " , " + lineStyles.get(i2));
|
||||
|
||||
boolean isint = be1.intersects(be2, t1Ref, t2Ref, intPoints);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println(" " + isint);
|
||||
if (isint) {
|
||||
System.err.println("xxx");
|
||||
for (int n = 0; n < t1Ref.size(); n++) {
|
||||
System.err.println("- " + t1Ref.get(n) + " , " + t2Ref.get(n) + " : " + intPoints.get(n));
|
||||
}
|
||||
}
|
||||
|
||||
if (!t1Ref.isEmpty()) {
|
||||
if ((t1Ref.size() == 2) && !((t1Ref.get(0) == 0 || t1Ref.get(0) == 1)
|
||||
&& (t2Ref.get(0) == 0 || t2Ref.get(0) == 1))) {
|
||||
t1Ref.add(0, t1Ref.remove(1));
|
||||
t2Ref.add(0, t2Ref.remove(1));
|
||||
intPoints.add(0, intPoints.remove(1));
|
||||
}
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("be1 " + be1);
|
||||
System.err.println("be2 " + be2);
|
||||
System.err.println("intersects " + be1.toSvg() + " " + be2.toSvg());
|
||||
System.err.println(" fillstyle0: " + fillStyles0.get(i1) + " , " + fillStyles0.get(i2));
|
||||
System.err.println(" fillstyle1: " + fillStyles1.get(i1) + " , " + fillStyles1.get(i2));
|
||||
System.err.println(" linestyle: " + lineStyles.get(i1) + " , " + lineStyles.get(i2));
|
||||
|
||||
for (int n = 0; n < t1Ref.size(); n++) {
|
||||
System.err.println("- " + t1Ref.get(n) + " , " + t2Ref.get(n) + " : " + intPoints.get(n));
|
||||
}
|
||||
}
|
||||
int splitPointIndex = 0;
|
||||
if (intPoints.size() > 1) {
|
||||
splitPointIndex = 1;
|
||||
}
|
||||
|
||||
|
||||
splittedPairs.add(pair);
|
||||
|
||||
if ((be1.getBeginPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getBeginPoint().equals(be2.getEndPoint())
|
||||
|| be1.getEndPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getEndPoint().equals(be2.getEndPoint())) && (t1Ref.size() == 1)) {
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("Same begin/end - continued");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Reference<BezierEdge> be1LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be1RRef = new Reference<>(null);
|
||||
be1.split(t1Ref.get(splitPointIndex), be1LRef, be1RRef);
|
||||
Reference<BezierEdge> be2LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be2RRef = new Reference<>(null);
|
||||
be2.split(t2Ref.get(splitPointIndex), be2LRef, be2RRef);
|
||||
|
||||
if ((t1Ref.size() == 2) && !((t1Ref.get(0) == 0 || t1Ref.get(0) == 1)
|
||||
&& (t2Ref.get(0) == 0 || t2Ref.get(0) == 1))) {
|
||||
t1Ref.add(0, t1Ref.remove(1));
|
||||
t2Ref.add(0, t2Ref.remove(1));
|
||||
intPoints.add(0, intPoints.remove(1));
|
||||
}
|
||||
BezierEdge be1L = be1LRef.getVal();
|
||||
BezierEdge be1R = be1RRef.getVal();
|
||||
BezierEdge be2L = be2LRef.getVal();
|
||||
BezierEdge be2R = be2RRef.getVal();
|
||||
|
||||
int splitPointIndex = 0;
|
||||
if (intPoints.size() > 1) {
|
||||
splitPointIndex = 1;
|
||||
}
|
||||
Point2D intP = intPoints.get(splitPointIndex);
|
||||
|
||||
splittedPairs.add(pair);
|
||||
be1L.setEndPoint(intP);
|
||||
be1R.setBeginPoint(intP);
|
||||
be2L.setEndPoint(intP);
|
||||
be2R.setBeginPoint(intP);
|
||||
|
||||
Reference<BezierEdge> be1LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be1RRef = new Reference<>(null);
|
||||
be1.split(t1Ref.get(splitPointIndex), be1LRef, be1RRef);
|
||||
Reference<BezierEdge> be2LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be2RRef = new Reference<>(null);
|
||||
be2.split(t2Ref.get(splitPointIndex), be2LRef, be2RRef);
|
||||
|
||||
BezierEdge be1L = be1LRef.getVal();
|
||||
BezierEdge be1R = be1RRef.getVal();
|
||||
BezierEdge be2L = be2LRef.getVal();
|
||||
BezierEdge be2R = be2RRef.getVal();
|
||||
|
||||
Point2D intP = intPoints.get(splitPointIndex);
|
||||
|
||||
be1L.setEndPoint(intP);
|
||||
be1R.setBeginPoint(intP);
|
||||
be2L.setEndPoint(intP);
|
||||
be2R.setBeginPoint(intP);
|
||||
|
||||
be1L.roundHalf();
|
||||
be1R.roundHalf();
|
||||
be2L.roundHalf();
|
||||
be2R.roundHalf();
|
||||
|
||||
if (i1 == i2 && j1 < j2) {
|
||||
be1L.roundHalf();
|
||||
be1R.roundHalf();
|
||||
be2L.roundHalf();
|
||||
be2R.roundHalf();
|
||||
if (i1 == i2) {
|
||||
if (j1 < j2) {
|
||||
shapes.get(i1).remove(j2);
|
||||
shapes.get(i2).remove(j1);
|
||||
j2--;
|
||||
} else {
|
||||
shapes.get(i1).remove(j1);
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2) {
|
||||
j1--;
|
||||
}
|
||||
j1--;
|
||||
}
|
||||
|
||||
int n1 = j1;
|
||||
int n2 = j2;
|
||||
|
||||
if (!be1L.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1L);
|
||||
if (i1 == i2 && n2 > n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1L.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be1R.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1R);
|
||||
if (i1 == i2 && n2 > n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1R.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be2L.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2L);
|
||||
if (i1 == i2 && n1 > n2) {
|
||||
n1++;
|
||||
}
|
||||
n2++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2L.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be2R.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2R);
|
||||
if (i1 == i2 && n1 > n2) {
|
||||
n1++;
|
||||
}
|
||||
n2++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2R.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
j1--;
|
||||
continue loopj1;
|
||||
} else {
|
||||
shapes.get(i1).remove(j1);
|
||||
shapes.get(i2).remove(j2);
|
||||
}
|
||||
int n1 = j1;
|
||||
int n2 = j2;
|
||||
|
||||
if (!be1L.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1L);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1L.toSvg() + " to j1=" + n1);
|
||||
}
|
||||
if (i1 == i2 && n2 >= n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
|
||||
}
|
||||
|
||||
if (!be1R.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1R);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1R.toSvg() + " to j1=" + n1);
|
||||
}
|
||||
if (i1 == i2 && n2 >= n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
|
||||
}
|
||||
|
||||
if (!be2L.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2L);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2L.toSvg() + " to j2=" + n2);
|
||||
}
|
||||
n2++;
|
||||
|
||||
}
|
||||
|
||||
if (!be2R.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2R);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2R.toSvg() + " to j2=" + n2);
|
||||
}
|
||||
n2++;
|
||||
}
|
||||
|
||||
j1--;
|
||||
continue loopj1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user