Replacing morphshape from start/end shape.

Fixed Display of morphshape end shape to be exactly at 65535 ratio
This commit is contained in:
Jindra Petřík
2023-10-24 23:29:32 +02:00
parent 247b1c17a4
commit 377c5ac458
23 changed files with 1249 additions and 99 deletions

View File

@@ -966,12 +966,7 @@ public final class Configuration {
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("ui")
public static ConfigurationItem<Boolean> displayAs3PCodePanel = null;
@ConfigurationDefaultBoolean(true)
@ConfigurationName("warning.replace.morphshape")
@ConfigurationCategory("ui")
public static ConfigurationItem<Boolean> warningReplaceMorphShape = null;
public static ConfigurationItem<Boolean> displayAs3PCodePanel = null;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -45,9 +45,19 @@ public class CurvedEdge extends StraightEdge implements IEdge {
return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, newFillStyleIdx);
}
@Override
public IEdge sameWithNewFillStyle(int newFillStyleIdx) {
return new CurvedEdge(fromX, fromY, controlX, controlY, toX, toY, lineStyleIdx, newFillStyleIdx);
}
@Override
public String toString() {
return "curved[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + " control:" + controlX / 20f + "," + controlY / 20f + "]";
}
@Override
public IEdge reverse() {
return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, getFillStyleIdx());
}
}

View File

@@ -35,4 +35,8 @@ public interface IEdge {
public int getFillStyleIdx();
public IEdge reverseWithNewFillStyle(int newFillStyleIdx);
public IEdge reverse();
public IEdge sameWithNewFillStyle(int newFillStyleIdx);
}

View File

@@ -64,10 +64,8 @@ public abstract class ShapeExporterBase implements IShapeExporter {
private final ColorTransform colorTransform;
private boolean canUseSmoothing = true;
protected int windingRule;
public ShapeExporterBase(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) {
this.shape = shape;

View File

@@ -83,4 +83,15 @@ public class StraightEdge implements IEdge {
return "straight[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + "]";
}
@Override
public IEdge sameWithNewFillStyle(int newFillStyleIdx) {
return new StraightEdge(fromX, fromY, toX, toY, lineStyleIdx, newFillStyleIdx);
}
@Override
public IEdge reverse() {
return new StraightEdge(toX, toY, fromX, fromY, lineStyleIdx, getFillStyleIdx());
}
}

View File

@@ -0,0 +1,335 @@
/*
* 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.importers.morphshape;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.math.BezierEdge;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
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.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author JPEXS
*/
public class MorphShapeGenerator {
public void generate(DefineMorphShape2Tag morphShape, ShapeTag startShape, ShapeTag endShape) throws StyleMismatchException {
ShapeForMorphExporter startExport = new ShapeForMorphExporter(startShape);
startExport.export();
ShapeForMorphExporter endExport = new ShapeForMorphExporter(endShape);
endExport.export();
List<List<BezierEdge>> startBeziers = new ArrayList<>();
List<List<BezierEdge>> endBeziers = new ArrayList<>();
List<FILLSTYLE> startFillStyles = new ArrayList<>();
List<Integer> startFillstyleIndices = new ArrayList<>();
List<LINESTYLE2> startLineStyles = new ArrayList<>();
List<Integer> startLineStyleIndices = new ArrayList<>();
List<FILLSTYLE> endFillStyles = new ArrayList<>();
List<LINESTYLE2> endLineStyles = new ArrayList<>();
Set<Integer> usedBs = new HashSet<>();
List<Integer> startShapeIndices = new ArrayList<>();
List<Integer> endShapeIndices = new ArrayList<>();
for (int a = 0; a < startExport.shapes.size(); a++) {
double minDistance = Double.MAX_VALUE;
double minDistanceNoUsed = Double.MAX_VALUE;
int selectedB = -1;
int selectedBNoUsed = -1;
int startFillStyleIndex = startExport.fillStyleIndices.get(a);
FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex);
int startLineStyleIndex = startExport.lineStyleIndices.get(a);
LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex);
for (int b = 0; b < endExport.shapes.size(); b++) {
int endFillStyleIndex = endExport.fillStyleIndices.get(b);
FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex);
int endLineStyleIndex = endExport.lineStyleIndices.get(b);
LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex);
if (((endFillStyle == null && startFillStyle == null)
|| (startFillStyle != null
&& endFillStyle != null
&& startFillStyle.isCompatibleFillStyle(endFillStyle)))
&& ((endLineStyle == null && startLineStyle == null)
|| (startLineStyle != null
&& endLineStyle != null
&& startLineStyle.isCompatibleLineStyle(endLineStyle)))) {
double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b));
if (distance < minDistance) {
minDistance = distance;
selectedB = b;
}
if (distance < minDistanceNoUsed && !usedBs.contains(b)) {
minDistanceNoUsed = distance;
selectedBNoUsed = b;
}
}
}
if (selectedB == -1) {
throw new StyleMismatchException();
}
if (selectedBNoUsed != -1) {
selectedB = selectedBNoUsed;
}
startShapeIndices.add(a);
endShapeIndices.add(selectedB);
usedBs.add(selectedB);
}
if (usedBs.size() < endExport.shapes.size()) {
for (int b = 0; b < endExport.shapes.size(); b++) {
if (!usedBs.contains(b)) {
double minDistance = Double.MAX_VALUE;
int selectedA = -1;
int endFillStyleIndex = endExport.fillStyleIndices.get(b);
FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex);
int endLineStyleIndex = endExport.lineStyleIndices.get(b);
LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex);
for (int a = 0; a < startExport.shapes.size(); a++) {
int startFillStyleIndex = startExport.fillStyleIndices.get(a);
FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex);
int startLineStyleIndex = startExport.lineStyleIndices.get(a);
LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex);
if (((endFillStyle == null && startFillStyle == null)
|| (startFillStyle != null
&& endFillStyle != null
&& startFillStyle.isCompatibleFillStyle(endFillStyle)))
&& ((endLineStyle == null && startLineStyle == null)
|| (startLineStyle != null
&& endLineStyle != null
&& startLineStyle.isCompatibleLineStyle(endLineStyle)))) {
double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b));
if (distance < minDistance) {
minDistance = distance;
selectedA = a;
}
}
}
if (selectedA == -1) {
throw new StyleMismatchException();
}
startShapeIndices.add(selectedA);
endShapeIndices.add(b);
}
}
}
for (int i = 0; i < startShapeIndices.size(); i++) {
int a = startShapeIndices.get(i);
int b = endShapeIndices.get(i);
List<BezierEdge> shapeStart = Helper.deepCopy(startExport.shapes.get(a));
split(shapeStart, startExport.pointsPosPercent.get(a), endExport.pointsPosPercent.get(b));
List<BezierEdge> shapeEnd = Helper.deepCopy(endExport.shapes.get(b));
split(shapeEnd, endExport.pointsPosPercent.get(b), startExport.pointsPosPercent.get(a));
startBeziers.add(shapeStart);
endBeziers.add(shapeEnd);
if (startExport.fillStyleIndices.get(a) != -1) {
startFillStyles.add(startExport.fillStyles.get(startExport.fillStyleIndices.get(a)));
}
startFillstyleIndices.add(startExport.fillStyleIndices.get(a));
if (startExport.lineStyleIndices.get(a) != -1) {
startLineStyles.add(startExport.lineStyles.get(startExport.lineStyleIndices.get(a)));
}
startLineStyleIndices.add(startExport.lineStyleIndices.get(a));
if (endExport.fillStyleIndices.get(b) != -1) {
endFillStyles.add(endExport.fillStyles.get(endExport.fillStyleIndices.get(b)));
}
if (endExport.lineStyleIndices.get(b) != -1) {
endLineStyles.add(endExport.lineStyles.get(endExport.lineStyleIndices.get(b)));
}
}
List<SHAPERECORD> startRecords = new ArrayList<>();
List<SHAPERECORD> endRecords = new ArrayList<>();
MORPHFILLSTYLEARRAY morphFillStyleArray = new MORPHFILLSTYLEARRAY();
morphFillStyleArray.fillStyles = new MORPHFILLSTYLE[startFillStyles.size()];
MORPHLINESTYLEARRAY morphLineStyleArray = new MORPHLINESTYLEARRAY();
morphLineStyleArray.lineStyles2 = new MORPHLINESTYLE2[startLineStyles.size()];
for (int i = 0; i < startFillStyles.size(); i++) {
FILLSTYLE fsStart = startFillStyles.get(i);
FILLSTYLE fsEnd = endFillStyles.get(i);
MORPHFILLSTYLE morphFillStyle = fsStart.toMorphStyle(fsEnd);
if (morphFillStyle == null) {
throw new StyleMismatchException();
}
morphFillStyleArray.fillStyles[i] = morphFillStyle;
}
for (int i = 0; i < startLineStyles.size(); i++) {
LINESTYLE2 lsStart = startLineStyles.get(i);
LINESTYLE2 lsEnd = endLineStyles.get(i);
MORPHLINESTYLE2 morphLineStyle = lsStart.toMorphLineStyle2(lsEnd);
if (morphLineStyle == null) {
throw new StyleMismatchException();
}
morphLineStyleArray.lineStyles2[i] = morphLineStyle;
if (morphLineStyle.noHScaleFlag || morphLineStyle.noVScaleFlag) {
morphShape.usesNonScalingStrokes = true;
}
if (!morphLineStyle.noHScaleFlag && !morphLineStyle.noVScaleFlag) {
morphShape.usesScalingStrokes = true;
}
}
for (int i = 0; i < startBeziers.size(); i++) {
List<BezierEdge> beList = startBeziers.get(i);
StyleChangeRecord scr = new StyleChangeRecord();
scr.stateFillStyle0 = true;
if (startFillstyleIndices.get(i) != -1) {
scr.fillStyle0 = startFillstyleIndices.get(i) + 1;
} else {
scr.fillStyle0 = 0;
}
scr.stateLineStyle = true;
if (startLineStyleIndices.get(i) != -1) {
scr.lineStyle = startLineStyleIndices.get(i) + 1;
} else {
scr.lineStyle = 0;
}
startRecords.add(scr);
BezierEdge firstBe = beList.get(0);
StyleChangeRecord scrMove = new StyleChangeRecord();
scrMove.stateMoveTo = true;
scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX());
scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY());
startRecords.add(scrMove);
for (BezierEdge be : beList) {
SHAPERECORD rec = bezierToRecord(be);
startRecords.add(rec);
}
}
startRecords.add(new EndShapeRecord());
for (int i = 0; i < endBeziers.size(); i++) {
List<BezierEdge> beList = endBeziers.get(i);
BezierEdge firstBe = beList.get(0);
StyleChangeRecord scrMove = new StyleChangeRecord();
scrMove.stateMoveTo = true;
scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX());
scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY());
endRecords.add(scrMove);
for (BezierEdge be : beList) {
SHAPERECORD rec = bezierToRecord(be);
endRecords.add(rec);
}
}
endRecords.add(new EndShapeRecord());
morphShape.morphFillStyles = morphFillStyleArray;
morphShape.morphLineStyles = morphLineStyleArray;
morphShape.startEdges = new SHAPE();
morphShape.startEdges.shapeRecords = startRecords;
morphShape.startEdges.numFillBits = SWFOutputStream.getNeededBitsU(morphFillStyleArray.fillStyles.length);
morphShape.startEdges.numLineBits = SWFOutputStream.getNeededBitsU(morphLineStyleArray.lineStyles2.length);
morphShape.endEdges = new SHAPE();
morphShape.endEdges.numFillBits = 0;
morphShape.endEdges.numLineBits = 0;
morphShape.endEdges.shapeRecords = endRecords;
morphShape.setModified(true);
morphShape.updateBounds();
}
private void split(List<BezierEdge> shape, List<Double> originalPointsPosPercent, List<Double> newPointPosPercent) {
List<Double> pointPointsPercent = new ArrayList<>(originalPointsPosPercent);
int nppPos = 0;
for (int i = 0; i < shape.size() && nppPos < newPointPosPercent.size(); i++) {
BezierEdge be = shape.get(i);
double pointPosPct = pointPointsPercent.get(i);
double pointPosNextPct = pointPointsPercent.get(i + 1);
double pct = newPointPosPercent.get(nppPos);
if (pct > pointPosPct && pct < pointPosNextPct) {
double deltaPct = pointPosNextPct - pointPosPct;
double newPct = pct - pointPosPct;
double insidePct = newPct / deltaPct;
Reference<BezierEdge> leftRef = new Reference<>(null);
Reference<BezierEdge> rightRef = new Reference<>(null);
be.split(insidePct, leftRef, rightRef);
shape.remove(i);
shape.add(i, leftRef.getVal());
shape.add(i + 1, rightRef.getVal());
pointPointsPercent.add(i + 1, pct);
nppPos++;
} else if (pct == pointPosPct) {
nppPos++;
i--;
}
}
}
private SHAPERECORD bezierToRecord(BezierEdge be) {
if (be.points.size() == 2) {
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.deltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX());
ser.deltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY());
ser.generalLineFlag = true;
ser.simplify();
return ser;
}
if (be.points.size() == 3) {
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX());
cer.controlDeltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY());
cer.anchorDeltaX = (int) Math.round(be.points.get(2).getX() - be.points.get(1).getX());
cer.anchorDeltaY = (int) Math.round(be.points.get(2).getY() - be.points.get(1).getY());
return cer;
}
return null;
}
}

View File

@@ -0,0 +1,430 @@
/*
* 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.importers.morphshape;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.shape.ShapeExporterBase;
import com.jpexs.decompiler.flash.math.BezierEdge;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FOCALGRADIENT;
import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ShapeForMorphExporter extends ShapeExporterBase {
public List<List<BezierEdge>> shapes = new ArrayList<>();
public List<Integer> fillStyleIndices = new ArrayList<>();
public List<Integer> lineStyleIndices = new ArrayList<>();
public List<List<Double>> pointsPosPercent = new ArrayList<>();
public List<Point2D.Double> centralPos = new ArrayList<>();
private final List<Point2D.Double> pointsSum = new ArrayList<>();
private final List<Integer> segmentCount = new ArrayList<>();
private final List<List<Double>> bezierLengths = new ArrayList<>();
private List<BezierEdge> currentShape = new ArrayList<>();
private List<Double> currentBezierLengths = new ArrayList<>();
private Point2D.Double currentPointsSum = new Point2D.Double();
private int currentSegmentCount = 0;
private int currentLineStyle = -1;
private int currentFillStyle = -1;
double currentShapeLen = 0;
public List<FILLSTYLE> fillStyles = new ArrayList<>();
public List<LINESTYLE2> lineStyles = new ArrayList<>();
private double lastX = 0;
private double lastY = 0;
public ShapeForMorphExporter(ShapeTag shape) {
super(ShapeTag.WIND_EVEN_ODD, shape.getShapeNum(), shape.getSwf(), shape.shapes, new ColorTransform());
}
@Override
public void beginShape() {
shapes = new ArrayList<>();
}
@Override
public void endShape() {
endCurrent();
for (int i = 0; i < segmentCount.size(); i++) {
Point2D.Double center = new Point2D.Double();
center.x = pointsSum.get(i).x / (double) segmentCount.get(i);
center.y = pointsSum.get(i).y / (double) segmentCount.get(i);
centralPos.add(center);
}
for (int i = 0; i < shapes.size(); i++) {
List<BezierEdge> shape = shapes.get(i);
List<Point2D> points = new ArrayList<>();
BezierEdge be = null;
for (int j = 0; j < shape.size(); j++) {
be = shape.get(j);
points.add(new Point2D.Double(
be.getBeginPoint().getX() - centralPos.get(i).getX(),
be.getBeginPoint().getY() - centralPos.get(i).getY()
));
}
points.add(new Point2D.Double(
be.getEndPoint().getX() - centralPos.get(i).getX(),
be.getEndPoint().getY() - centralPos.get(i).getY()
));
double w = 0;
for (int j = 0; j < points.size(); j++) {
int secondPoint = j + 1 == points.size() ? 0 : j + 1;
double x = points.get(j).getX();
double xp1 = points.get(secondPoint).getX();
double y = points.get(j).getY();
double yp1 = points.get(secondPoint).getY();
if (y * yp1 < 0) {
double r = x + ((y * (xp1 - x)) / (y - yp1));
if (r > 0) {
if (y < 0) {
w = w + 1;
} else {
w = w - 1;
}
}
} else if ((y == 0) && (x > 0)) {
if (yp1 > 0) {
w = w + .5;
} else {
w = w - .5;
}
} else if ((yp1 == 0) && (xp1 > 0)) {
if (y < 0) {
w = w + .5;
} else {
w = w - .5;
}
}
}
if (w > 0) {
//clockwise
} else {
//counter clock wise
List<BezierEdge> newShape = new ArrayList<>();
List<Double> newBeLength = new ArrayList<>();
for (int j = shape.size() - 1; j >= 0; j--) {
newShape.add(shape.get(j).reverse());
newBeLength.add(bezierLengths.get(i).get(j));
}
shape.clear();
shape.addAll(newShape);
bezierLengths.get(i).clear();
bezierLengths.get(i).addAll(newBeLength);
}
}
for (int i = 0; i < shapes.size(); i++) {
List<BezierEdge> shape = shapes.get(i);
//closed shape
if (shape.get(0).getBeginPoint().equals(shape.get(shape.size() - 1).getEndPoint())) {
//Find most top left point
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
for (int j = 0; j < shape.size(); j++) {
if (shape.get(j).getBeginPoint().getX() < minX) {
minX = shape.get(j).getBeginPoint().getX();
}
if (shape.get(j).getBeginPoint().getY() < minY) {
minY = shape.get(j).getBeginPoint().getY();
}
}
double minDist = Double.MAX_VALUE;
int minPos = -1;
for (int j = 0; j < shape.size(); j++) {
double dist = shape.get(j).getBeginPoint().distance(minX, minY);
if (dist < minDist) {
minDist = dist;
minPos = j;
}
}
if (minPos > -1) {
//Rearange shape to start with the top left point
for (int j = 0; j < minPos; j++) {
shape.add(shape.remove(0));
bezierLengths.get(i).add(bezierLengths.get(i).remove(0));
}
}
}
}
for (List<Double> pp : bezierLengths) {
List<Double> ppPercent = new ArrayList<>();
double len = 0.0;
for (double bLength : pp) {
len += bLength;
}
double pos = 0;
for (double bLength : pp) {
double pct = roundPct(pos / len);
pos += bLength;
ppPercent.add(pct);
}
ppPercent.add(1.0);
pointsPosPercent.add(ppPercent);
}
}
@Override
public void beginAliasedFills() {
}
@Override
public void beginFills() {
}
@Override
public void endFills() {
endCurrent();
}
@Override
public void beginLines() {
currentShape = null;
}
@Override
public void endLines(boolean close) {
endCurrent();
}
@Override
public void beginFill(RGB color) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = FILLSTYLE.SOLID;
fillStyle.color = color;
fillStyles.add(fillStyle);
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = type;
fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT();
fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords);
fillStyle.gradientMatrix = matrix.toMATRIX();
fillStyle.gradient.spreadMode = spreadMethod;
fillStyle.gradient.interpolationMode = interpolationMethod;
if (focalPointRatio != 0) {
((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio;
}
fillStyles.add(fillStyle);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
if (repeat) {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP;
}
} else {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP;
}
}
fillStyle.bitmapMatrix = matrix.toMATRIX();
fillStyle.bitmapId = bitmapId;
fillStyles.add(fillStyle);
}
@Override
public void endFill() {
endCurrent();
}
private void endCurrent() {
if (currentShape != null && !currentShape.isEmpty()) {
shapes.add(currentShape);
bezierLengths.add(currentBezierLengths);
pointsSum.add(currentPointsSum);
segmentCount.add(currentSegmentCount);
fillStyleIndices.add(currentFillStyle);
lineStyleIndices.add(currentLineStyle);
}
currentShapeLen = 0;
currentShape = new ArrayList<>();
currentBezierLengths = new ArrayList<>();
currentPointsSum = new Point2D.Double();
currentSegmentCount = 0;
currentFillStyle = -1;
currentLineStyle = -1;
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
endCurrent();
currentLineStyle = lineStyles.size();
LINESTYLE2 lineStyle = new LINESTYLE2();
lineStyle.width = (int) thickness;
lineStyle.color = new RGBA(color);
lineStyle.pixelHintingFlag = pixelHinting;
switch (scaleMode) {
case "NONE":
lineStyle.noHScaleFlag = true;
lineStyle.noVScaleFlag = true;
break;
case "VERTICAL":
lineStyle.noHScaleFlag = true;
break;
case "HORIZONTAL":
lineStyle.noVScaleFlag = true;
break;
}
lineStyle.startCapStyle = startCaps;
lineStyle.endCapStyle = endCaps;
lineStyle.joinStyle = joints;
lineStyle.miterLimitFactor = miterLimit;
lineStyles.add(lineStyle);
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1);
lineStyle.hasFillFlag = true;
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = type;
fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT();
fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords);
fillStyle.gradientMatrix = matrix.toMATRIX();
fillStyle.gradient.spreadMode = spreadMethod;
fillStyle.gradient.interpolationMode = interpolationMethod;
if (focalPointRatio != 0) {
((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio;
}
lineStyle.fillType = fillStyle;
}
@Override
public void lineBitmapStyle(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1);
lineStyle.hasFillFlag = true;
FILLSTYLE fillStyle = new FILLSTYLE();
if (repeat) {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP;
}
} else {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP;
}
}
fillStyle.bitmapMatrix = matrix.toMATRIX();
fillStyle.bitmapId = bitmapId;
lineStyle.fillType = fillStyle;
}
@Override
public void moveTo(double x, double y) {
int backupFillStyle = currentFillStyle;
int backupLineStyle = currentLineStyle;
endCurrent();
currentFillStyle = backupFillStyle;
currentLineStyle = backupLineStyle;
lastX = (int) x;
lastY = (int) y;
//currentPointsPos.add(0.0);
}
@Override
public void lineTo(double x, double y) {
if (x == lastX && y == lastY) {
return;
}
BezierEdge be = new BezierEdge(lastX, lastY, x, y);
currentPointsSum.x += x; // - lastX;
currentPointsSum.y += y; // - lastY;
currentShape.add(be);
lastX = x;
lastY = y;
currentBezierLengths.add(be.length());
currentShapeLen += be.length();
currentSegmentCount++;
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
if (anchorX == lastX && anchorY == lastY) {
return;
}
BezierEdge be = new BezierEdge(lastX, lastY, controlX, controlY, anchorX, anchorY);
currentShape.add(be);
currentPointsSum.x += anchorX; // - lastX;
currentPointsSum.y += anchorY; // - lastY;
lastX = anchorX;
lastY = anchorY;
currentBezierLengths.add(be.length());
currentShapeLen += be.length();
currentSegmentCount++;
}
private double roundPct(double pct) {
double precision = 1000000d;
return Math.round(pct * precision) / precision;
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.importers.morphshape;
/**
*
* @author JPEXS
*/
public class StyleMismatchException extends Exception {
}

View File

@@ -226,6 +226,11 @@ public class SvgImporter {
transform = Matrix.getScaleInstance(ratioX, ratioY);
transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY);
}
transform = transform.preConcatenate(Matrix.getTranslateInstance(-viewBox.x, -viewBox.y));
if (viewBox.height != 0 && viewBox.width != 0) {
transform = transform.preConcatenate(Matrix.getScaleInstance(width / viewBox.width, height / viewBox.height));
}
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape);
} catch (SAXException | IOException | ParserConfigurationException ex) {

View File

@@ -19,14 +19,16 @@ package com.jpexs.decompiler.flash.math;
import com.jpexs.helpers.Reference;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
*
* @author JPEXS
*/
public class BezierEdge {
public class BezierEdge implements Serializable {
public List<Point2D> points = new ArrayList<>();
@@ -45,6 +47,14 @@ public class BezierEdge {
points.add(new Point2D.Double(x1, y1));
}
public Point2D getBeginPoint() {
return points.get(0);
}
public Point2D getEndPoint() {
return points.get(points.size() - 1);
}
public Point2D pointAt(double t) {
if (points.size() == 2) {
double x = (1 - t) * points.get(0).getX() + t * points.get(1).getX();
@@ -189,6 +199,30 @@ public class BezierEdge {
}
return ok;
}
public double length() {
double distance = 0;
double epsilon = 1;
Stack<BezierEdge> parts = new Stack<BezierEdge>();
parts.push(this);
while (!parts.isEmpty()) {
BezierEdge curve = parts.pop();
double d = curve.points.get(0).distance(curve.points.get(curve.points.size() - 1));
if (d < epsilon) {
distance += d;
} else {
Reference<BezierEdge> leftRef = new Reference<>(null);
Reference<BezierEdge> rightRef = new Reference<>(null);
curve.split(0.5, leftRef, rightRef);
parts.add(leftRef.getVal());
parts.add(rightRef.getVal());
}
}
return distance;
}
@Override
public String toString() {
@@ -207,6 +241,14 @@ public class BezierEdge {
left.setVal(new BezierEdge(leftPoints));
right.setVal(new BezierEdge(rightPoints));
}
public BezierEdge reverse() {
List<Point2D> revPoints = new ArrayList<>();
for (int i = points.size() - 1; i >= 0; i--) {
revPoints.add(points.get(i));
}
return new BezierEdge(revPoints);
}
public static void main(String[] args) {
List<Double> t1 = new ArrayList<>();
@@ -245,6 +287,10 @@ public class BezierEdge {
);
System.out.println("Intersection point: " + c);
BezierEdge be = new BezierEdge(0, 0, 100, 50, 0, 100);
System.out.println("be5.dist: " + be.length());
//Rectangle2D out = new Rectangle2D.Double();
//rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out);

View File

@@ -84,7 +84,7 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject {
public abstract int getShapeNum();
public SHAPEWITHSTYLE getShapes() {
public synchronized SHAPEWITHSTYLE getShapes() {
if (shapes == null && shapeData != null) {
try {
SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength());

View File

@@ -151,6 +151,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
gradient = g;
}
}
}
public boolean isCompatibleFillStyle(FILLSTYLE otherFillStyle) {
if (fillStyleType != otherFillStyle.fillStyleType) {
return false;
}
switch (fillStyleType) {
case CLIPPED_BITMAP:
case NON_SMOOTHED_CLIPPED_BITMAP:
case NON_SMOOTHED_REPEATING_BITMAP:
case REPEATING_BITMAP:
if (bitmapId != otherFillStyle.bitmapId) {
return false;
}
break;
case LINEAR_GRADIENT:
case RADIAL_GRADIENT:
case FOCAL_RADIAL_GRADIENT:
if (!gradient.isCompatibleGradient(otherFillStyle.gradient)) {
return false;
}
break;
}
return true;
}
public MORPHFILLSTYLE toMorphStyle() {
@@ -171,4 +195,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
return morphFillStyle;
}
public MORPHFILLSTYLE toMorphStyle(FILLSTYLE endFillStyle) {
if (!isCompatibleFillStyle(endFillStyle)) {
return null;
}
MORPHFILLSTYLE morphFillStyle = new MORPHFILLSTYLE();
morphFillStyle.bitmapId = bitmapId;
if (bitmapMatrix != null) {
morphFillStyle.startBitmapMatrix = new MATRIX(bitmapMatrix);
}
if (endFillStyle.bitmapMatrix != null) {
morphFillStyle.endBitmapMatrix = new MATRIX(endFillStyle.bitmapMatrix);
}
if (color != null) {
morphFillStyle.startColor = new RGBA(color);
}
if (endFillStyle.color != null) {
morphFillStyle.endColor = new RGBA(endFillStyle.color);
}
morphFillStyle.fillStyleType = fillStyleType;
if (gradient != null) {
morphFillStyle.gradient = gradient.toMorphGradient(endFillStyle.gradient);
}
return morphFillStyle;
}
}

View File

@@ -61,7 +61,20 @@ public class GRADIENT implements Serializable {
public static final int INTERPOLATION_RESERVED2 = 3;
@SWFArray(value = "record")
public GRADRECORD[] gradientRecords = new GRADRECORD[0];
public GRADRECORD[] gradientRecords = new GRADRECORD[0];
public boolean isCompatibleGradient(GRADIENT otherGradient) {
if (interpolationMode != otherGradient.interpolationMode) {
return false;
}
if (spreadMode != otherGradient.spreadMode) {
return false;
}
if (gradientRecords.length != otherGradient.gradientRecords.length) {
return false;
}
return true;
}
public MORPHGRADIENT toMorphGradient() {
MORPHGRADIENT morphGradient = new MORPHGRADIENT();
@@ -73,4 +86,18 @@ public class GRADIENT implements Serializable {
}
return morphGradient;
}
public MORPHGRADIENT toMorphGradient(GRADIENT endGradient) {
if (!isCompatibleGradient(endGradient)) {
return null;
}
MORPHGRADIENT morphGradient = new MORPHGRADIENT();
morphGradient.interPolationMode = interpolationMode;
morphGradient.spreadMode = spreadMode;
morphGradient.gradientRecords = new MORPHGRADRECORD[gradientRecords.length];
for (int i = 0; i < gradientRecords.length; i++) {
morphGradient.gradientRecords[i] = gradientRecords[i].toMorphGradRecord();
}
return morphGradient;
}
}

View File

@@ -46,4 +46,13 @@ public class GRADRECORD implements Serializable {
morphGradRecord.endRatio = ratio;
return morphGradRecord;
}
public MORPHGRADRECORD toMorphGradRecord(GRADRECORD endGradRecord) {
MORPHGRADRECORD morphGradRecord = new MORPHGRADRECORD();
morphGradRecord.startColor = new RGBA(color);
morphGradRecord.endColor = new RGBA(endGradRecord.color);
morphGradRecord.startRatio = ratio;
morphGradRecord.endRatio = endGradRecord.ratio;
return morphGradRecord;
}
}

View File

@@ -141,6 +141,44 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
@Override
public void setWidth(int width) {
this.width = width;
}
public boolean isCompatibleLineStyle(LINESTYLE2 otherLineStyle) {
if (startCapStyle != otherLineStyle.startCapStyle) {
return false;
}
if (endCapStyle != otherLineStyle.endCapStyle) {
return false;
}
if (joinStyle != otherLineStyle.joinStyle) {
return false;
}
if (hasFillFlag != otherLineStyle.hasFillFlag) {
return false;
}
if (noVScaleFlag != otherLineStyle.noVScaleFlag) {
return false;
}
if (pixelHintingFlag != otherLineStyle.pixelHintingFlag) {
return false;
}
if (noClose != otherLineStyle.noClose) {
return false;
}
if (miterLimitFactor != otherLineStyle.miterLimitFactor) {
return false;
}
if (hasFillFlag) {
if (!fillType.isCompatibleFillStyle(otherLineStyle.fillType)) {
return false;
}
}
return true;
}
public MORPHLINESTYLE2 toMorphLineStyle2() {
@@ -165,4 +203,33 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
}
return morphLineStyle2;
}
public MORPHLINESTYLE2 toMorphLineStyle2(LINESTYLE2 endLineStyle) {
if (!isCompatibleLineStyle(endLineStyle)) {
return null;
}
MORPHLINESTYLE2 morphLineStyle2 = new MORPHLINESTYLE2();
morphLineStyle2.startWidth = width;
morphLineStyle2.endWidth = endLineStyle.width;
morphLineStyle2.startCapStyle = startCapStyle;
morphLineStyle2.joinStyle = joinStyle;
morphLineStyle2.hasFillFlag = hasFillFlag;
morphLineStyle2.noHScaleFlag = noHScaleFlag;
morphLineStyle2.noVScaleFlag = noVScaleFlag;
morphLineStyle2.pixelHintingFlag = pixelHintingFlag;
morphLineStyle2.noClose = noClose;
morphLineStyle2.endCapStyle = endCapStyle;
morphLineStyle2.miterLimitFactor = miterLimitFactor;
if (color != null) {
morphLineStyle2.startColor = new RGBA(color);
}
if (endLineStyle.color != null) {
morphLineStyle2.endColor = new RGBA(endLineStyle.color);
}
if (hasFillFlag) {
morphLineStyle2.fillType = fillType.toMorphStyle(endLineStyle.fillType);
}
return morphLineStyle2;
}
}