Added Replacing morphshapes (currently only same shape for start/end)

Fixed miterLimitFactor is FIXED8 value in MORPHLINESTYLE2
This commit is contained in:
Jindra Petřík
2023-10-22 14:49:17 +02:00
parent 194e358740
commit 247b1c17a4
27 changed files with 506 additions and 87 deletions

View File

@@ -3221,7 +3221,7 @@ public class SWFInputStream implements AutoCloseable {
ret.noClose = (int) readUB(1, "noClose") == 1;
ret.endCapStyle = (int) readUB(2, "endCapStyle");
if (ret.joinStyle == LINESTYLE2.MITER_JOIN) {
ret.miterLimitFactor = readUI16("miterLimitFactor");
ret.miterLimitFactor = readFIXED8("miterLimitFactor");
}
if (!ret.hasFillFlag) {
ret.startColor = readRGBA("startColor");

View File

@@ -1773,7 +1773,7 @@ public class SWFOutputStream extends OutputStream {
writeUB(1, value.noClose ? 1 : 0);
writeUB(2, value.endCapStyle);
if (value.joinStyle == LINESTYLE2.MITER_JOIN) {
writeUI16(value.miterLimitFactor);
writeFIXED8(value.miterLimitFactor);
}
if (!value.hasFillFlag) {
writeRGBA(value.startColor);

View File

@@ -967,6 +967,11 @@ 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;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -31,6 +31,7 @@ import com.jpexs.decompiler.flash.tags.DefineShapeTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.types.RECT;
@@ -57,22 +58,55 @@ import java.util.logging.Logger;
public class ShapeImporter {
public Tag importImage(ShapeTag st, byte[] newData) throws IOException {
return importImage(st, newData, 0, true);
return importImage((Tag) st, newData, 0, true);
}
public Tag importImage(MorphShapeTag mst, byte[] newData) throws IOException {
return importImage((Tag) mst, newData, 0, true);
}
public Tag importImage(MorphShapeTag mst, byte[] newData, int tagType, boolean fill) throws IOException {
return importImage((Tag) mst, newData, tagType, fill);
}
public Tag importImage(ShapeTag st, byte[] newData, int tagType, boolean fill) throws IOException {
return importImage((Tag) st, newData, tagType, fill);
}
private Tag importImage(Tag st, byte[] newData, int tagType, boolean fill) throws IOException {
ImageTag imageTag = addImage(st, newData, tagType);
st.setModified(true);
RECT rect = st.getRect();
RECT rect = null;
int shapeNum = 0;
if (st instanceof ShapeTag) {
rect = ((ShapeTag) st).getRect();
shapeNum = ((ShapeTag) st).getShapeNum();
}
if (st instanceof MorphShapeTag) {
rect = ((MorphShapeTag) st).getRect();
int morphShapeNum = ((MorphShapeTag) st).getShapeNum();
if (morphShapeNum == 2) {
shapeNum = 4;
} else {
shapeNum = 3;
}
}
if (!fill) {
Dimension dimension = imageTag.getImageDimension();
rect.Xmax = rect.Xmin + (int) (SWF.unitDivisor * dimension.getWidth());
rect.Ymax = rect.Ymin + (int) (SWF.unitDivisor * dimension.getHeight());
}
SHAPEWITHSTYLE shapes = imageTag.getShape(rect, fill, st.getShapeNum());
st.shapes = shapes;
SHAPEWITHSTYLE shapes = imageTag.getShape(rect, fill, shapeNum);
if (st instanceof ShapeTag) {
ShapeTag shapeTag = (ShapeTag) st;
shapeTag.shapes = shapes;
}
if (st instanceof MorphShapeTag) {
MorphShapeTag morphShapeTag = (MorphShapeTag) st;
shapes.updateMorphShapeTag(morphShapeTag, fill);
}
return (Tag) st;
}

View File

@@ -28,9 +28,11 @@ import com.jpexs.decompiler.flash.importers.ShapeImporter;
import com.jpexs.decompiler.flash.importers.svg.css.CssParseException;
import com.jpexs.decompiler.flash.importers.svg.css.CssParser;
import com.jpexs.decompiler.flash.importers.svg.css.CssSelectorToXPath;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
@@ -91,16 +93,33 @@ public class SvgImporter {
private final Set<String> shownWarnings = new HashSet<>();
ShapeTag shapeTag;
/**
* Shape or morphshape tag
*/
Tag shapeTag;
private Rectangle2D.Double viewBox;
public Tag importSvg(ShapeTag st, String svgXml) {
return importSvg(st, svgXml, true);
return importSvg((Tag) st, svgXml, true);
}
public Tag importSvg(MorphShapeTag mst, String svgXml) {
return importSvg((Tag) mst, svgXml, true);
}
public Tag importSvg(ShapeTag st, String svgXml, boolean fill) {
return importSvg((Tag) st, svgXml, fill);
}
public Tag importSvg(MorphShapeTag mst, String svgXml, boolean fill) {
return importSvg((Tag) mst, svgXml, fill);
}
private Tag importSvg(Tag st, String svgXml, boolean fill) {
shapeTag = st;
boolean morphShape = st instanceof MorphShapeTag;
if (st instanceof DefineShape4Tag) {
DefineShape4Tag shape4 = (DefineShape4Tag) st;
@@ -115,8 +134,23 @@ public class SvgImporter {
shapes.fillStyles.fillStyles = new FILLSTYLE[0];
shapes.lineStyles.lineStyles = new LINESTYLE[0];
int shapeNum = st.getShapeNum();
RECT rect = st.getRect();
int shapeNum = 0;
RECT rect = null;
if (st instanceof ShapeTag) {
shapeNum = ((ShapeTag) st).getShapeNum();
rect = ((ShapeTag) st).getRect();
}
if (st instanceof MorphShapeTag) {
int morphShapeNum = ((MorphShapeTag) st).getShapeNum();
if (morphShapeNum == 2) {
shapeNum = 4;
} else {
shapeNum = 3;
}
rect = ((MorphShapeTag) st).getRect();
}
int origXmin = rect.Xmin;
int origYmin = rect.Ymin;
@@ -193,17 +227,24 @@ public class SvgImporter {
transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY);
}
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style);
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape);
} catch (SAXException | IOException | ParserConfigurationException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex);
}
shapes.shapeRecords.add(new EndShapeRecord());
st.shapes = shapes;
if (!fill) {
st.updateBounds();
if (st instanceof ShapeTag) {
ShapeTag shape = (ShapeTag) st;
shape.shapes = shapes;
if (!fill) {
shape.updateBounds();
}
}
if (st instanceof MorphShapeTag) {
shapes.updateMorphShapeTag((MorphShapeTag) st, fill);
}
st.setModified(true);
return (Tag) st;
@@ -272,7 +313,7 @@ public class SvgImporter {
}
}
private void processSwitch(Element element, Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style) {
private void processSwitch(Element element, Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, boolean morphShape) {
for (int i = 0; i < element.getChildNodes().getLength(); i++) {
Node childNode = element.getChildNodes().item(i);
if (childNode instanceof Element) {
@@ -283,18 +324,18 @@ public class SvgImporter {
if (childElement.hasAttribute("systemLanguage")) {
String systemLanguage = childElement.getAttribute("systemLanguage");
if (systemLanguage.equals("en-us") || systemLanguage.equals("en")) {
processElement(childElement, idMap, shapeNum, shapes, transform, style);
processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape);
return;
}
continue;
}
processElement(childElement, idMap, shapeNum, shapes, transform, style);
processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape);
return;
}
}
}
private void processElement(Element element, Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style) {
private void processElement(Element element, Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, boolean morphShape) {
if (element.hasAttribute("requiredExtensions") && !element.getAttribute("requiredExtensions").isEmpty()) {
return;
}
@@ -303,25 +344,25 @@ public class SvgImporter {
Matrix m = Matrix.parseSvgMatrix(element.getAttribute("transform"), 1, 1);
Matrix m2 = m == null ? transform : transform.concatenate(m);
if ("switch".equals(tagName)) {
processSwitch(element, idMap, shapeNum, shapes, transform, style);
processSwitch(element, idMap, shapeNum, shapes, transform, style, morphShape);
} else if ("style".equals(tagName)) {
processStyle(element);
} else if ("g".equals(tagName)) {
processSvgObject(idMap, shapeNum, shapes, element, m2, newStyle);
processSvgObject(idMap, shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("path".equals(tagName)) {
processPath(shapeNum, shapes, element, m2, newStyle);
processPath(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("circle".equals(tagName)) {
processCircle(shapeNum, shapes, element, m2, newStyle);
processCircle(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("ellipse".equals(tagName)) {
processEllipse(shapeNum, shapes, element, m2, newStyle);
processEllipse(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("rect".equals(tagName)) {
processRect(shapeNum, shapes, element, m2, newStyle);
processRect(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("line".equals(tagName)) {
processLine(shapeNum, shapes, element, m2, newStyle);
processLine(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("polyline".equals(tagName)) {
processPolyline(shapeNum, shapes, element, m2, newStyle);
processPolyline(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("polygon".equals(tagName)) {
processPolygon(shapeNum, shapes, element, m2, newStyle);
processPolygon(shapeNum, shapes, element, m2, newStyle, morphShape);
} else if ("defs".equals(tagName)) {
processDefs(element);
} else if ("title".equals(tagName) || "desc".equals(tagName)
@@ -332,12 +373,12 @@ public class SvgImporter {
}
}
private void processSvgObject(Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style) {
private void processSvgObject(Map<String, Element> idMap, int shapeNum, SHAPEWITHSTYLE shapes, Element element, Matrix transform, SvgStyle style, boolean morphShape) {
for (int i = 0; i < element.getChildNodes().getLength(); i++) {
Node childNode = element.getChildNodes().item(i);
if (childNode instanceof Element) {
Element childElement = (Element) childNode;
processElement(childElement, idMap, shapeNum, shapes, transform, style);
processElement(childElement, idMap, shapeNum, shapes, transform, style, morphShape);
}
}
}
@@ -372,7 +413,7 @@ public class SvgImporter {
}
}
private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List<PathCommand> commands, Matrix transform, SvgStyle style) {
private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List<PathCommand> commands, Matrix transform, SvgStyle style, boolean morphShape) {
if ("nonzero".equals(style.getFillRule())) {
if (shapeTag instanceof DefineShape4Tag) {
@@ -387,14 +428,18 @@ public class SvgImporter {
double x0 = 0;
double y0 = 0;
StyleChangeRecord scrStyle = getStyleChangeRecord(shapeNum, style);
int fillStyle = scrStyle.fillStyle1;
StyleChangeRecord scrStyle = getStyleChangeRecord(shapeNum, style, morphShape);
int fillStyle = morphShape ? scrStyle.fillStyle0 : scrStyle.fillStyle1;
int lineStyle = scrStyle.lineStyle;
scrStyle.stateFillStyle0 = true;
scrStyle.stateFillStyle1 = true;
if (!morphShape) {
scrStyle.stateFillStyle1 = true;
}
scrStyle.stateLineStyle = true;
scrStyle.fillStyle0 = 0;
scrStyle.fillStyle1 = 0;
if (!morphShape) {
scrStyle.fillStyle1 = 0;
}
scrStyle.lineStyle = 0;
List<SHAPERECORD> newRecords = new ArrayList<>();
@@ -428,8 +473,13 @@ public class SvgImporter {
StyleChangeRecord scr = new StyleChangeRecord();
if (fillStyle != 0) {
scr.stateFillStyle1 = true;
scr.fillStyle1 = fillStyle;
if (morphShape) {
scr.stateFillStyle0 = true;
scr.fillStyle0 = fillStyle;
} else {
scr.stateFillStyle1 = true;
scr.fillStyle1 = fillStyle;
}
}
if (lineStyle != 0) {
scr.lineStyle = lineStyle;
@@ -584,7 +634,7 @@ public class SvgImporter {
shapes.shapeRecords.addAll(newRecords);
}
private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
String data = childElement.getAttribute("d");
char command = 0;
@@ -889,7 +939,7 @@ public class SvgImporter {
// ignore remaining data as specified in SVG Specification F.2 Error processing
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape);
}
private double calcAngle(double ux, double uy, double vx, double vy) {
@@ -903,7 +953,7 @@ public class SvgImporter {
return sign * Math.acos(ux * vx + uy * vy / (lu * lv));
}
private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
private void processCircle(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
String attr = childElement.getAttribute("cx");
double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0;
@@ -913,10 +963,10 @@ public class SvgImporter {
attr = childElement.getAttribute("r");
double r = attr.length() > 0 ? parseLength(attr, (viewBox.width + viewBox.height) / 2) : 0;
processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r);
processEllipse(shapeNum, shapes, transform, style, cx, cy, r, r, morphShape);
}
private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
String attr = childElement.getAttribute("cx");
double cx = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0;
@@ -929,10 +979,10 @@ public class SvgImporter {
attr = childElement.getAttribute("ry");
double ry = attr.length() > 0 ? parseLength(attr, viewBox.height) : 0;
processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry);
processEllipse(shapeNum, shapes, transform, style, cx, cy, rx, ry, morphShape);
}
private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry) {
private void processEllipse(int shapeNum, SHAPEWITHSTYLE shapes, Matrix transform, SvgStyle style, double cx, double cy, double rx, double ry, boolean morphShape) {
double sqrt2RXHalf = Math.sqrt(2) * rx / 2;
double sqrt2Minus1RX = (Math.sqrt(2) - 1) * rx;
double sqrt2RYHalf = Math.sqrt(2) * ry / 2;
@@ -987,10 +1037,10 @@ public class SvgImporter {
serz.command = 'Z';
pathCommands.add(serz);
processCommands(shapeNum, shapes, pathCommands, transform, style);
processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape);
}
private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
private void processRect(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
String attr = childElement.getAttribute("x");
double x = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0;
@@ -1098,10 +1148,10 @@ public class SvgImporter {
serz.command = 'Z';
pathCommands.add(serz);
processCommands(shapeNum, shapes, pathCommands, transform, style);
processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape);
}
private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
private void processLine(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
String attr = childElement.getAttribute("x1");
double x1 = attr.length() > 0 ? parseCoordinate(attr, viewBox.width) : 0;
@@ -1126,18 +1176,18 @@ public class SvgImporter {
pathCommands.add(cer);
processCommands(shapeNum, shapes, pathCommands, transform, style);
processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape);
}
private void processPolygon(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
processPolyline(shapeNum, shapes, childElement, transform, style, true);
private void processPolygon(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
processPolyline(shapeNum, shapes, childElement, transform, style, true, morphShape);
}
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
processPolyline(shapeNum, shapes, childElement, transform, style, false);
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean morphShape) {
processPolyline(shapeNum, shapes, childElement, transform, style, false, morphShape);
}
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean close) {
private void processPolyline(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style, boolean close, boolean morphShape) {
String data = childElement.getAttribute("points");
char command = 'M';
@@ -1187,7 +1237,7 @@ public class SvgImporter {
pathCommands.add(serz);
}
processCommands(shapeNum, shapes, pathCommands, transform, style);
processCommands(shapeNum, shapes, pathCommands, transform, style, morphShape);
}
//Stub for w3 test. TODO: refactor and move to test directory. It's here because of easy access - compiling single file
@@ -1671,12 +1721,16 @@ public class SvgImporter {
}
}
private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style) {
private StyleChangeRecord getStyleChangeRecord(int shapeNum, SvgStyle style, boolean morphShape) {
StyleChangeRecord scr = new StyleChangeRecord();
scr.stateNewStyles = true;
scr.fillStyles = new FILLSTYLEARRAY();
scr.stateFillStyle1 = true;
if (morphShape) {
scr.stateFillStyle0 = true;
} else {
scr.stateFillStyle1 = true;
}
scr.stateLineStyle = true;
SvgFill fill = style.getFillWithOpacity();
if (fill != null && fill != SvgTransparentFill.INSTANCE) {
@@ -1690,10 +1744,18 @@ public class SvgImporter {
//...apply in second step - applyStyleGradients
}
scr.fillStyle1 = 1;
if (morphShape) {
scr.fillStyle0 = 1;
} else {
scr.fillStyle1 = 1;
}
} else {
scr.fillStyles.fillStyles = new FILLSTYLE[0];
scr.fillStyle1 = 0;
if (morphShape) {
scr.fillStyle0 = 0;
} else {
scr.fillStyle1 = 0;
}
}
scr.lineStyles = new LINESTYLEARRAY();
@@ -1717,11 +1779,19 @@ public class SvgImporter {
DefineShape4Tag shape4 = (DefineShape4Tag) shapeTag;
shape4.usesNonScalingStrokes = true;
}
if (shapeTag instanceof DefineMorphShape2Tag) {
DefineMorphShape2Tag morph2 = (DefineMorphShape2Tag) shapeTag;
morph2.usesNonScalingStrokes = true;
}
} else {
if (shapeTag instanceof DefineShape4Tag) {
DefineShape4Tag shape4 = (DefineShape4Tag) shapeTag;
shape4.usesScalingStrokes = true;
}
if (shapeTag instanceof DefineMorphShape2Tag) {
DefineMorphShape2Tag morph2 = (DefineMorphShape2Tag) shapeTag;
morph2.usesScalingStrokes = true;
}
}
int swfCap = lineCap == SvgLineCap.BUTT ? LINESTYLE2.NO_CAP

View File

@@ -152,4 +152,23 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
}
}
}
public MORPHFILLSTYLE toMorphStyle() {
MORPHFILLSTYLE morphFillStyle = new MORPHFILLSTYLE();
morphFillStyle.bitmapId = bitmapId;
if (bitmapMatrix != null) {
morphFillStyle.startBitmapMatrix = new MATRIX(bitmapMatrix);
morphFillStyle.endBitmapMatrix = new MATRIX(bitmapMatrix);
}
if (color != null) {
morphFillStyle.startColor = new RGBA(color);
morphFillStyle.endColor = new RGBA(color);
}
morphFillStyle.fillStyleType = fillStyleType;
if (gradient != null) {
morphFillStyle.gradient = gradient.toMorphGradient();
}
return morphFillStyle;
}
}

View File

@@ -55,4 +55,13 @@ public class FILLSTYLEARRAY implements NeedsCharacters, Serializable {
}
return modified;
}
public MORPHFILLSTYLEARRAY toMorphFillStyleArray() {
MORPHFILLSTYLEARRAY morphFillStyleArray = new MORPHFILLSTYLEARRAY();
morphFillStyleArray.fillStyles = new MORPHFILLSTYLE[fillStyles.length];
for (int i = 0; i < fillStyles.length; i++) {
morphFillStyleArray.fillStyles[i] = fillStyles[i].toMorphStyle();
}
return morphFillStyleArray;
}
}

View File

@@ -62,4 +62,15 @@ public class GRADIENT implements Serializable {
@SWFArray(value = "record")
public GRADRECORD[] gradientRecords = new GRADRECORD[0];
public MORPHGRADIENT toMorphGradient() {
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

@@ -37,4 +37,13 @@ public class GRADRECORD implements Serializable {
public float getRatioFloat() {
return ((float) ratio) / 255.0f;
}
public MORPHGRADRECORD toMorphGradRecord() {
MORPHGRADRECORD morphGradRecord = new MORPHGRADRECORD();
morphGradRecord.startColor = new RGBA(color);
morphGradRecord.endColor = new RGBA(color);
morphGradRecord.startRatio = ratio;
morphGradRecord.endRatio = ratio;
return morphGradRecord;
}
}

View File

@@ -75,4 +75,13 @@ public class LINESTYLE implements NeedsCharacters, Serializable, ILINESTYLE {
public void setWidth(int width) {
this.width = width;
}
public MORPHLINESTYLE toMorphLineStyle() {
MORPHLINESTYLE morphLineStyle = new MORPHLINESTYLE();
morphLineStyle.startColor = new RGBA(color);
morphLineStyle.endColor = new RGBA(color);
morphLineStyle.startWidth = width;
morphLineStyle.endWidth = width;
return morphLineStyle;
}
}

View File

@@ -142,4 +142,27 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
public void setWidth(int width) {
this.width = width;
}
public MORPHLINESTYLE2 toMorphLineStyle2() {
MORPHLINESTYLE2 morphLineStyle2 = new MORPHLINESTYLE2();
morphLineStyle2.startWidth = width;
morphLineStyle2.endWidth = 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);
morphLineStyle2.endColor = new RGBA(color);
}
if (fillType != null) {
morphLineStyle2.fillType = fillType.toMorphStyle();
}
return morphLineStyle2;
}
}

View File

@@ -87,4 +87,21 @@ public class LINESTYLEARRAY implements NeedsCharacters, Serializable {
}
return modified;
}
public MORPHLINESTYLEARRAY toMorphLineStyleArray() {
MORPHLINESTYLEARRAY morphLineStyleArray = new MORPHLINESTYLEARRAY();
if (lineStyles != null) {
morphLineStyleArray.lineStyles = new MORPHLINESTYLE[lineStyles.length];
for (int i = 0; i < lineStyles.length; i++) {
morphLineStyleArray.lineStyles[i] = lineStyles[i].toMorphLineStyle();
}
}
if (lineStyles2 != null) {
morphLineStyleArray.lineStyles2 = new MORPHLINESTYLE2[lineStyles2.length];
for (int i = 0; i < lineStyles2.length; i++) {
morphLineStyleArray.lineStyles2[i] = lineStyles2[i].toMorphLineStyle2();
}
}
return morphLineStyleArray;
}
}

View File

@@ -78,9 +78,9 @@ public class MORPHLINESTYLE2 implements Serializable {
public static final int SQUARE_CAP = 2;
@SWFType(value = BasicType.UI16)
@SWFType(value = BasicType.FIXED8)
@Conditional(value = "joinStyle", options = {MITER_JOIN})
public int miterLimitFactor;
public float miterLimitFactor;
@Conditional("!hasFillFlag")
public RGBA startColor;

View File

@@ -46,6 +46,12 @@ public class RGB implements Serializable {
@SWFType(BasicType.UI8)
public int blue;
public RGB(RGB color) {
this.red = color.red;
this.green = color.green;
this.blue = color.blue;
}
public RGB(int red, int green, int blue) {
this.red = red;
this.green = green;

View File

@@ -72,6 +72,15 @@ public class RGBA extends RGB implements Serializable {
super(rgb);
alpha = (rgb >> 24) & 0xFF;
}
public RGBA(RGB color) {
super(color);
if (color instanceof RGBA) {
alpha = ((RGBA) color).alpha;
} else {
alpha = 255;
}
}
public RGBA() {
}

View File

@@ -17,9 +17,13 @@
package com.jpexs.decompiler.flash.types;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import com.jpexs.helpers.Helper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -104,4 +108,129 @@ public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializab
return SHAPERECORD.getBounds(shapeRecords, lineStyles, shapeNum, false);
}
public void updateMorphShapeTag(MorphShapeTag morphShapeTag, boolean fill) {
morphShapeTag.startEdges.shapeRecords.clear();
morphShapeTag.endEdges.shapeRecords.clear();
FILLSTYLEARRAY mergedFillStyles = new FILLSTYLEARRAY();
LINESTYLEARRAY mergedLineStyles = new LINESTYLEARRAY();
List<FILLSTYLE> mergedFillStyleList = new ArrayList<>();
List<LINESTYLE> mergedLineStyleList = new ArrayList<>();
List<LINESTYLE2> mergedLineStyle2List = new ArrayList<>();
int lastFillCount = fillStyles.fillStyles.length;
for (int i = 0; i < fillStyles.fillStyles.length; i++) {
mergedFillStyleList.add(fillStyles.fillStyles[i]);
}
int lastLineCount = 0;
if (lineStyles.lineStyles != null) {
lastLineCount = lineStyles.lineStyles.length;
for (int i = 0; i < lineStyles.lineStyles.length; i++) {
mergedLineStyleList.add(lineStyles.lineStyles[i]);
}
}
if (lineStyles.lineStyles2 != null) {
lastLineCount = lineStyles.lineStyles2.length;
for (int i = 0; i < lineStyles.lineStyles2.length; i++) {
mergedLineStyle2List.add(lineStyles.lineStyles2[i]);
}
}
int fillOffset = 0;
int lineOffset = 0;
List<SHAPERECORD> newShapeRecords = new ArrayList<>();
for (int r = 0; r < shapeRecords.size(); r++) {
SHAPERECORD rec = shapeRecords.get(r);
rec = Helper.deepCopy(rec);
if (rec instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) rec;
if (scr.stateNewStyles) {
for (int i = 0; i < scr.fillStyles.fillStyles.length; i++) {
mergedFillStyleList.add(scr.fillStyles.fillStyles[i]);
}
fillOffset += lastFillCount;
lastFillCount = scr.fillStyles.fillStyles.length;
if (scr.lineStyles.lineStyles != null) {
for (int i = 0; i < scr.lineStyles.lineStyles.length; i++) {
mergedLineStyleList.add(scr.lineStyles.lineStyles[i]);
}
lineOffset += lastLineCount;
lastLineCount = scr.lineStyles.lineStyles.length;
}
if (scr.lineStyles.lineStyles2 != null) {
for (int i = 0; i < scr.lineStyles.lineStyles2.length; i++) {
mergedLineStyle2List.add(scr.lineStyles.lineStyles2[i]);
}
lineOffset += lastLineCount;
lastLineCount = scr.lineStyles.lineStyles2.length;
}
scr.stateNewStyles = false;
}
if (scr.stateFillStyle0) {
scr.fillStyle0 += fillOffset;
}
if (scr.stateFillStyle1) {
scr.fillStyle1 += fillOffset;
}
if (scr.stateLineStyle) {
scr.lineStyle += lineOffset;
}
}
newShapeRecords.add(rec);
}
mergedFillStyles.fillStyles = new FILLSTYLE[mergedFillStyleList.size()];
for (int i = 0; i < mergedFillStyleList.size(); i++) {
mergedFillStyles.fillStyles[i] = mergedFillStyleList.get(i);
}
mergedLineStyles.lineStyles = new LINESTYLE[mergedLineStyleList.size()];
for (int i = 0; i < mergedLineStyleList.size(); i++) {
mergedLineStyles.lineStyles[i] = mergedLineStyleList.get(i);
}
mergedLineStyles.lineStyles2 = new LINESTYLE2[mergedLineStyle2List.size()];
for (int i = 0; i < mergedLineStyle2List.size(); i++) {
mergedLineStyles.lineStyles2[i] = mergedLineStyle2List.get(i);
}
morphShapeTag.morphFillStyles = mergedFillStyles.toMorphFillStyleArray();
morphShapeTag.morphLineStyles = mergedLineStyles.toMorphLineStyleArray();
SHAPE startShapes = new SHAPE();
startShapes.numFillBits = SWFOutputStream.getNeededBitsU(mergedFillStyleList.size());
startShapes.numLineBits = SWFOutputStream.getNeededBitsU(mergedLineStyleList.size() + mergedLineStyle2List.size());
startShapes.shapeRecords = newShapeRecords;
morphShapeTag.startEdges = startShapes;
SHAPE endShapes = new SHAPE();
endShapes.numFillBits = 0;
endShapes.numLineBits = 0;
List<SHAPERECORD> endRecords = new ArrayList<>();
for (int i = 0; i < newShapeRecords.size(); i++) {
SHAPERECORD rec = newShapeRecords.get(i);
if (rec instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) rec;
if (scr.stateMoveTo) {
StyleChangeRecord nscr = new StyleChangeRecord();
nscr.stateMoveTo = true;
nscr.moveDeltaX = scr.moveDeltaX;
nscr.moveDeltaY = scr.moveDeltaY;
nscr.calculateBits();
endRecords.add(nscr);
}
} else {
endRecords.add(Helper.deepCopy(rec));
}
}
endShapes.shapeRecords = endRecords;
morphShapeTag.endEdges = endShapes;
if (!fill) {
morphShapeTag.updateBounds();
}
}
}