diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index 66bfc8b38..55a78e147 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -1871,7 +1871,7 @@ public class SWFInputStream extends InputStream { GRADRECORD ret = new GRADRECORD(); ret.ratio = readUI8(); if (shapeNum >= 3) { - ret.colorA = readRGBA(); + ret.color = readRGBA(); } else { ret.color = readRGB(); } @@ -1888,7 +1888,7 @@ public class SWFInputStream extends InputStream { public GRADIENT readGRADIENT(int shapeNum) throws IOException { GRADIENT ret = new GRADIENT(); ret.spreadMode = (int) readUB(2); - ret.interPolationMode = (int) readUB(2); + ret.interpolationMode = (int) readUB(2); int numGradients = (int) readUB(4); ret.gradientRecords = new GRADRECORD[numGradients]; for (int i = 0; i < numGradients; i++) { @@ -1908,7 +1908,7 @@ public class SWFInputStream extends InputStream { public FOCALGRADIENT readFOCALGRADIENT(int shapeNum) throws IOException { FOCALGRADIENT ret = new FOCALGRADIENT(); ret.spreadMode = (int) readUB(2); - ret.interPolationMode = (int) readUB(2); + ret.interpolationMode = (int) readUB(2); int numGradients = (int) readUB(4); ret.gradientRecords = new GRADRECORD[numGradients]; for (int i = 0; i < numGradients; i++) { @@ -1930,7 +1930,7 @@ public class SWFInputStream extends InputStream { ret.fillStyleType = readUI8(); if (ret.fillStyleType == FILLSTYLE.SOLID) { if (shapeNum >= 3) { - ret.colorA = readRGBA(); + ret.color = readRGBA(); } else { ret.color = readRGB(); } @@ -1945,7 +1945,7 @@ public class SWFInputStream extends InputStream { ret.gradient = readGRADIENT(shapeNum); } if (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - ret.focalGradient = readFOCALGRADIENT(shapeNum); + ret.gradient = readFOCALGRADIENT(shapeNum); } if ((ret.fillStyleType == FILLSTYLE.REPEATING_BITMAP) @@ -1993,7 +1993,7 @@ public class SWFInputStream extends InputStream { ret.color = readRGB(); } if (shapeNum == 3) { - ret.colorA = readRGBA(); + ret.color = readRGBA(); } return ret; } @@ -2047,9 +2047,9 @@ public class SWFInputStream extends InputStream { ret.lineStyles[i] = readLINESTYLE(shapeNum); } } else if (shapeNum == 4) { - ret.lineStyles2 = new LINESTYLE2[lineStyleCount]; + ret.lineStyles = new LINESTYLE2[lineStyleCount]; for (int i = 0; i < lineStyleCount; i++) { - ret.lineStyles2[i] = readLINESTYLE2(shapeNum); + ret.lineStyles[i] = readLINESTYLE2(shapeNum); } } return ret; diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java index 5b4ae6826..3f7ae4f70 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1035,7 +1035,7 @@ public class SWFOutputStream extends OutputStream { writeUI8(value.fillStyleType); if (value.fillStyleType == FILLSTYLE.SOLID) { if (shapeNum >= 3) { - writeRGBA(value.colorA); + writeRGBA((RGBA) value.color); } else if (shapeNum == 1 || shapeNum == 2) { writeRGB(value.color); } @@ -1050,7 +1050,7 @@ public class SWFOutputStream extends OutputStream { writeGRADIENT(value.gradient, shapeNum); } if (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - writeFOCALGRADIENT(value.focalGradient, shapeNum); + writeFOCALGRADIENT((FOCALGRADIENT) value.gradient, shapeNum); } if ((value.fillStyleType == FILLSTYLE.REPEATING_BITMAP) @@ -1095,7 +1095,7 @@ public class SWFOutputStream extends OutputStream { */ public void writeFOCALGRADIENT(FOCALGRADIENT value, int shapeNum) throws IOException { writeUB(2, value.spreadMode); - writeUB(2, value.interPolationMode); + writeUB(2, value.interpolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeGRADRECORD(value.gradientRecords[i], shapeNum); @@ -1112,7 +1112,7 @@ public class SWFOutputStream extends OutputStream { */ public void writeGRADIENT(GRADIENT value, int shapeNum) throws IOException { writeUB(2, value.spreadMode); - writeUB(2, value.interPolationMode); + writeUB(2, value.interpolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeGRADRECORD(value.gradientRecords[i], shapeNum); @@ -1131,7 +1131,7 @@ public class SWFOutputStream extends OutputStream { if (shapeNum == 1 || shapeNum == 2) { writeRGB(value.color); } else if (shapeNum == 3) { - writeRGBA(value.colorA); + writeRGBA((RGBA) value.color); } } @@ -1147,7 +1147,7 @@ public class SWFOutputStream extends OutputStream { if (shapeNum == 1 || shapeNum == 2) { writeRGB(value.color); } else if (shapeNum == 3) { - writeRGBA(value.colorA); + writeRGBA((RGBA) value.color); } } @@ -1173,7 +1173,7 @@ public class SWFOutputStream extends OutputStream { writeUI16(value.miterLimitFactor); } if (!value.hasFillFlag) { - writeRGBA(value.color); + writeRGBA((RGBA) value.color); } else { writeFILLSTYLE(value.fillType, shapeNum); } @@ -1200,7 +1200,7 @@ public class SWFOutputStream extends OutputStream { writeLINESTYLE(value.lineStyles[i], shapeNum); } } else if (shapeNum == 4) { - lineStyleCount = value.lineStyles2.length; + lineStyleCount = value.lineStyles.length; if (lineStyleCount >= 0xff) { writeUI8(0xff); writeUI16(lineStyleCount); @@ -1208,7 +1208,7 @@ public class SWFOutputStream extends OutputStream { writeUI8(lineStyleCount); } for (int i = 0; i < lineStyleCount; i++) { - writeLINESTYLE2(value.lineStyles2[i], shapeNum); + writeLINESTYLE2((LINESTYLE2) value.lineStyles[i], shapeNum); } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/CurvedEdge.java b/trunk/src/com/jpexs/decompiler/flash/exporters/CurvedEdge.java new file mode 100644 index 000000000..a5de4833f --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/CurvedEdge.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +/** + * + * @author JPEXS + */ +public class CurvedEdge extends StraightEdge implements IEdge { + + private Point control; + + CurvedEdge(Point from, Point control, Point to, int lineStyleIdx, int fillStyleIdx) { + super(from, to, lineStyleIdx, fillStyleIdx); + this.control = control; + } + + public Point getControl() { + return control; + } + + @Override + public IEdge reverseWithNewFillStyle(int newFillStyleIdx) { + return new CurvedEdge(to, control, from, lineStyleIdx, newFillStyleIdx); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGShapeExporter.java new file mode 100644 index 000000000..7f3215150 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGShapeExporter.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGB; +import java.util.List; + +/** + * + * @author JPEXS, Claus Wahlers + */ +public class DefaultSVGShapeExporter extends ShapeExporterBase implements IShapeExporter { + + protected static final String DRAW_COMMAND_L = "L"; + protected static final String DRAW_COMMAND_Q = "Q"; + + protected String currentDrawCommand = ""; + protected String pathData; + + public DefaultSVGShapeExporter(ShapeTag tag) { + super(tag); + } + + @Override + public void beginShape() { + } + + @Override + public void endShape(double xMin, double yMin, double xMax, double yMax) { + } + + @Override + public void beginFills() { + } + + @Override + public void endFills() { + } + + @Override + public void beginLines() { + } + + @Override + public void endLines() { + finalizePath(); + } + + @Override + public void beginFill(RGB color) { + finalizePath(); + } + + @Override + public void beginGradientFill(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + finalizePath(); + } + + @Override + public void beginBitmapFill(int bitmapId, MATRIX matrix, boolean repeat, boolean smooth) { + finalizePath(); + } + + @Override + public void endFill() { + finalizePath(); + } + + @Override + public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { + finalizePath(); + } + + @Override + public void lineGradientStyle(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + } + + @Override + public void moveTo(double x, double y) { + currentDrawCommand = ""; + pathData += "M" + + roundPixels20(x) + " " + + roundPixels20(y) + " "; + } + + @Override + public void lineTo(double x, double y) { + if (currentDrawCommand != DRAW_COMMAND_L) { + currentDrawCommand = DRAW_COMMAND_L; + pathData += "L"; + } + pathData += roundPixels20(x) + " " + + roundPixels20(y) + " "; + } + + @Override + public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { + if (currentDrawCommand != DRAW_COMMAND_Q) { + currentDrawCommand = DRAW_COMMAND_Q; + pathData += "Q"; + } + pathData += roundPixels20(controlX) + " " + + roundPixels20(controlY) + " " + + roundPixels20(anchorX) + " " + + roundPixels20(anchorY) + " "; + } + + protected void finalizePath() { + pathData = ""; + currentDrawCommand = ""; + } + + protected double roundPixels20(double pixels) { + return Math.round(pixels * 100) / 100.0; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/IEdge.java b/trunk/src/com/jpexs/decompiler/flash/exporters/IEdge.java new file mode 100644 index 000000000..1c7342308 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/IEdge.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +/** + * + * @author JPEXS + */ +public interface IEdge { + + public Point getFrom(); + + public Point getTo(); + + public int getLineStyleIdx(); + + public int getFillStyleIdx(); + + public IEdge reverseWithNewFillStyle(int newFillStyleIdx); +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/IShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/IShapeExporter.java new file mode 100644 index 000000000..05c25826c --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/IShapeExporter.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGB; +import java.util.List; + +/** + * + * @author JPEXS + */ +public interface IShapeExporter { + + public void beginShape(); + + public void endShape(double xMin, double yMin, double xMax, double yMax); + + public void beginFills(); + + public void endFills(); + + public void beginLines(); + + public void endLines(); + + public void beginFill(RGB color); + + public void beginGradientFill(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio); + + public void beginBitmapFill(int bitmapId, MATRIX matrix, boolean repeat, boolean smooth); + + public void endFill(); + + public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit); + + public void lineGradientStyle(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio); + + public void moveTo(double x, double y); + + public void lineTo(double x, double y); + + public void curveTo(double controlX, double controlY, double anchorX, double anchorY); +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/Point.java b/trunk/src/com/jpexs/decompiler/flash/exporters/Point.java new file mode 100644 index 000000000..e903a4d74 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/Point.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +/** + * + * @author JPEXS + */ +public class Point { + + public double x; + public double y; + + Point(double x, double y) { + this.x = x; + this.y = y; + } + + public int hashCode() { + long bits = Double.doubleToLongBits(x); + bits ^= Double.doubleToLongBits(y) * 31; + return (((int) bits) ^ ((int) (bits >> 32))); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Point) { + Point pt = (Point) obj; + return (x == pt.x) && (y == pt.y); + } + return super.equals(obj); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/Rectangle.java b/trunk/src/com/jpexs/decompiler/flash/exporters/Rectangle.java new file mode 100644 index 000000000..95944ce3c --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/Rectangle.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +/** + * + * @author JPEXS + */ +public class Rectangle { + + public double xMin; + public double yMin; + public double xMax; + public double yMax; + + Rectangle(double xMin, double yMin, double xMax, double yMax) { + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + + public int hashCode() { + long bits = Double.doubleToLongBits(xMin); + bits += Double.doubleToLongBits(yMin) * 37; + bits += Double.doubleToLongBits(xMax) * 43; + bits += Double.doubleToLongBits(yMax) * 47; + return (((int) bits) ^ ((int) (bits >> 32))); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Rectangle) { + Rectangle r = (Rectangle) obj; + return (xMin == r.xMin) && (yMin == r.yMin) && (xMax == r.xMax) && (yMax == r.yMax); + } + return super.equals(obj); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/SVGShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/SVGShapeExporter.java new file mode 100644 index 000000000..5c7c3fdb1 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/SVGShapeExporter.java @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.helpers.Helper; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.xml.bind.DatatypeConverter; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; + +/** + * + * @author JPEXS, Claus Wahlers + */ +public class SVGShapeExporter extends DefaultSVGShapeExporter { + + protected static final String sNamespace = "http://www.w3.org/2000/svg"; + protected static final String xlinkNamespace = "http://www.w3.org/1999/xlink"; + + protected Document _svg; + protected Element _svgDefs; + protected Element _svgG; + protected Element path; + protected List gradients; + protected int lastPatternId; + private SWF swf; + + public SVGShapeExporter(SWF swf, ShapeTag tag) { + super(tag); + this.swf = swf; + } + + public String getSVG() { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + StringWriter writer = new StringWriter(); + try { + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + DOMSource source = new DOMSource(_svg); + StreamResult result = new StreamResult(writer); + transformer.transform(source, result); + } catch (TransformerException ex) { + Logger.getLogger(SVGShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + return writer.toString(); + } + + @Override + public void beginShape() { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + DOMImplementation impl = docBuilder.getDOMImplementation(); + DocumentType svgDocType = impl.createDocumentType("svg", "-//W3C//DTD SVG 1.0//EN", + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"); + _svg = impl.createDocument(sNamespace, "svg", svgDocType); + Element svgRoot = _svg.getDocumentElement(); + svgRoot.setAttribute("xmlns:xlink", xlinkNamespace); + _svgDefs = _svg.createElement("defs"); + svgRoot.appendChild(_svgDefs); + _svgG = _svg.createElement("g"); + svgRoot.appendChild(_svgG); + } catch (ParserConfigurationException ex) { + Logger.getLogger(SVGShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + gradients = new ArrayList<>(); + } + + @Override + public void endShape(double xMin, double yMin, double xMax, double yMax) { + _svgG.setAttribute("transform", "matrix(1, 0, 0, 1, " + + roundPixels20(-xMin) + ", " + roundPixels20(-yMin) + ")"); + } + + @Override + public void beginFill(RGB color) { + finalizePath(); + path.setAttribute("stroke", "none"); + path.setAttribute("fill", color.toHexRGB()); + if (color instanceof RGBA) { + RGBA colorA = (RGBA) color; + if (colorA.alpha != 255) { + path.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); + } + } + } + + @Override + public void beginGradientFill(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + finalizePath(); + Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT) + ? _svg.createElement("linearGradient") + : _svg.createElement("radialGradient"); + populateGradientElement(gradient, type, colors, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio); + int id = gradients.indexOf(gradient); + if (id < 0) { + // todo: filter same gradients + id = gradients.size(); + gradients.add(gradient); + } + gradient.setAttribute("id", "gradient" + id); + path.setAttribute("stroke", "none"); + path.setAttribute("fill", "url(#gradient" + id + ")"); + _svgDefs.appendChild(gradient); + } + + @Override + public void beginBitmapFill(int bitmapId, MATRIX matrix, boolean repeat, boolean smooth) { + finalizePath(); + ImageTag image = null; + for (Tag t : swf.tags) { + if (t instanceof ImageTag) { + ImageTag i = (ImageTag) t; + if (i.getCharacterId() == bitmapId) { + image = i; + break; + } + } + } + if (image != null) { + BufferedImage img = image.getImage(swf.tags); + if (img != null) { + int width = img.getWidth(); + int height = img.getHeight(); + lastPatternId++; + String patternId = "PatternID_" + lastPatternId; + String format = image.getImageFormat(); + InputStream imageStream = image.getImageData(); + byte[] imageData; + if (imageStream != null) { + imageData = Helper.readStream(image.getImageData()); + } else { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ImageIO.write(img, format.toUpperCase(Locale.ENGLISH), baos); + } catch (IOException ex) { + } + imageData = baos.toByteArray(); + } + String base64ImgData = DatatypeConverter.printBase64Binary(imageData); + float translateX = 0; + float translateY = 0; + float rotateSkew0 = 0; + float rotateSkew1 = 0; + float scaleX = 1; + float scaleY = 1; + if (matrix != null) { + translateX = SWF.twipToPixel(matrix.translateX); + translateY = SWF.twipToPixel(matrix.translateY); + if (matrix.hasRotate) { + rotateSkew0 = matrix.getRotateSkew0Float(); + rotateSkew1 = matrix.getRotateSkew1Float(); + } + if (matrix.hasScale) { + scaleX = SWF.twipToPixel((int) matrix.getScaleXFloat()); + scaleY = SWF.twipToPixel((int) matrix.getScaleYFloat()); + } + } + path.setAttribute("style", "fill:url(#" + patternId + ")"); + Element pattern = _svg.createElement("pattern"); + pattern.setAttribute("id", patternId); + pattern.setAttribute("patternUnits", "userSpaceOnUse"); + pattern.setAttribute("overflow", "visible"); + pattern.setAttribute("width", "" + width); + pattern.setAttribute("height", "" + height); + pattern.setAttribute("viewBox", "0 0 " + width + " " + height); + pattern.setAttribute("patternTransform", "matrix(" + scaleX + ", " + rotateSkew0 + + ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"); + Element imageElement = _svg.createElement("image"); + imageElement.setAttribute("width", "" + width); + imageElement.setAttribute("height", "" + height); + imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData); + pattern.appendChild(imageElement); + _svgG.appendChild(pattern); + } + } + } + + @Override + public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { + finalizePath(); + path.setAttribute("fill", "none"); + path.setAttribute("stroke", color.toHexRGB()); + path.setAttribute("stroke-width", Double.toString(thickness == 0 ? 1 : thickness)); + if (color instanceof RGBA) { + RGBA colorA = (RGBA) color; + if (colorA.alpha != 255) { + path.setAttribute("stroke-opacity", Float.toString(colorA.getAlphaFloat())); + } + } + switch (startCaps) { + case LINESTYLE2.NO_CAP: + path.setAttribute("stroke-linecap", "butt"); + break; + case LINESTYLE2.SQUARE_CAP: + path.setAttribute("stroke-linecap", "square"); + break; + default: + path.setAttribute("stroke-linecap", "round"); + break; + } + switch (joints) { + case LINESTYLE2.BEVEL_JOIN: + path.setAttribute("stroke-linejoin", "bevel"); + break; + case LINESTYLE2.ROUND_JOIN: + path.setAttribute("stroke-linejoin", "round"); + break; + default: + path.setAttribute("stroke-linejoin", "miter"); + if (miterLimit >= 1 && miterLimit != 4) { + path.setAttribute("stroke-miterlimit", Integer.toString(miterLimit)); + } + break; + } + } + + @Override + public void lineGradientStyle(int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + path.removeAttribute("stroke-opacity"); + Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT) + ? _svg.createElement("linearGradient") + : _svg.createElement("radialGradient"); + populateGradientElement(gradient, type, colors, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio); + int id = gradients.indexOf(gradient); + if (id < 0) { + // todo: filter same gradients + id = gradients.size(); + gradients.add(gradient); + } + gradient.setAttribute("id", "gradient" + id); + path.setAttribute("stroke", "url(#gradient" + id + ")"); + path.setAttribute("fill", "none"); + _svgDefs.appendChild(gradient); + } + + @Override + protected void finalizePath() { + if (path != null && pathData != "") { + path.setAttribute("d", pathData.trim()); + _svgG.appendChild(path); + } + path = _svg.createElement("path"); + super.finalizePath(); + } + + protected void populateGradientElement(Element gradient, int type, List colors, List ratios, MATRIX matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + gradient.setAttribute("gradientUnits", "userSpaceOnUse"); + if (type == FILLSTYLE.LINEAR_GRADIENT) { + gradient.setAttribute("x1", "-819.2"); + gradient.setAttribute("x2", "819.2"); + } else { + gradient.setAttribute("r", "819.2"); + gradient.setAttribute("cx", "0"); + gradient.setAttribute("cy", "0"); + if (focalPointRatio != 0) { + gradient.setAttribute("fx", Double.toString(819.2 * focalPointRatio)); + gradient.setAttribute("fy", "0"); + } + } + switch (spreadMethod) { + case GRADIENT.SPREAD_PAD_MODE: + gradient.setAttribute("spreadMethod", "pad"); + break; + case GRADIENT.SPREAD_REFLECT_MODE: + gradient.setAttribute("spreadMethod", "reflect"); + break; + case GRADIENT.SPREAD_REPEAT_MODE: + gradient.setAttribute("spreadMethod", "repeat"); + break; + } + if (interpolationMethod == GRADIENT.INTERPOLATION_LINEAR_RGB_MODE) { + gradient.setAttribute("color-interpolation", "linearRGB"); + } + if (matrix != null) { + float translateX = SWF.twipToPixel(matrix.translateX); + float translateY = SWF.twipToPixel(matrix.translateY); + float rotateSkew0 = 0; + float rotateSkew1 = 0; + float scaleX = 1; + float scaleY = 1; + if (matrix.hasRotate) { + rotateSkew0 = matrix.getRotateSkew0Float(); + rotateSkew1 = matrix.getRotateSkew1Float(); + } + if (matrix.hasScale) { + scaleX = SWF.twipToPixel((int) matrix.getScaleXFloat()); + scaleY = SWF.twipToPixel((int) matrix.getScaleYFloat()); + } + gradient.setAttribute("gradientTransform", "matrix(" + scaleX + ", " + rotateSkew0 + + ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"); + } + for (int i = 0; i < colors.size(); i++) { + Element gradientEntry = _svg.createElement("stop"); + gradient.setAttribute("offset", Double.toString(ratios.get(i) / 255)); + RGB color = colors.get(i); + //if(colors.get(i) != 0) { + gradientEntry.setAttribute("stop-color", color.toHexRGB()); + //} + if (color instanceof RGBA) { + RGBA colorA = (RGBA) color; + if (colorA.alpha != 255) { + gradientEntry.setAttribute("stop-opacity", Float.toString(colorA.getAlphaFloat())); + } + } + gradient.appendChild(gradientEntry); + } + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java new file mode 100644 index 000000000..ee0c28cd2 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +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 java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author JPEXS, Claus Wahlers + */ +public abstract class ShapeExporterBase implements IShapeExporter { + + private final ShapeTag tag; + + private static final double unitDivisor = 20; + + protected List _fillStyles; + protected List _lineStyles; + + protected List>> fillEdgeMaps; + protected List>> lineEdgeMaps; + protected Map> currentFillEdgeMap; + protected Map> currentLineEdgeMap; + private int numGroups; + protected Map> coordMap; + private Rectangle bounds = new Rectangle(Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); + + private boolean edgeMapsCreated; + + public ShapeExporterBase(ShapeTag tag) { + this.tag = tag; + _fillStyles = new ArrayList<>(); + _fillStyles.addAll(Arrays.asList(tag.getShapes().fillStyles.fillStyles)); + _lineStyles = new ArrayList<>(); + _lineStyles.addAll(Arrays.asList(tag.getShapes().lineStyles.lineStyles)); + } + + public void export() { + // Create edge maps + createEdgeMaps(); + // Let the doc handler know that a shape export starts + beginShape(); + // Export fills and strokes for each group separately + for (int i = 0; i < numGroups; i++) { + // Export fills first + exportFillPath(i); + // Export strokes last + exportLinePath(i); + } + // Let the doc handler know that we're done exporting a shape + endShape(bounds.xMin, bounds.yMin, bounds.xMax, bounds.yMax); + } + + protected void createEdgeMaps() { + if (!edgeMapsCreated) { + double xPos = 0; + double yPos = 0; + Point from; + Point to; + Point control; + int fillStyleIdxOffset = 0; + int lineStyleIdxOffset = 0; + int currentFillStyleIdx0 = 0; + int currentFillStyleIdx1 = 0; + int currentLineStyleIdx = 0; + List subPath = new ArrayList<>(); + numGroups = 0; + fillEdgeMaps = new ArrayList<>(); + lineEdgeMaps = new ArrayList<>(); + currentFillEdgeMap = new HashMap<>(); + currentLineEdgeMap = new HashMap<>(); + List records = tag.getShapes().shapeRecords; + for (int i = 0; i < records.size(); i++) { + SHAPERECORD shapeRecord = records.get(i); + if (shapeRecord instanceof StyleChangeRecord) { + StyleChangeRecord styleChangeRecord = (StyleChangeRecord) shapeRecord; + if (styleChangeRecord.stateLineStyle || styleChangeRecord.stateFillStyle0 || styleChangeRecord.stateFillStyle1) { + processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1); + subPath = new ArrayList<>(); + } + if (styleChangeRecord.stateNewStyles) { + fillStyleIdxOffset = _fillStyles.size(); + lineStyleIdxOffset = _lineStyles.size(); + appendFillStyles(_fillStyles, styleChangeRecord.fillStyles.fillStyles); + appendLineStyles(_lineStyles, styleChangeRecord.lineStyles.lineStyles); + } + // Check if all styles are reset to 0. + // This (probably) means that a new group starts with the next record + if (styleChangeRecord.stateLineStyle && styleChangeRecord.lineStyle == 0 + && styleChangeRecord.stateFillStyle0 && styleChangeRecord.fillStyle0 == 0 + && styleChangeRecord.stateFillStyle1 && styleChangeRecord.fillStyle1 == 0) { + cleanEdgeMap(currentFillEdgeMap); + cleanEdgeMap(currentLineEdgeMap); + fillEdgeMaps.add(currentFillEdgeMap); + lineEdgeMaps.add(currentLineEdgeMap); + currentFillEdgeMap = new HashMap<>(); + currentLineEdgeMap = new HashMap<>(); + currentLineStyleIdx = 0; + currentFillStyleIdx0 = 0; + currentFillStyleIdx1 = 0; + numGroups++; + } else { + if (styleChangeRecord.stateLineStyle) { + currentLineStyleIdx = styleChangeRecord.lineStyle; + if (currentLineStyleIdx > 0) { + currentLineStyleIdx += lineStyleIdxOffset; + } + } + if (styleChangeRecord.stateFillStyle0) { + currentFillStyleIdx0 = styleChangeRecord.fillStyle0; + if (currentFillStyleIdx0 > 0) { + currentFillStyleIdx0 += fillStyleIdxOffset; + } + } + if (styleChangeRecord.stateFillStyle1) { + currentFillStyleIdx1 = styleChangeRecord.fillStyle1; + if (currentFillStyleIdx1 > 0) { + currentFillStyleIdx1 += fillStyleIdxOffset; + } + } + } + if (styleChangeRecord.stateMoveTo) { + xPos = styleChangeRecord.moveDeltaX / unitDivisor; + yPos = styleChangeRecord.moveDeltaY / unitDivisor; + } + } else if (shapeRecord instanceof StraightEdgeRecord) { + StraightEdgeRecord straightEdgeRecord = (StraightEdgeRecord) shapeRecord; + from = new Point(roundPixels400(xPos), roundPixels400(yPos)); + if (straightEdgeRecord.generalLineFlag) { + xPos += straightEdgeRecord.deltaX / unitDivisor; + yPos += straightEdgeRecord.deltaY / unitDivisor; + } else { + if (straightEdgeRecord.vertLineFlag) { + yPos += straightEdgeRecord.deltaY / unitDivisor; + } else { + xPos += straightEdgeRecord.deltaX / unitDivisor; + } + } + to = new Point(roundPixels400(xPos), roundPixels400(yPos)); + subPath.add(new StraightEdge(from, to, currentLineStyleIdx, currentFillStyleIdx1)); + } else if (shapeRecord instanceof CurvedEdgeRecord) { + CurvedEdgeRecord curvedEdgeRecord = (CurvedEdgeRecord) shapeRecord; + from = new Point(roundPixels400(xPos), roundPixels400(yPos)); + double xPosControl = xPos + curvedEdgeRecord.controlDeltaX / unitDivisor; + double yPosControl = yPos + curvedEdgeRecord.controlDeltaY / unitDivisor; + xPos = xPosControl + curvedEdgeRecord.anchorDeltaX / unitDivisor; + yPos = yPosControl + curvedEdgeRecord.anchorDeltaY / unitDivisor; + control = new Point(xPosControl, yPosControl); + to = new Point(roundPixels400(xPos), roundPixels400(yPos)); + subPath.add(new CurvedEdge(from, control, to, currentLineStyleIdx, currentFillStyleIdx1)); + } else if (shapeRecord instanceof EndShapeRecord) { + // We're done. Process the last subpath, if any + processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1); + cleanEdgeMap(currentFillEdgeMap); + cleanEdgeMap(currentLineEdgeMap); + fillEdgeMaps.add(currentFillEdgeMap); + lineEdgeMaps.add(currentLineEdgeMap); + numGroups++; + } + } + edgeMapsCreated = true; + } + } + + protected void processSubPath(List subPath, int lineStyleIdx, int fillStyleIdx0, int fillStyleIdx1) { + List path; + if (fillStyleIdx0 != 0) { + path = currentFillEdgeMap.get(fillStyleIdx0); + if (path == null) { + path = new ArrayList<>(); + currentFillEdgeMap.put(fillStyleIdx0, path); + } + for (int j = subPath.size() - 1; j >= 0; j--) { + path.add(subPath.get(j).reverseWithNewFillStyle(fillStyleIdx0)); + } + } + if (fillStyleIdx1 != 0) { + path = currentFillEdgeMap.get(fillStyleIdx1); + if (path == null) { + path = new ArrayList<>(); + currentFillEdgeMap.put(fillStyleIdx1, path); + } + appendEdges(path, subPath); + } + if (lineStyleIdx != 0) { + path = currentLineEdgeMap.get(lineStyleIdx); + if (path == null) { + path = new ArrayList<>(); + currentLineEdgeMap.put(lineStyleIdx, path); + } + appendEdges(path, subPath); + } + } + + private void calculateBound(IEdge edge) { + Point from = edge.getFrom(); + Point to = edge.getTo(); + if (from.x < bounds.xMin) { + bounds.xMin = from.x; + } + if (from.x > bounds.xMax) { + bounds.xMax = from.x; + } + if (to.y < bounds.yMin) { + bounds.yMin = to.y; + } + if (to.y > bounds.yMax) { + bounds.yMax = to.y; + } + } + + protected void exportFillPath(int groupIndex) { + List path = createPathFromEdgeMap(fillEdgeMaps.get(groupIndex)); + Point pos = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + int fillStyleIdx = Integer.MAX_VALUE; + if (path.size() > 0) { + beginFills(); + for (int i = 0; i < path.size(); i++) { + IEdge e = path.get(i); + calculateBound(e); + if (fillStyleIdx != e.getFillStyleIdx()) { + if (fillStyleIdx != Integer.MAX_VALUE) { + endFill(); + } + fillStyleIdx = e.getFillStyleIdx(); + pos = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + try { + MATRIX matrix; + FILLSTYLE fillStyle = _fillStyles.get(fillStyleIdx - 1); + switch (fillStyle.fillStyleType) { + case FILLSTYLE.SOLID: + // Solid fill + beginFill(fillStyle.color); + break; + case FILLSTYLE.LINEAR_GRADIENT: + case FILLSTYLE.RADIAL_GRADIENT: + case FILLSTYLE.FOCAL_RADIAL_GRADIENT: + // Gradient fill + List colors = new ArrayList<>(); + List ratios = new ArrayList<>(); + GRADRECORD gradientRecord; + matrix = new MATRIX(fillStyle.gradientMatrix); + matrix.translateX /= 20; + matrix.translateY /= 20; + for (int gri = 0; gri < fillStyle.gradient.gradientRecords.length; gri++) { + gradientRecord = fillStyle.gradient.gradientRecords[gri]; + colors.add(gradientRecord.color); + ratios.add(gradientRecord.ratio); + } + beginGradientFill( + fillStyle.fillStyleType, + colors, ratios, matrix, + fillStyle.gradient.spreadMode, + fillStyle.gradient.interpolationMode, + (fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0 + ); + break; + case FILLSTYLE.REPEATING_BITMAP: + case FILLSTYLE.CLIPPED_BITMAP: + case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP: + case FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP: + // Bitmap fill + MATRIX m = fillStyle.bitmapMatrix; + //matrix = new MATRIX(m.scaleX / 20.0, m.scaleY / 20.0, m.getRotation(), m.translateX / 20.0, m.translateY / 20.0); + beginBitmapFill( + fillStyle.bitmapId, + m, + (fillStyle.fillStyleType == FILLSTYLE.REPEATING_BITMAP || fillStyle.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP), + (fillStyle.fillStyleType == FILLSTYLE.REPEATING_BITMAP || fillStyle.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) + ); + break; + } + } catch (Exception ex) { + // Font shapes define no fillstyles per se, but do reference fillstyle index 1, + // which represents the font color. We just report solid black in this case. + beginFill(new RGB(Color.BLACK)); + } + } + if (!pos.equals(e.getFrom())) { + moveTo(e.getFrom().x, e.getFrom().y); + } + if (e instanceof CurvedEdge) { + CurvedEdge c = (CurvedEdge) e; + curveTo(c.getControl().x, c.getControl().y, c.to.x, c.to.y); + } else { + lineTo(e.getTo().x, e.getTo().y); + } + pos = e.getTo(); + } + if (fillStyleIdx != Integer.MAX_VALUE) { + endFill(); + } + endFills(); + } + } + + protected void exportLinePath(int groupIndex) { + List path = createPathFromEdgeMap(lineEdgeMaps.get(groupIndex)); + Point pos = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + int lineStyleIdx = Integer.MAX_VALUE; + LINESTYLE lineStyle; + if (path.size() > 0) { + beginLines(); + for (int i = 0; i < path.size(); i++) { + IEdge e = path.get(i); + calculateBound(e); + if (lineStyleIdx != e.getLineStyleIdx()) { + lineStyleIdx = e.getLineStyleIdx(); + pos = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + try { + lineStyle = _lineStyles.get(lineStyleIdx - 1); + } catch (Exception ex) { + lineStyle = null; + } + if (lineStyle != null) { + String scaleMode = "NORMAL"; + boolean pixelHintingFlag = false; + int startCapStyle = LINESTYLE2.ROUND_CAP; + int endCapStyle = LINESTYLE2.ROUND_CAP; + int joinStyle = LINESTYLE2.ROUND_JOIN; + int miterLimitFactor = 3; + boolean hasFillFlag = false; + if (lineStyle instanceof LINESTYLE2) { + LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle; + if (lineStyle2.noHScaleFlag && lineStyle2.noVScaleFlag) { + scaleMode = "NONE"; + } else if (lineStyle2.noHScaleFlag) { + scaleMode = "HORIZONTAL"; + } else if (lineStyle2.noVScaleFlag) { + scaleMode = "VERTICAL"; + } + pixelHintingFlag = lineStyle2.pixelHintingFlag; + startCapStyle = lineStyle2.startCapStyle; + endCapStyle = lineStyle2.endCapStyle; + joinStyle = lineStyle2.joinStyle; + miterLimitFactor = lineStyle2.miterLimitFactor; + hasFillFlag = lineStyle2.hasFillFlag; + } + lineStyle( + lineStyle.width / 20.0, + lineStyle.color, + pixelHintingFlag, + scaleMode, + startCapStyle, + endCapStyle, + joinStyle, + miterLimitFactor); + + if (hasFillFlag) { + LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle; + FILLSTYLE fillStyle = lineStyle2.fillType; + switch (fillStyle.fillStyleType) { + case FILLSTYLE.LINEAR_GRADIENT: + case FILLSTYLE.RADIAL_GRADIENT: + case FILLSTYLE.FOCAL_RADIAL_GRADIENT: + // Gradient fill + List colors = new ArrayList<>(); + List ratios = new ArrayList<>(); + GRADRECORD gradientRecord; + MATRIX matrix = new MATRIX(fillStyle.gradientMatrix); + matrix.translateX /= 20; + matrix.translateY /= 20; + for (int gri = 0; gri < fillStyle.gradient.gradientRecords.length; gri++) { + gradientRecord = fillStyle.gradient.gradientRecords[gri]; + colors.add(gradientRecord.color); + ratios.add(gradientRecord.ratio); + } + lineGradientStyle( + fillStyle.fillStyleType, + colors, ratios, matrix, + fillStyle.gradient.spreadMode, + fillStyle.gradient.interpolationMode, + (fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0 + ); + break; + } + } + } else { + // We should never get here + lineStyle(1, new RGB(Color.BLACK), false, "NORMAL", 0, 0, 0, 3); + } + } + if (!e.getFrom().equals(pos)) { + moveTo(e.getFrom().x, e.getFrom().y); + } + if (e instanceof CurvedEdge) { + CurvedEdge c = (CurvedEdge) e; + curveTo(c.getControl().x, c.getControl().y, c.to.x, c.to.y); + } else { + lineTo(e.getTo().x, e.getTo().y); + } + pos = e.getTo(); + } + endLines(); + } + } + + protected List createPathFromEdgeMap(Map> edgeMap) { + List newPath = new ArrayList<>(); + List styleIdxArray = new ArrayList<>(); + for (Integer styleIdx : edgeMap.keySet()) { + styleIdxArray.add(styleIdx); + } + Collections.sort(styleIdxArray); + for (int i = 0; i < styleIdxArray.size(); i++) { + appendEdges(newPath, edgeMap.get(styleIdxArray.get(i))); + } + return newPath; + } + + protected void cleanEdgeMap(Map> edgeMap) { + for (Integer styleIdx : edgeMap.keySet()) { + List subPath = edgeMap.get(styleIdx); + if (subPath != null && subPath.size() > 0) { + int idx; + IEdge prevEdge = null; + List tmpPath = new ArrayList<>(); + createCoordMap(subPath); + while (subPath.size() > 0) { + idx = 0; + while (idx < subPath.size()) { + if (prevEdge == null || prevEdge.getTo().equals(subPath.get(idx).getFrom())) { + IEdge edge = subPath.remove(idx); + tmpPath.add(edge); + removeEdgeFromCoordMap(edge); + prevEdge = edge; + } else { + IEdge edge = findNextEdgeInCoordMap(prevEdge); + if (edge != null) { + idx = subPath.indexOf(edge); + } else { + idx = 0; + prevEdge = null; + } + } + } + } + edgeMap.put(styleIdx, tmpPath); + } + } + } + + protected void createCoordMap(List path) { + coordMap = new HashMap<>(); + for (int i = 0; i < path.size(); i++) { + Point from = path.get(i).getFrom(); + String key = from.x + "_" + from.y; + List coordMapArray = coordMap.get(key); + if (coordMapArray == null) { + List list = new ArrayList<>(); + list.add(path.get(i)); + coordMap.put(key, list); + } else { + coordMapArray.add(path.get(i)); + } + } + } + + protected void removeEdgeFromCoordMap(IEdge edge) { + String key = edge.getFrom().x + "_" + edge.getFrom().y; + List coordMapArray = coordMap.get(key); + if (coordMapArray != null) { + if (coordMapArray.size() == 1) { + coordMap.remove(key); + } else { + int i = coordMapArray.indexOf(edge); + if (i > -1) { + coordMapArray.remove(i); + } + } + } + } + + protected IEdge findNextEdgeInCoordMap(IEdge edge) { + String key = edge.getTo().x + "_" + edge.getTo().y; + List coordMapArray = coordMap.get(key); + if (coordMapArray != null && coordMapArray.size() > 0) { + return coordMapArray.get(0); + } + return null; + } + + protected void appendFillStyles(List v1, FILLSTYLE[] v2) { + for (int i = 0; i < v2.length; i++) { + v1.add(v2[i]); + } + } + + protected void appendLineStyles(List v1, LINESTYLE[] v2) { + for (int i = 0; i < v2.length; i++) { + v1.add(v2[i]); + } + } + + protected void appendEdges(List v1, List v2) { + for (int i = 0; i < v2.size(); i++) { + v1.add(v2.get(i)); + } + } + + private double roundPixels400(double pixels) { + return Math.round(pixels * 10000) / 10000.0; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/StraightEdge.java b/trunk/src/com/jpexs/decompiler/flash/exporters/StraightEdge.java new file mode 100644 index 000000000..8877a4c2b --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/StraightEdge.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.exporters; + +/** + * + * @author JPEXS + */ +public class StraightEdge implements IEdge { + + protected final Point from; + protected final Point to; + protected final int lineStyleIdx; + private final int fillStyleIdx; + + StraightEdge(Point from, Point to, int lineStyleIdx, int fillStyleIdx) { + this.from = from; + this.to = to; + this.lineStyleIdx = lineStyleIdx; + this.fillStyleIdx = fillStyleIdx; + } + + @Override + public Point getFrom() { + return from; + } + + @Override + public Point getTo() { + return to; + } + + @Override + public int getLineStyleIdx() { + return lineStyleIdx; + } + + @Override + public int getFillStyleIdx() { + return fillStyleIdx; + } + + @Override + public IEdge reverseWithNewFillStyle(int newFillStyleIdx) { + return new StraightEdge(to, from, lineStyleIdx, newFillStyleIdx); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java index c9cdb1ade..b8320409e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java @@ -482,7 +482,7 @@ public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener { closeFileMenu.setEnabled(swfLoaded); closeAllFilesMenu.setEnabled(swfLoaded); - boolean isBundle = swfLoaded && (swf.swfList!=null ) && swf.swfList.isBundle; + boolean isBundle = swfLoaded && (swf.swfList != null) && swf.swfList.isBundle; saveCommandButton.setEnabled(swfLoaded && !isBundle); saveasCommandButton.setEnabled(swfLoaded); saveasexeCommandButton.setEnabled(swfLoaded); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java index 519b59f79..ac14a3e38 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.SVGShapeExporter; import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; @@ -97,7 +98,10 @@ public class DefineShapeTag extends CharacterTag implements BoundedTag, ShapeTag @Override public String toSVG() { - return shapes.toSVG(1, swf.tags, shapeBounds); + SVGShapeExporter exporter = new SVGShapeExporter(swf, this); + exporter.export(); + return exporter.getSVG(); + //return shapes.toSVG(1, swf.tags, shapeBounds); } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java b/trunk/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java index 62b68a917..556d8dd9f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java @@ -37,10 +37,8 @@ public class FILLSTYLE implements NeedsCharacters { public static final int NON_SMOOTHED_CLIPPED_BITMAP = 0x43; public boolean inShape3; public RGB color; - public RGBA colorA; //Shape3 public MATRIX gradientMatrix; public GRADIENT gradient; - public FOCALGRADIENT focalGradient; public int bitmapId; public MATRIX bitmapMatrix; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/FOCALGRADIENT.java b/trunk/src/com/jpexs/decompiler/flash/types/FOCALGRADIENT.java index 2e60b6896..06bb5d014 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/FOCALGRADIENT.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/FOCALGRADIENT.java @@ -20,10 +20,7 @@ package com.jpexs.decompiler.flash.types; * * @author JPEXS */ -public class FOCALGRADIENT { +public class FOCALGRADIENT extends GRADIENT { - public int spreadMode; - public int interPolationMode; - public GRADRECORD[] gradientRecords = new GRADRECORD[0]; public float focalPoint; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/GRADIENT.java b/trunk/src/com/jpexs/decompiler/flash/types/GRADIENT.java index c5a8c0c7b..3cb1e3ce5 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/GRADIENT.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/GRADIENT.java @@ -33,10 +33,10 @@ public class GRADIENT { /** * Interpolation mode */ - public int interPolationMode; + public int interpolationMode; public static final int INTERPOLATION_RGB_MODE = 0; public static final int INTERPOLATION_LINEAR_RGB_MODE = 1; public static final int INTERPOLATION_RESERVED1 = 2; public static final int INTERPOLATION_RESERVED2 = 3; - public GRADRECORD[] gradientRecords; + public GRADRECORD[] gradientRecords = new GRADRECORD[0]; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/GRADRECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/GRADRECORD.java index 4e0b58d3e..1a1b74b15 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/GRADRECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/GRADRECORD.java @@ -25,7 +25,6 @@ public class GRADRECORD { public int ratio; public boolean inShape3; public RGB color; - public RGBA colorA; public float getRatioFloat() { return ((float) ratio) / 255.0f; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE.java b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE.java index 8f4383a04..a8fcd530f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE.java @@ -24,5 +24,4 @@ public class LINESTYLE { public int width; public RGB color; - public RGBA colorA; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java index e03c35135..64c8ddfb0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java @@ -20,9 +20,8 @@ package com.jpexs.decompiler.flash.types; * * @author JPEXS */ -public class LINESTYLE2 { +public class LINESTYLE2 extends LINESTYLE { - public int width; public int startCapStyle; public int joinStyle; public static final int ROUND_JOIN = 0; @@ -38,6 +37,5 @@ public class LINESTYLE2 { public static final int NO_CAP = 1; public static final int SQUARE_CAP = 2; public int miterLimitFactor; - public RGBA color; public FILLSTYLE fillType; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java index 3ee172a62..d6e8f9aba 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java @@ -23,5 +23,4 @@ package com.jpexs.decompiler.flash.types; public class LINESTYLEARRAY { public LINESTYLE[] lineStyles = new LINESTYLE[0]; - public LINESTYLE2[] lineStyles2 = new LINESTYLE2[0]; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java b/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java index a91069d93..23c1f7718 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.types; +import com.jpexs.decompiler.flash.SWF; import java.awt.Point; import java.io.Serializable; @@ -156,4 +157,12 @@ public class MATRIX implements Serializable { public boolean isEmpty() { return (translateX == 0) && (translateY == 0) && (!hasRotate) && (!hasScale); } + + public double getRotation() { + float x = getRotateSkew1Float(); + float y = SWF.twipToPixel((int) getScaleYFloat()); + double rotation = ((180 / Math.PI) * Math.atan2(y, x) - 90); + if(rotation < 0) { rotation = 360 + rotation; } + return rotation; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java b/trunk/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java index b3352782e..a32bbc597 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java @@ -81,7 +81,7 @@ public class MORPHFILLSTYLE implements NeedsCharacters { ret.bitmapMatrix = morphMatrix(startBitmapMatrix, endBitmapMatrix, ratio); } if (startColor != null) { - ret.colorA = MORPHGRADIENT.morphColor(startColor, endColor, ratio); + ret.color = MORPHGRADIENT.morphColor(startColor, endColor, ratio); } ret.fillStyleType = fillStyleType; if (gradient != null) { @@ -97,7 +97,7 @@ public class MORPHFILLSTYLE implements NeedsCharacters { FILLSTYLE ret = new FILLSTYLE(); ret.bitmapId = bitmapId; ret.bitmapMatrix = startBitmapMatrix; - ret.colorA = startColor; + ret.color = startColor; ret.fillStyleType = fillStyleType; if (gradient != null) { ret.gradient = gradient.getStartGradient(); @@ -110,7 +110,7 @@ public class MORPHFILLSTYLE implements NeedsCharacters { FILLSTYLE ret = new FILLSTYLE(); ret.bitmapId = bitmapId; ret.bitmapMatrix = endBitmapMatrix; - ret.colorA = endColor; + ret.color = endColor; ret.fillStyleType = fillStyleType; if (gradient != null) { ret.gradient = gradient.getEndGradient(); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADIENT.java b/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADIENT.java index e01dd0c23..ebe70b6d7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADIENT.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADIENT.java @@ -60,7 +60,7 @@ public class MORPHGRADIENT { int gratio = (gradientRecords[m].startRatio + (gradientRecords[m].endRatio - gradientRecords[m].startRatio) * ratio / 65535); ret.gradientRecords[m] = new GRADRECORD(); - ret.gradientRecords[m].colorA = morphColor(gradientRecords[m].startColor, gradientRecords[m].endColor, ratio); + ret.gradientRecords[m].color = morphColor(gradientRecords[m].startColor, gradientRecords[m].endColor, ratio); ret.gradientRecords[m].ratio = gratio; } return ret; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADRECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADRECORD.java index da60c1492..2474c2f5a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADRECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MORPHGRADRECORD.java @@ -30,14 +30,14 @@ public class MORPHGRADRECORD { public GRADRECORD getStartRecord() { GRADRECORD ret = new GRADRECORD(); ret.ratio = startRatio; - ret.colorA = startColor; + ret.color = startColor; return ret; } public GRADRECORD getEndRecord() { GRADRECORD ret = new GRADRECORD(); ret.ratio = endRatio; - ret.colorA = endColor; + ret.color = endColor; return ret; } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE.java b/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE.java index f1fe61bf3..5e5037cbf 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLE.java @@ -29,21 +29,21 @@ public class MORPHLINESTYLE { public LINESTYLE getStartLineStyle() { LINESTYLE ret = new LINESTYLE(); - ret.colorA = startColor; + ret.color = startColor; ret.width = startWidth; return ret; } public LINESTYLE getLineStyleAt(int ratio) { LINESTYLE ret = new LINESTYLE(); - ret.colorA = MORPHGRADIENT.morphColor(startColor, endColor, ratio); + ret.color = MORPHGRADIENT.morphColor(startColor, endColor, ratio); ret.width = startWidth + (endWidth - startWidth) * ratio / 65535; return ret; } public LINESTYLE getEndLineStyle() { LINESTYLE ret = new LINESTYLE(); - ret.colorA = endColor; + ret.color = endColor; ret.width = endWidth; return ret; } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLEARRAY.java b/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLEARRAY.java index 6c69f1035..c80c1be31 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLEARRAY.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MORPHLINESTYLEARRAY.java @@ -34,9 +34,9 @@ public class MORPHLINESTYLEARRAY { } } if (shapeNum == 2) { - ret.lineStyles2 = new LINESTYLE2[lineStyles2.length]; + ret.lineStyles = new LINESTYLE2[lineStyles2.length]; for (int m = 0; m < lineStyles2.length; m++) { - ret.lineStyles2[m] = lineStyles2[m].getLineStyle2At(ratio); + ret.lineStyles[m] = lineStyles2[m].getLineStyle2At(ratio); } } return ret; @@ -51,9 +51,9 @@ public class MORPHLINESTYLEARRAY { } } if (shapeNum == 2) { - ret.lineStyles2 = new LINESTYLE2[lineStyles2.length]; + ret.lineStyles = new LINESTYLE2[lineStyles2.length]; for (int m = 0; m < lineStyles2.length; m++) { - ret.lineStyles2[m] = lineStyles2[m].getStartLineStyle2(); + ret.lineStyles[m] = lineStyles2[m].getStartLineStyle2(); } } return ret; @@ -68,9 +68,9 @@ public class MORPHLINESTYLEARRAY { } } if (shapeNum == 2) { - ret.lineStyles2 = new LINESTYLE2[lineStyles2.length]; + ret.lineStyles = new LINESTYLE2[lineStyles2.length]; for (int m = 0; m < lineStyles2.length; m++) { - ret.lineStyles2[m] = lineStyles2[m].getEndLineStyle2(); + ret.lineStyles[m] = lineStyles2[m].getEndLineStyle2(); } } return ret; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java b/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java index 17002513c..bc201c240 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java @@ -23,20 +23,8 @@ import java.awt.Color; * * @author JPEXS */ -public class RGBA { +public class RGBA extends RGB { - /** - * Red color value - */ - public int red; - /** - * Green color value - */ - public int green; - /** - * Blue color value - */ - public int blue; /** * Alpha value defining opacity */ @@ -46,22 +34,6 @@ public class RGBA { return ((float) alpha) / 255.0f; } - public String toHexRGB() { - String rh = Integer.toHexString(red); - if (rh.length() < 2) { - rh = "0" + rh; - } - String gh = Integer.toHexString(green); - if (gh.length() < 2) { - gh = "0" + gh; - } - String bh = Integer.toHexString(blue); - if (bh.length() < 2) { - bh = "0" + bh; - } - return "#" + rh + gh + bh; - } - public String toHexARGB() { String ra = Integer.toHexString(alpha); if (ra.length() < 2) { @@ -83,22 +55,19 @@ public class RGBA { } public RGBA(int red, int green, int blue, int alpha) { - this.red = red; - this.green = green; - this.blue = blue; + super(red, green, blue); this.alpha = alpha; } public RGBA(Color color) { - this.red = color.getRed(); - this.green = color.getGreen(); - this.blue = color.getBlue(); + super(color); this.alpha = color.getAlpha(); } public RGBA() { } + @Override public Color toColor() { return new Color(red, green, blue, alpha); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index 6f4479e67..7deac2403 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; import com.jpexs.decompiler.flash.types.GRADIENT; import com.jpexs.decompiler.flash.types.LINESTYLE; import com.jpexs.decompiler.flash.types.LINESTYLE2; @@ -110,7 +111,8 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali if (lineStyle2 == null) { ok = false; } else if (!lineStyle2.hasFillFlag) { - g.setPaint(new Color(lineStyle2.color.red, lineStyle2.color.green, lineStyle2.color.blue, lineStyle2.color.alpha)); + RGBA color = (RGBA) lineStyle2.color; + g.setPaint(new Color(color.red, color.green, color.blue, color.alpha)); int capStyle = 0; switch (lineStyle2.startCapStyle) { case LINESTYLE2.NO_CAP: @@ -149,7 +151,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali if (shapeNum == 1 || shapeNum == 2) { g.setPaint(lineStyle.color.toColor()); } else /*shapeNum == 3*/ { - g.setPaint(lineStyle.colorA.toColor()); + g.setPaint(lineStyle.color.toColor()); } g.setStroke(new BasicStroke(lineStyle.width / DESCALE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); ok = true; @@ -203,17 +205,20 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali break; case FILLSTYLE.FOCAL_RADIAL_GRADIENT: + FOCALGRADIENT focalGradient = (FOCALGRADIENT) fillStyle0.gradient; List colorsFocRad = new ArrayList<>(); List ratiosFocRad = new ArrayList<>(); - for (int i = 0; i < fillStyle0.focalGradient.gradientRecords.length; i++) { - if ((i > 0) && (fillStyle0.focalGradient.gradientRecords[i - 1].ratio == fillStyle0.focalGradient.gradientRecords[i].ratio)) { + for (int i = 0; i < focalGradient.gradientRecords.length; i++) { + if ((i > 0) && (focalGradient.gradientRecords[i - 1].ratio == focalGradient.gradientRecords[i].ratio)) { continue; } - ratiosFocRad.add(fillStyle0.focalGradient.gradientRecords[i].getRatioFloat()); + ratiosFocRad.add(focalGradient.gradientRecords[i].getRatioFloat()); + RGB color = focalGradient.gradientRecords[i].color; if (shapeNum >= 3) { - colorsFocRad.add(new Color(fillStyle0.focalGradient.gradientRecords[i].colorA.red, fillStyle0.focalGradient.gradientRecords[i].colorA.green, fillStyle0.focalGradient.gradientRecords[i].colorA.blue, fillStyle0.focalGradient.gradientRecords[i].colorA.alpha)); + RGBA colorA = (RGBA) color; + colorsFocRad.add(new Color(colorA.red, colorA.green, colorA.blue, colorA.alpha)); } else { - colorsFocRad.add(new Color(fillStyle0.focalGradient.gradientRecords[i].color.red, fillStyle0.focalGradient.gradientRecords[i].color.green, fillStyle0.focalGradient.gradientRecords[i].color.blue)); + colorsFocRad.add(new Color(color.red, color.green, color.blue)); } } @@ -223,9 +228,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali } Color[] focRadColors = colorsFocRad.toArray(new Color[colorsFocRad.size()]); - RGB focEndColor = fillStyle0.focalGradient.gradientRecords[fillStyle0.focalGradient.gradientRecords.length - 1].color; - RGBA focEndColorA = fillStyle0.focalGradient.gradientRecords[fillStyle0.focalGradient.gradientRecords.length - 1].colorA; + RGB focEndColor = focalGradient.gradientRecords[focalGradient.gradientRecords.length - 1].color; if (shapeNum >= 3) { + RGBA focEndColorA = (RGBA) focEndColor; g.setPaint(new Color(focEndColorA.red, focEndColorA.green, focEndColorA.blue, focEndColorA.alpha)); } else { g.setPaint(new Color(focEndColor.red, focEndColor.green, focEndColor.blue)); @@ -238,15 +243,15 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali focTrans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE)); g.setTransform(focTrans); CycleMethod cm = CycleMethod.NO_CYCLE; - if (fillStyle0.focalGradient.spreadMode == GRADIENT.SPREAD_PAD_MODE) { + if (fillStyle0.gradient.spreadMode == GRADIENT.SPREAD_PAD_MODE) { cm = CycleMethod.NO_CYCLE; - } else if (fillStyle0.focalGradient.spreadMode == GRADIENT.SPREAD_REFLECT_MODE) { + } else if (focalGradient.spreadMode == GRADIENT.SPREAD_REFLECT_MODE) { cm = CycleMethod.REFLECT; - } else if (fillStyle0.focalGradient.spreadMode == GRADIENT.SPREAD_REPEAT_MODE) { + } else if (focalGradient.spreadMode == GRADIENT.SPREAD_REPEAT_MODE) { cm = CycleMethod.REPEAT; } - g.setPaint(new RadialGradientPaint(new Point(0, 0), 16384, new Point((int) (fillStyle0.focalGradient.focalPoint * 16384), 0), focRadFractions, focRadColors, cm)); + g.setPaint(new RadialGradientPaint(new Point(0, 0), 16384, new Point((int) (focalGradient.focalPoint * 16384), 0), focRadFractions, focRadColors, cm)); g.fill(new Rectangle(-16384 * maxRepeat, -16384 * maxRepeat, 16384 * 2 * maxRepeat, 16384 * 2 * maxRepeat)); g.setTransform(oldAf); g.setClip(null); @@ -259,10 +264,12 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali continue; } ratiosRad.add(fillStyle0.gradient.gradientRecords[i].getRatioFloat()); + RGB color = fillStyle0.gradient.gradientRecords[i].color; if (shapeNum >= 3) { - colorsRad.add(new Color(fillStyle0.gradient.gradientRecords[i].colorA.red, fillStyle0.gradient.gradientRecords[i].colorA.green, fillStyle0.gradient.gradientRecords[i].colorA.blue, fillStyle0.gradient.gradientRecords[i].colorA.alpha)); + RGBA colorA = (RGBA) color; + colorsRad.add(new Color(colorA.red, colorA.green, colorA.blue, colorA.alpha)); } else { - colorsRad.add(new Color(fillStyle0.gradient.gradientRecords[i].color.red, fillStyle0.gradient.gradientRecords[i].color.green, fillStyle0.gradient.gradientRecords[i].color.blue)); + colorsRad.add(new Color(color.red, color.green, color.blue)); } } @@ -273,8 +280,8 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali Color[] colorsRadArr = colorsRad.toArray(new Color[colorsRad.size()]); RGB endColor = fillStyle0.gradient.gradientRecords[fillStyle0.gradient.gradientRecords.length - 1].color; - RGBA endColorA = fillStyle0.gradient.gradientRecords[fillStyle0.gradient.gradientRecords.length - 1].colorA; if (shapeNum >= 3) { + RGBA endColorA = (RGBA) endColor; g.setPaint(new Color(endColorA.red, endColorA.green, endColorA.blue, endColorA.alpha)); } else { g.setPaint(new Color(endColor.red, endColor.green, endColor.blue)); @@ -309,10 +316,12 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali continue; } ratios.add(fillStyle0.gradient.gradientRecords[i].getRatioFloat()); + RGB color = fillStyle0.gradient.gradientRecords[i].color; if (shapeNum >= 3) { - colors.add(new Color(fillStyle0.gradient.gradientRecords[i].colorA.red, fillStyle0.gradient.gradientRecords[i].colorA.green, fillStyle0.gradient.gradientRecords[i].colorA.blue, fillStyle0.gradient.gradientRecords[i].colorA.alpha)); + RGBA colorA = (RGBA) color; + colors.add(new Color(colorA.red, colorA.green, colorA.blue, colorA.alpha)); } else { - colors.add(new Color(fillStyle0.gradient.gradientRecords[i].color.red, fillStyle0.gradient.gradientRecords[i].color.green, fillStyle0.gradient.gradientRecords[i].color.blue)); + colors.add(new Color(color.red, color.green, color.blue)); } } @@ -347,10 +356,12 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali return; case FILLSTYLE.SOLID: Color c = null; + RGB color = fillStyle0.color; if (shapeNum >= 3) { - c = new Color(fillStyle0.colorA.red, fillStyle0.colorA.green, fillStyle0.colorA.blue, fillStyle0.colorA.alpha); + RGBA colorA = (RGBA) color; + c = new Color(colorA.red, colorA.green, colorA.blue, colorA.alpha); } else { - c = new Color(fillStyle0.color.red, fillStyle0.color.green, fillStyle0.color.blue); + c = new Color(color.red, color.green, color.blue); } g.setPaint(c); ok = true; @@ -489,7 +500,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali } break; case FILLSTYLE.SOLID: - f = " fill=\"" + ((shapeNum >= 3) ? fillStyle0.colorA.toHexRGB() : fillStyle0.color.toHexRGB()) + "\""; + f = " fill=\"" + fillStyle0.color.toHexRGB() + "\""; break; } } else { @@ -497,7 +508,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali } params += f; if ((!useLineStyle2) && lineStyle != null) { - params += " stroke=\"" + ((shapeNum >= 3) ? lineStyle.colorA.toHexRGB() : lineStyle.color.toHexRGB()) + "\""; + params += " stroke=\"" + lineStyle.color.toHexRGB() + "\""; } if (useLineStyle2 && lineStyle2 != null) { params += " stroke-width=\"" + SWF.twipToPixel(lineStyle2.width) + "\"" + (lineStyle2.color != null ? " stroke=\"" + lineStyle2.color.toHexRGB() + "\"" : ""); @@ -613,7 +624,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali if (scr.lineStyle == 0) { path.lineStyle2 = null; } else { - path.lineStyle2 = lineStylesList.lineStyles2[scr.lineStyle - 1]; + path.lineStyle2 = (LINESTYLE2) lineStylesList.lineStyles[scr.lineStyle - 1]; } } else { path.useLineStyle2 = false; @@ -995,7 +1006,6 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali p.fillStyle0 = new FILLSTYLE(); p.fillStyle0.fillStyleType = FILLSTYLE.SOLID; p.fillStyle0.color = new RGB(defaultColor); - p.fillStyle0.colorA = new RGBA(defaultColor); } p.drawTo(tags, -rect.Xmin, -rect.Ymin, g, shapeNum); } diff --git a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index bebfeb9e1..511992412 100644 --- a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -64,6 +64,7 @@ import com.jpexs.decompiler.flash.types.CXFORM; import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +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.LINESTYLE; @@ -200,8 +201,8 @@ public class XFLConverter { public static String convertLineStyle(LINESTYLE ls, int shapeNum) { return "" + "" - + "" + "" + ""; @@ -251,8 +252,9 @@ public class XFLConverter { ret += ""; if (!ls.hasFillFlag) { - ret += ""; } else { ret += convertFillStyle(null/* FIXME */, characters, ls.fillType, shapeNum); @@ -275,14 +277,10 @@ public class XFLConverter { switch (fs.fillStyleType) { case FILLSTYLE.SOLID: ret += "= 3) { - ret += fs.colorA.toHexRGB(); - } else { - ret += fs.color.toHexRGB(); - } + ret += fs.color.toHexRGB(); ret += "\""; if (shapeNum >= 3) { - ret += " alpha=\"" + fs.colorA.getAlphaFloat() + "\""; + ret += " alpha=\"" + ((RGBA) fs.color).getAlphaFloat() + "\""; } ret += " />"; break; @@ -322,7 +320,7 @@ public class XFLConverter { ret += ""; GRADRECORD[] records; if (fs.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - records = fs.focalGradient.gradientRecords; + records = fs.gradient.gradientRecords; } else { records = fs.gradient.gradientRecords; } for (GRADRECORD rec : records) { ret += "= 3 ? rec.colorA.toHexRGB() : rec.color.toHexRGB()) + "\""; + ret += " color=\"" + (shapeNum >= 3 ? rec.color.toHexRGB() : rec.color.toHexRGB()) + "\""; if (shapeNum >= 3) { - ret += " alpha=\"" + rec.colorA.getAlphaFloat() + "\""; + ret += " alpha=\"" + ((RGBA) rec.color).getAlphaFloat() + "\""; } ret += " ratio=\"" + rec.getRatioFloat() + "\""; ret += " />"; @@ -453,10 +451,10 @@ public class XFLConverter { } } if (lineStyles != null) { - if ((shapeNum == 4) && (lineStyles.lineStyles2 != null)) { //(shapeNum == 4) { - for (int l = 0; l < lineStyles.lineStyles2.length; l++) { + if ((shapeNum == 4) && (lineStyles.lineStyles != null)) { //(shapeNum == 4) { + for (int l = 0; l < lineStyles.lineStyles.length; l++) { strokesStr += ""; - strokesStr += convertLineStyle(characters, lineStyles.lineStyles2[l], shapeNum); + strokesStr += convertLineStyle(characters, (LINESTYLE2) lineStyles.lineStyles[l], shapeNum); strokesStr += ""; lineStyleCount++; } @@ -511,15 +509,10 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!actualLinestyles.lineStyles2[strokeStyleOrig].hasFillFlag) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.alpha == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.red == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.green == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.blue == 0) { - empty = true; - } - } - } + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; } } } @@ -565,9 +558,9 @@ public class XFLConverter { lineStyleCount = 0; if (shapeNum == 4) { - for (int l = 0; l < scr.lineStyles.lineStyles2.length; l++) { + for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) { strokesStr += ""; - strokesStr += convertLineStyle(characters, scr.lineStyles.lineStyles2[l], shapeNum); + strokesStr += convertLineStyle(characters, (LINESTYLE2) scr.lineStyles.lineStyles[l], shapeNum); strokesStr += ""; lineStyleCount++; } @@ -630,15 +623,10 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!actualLinestyles.lineStyles2[strokeStyleOrig].hasFillFlag) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.alpha == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.red == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.green == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.blue == 0) { - empty = true; - } - } - } + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; } } } @@ -678,15 +666,10 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!actualLinestyles.lineStyles2[strokeStyleOrig].hasFillFlag) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.alpha == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.red == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.green == 0) { - if (actualLinestyles.lineStyles2[strokeStyleOrig].color.blue == 0) { - empty = true; - } - } - } + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; } } }