From c9e47792241811cd280253c610df0c783c4877c1 Mon Sep 17 00:00:00 2001 From: Honfika Date: Thu, 17 Apr 2014 17:53:17 +0200 Subject: [PATCH] #559 morphshapes as animated SVG/CSS3: animate styles --- .../DefaultSVGMorphShapeExporter.java | 8 +- .../flash/exporters/IMorphShapeExporter.java | 8 +- .../exporters/MorphShapeExporterBase.java | 28 +++++-- .../exporters/SVGMorphShapeExporter.java | 80 +++++++++++++++---- .../flash/exporters/ShapeExporterBase.java | 3 +- 5 files changed, 95 insertions(+), 32 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGMorphShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGMorphShapeExporter.java index 1797d1f1a..f3b679b43 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGMorphShapeExporter.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/DefaultSVGMorphShapeExporter.java @@ -65,12 +65,12 @@ public abstract class DefaultSVGMorphShapeExporter extends MorphShapeExporterBas } @Override - public void beginFill(RGB color) { + public void beginFill(RGB color, RGB colorEnd) { finalizePath(); } @Override - public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio) { finalizePath(); } @@ -85,12 +85,12 @@ public abstract class DefaultSVGMorphShapeExporter extends MorphShapeExporterBas } @Override - public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { + public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { finalizePath(); } @Override - public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio) { } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/IMorphShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/IMorphShapeExporter.java index 3dc28fabc..98b8dc488 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/IMorphShapeExporter.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/IMorphShapeExporter.java @@ -38,17 +38,17 @@ public interface IMorphShapeExporter { public void endLines(); - public void beginFill(RGB color); + public void beginFill(RGB color, RGB colorEnd); - public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio); + public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio); public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform); public void endFill(); - public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit); + public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit); - public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio); + public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio); public void moveTo(double x, double y, double x2, double y2); diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporterBase.java b/trunk/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporterBase.java index ce9f4806b..7e30079d9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporterBase.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporterBase.java @@ -82,7 +82,7 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { // Create edge maps _fillEdgeMaps = new ArrayList<>(); _lineEdgeMaps = new ArrayList<>(); - createEdgeMaps(_fillStyles, _lineStyles, _fillEdgeMaps, _lineEdgeMaps); + createEdgeMaps(_fillStyles, _lineStyles, _fillStylesEnd, _lineStylesEnd, _fillEdgeMaps, _lineEdgeMaps); // Let the doc handler know that a shape export starts beginShape(); @@ -97,7 +97,8 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { endShape(); } - protected void createEdgeMaps(List fillStyles, List lineStyles, + protected void createEdgeMaps(List fillStyles, List lineStyles, + List fillStylesEnd, List lineStylesEnd, List>> fillEdgeMaps, List>> lineEdgeMaps) { if (!edgeMapsCreated) { int xPos = 0; @@ -137,6 +138,8 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { lineStyleIdxOffset = lineStyles.size(); appendFillStyles(fillStyles, styleChangeRecord.fillStyles.fillStyles); appendLineStyles(lineStyles, styleChangeRecord.lineStyles.lineStyles); + appendFillStyles(fillStylesEnd, styleChangeRecord.fillStyles.fillStyles); + appendLineStyles(lineStylesEnd, styleChangeRecord.lineStyles.lineStyles); } // Check if all styles are reset to 0. // This (probably) means that a new group starts with the next record @@ -294,21 +297,25 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE); try { Matrix matrix; + Matrix matrixEnd; FILLSTYLE fillStyle = _fillStyles.get(fillStyleIdx - 1); + FILLSTYLE fillStyleEnd = _fillStylesEnd.get(fillStyleIdx - 1); switch (fillStyle.fillStyleType) { case FILLSTYLE.SOLID: // Solid fill - beginFill(colorTransform.apply(fillStyle.color)); + beginFill(colorTransform.apply(fillStyle.color), colorTransform.apply(fillStyleEnd.color)); break; case FILLSTYLE.LINEAR_GRADIENT: case FILLSTYLE.RADIAL_GRADIENT: case FILLSTYLE.FOCAL_RADIAL_GRADIENT: // Gradient fill matrix = new Matrix(fillStyle.gradientMatrix); + matrixEnd = new Matrix(fillStyleEnd.gradientMatrix); beginGradientFill( fillStyle.fillStyleType, colorTransform.apply(fillStyle.gradient.gradientRecords), matrix, + matrixEnd, fillStyle.gradient.spreadMode, fillStyle.gradient.interpolationMode, (fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0 @@ -332,7 +339,7 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { } catch (Exception ex) { // Font shapes define no fillstyles per se, but do reference fillstyle index 1, // which represents the font color. We just report null in this case. - beginFill(null); + beginFill(null, null); } } if (!pos.equals(e.getFrom())) { @@ -357,7 +364,6 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { List path = createPathFromEdgeMap(_lineEdgeMaps.get(groupIndex)); PointInt pos = new PointInt(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++) { @@ -365,10 +371,12 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { if (lineStyleIdx != e.getLineStyleIdx()) { lineStyleIdx = e.getLineStyleIdx(); pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE); + LINESTYLE lineStyle = null; + LINESTYLE lineStyleEnd = null; try { lineStyle = _lineStyles.get(lineStyleIdx - 1); + lineStyleEnd = _lineStylesEnd.get(lineStyleIdx - 1); } catch (Exception ex) { - lineStyle = null; } if (lineStyle != null) { String scaleMode = "NORMAL"; @@ -396,7 +404,9 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { } lineStyle( lineStyle.width, + lineStyleEnd.width, colorTransform.apply(lineStyle.color), + colorTransform.apply(lineStyleEnd.color), pixelHintingFlag, scaleMode, startCapStyle, @@ -407,16 +417,20 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { if (hasFillFlag) { LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle; FILLSTYLE fillStyle = lineStyle2.fillType; + LINESTYLE2 lineStyle2End = (LINESTYLE2) lineStyleEnd; + FILLSTYLE fillStyleEnd = lineStyle2End.fillType; switch (fillStyle.fillStyleType) { case FILLSTYLE.LINEAR_GRADIENT: case FILLSTYLE.RADIAL_GRADIENT: case FILLSTYLE.FOCAL_RADIAL_GRADIENT: // Gradient fill Matrix matrix = new Matrix(fillStyle.gradientMatrix); + Matrix matrixEnd = new Matrix(fillStyleEnd.gradientMatrix); lineGradientStyle( fillStyle.fillStyleType, fillStyle.gradient.gradientRecords, matrix, + matrixEnd, fillStyle.gradient.spreadMode, fillStyle.gradient.interpolationMode, (fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0 @@ -426,7 +440,7 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter { } } else { // We should never get here - lineStyle(1, new RGB(Color.BLACK), false, "NORMAL", 0, 0, 0, 3); + lineStyle(1, 1, new RGB(Color.BLACK), new RGB(Color.BLACK), false, "NORMAL", 0, 0, 0, 3); } } if (!e.getFrom().equals(pos)) { diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/SVGMorphShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/SVGMorphShapeExporter.java index e89759439..75793b04c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/SVGMorphShapeExporter.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/SVGMorphShapeExporter.java @@ -120,13 +120,17 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { } @Override - public void beginFill(RGB color) { + public void beginFill(RGB color, RGB colorEnd) { if (color == null) { color = new RGB(Color.BLACK); } + if (colorEnd == null) { + colorEnd = new RGB(Color.BLACK); + } finalizePath(); path.setAttribute("stroke", "none"); path.setAttribute("fill", color.toHexRGB()); + path.appendChild(createAnimateElement("fill", color.toHexRGB(), colorEnd.toHexRGB())); if (color instanceof RGBA) { RGBA colorA = (RGBA) color; if (colorA.alpha != 255) { @@ -136,12 +140,12 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { } @Override - public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio) { finalizePath(); Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT) ? _svg.createElement("linearGradient") : _svg.createElement("radialGradient"); - populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio); + populateGradientElement(gradient, type, gradientRecords, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio); int id = gradients.indexOf(gradient); if (id < 0) { // todo: filter same gradients @@ -218,20 +222,26 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { } @Override - public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { + public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { finalizePath(); if (thickness > maxLineWidth) { maxLineWidth = thickness; } thickness /= SWF.unitDivisor; + thicknessEnd /= SWF.unitDivisor; path.setAttribute("fill", "none"); path.setAttribute("stroke", color.toHexRGB()); + path.appendChild(createAnimateElement("stroke", color.toHexRGB(), colorEnd.toHexRGB())); path.setAttribute("stroke-width", Double.toString(thickness == 0 ? 1 : thickness)); + path.appendChild(createAnimateElement("stroke-width", thickness, thicknessEnd)); + if (color instanceof RGBA) { RGBA colorA = (RGBA) color; if (colorA.alpha != 255) { path.setAttribute("stroke-opacity", Float.toString(colorA.getAlphaFloat())); } + RGBA colorAEnd = (RGBA) colorEnd; + path.appendChild(createAnimateElement("fill-opacity", colorA.getAlphaFloat(), colorAEnd.getAlphaFloat())); } switch (startCaps) { case LINESTYLE2.NO_CAP: @@ -261,12 +271,12 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { } @Override - public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, 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, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio); + populateGradientElement(gradient, type, gradientRecords, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio); int id = gradients.indexOf(gradient); if (id < 0) { // todo: filter same gradients @@ -279,23 +289,27 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { _svgDefs.appendChild(gradient); } + private Element createAnimateElement(String attributeName, Object startValue, Object endValue) { + Element animate = _svg.createElement("animate"); + animate.setAttribute("dur", "2s"); // todo + animate.setAttribute("repeatCount", "indefinite"); + animate.setAttribute("attributeName", attributeName); + animate.setAttribute("values", startValue + ";" + endValue); + return animate; + } + @Override protected void finalizePath() { if (path != null && !"".equals(pathData)) { path.setAttribute("d", pathData.trim()); - Element animatePath = _svg.createElement("animate"); - animatePath.setAttribute("dur", "2s"); // todo - animatePath.setAttribute("repeatCount", "indefinite"); - animatePath.setAttribute("attributeName", "d"); - animatePath.setAttribute("values", pathData.trim() + ";" + pathDataEnd.trim()); - path.appendChild(animatePath); + path.appendChild(createAnimateElement("d", pathData.trim(), pathDataEnd.trim())); _svgG.appendChild(path); } path = _svg.createElement("path"); super.finalizePath(); } - protected void populateGradientElement(Element gradient, int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + protected void populateGradientElement(Element gradient, int type, GRADRECORD[] gradientRecords, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio) { gradient.setAttribute("gradientUnits", "userSpaceOnUse"); if (type == FILLSTYLE.LINEAR_GRADIENT) { gradient.setAttribute("x1", "-819.2"); @@ -330,8 +344,44 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter { double rotateSkew1 = roundPixels400(matrix.rotateSkew1); double scaleX = roundPixels400(matrix.scaleX); double scaleY = roundPixels400(matrix.scaleY); - gradient.setAttribute("gradientTransform", "matrix(" + scaleX + ", " + rotateSkew0 - + ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"); + String matrixStr = "matrix(" + scaleX + ", " + rotateSkew0 + + ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"; + gradient.setAttribute("gradientTransform", matrixStr); + + double translateXEnd = roundPixels400(matrixEnd.translateX / SWF.unitDivisor); + double translateYEnd = roundPixels400(matrixEnd.translateY / SWF.unitDivisor); + double rotateSkew0End = roundPixels400(matrixEnd.rotateSkew0); + double rotateSkew1End = roundPixels400(matrixEnd.rotateSkew1); + double scaleXEnd = roundPixels400(matrixEnd.scaleX); + double scaleYEnd = roundPixels400(matrixEnd.scaleY); + + Element animateRotate = _svg.createElement("animateTransform"); + animateRotate.setAttribute("dur", "2s"); // todo + animateRotate.setAttribute("repeatCount", "indefinite"); + animateRotate.setAttribute("attributeName", "gradientTransform"); + animateRotate.setAttribute("type", "rotate"); + animateRotate.setAttribute("from", rotateSkew0 + " " + rotateSkew1); + animateRotate.setAttribute("to", rotateSkew0End + " " + rotateSkew1End); + + Element animateScale = _svg.createElement("animateTransform"); + animateScale.setAttribute("dur", "2s"); // todo + animateScale.setAttribute("repeatCount", "indefinite"); + animateScale.setAttribute("attributeName", "gradientTransform"); + animateScale.setAttribute("type", "scale"); + animateScale.setAttribute("from", scaleX + " " + scaleY); + animateScale.setAttribute("to", scaleXEnd + " " + scaleYEnd); + + Element animateTranslate = _svg.createElement("animateTransform"); + animateTranslate.setAttribute("dur", "2s"); // todo + animateTranslate.setAttribute("repeatCount", "indefinite"); + animateTranslate.setAttribute("attributeName", "gradientTransform"); + animateTranslate.setAttribute("type", "translate"); + animateTranslate.setAttribute("from", translateX + " " + translateY); + animateTranslate.setAttribute("to", translateXEnd + " " + translateYEnd); + + gradient.appendChild(animateRotate); + gradient.appendChild(animateScale); + gradient.appendChild(animateTranslate); } for (int i = 0; i < gradientRecords.length; i++) { GRADRECORD record = gradientRecords[i]; diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java index c5d827a07..85947a12e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java +++ b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporterBase.java @@ -299,7 +299,6 @@ public abstract class ShapeExporterBase implements IShapeExporter { List path = createPathFromEdgeMap(_lineEdgeMaps.get(groupIndex)); PointInt pos = new PointInt(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++) { @@ -307,10 +306,10 @@ public abstract class ShapeExporterBase implements IShapeExporter { if (lineStyleIdx != e.getLineStyleIdx()) { lineStyleIdx = e.getLineStyleIdx(); pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE); + LINESTYLE lineStyle = null; try { lineStyle = _lineStyles.get(lineStyleIdx - 1); } catch (Exception ex) { - lineStyle = null; } if (lineStyle != null) { String scaleMode = "NORMAL";