diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c92236f3..f4155c5de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - SVG Frames export - filter - SVG Shapes export - non-scaling strokes - SVG Shapes import - non-scaling strokes +- Support for DefineShape4 nonzero winding rule - display, svg (import, export), canvas export ### Fixed - Close action on SWF inside DefineBinaryData @@ -34,6 +35,7 @@ All notable changes to this project will be documented in this file. - [#116] §§push at the end of switch branches - Convolution matrix filter display and editing - Generic tag editor - Disallow add before/after or remove on parent field with indices +- Calculating fillBits, lineBits on SHAPE structure (storing morphshapes, fonts) ## [19.0.0] - 2023-10-01 ### Added diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index a85b87f07..dd234ad99 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1353,11 +1353,15 @@ public class SWFOutputStream extends OutputStream { /** * Writes SHAPE value to the stream * + * @param fillStyleCount + * @param lineStyleCount * @param value SHAPE value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ - public void writeSHAPE(SHAPE value, int shapeNum) throws IOException { + public void writeSHAPE(int fillStyleCount, int lineStyleCount, SHAPE value, int shapeNum) throws IOException { + value.numFillBits = getNeededBitsU(fillStyleCount); + value.numLineBits = getNeededBitsU(lineStyleCount); writeUB(4, value.numFillBits); writeUB(4, value.numLineBits); writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java index 36c0c7c5e..ad525244f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FontExporter.java @@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; import com.jpexs.decompiler.flash.exporters.shape.PathExporter; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; @@ -202,7 +203,7 @@ public class FontExporter { char c = t.glyphToChar(i); SHAPE s = shapes.get(i); final List contours = new ArrayList<>(); - PathExporter seb = new PathExporter(1, swf, s, null) { + PathExporter seb = new PathExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, s, null) { private double transformX(double x) { return Math.ceil((double) (x / divider)); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index e41a01eda..60988ddfe 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -139,7 +139,7 @@ public class ShapeExporter { SHAPE shp = st.getShapes(); int deltaX = -shp.getBounds(1).Xmin; int deltaY = -shp.getBounds(1).Ymin; - CanvasShapeExporter cse = new CanvasShapeExporter(st.getShapeNum(), null, SWF.unitDivisor / settings.zoom, ((Tag) st).getSwf(), shp, new CXFORMWITHALPHA(), deltaX, deltaY); + CanvasShapeExporter cse = new CanvasShapeExporter(st.getWindingRule(), st.getShapeNum(), null, SWF.unitDivisor / settings.zoom, ((Tag) st).getSwf(), shp, new CXFORMWITHALPHA(), deltaX, deltaY); cse.export(); Set needed = new HashSet<>(); needed.add(st.getCharacterId()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/BitmapExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/BitmapExporter.java index ef057d0ea..0f1cee78e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/BitmapExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/BitmapExporter.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.exporters.ImageTagBufferedImage; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.GRADIENT; @@ -74,7 +75,7 @@ public class BitmapExporter extends ShapeExporterBase { private final SWF swf; - private final GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); //For correct intersections display; + private final GeneralPath path; private Shape aliasedShape; @@ -166,16 +167,17 @@ public class BitmapExporter extends ShapeExporterBase { } } - public static void export(int shapeNum, SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, ColorTransform colorTransform, boolean scaleStrokes, boolean canUseSmoothing) { - BitmapExporter exporter = new BitmapExporter(shapeNum, swf, shape, defaultColor, colorTransform); + public static void export(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, ColorTransform colorTransform, boolean scaleStrokes, boolean canUseSmoothing) { + BitmapExporter exporter = new BitmapExporter(windingRule, shapeNum, swf, shape, defaultColor, colorTransform); exporter.setCanUseSmoothing(canUseSmoothing); exporter.exportTo(shapeNum, image, unzoom, transformation, strokeTransformation, scaleStrokes); } - private BitmapExporter(int shapeNum, SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform) { - super(shapeNum, swf, shape, colorTransform); + private BitmapExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform) { + super(windingRule, shapeNum, swf, shape, colorTransform); this.swf = swf; this.defaultColor = defaultColor; + path = new GeneralPath(windingRule == ShapeTag.WIND_NONZERO ? GeneralPath.WIND_NON_ZERO : GeneralPath.WIND_EVEN_ODD); } private void exportTo(int shapeNum, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, boolean scaleStrokes) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java index 3f9f21e93..13fa0b8b5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.Point; import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.GRADIENT; @@ -150,8 +151,8 @@ public class CanvasShapeExporter extends ShapeExporterBase { return shapeData.toString(); } - public CanvasShapeExporter(int shapeNum, RGB basicFill, double unitDivisor, SWF swf, SHAPE shape, ColorTransform colorTransform, int deltaX, int deltaY) { - super(shapeNum, swf, shape, colorTransform); + public CanvasShapeExporter(int windingRule, int shapeNum, RGB basicFill, double unitDivisor, SWF swf, SHAPE shape, ColorTransform colorTransform, int deltaX, int deltaY) { + super(windingRule, shapeNum, swf, shape, colorTransform); this.swf = swf; this.unitDivisor = unitDivisor; this.basicFill = basicFill; @@ -437,7 +438,7 @@ public class CanvasShapeExporter extends ShapeExporterBase { preLineFillData.append("\tlfctx.applyTransforms(ctx._matrix);\r\n"); preLineFillData.append("\tctx = lfctx;"); if (lineLastRadColor != null) { - preLineFillData.append("\tctx.fillStyle=").append(lineLastRadColor).append(";\r\n\tctx.fill(\"evenodd\");\r\n"); + preLineFillData.append("\tctx.fillStyle=").append(lineLastRadColor).append(";\r\n\tctx.fill(\"").append(windingRule == ShapeTag.WIND_NONZERO ? "nonzero" : "evenodd").append("\");\r\n"); } if (lineFillMatrix != null) { @@ -474,7 +475,7 @@ public class CanvasShapeExporter extends ShapeExporterBase { if (fillMatrix != null) { pathData.append(drawFill); if (lastRadColor != null) { - pathData.append("\tctx.fillStyle=").append(lastRadColor).append(";\r\n\tctx.fill(\"evenodd\");\r\n"); + pathData.append("\tctx.fillStyle=").append(lastRadColor).append(";\r\n\tctx.fill(\"").append(windingRule == ShapeTag.WIND_NONZERO ? "nonzero" : "evenodd").append("\");\r\n"); } pathData.append("\tctx.save();\r\n"); pathData.append("\tctx.clip();\r\n"); @@ -497,7 +498,7 @@ public class CanvasShapeExporter extends ShapeExporterBase { shapeData.append(pathData); } else { if (fillData != null && fillData.length() > 0) { - pathData.append(drawFill).append("\tctx.fill(\"evenodd\");\r\n"); + pathData.append(drawFill).append("\tctx.fill(\"").append(windingRule == ShapeTag.WIND_NONZERO ? "nonzero" : "evenodd").append("\");\r\n"); } shapeData.append(fillData).append(pathData); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/DefaultSVGShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/DefaultSVGShapeExporter.java index 108cd2dfc..d6b2ba8d7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/DefaultSVGShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/DefaultSVGShapeExporter.java @@ -41,8 +41,8 @@ public abstract class DefaultSVGShapeExporter extends ShapeExporterBase { protected boolean aliasedFill; - public DefaultSVGShapeExporter(int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform, double zoom) { - super(shapeNum, swf, shape, colorTransform); + public DefaultSVGShapeExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform, double zoom) { + super(windingRule, shapeNum, swf, shape, colorTransform); this.zoom = zoom; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/PathExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/PathExporter.java index 974632433..b31c5f6a7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/PathExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/PathExporter.java @@ -43,19 +43,19 @@ public class PathExporter extends ShapeExporterBase { private GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); - public static List export(int shapeNum, SWF swf, SHAPE shape) { - return export(shapeNum, swf, shape, new ArrayList<>()); + public static List export(int windingRule, int shapeNum, SWF swf, SHAPE shape) { + return export(windingRule, shapeNum, swf, shape, new ArrayList<>()); } - public static List export(int shapeNum, SWF swf, SHAPE shape, List strokes) { - PathExporter exporter = new PathExporter(shapeNum, swf, shape, null); + public static List export(int windingRule, int shapeNum, SWF swf, SHAPE shape, List strokes) { + PathExporter exporter = new PathExporter(windingRule, shapeNum, swf, shape, null); exporter.export(); strokes.addAll(exporter.strokes); return exporter.paths; } - protected PathExporter(int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { - super(shapeNum, swf, shape, colorTransform); + protected PathExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { + super(windingRule, shapeNum, swf, shape, colorTransform); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java index 2ba22c273..83a743d60 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.FILLSTYLE; @@ -52,8 +53,8 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter { private final SVGExporter exporter; - public SVGShapeExporter(int shapeNum, SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) { - super(shapeNum, swf, shape, colorTransform, zoom); + public SVGShapeExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) { + super(windingRule, shapeNum, swf, shape, colorTransform, zoom); this.swf = swf; this.id = id; this.defaultColor = defaultColor; @@ -73,7 +74,7 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter { if (color != null) { path.setAttribute("fill", color.toHexRGB()); } - path.setAttribute("fill-rule", "evenodd"); + path.setAttribute("fill-rule", windingRule == ShapeTag.WIND_NONZERO ? "nonzero" : "evenodd"); if (color instanceof RGBA) { RGBA colorA = (RGBA) color; if (colorA.alpha != 255) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java index 2e026677e..d04d40b62 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/ShapeExporterBase.java @@ -64,10 +64,15 @@ public abstract class ShapeExporterBase implements IShapeExporter { private final ColorTransform colorTransform; private boolean canUseSmoothing = true; + + protected int windingRule; + + - public ShapeExporterBase(int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { + public ShapeExporterBase(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { this.shape = shape; this.colorTransform = colorTransform; + this.windingRule = windingRule; Cache cache = swf.getShapeExportDataCache(); ShapeExportData cachedData = cache.get(shape); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java index b5a52df3d..c37dcfb63 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImporter.java @@ -104,6 +104,7 @@ public class SvgImporter { if (st instanceof DefineShape4Tag) { DefineShape4Tag shape4 = (DefineShape4Tag) st; + shape4.usesFillWindingRule = false; shape4.usesNonScalingStrokes = false; shape4.usesScalingStrokes = false; } @@ -371,6 +372,14 @@ public class SvgImporter { } private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List commands, Matrix transform, SvgStyle style) { + + if ("nonzero".equals(style.getFillRule())) { + if (shapeTag instanceof DefineShape4Tag) { + DefineShape4Tag shape4 = (DefineShape4Tag) shapeTag; + shape4.usesFillWindingRule = true; + } + } + Matrix transform2 = transform.preConcatenate(Matrix.getScaleInstance(SWF.unitDivisor)); Point prevPoint = new Point(0, 0); Point startPoint = prevPoint; @@ -1192,7 +1201,7 @@ public class SvgImporter { st = (DefineShape4Tag) (new SvgImporter().importSvg(st, svgDataS, false)); swf.addTag(st); SerializableImage si = new SerializableImage(480, 360, BufferedImage.TYPE_4BYTE_ABGR); - BitmapExporter.export(st.getShapeNum(), swf, st.shapes, Color.yellow, si, 1, new Matrix(), new Matrix(), null, true, true); + BitmapExporter.export(st.getWindingRule(), st.getShapeNum(), swf, st.shapes, Color.yellow, si, 1, new Matrix(), new Matrix(), null, true, true); List li = new ArrayList<>(); li.add(st); ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png")); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java index c29826af4..8e50e0eb8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java @@ -168,6 +168,10 @@ class SvgStyle { public double getFillOpacity() { return getValue(element, "fill-opacity"); } + + public String getFillRule() { + return getValue(element, "fill-rule"); + } public SvgFill getStroke() { return getValue(element, "stroke"); @@ -648,6 +652,9 @@ class SvgStyle { double opacity = Double.parseDouble(value); return opacity; } + case "fill-rule": { + return value; + } case "stroke": { if ("currentColor".equals(value)) { return new SvgColor(style.getColor()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java index f813bf909..9f9835feb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java @@ -58,6 +58,7 @@ public class SvgStyleProperty { p.put("color", new SvgStyleProperty("color", true, Color.BLACK /* depends on user agent */)); p.put("fill", new SvgStyleProperty("fill", true, new SvgColor(Color.BLACK))); p.put("fill-opacity", new SvgStyleProperty("fill-opacity", true, 1.0)); + p.put("fill-rule", new SvgStyleProperty("fill-rule", true, "nonzero")); p.put("stroke", new SvgStyleProperty("stroke", true, null)); p.put("stroke-width", new SvgStyleProperty("stroke-width", true, 1.0)); p.put("stroke-opacity", new SvgStyleProperty("stroke-opacity", true, 1.0)); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index a80427596..ee1a0593d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -209,7 +209,7 @@ public class DefineFont2Tag extends FontTag { return; } try { - sos3.writeSHAPE(glyphShapeTable.get(i), 1); + sos3.writeSHAPE(1, 0, glyphShapeTable.get(i), 1); } catch (IOException ex) { //should not happen return; @@ -266,7 +266,7 @@ public class DefineFont2Tag extends FontTag { SWFOutputStream sos3 = new SWFOutputStream(baosGlyphShapes, getVersion(), getCharset()); for (int i = 0; i < numGlyphs; i++) { offsetTable.add((glyphShapeTable.size() + 1/*CodeTableOffset*/) * (fontFlagsWideOffsets ? 4 : 2) + sos3.getPos()); - sos3.writeSHAPE(glyphShapeTable.get(i), 1); + sos3.writeSHAPE(1, 0, glyphShapeTable.get(i), 1); } byte[] baGlyphShapes = baosGlyphShapes.toByteArray(); for (Long offset : offsetTable) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index e41ff7d67..c329bed13 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -196,7 +196,7 @@ public class DefineFont3Tag extends FontTag { return; } try { - sos3.writeSHAPE(glyphShapeTable.get(i), 1); + sos3.writeSHAPE(1, 0, glyphShapeTable.get(i), 1); } catch (IOException ex) { //should not happen return; @@ -239,7 +239,7 @@ public class DefineFont3Tag extends FontTag { int numGlyphs = glyphShapeTable.size(); for (int i = 0; i < numGlyphs; i++) { offsetTable.add(sos3.getPos()); - sos3.writeSHAPE(glyphShapeTable.get(i), 1); + sos3.writeSHAPE(1, 0, glyphShapeTable.get(i), 1); } byte[] baGlyphShapes = baosGlyphShapes.toByteArray(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index 0f925a56f..f5c0669c7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -114,7 +114,7 @@ public class DefineFontTag extends FontTag { SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion(), getCharset()); for (SHAPE shape : glyphShapeTable) { offsetTable.add(glyphShapeTable.size() * 2 + (int) sos2.getPos()); - sos2.writeSHAPE(shape, 1); + sos2.writeSHAPE(1, 0, shape, 1); } for (int offset : offsetTable) { sos.writeUI16(offset); @@ -323,7 +323,7 @@ public class DefineFontTag extends FontTag { return false; } try { - sos2.writeSHAPE(shape, 1); + sos2.writeSHAPE(1, 0, shape, 1); } catch (IOException ex) { //should not happen } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java index c3b3fb594..9e8a38d66 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java @@ -127,11 +127,11 @@ public class DefineMorphShape2Tag extends MorphShapeTag { SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion(), getCharset()); sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 2); sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 2); - sos2.writeSHAPE(startEdges, 2); + sos2.writeSHAPE(morphFillStyles.fillStyles.length, morphLineStyles.lineStyles2.length, startEdges, 2); byte[] ba2 = baos2.toByteArray(); sos.writeUI32(ba2.length); sos.write(ba2); - sos.writeSHAPE(endEdges, 2); + sos.writeSHAPE(morphFillStyles.fillStyles.length, morphLineStyles.lineStyles2.length, endEdges, 2); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java index c3af75766..2b4a2c0e1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java @@ -99,11 +99,11 @@ public class DefineMorphShapeTag extends MorphShapeTag { SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion(), getCharset()); sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 1); sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 1); - sos2.writeSHAPE(startEdges, 1); + sos2.writeSHAPE(morphFillStyles.fillStyles.length, morphLineStyles.lineStyles.length, startEdges, 1); byte[] ba2 = baos2.toByteArray(); sos.writeUI32(ba2.length); sos.write(ba2); - sos.writeSHAPE(endEdges, 1); + sos.writeSHAPE(morphFillStyles.fillStyles.length, morphLineStyles.lineStyles.length, endEdges, 1); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java index 01c671129..d41bf83a0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java @@ -83,4 +83,9 @@ public class DefineShape2Tag extends ShapeTag { public int getShapeNum() { return 2; } + + @Override + public int getWindingRule() { + return WIND_EVEN_ODD; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java index dd365a604..442225cf2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java @@ -83,4 +83,9 @@ public class DefineShape3Tag extends ShapeTag { public int getShapeNum() { return 3; } + + @Override + public int getWindingRule() { + return WIND_EVEN_ODD; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java index 5dc7a32ed..ddb25099a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java @@ -117,4 +117,11 @@ public class DefineShape4Tag extends ShapeTag { edgeBounds = SHAPERECORD.getBounds(shapes.shapeRecords, null, 4, true); } + @Override + public int getWindingRule() { + if (usesFillWindingRule) { + return WIND_NONZERO; + } + return WIND_EVEN_ODD; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java index a43343213..4322e320e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java @@ -83,4 +83,9 @@ public class DefineShapeTag extends ShapeTag { public int getShapeNum() { return 1; } + + @Override + public int getWindingRule() { + return WIND_EVEN_ODD; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index 53533bbf6..c932248af 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -440,7 +440,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag { @Override public synchronized void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing) { - SHAPERECORD.shapeListToImage(1, swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); + SHAPERECORD.shapeListToImage(ShapeTag.WIND_EVEN_ODD, 1, swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); } @Override @@ -457,7 +457,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag { String cs = "" + c; cs = cs.replace("\\", "\\\\").replace("\"", "\\\""); result.append("\t\tcase \"").append(cs).append("\":\r\n"); - CanvasShapeExporter exporter = new CanvasShapeExporter(1, null, unitDivisor, swf, shapes.get(i), null, 0, 0); + CanvasShapeExporter exporter = new CanvasShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, null, unitDivisor, swf, shapes.get(i), null, 0, 0); exporter.export(); result.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t")); result.append("\tbreak;\r\n"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java index 2b62eb904..968ff5a52 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java @@ -259,18 +259,18 @@ public abstract class ImageTag extends DrawableTag { @Override public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing) { - BitmapExporter.export(1, swf, getShape(1), null, image, unzoom, transformation, strokeTransformation, colorTransform, true, canUseSmoothing); + BitmapExporter.export(ShapeTag.WIND_EVEN_ODD, 1, swf, getShape(1), null, image, unzoom, transformation, strokeTransformation, colorTransform, true, canUseSmoothing); } @Override public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { - SVGShapeExporter shapeExporter = new SVGShapeExporter(1, swf, getShape(1), getCharacterId(), exporter, null, colorTransform, 1); + SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, getShape(1), getCharacterId(), exporter, null, colorTransform, 1); shapeExporter.export(); } @Override public void toHtmlCanvas(StringBuilder result, double unitDivisor) { - CanvasShapeExporter cse = new CanvasShapeExporter(1, null, unitDivisor, swf, getShape(1), null, 0, 0); + CanvasShapeExporter cse = new CanvasShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, null, unitDivisor, swf, getShape(1), null, 0, 0); cse.export(); result.append(cse.getShapeData()); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java index 89c154827..2eb6e56a6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/MorphShapeTag.java @@ -323,7 +323,7 @@ public abstract class MorphShapeTag extends DrawableTag { // todo: Currently the generated image is not cached, because the cache // key contains the hashCode of the finalRecord object, and it is always // recreated - BitmapExporter.export(getShapeNum() == 2 ? 4 : 1, swf, shape, null, image, unzoom, transformation, strokeTransformation, colorTransform, scaleStrokes, canUseSmoothing); + BitmapExporter.export(ShapeTag.WIND_EVEN_ODD /*??? FIXME*/, getShapeNum() == 2 ? 4 : 1, swf, shape, null, image, unzoom, transformation, strokeTransformation, colorTransform, scaleStrokes, canUseSmoothing); } @Override @@ -335,7 +335,7 @@ public abstract class MorphShapeTag extends DrawableTag { shapeExporter.export(); } else { SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); - SVGShapeExporter shapeExporter = new SVGShapeExporter(getShapeNum() == 2 ? 4 : 1, swf, shapes, getCharacterId(), exporter, null, colorTransform, 1); + SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD /*??? FIXME*/, getShapeNum() == 2 ? 4 : 1, swf, shapes, getCharacterId(), exporter, null, colorTransform, 1); shapeExporter.export(); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java index ae857bc75..003448a66 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java @@ -66,6 +66,10 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { protected ByteArrayRange shapeData; private final int markerSize = 10; + + public static final int WIND_EVEN_ODD = 0; + + public static final int WIND_NONZERO = 1; public ShapeTag(SWF swf, int id, String name, ByteArrayRange data) { super(swf, id, name, data); @@ -75,6 +79,8 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { public void load() { getShapes(); } + + public abstract int getWindingRule(); public abstract int getShapeNum(); @@ -189,9 +195,9 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { @Override public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing) { - BitmapExporter.export(getShapeNum(), swf, getShapes(), null, image, unzoom, transformation, strokeTransformation, colorTransform, scaleStrokes, canUseSmoothing); + BitmapExporter.export(getWindingRule(), getShapeNum(), swf, getShapes(), null, image, unzoom, transformation, strokeTransformation, colorTransform, scaleStrokes, canUseSmoothing); if (Configuration._debugMode.get()) { // show control points - List paths = PathExporter.export(getShapeNum(), swf, getShapes()); + List paths = PathExporter.export(getWindingRule(), getShapeNum(), swf, getShapes()); double[] coords = new double[6]; AffineTransform at = transformation.toTransform(); at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor)); @@ -233,13 +239,13 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject { @Override public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { - SVGShapeExporter shapeExporter = new SVGShapeExporter(getShapeNum(), swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1); + SVGShapeExporter shapeExporter = new SVGShapeExporter(getWindingRule(), getShapeNum(), swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1); shapeExporter.export(); } @Override public void toHtmlCanvas(StringBuilder result, double unitDivisor) { - CanvasShapeExporter cse = new CanvasShapeExporter(getShapeNum(), null, unitDivisor, swf, getShapes(), null, 0, 0); + CanvasShapeExporter cse = new CanvasShapeExporter(getWindingRule(), getShapeNum(), null, unitDivisor, swf, getShapes(), null, 0, 0); cse.export(); result.append(cse.getShapeData()); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index 70a542888..2f4e991ac 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -423,7 +423,7 @@ public abstract class TextTag extends DrawableTag { Graphics2D g = (Graphics2D) image.getGraphics(); Matrix mat = transformation.clone(); mat = mat.concatenate(new Matrix(textMatrix)); - BitmapExporter.export(1, swf, getBorderShape(borderColor, fillColor, rect), null, image, 1 /*FIXME??*/, mat, mat, colorTransform, true, false); + BitmapExporter.export(ShapeTag.WIND_EVEN_ODD, 1, swf, getBorderShape(borderColor, fillColor, rect), null, image, 1 /*FIXME??*/, mat, mat, colorTransform, true, false); } public static void drawBorderHtmlCanvas(SWF swf, StringBuilder result, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double unitDivisor) { @@ -431,7 +431,7 @@ public abstract class TextTag extends DrawableTag { result.append("\tctx.save();\r\n"); result.append("\tctx.transform(").append(mat.scaleX).append(",").append(mat.rotateSkew0).append(",").append(mat.rotateSkew1).append(",").append(mat.scaleY).append(",").append(mat.translateX).append(",").append(mat.translateY).append(");\r\n"); SHAPE shape = getBorderShape(borderColor, fillColor, rect); - CanvasShapeExporter cse = new CanvasShapeExporter(1, null, unitDivisor, swf, shape, colorTransform, 0, 0); + CanvasShapeExporter cse = new CanvasShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, null, unitDivisor, swf, shape, colorTransform, 0, 0); cse.export(); result.append(cse.getShapeData()); result.append("\tctx.restore();\r\n"); @@ -440,7 +440,7 @@ public abstract class TextTag extends DrawableTag { public static void drawBorderSVG(SWF swf, SVGExporter exporter, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double zoom) { exporter.createSubGroup(new Matrix(textMatrix), null); SHAPE shape = getBorderShape(borderColor, fillColor, rect); - SVGShapeExporter shapeExporter = new SVGShapeExporter(1, swf, shape, 0, exporter, null, colorTransform, zoom); + SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, zoom); shapeExporter.export(); exporter.endGroup(); } @@ -513,7 +513,7 @@ public abstract class TextTag extends DrawableTag { } if (shape != null) { - BitmapExporter.export(1, swf, shape, textColor2, image, 1 /*FIXME??*/, mat, mat, colorTransform, true, false); + BitmapExporter.export(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, textColor2, image, 1 /*FIXME??*/, mat, mat, colorTransform, true, false); if (SHAPERECORD.DRAW_BOUNDING_BOX) { RGB borderColor = new RGBA(Color.black); RGB fillColor = new RGBA(new Color(255, 255, 255, 0)); @@ -761,7 +761,7 @@ public abstract class TextTag extends DrawableTag { if (charId == null) { charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontNameIntag() + "_" + ch)); exporter.createDefGroup(null, charId); - SVGShapeExporter shapeExporter = new SVGShapeExporter(1, swf, shape, 0, exporter, null, colorTransform, zoom); + SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, zoom); shapeExporter.export(); if (!exporter.endGroup()) { charId = ""; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java index 0a7c15dc3..7f7f302c7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/SHAPE.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.types; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.shape.PathExporter; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.types.annotations.SWFArray; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; @@ -108,7 +109,7 @@ public class SHAPE implements NeedsCharacters, Serializable { } List strokes = new ArrayList<>(); - List paths = PathExporter.export(shapeNum, swf, this, strokes); + List paths = PathExporter.export(ShapeTag.WIND_EVEN_ODD, shapeNum, swf, this, strokes); boolean large = shapeRecords.size() > 500; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index 9846572ee..c3d114cae 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.helpers.FontHelper; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.LINESTYLE; @@ -197,7 +198,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali return ret; } - public static void shapeListToImage(int shapeNum, SWF swf, List shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) { + public static void shapeListToImage(int windingRule, int shapeNum, SWF swf, List shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) { if (shapes.isEmpty()) { return; } @@ -281,7 +282,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali Matrix transformation = Matrix.getTranslateInstance(px, py); transformation.scale(ratio); - BitmapExporter.export(shapeNum, swf, shape, color, image, 1 /*FIXME??*/, transformation, transformation, colorTransform, true, true); + BitmapExporter.export(windingRule, shapeNum, swf, shape, color, image, 1 /*FIXME??*/, transformation, transformation, colorTransform, true, true); // draw bounding boxes if (DRAW_BOUNDING_BOX) {