diff --git a/CHANGELOG.md b/CHANGELOG.md index 2abde7ef6..d0b520f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - AS3 Direct editation - Error when accessing inaccessible namespace - AS3 ambiguous namespace detection (back again) - [#2648] Dockerfile -- SVG export - Gradient bevel filter +- SVG export - Gradient bevel filter, Gradient glow filter ### Fixed - [#2643] APNG export - images containing multiple IDAT chunks diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java index 982562928..93831231d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java @@ -129,11 +129,13 @@ public class BEVELFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - RGBA shadowColorTransparent = new RGBA(shadowColor); - shadowColorTransparent.alpha = 0; - RGBA highlightColorTransparent = new RGBA(highlightColor); - highlightColorTransparent.alpha = 0; - return bevelSvg(distance, angle, new RGBA[]{shadowColor, shadowColorTransparent, highlightColorTransparent, highlightColor}, new int[]{0, 127, 128, 255}, knockout, onTop, innerShadow, blurX, blurY, strength, passes, document, filtersElement, exporter, in); + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + return SvgFiltering.bevel(distance, angle, highlightColor, shadowColor, knockout, compositeSource, type, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java index 9283a3c7a..b453f4e93 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java @@ -80,7 +80,7 @@ public class BLURFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - return blurSvg(blurX, blurY, passes, document, filtersElement, exporter, in); + return SvgFiltering.blur(blurX, blurY, passes, document, filtersElement, exporter, in); } @Override @@ -116,6 +116,5 @@ public class BLURFILTER extends FILTER { } return this.reserved == other.reserved; } - - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java index e27ed72f2..ea6123e45 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java @@ -172,6 +172,7 @@ public final class BlendComposite implements Composite { /** * Gets alpha. + * * @return Alpha */ public float getAlpha() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java index 4485d5c2a..d12c28544 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java @@ -119,5 +119,4 @@ public class COLORMATRIXFILTER extends FILTER { return Arrays.equals(this.matrix, other.matrix); } - } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java index 3b69c6f73..231fdef57 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java @@ -194,6 +194,5 @@ public class CONVOLUTIONFILTER extends FILTER { } return Objects.equals(this.defaultColor, other.defaultColor); } - - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java index 994f3a2f1..34c51d64d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java @@ -113,7 +113,7 @@ public class DROPSHADOWFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - return dropShadowSvg(distance, angle, dropShadowColor, innerShadow, knockout, compositeSource, blurX, blurY, strength, passes, document, filtersElement, exporter, in); + return SvgFiltering.dropShadow(distance, angle, dropShadowColor, innerShadow, knockout, compositeSource, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } @Override @@ -173,6 +173,5 @@ public class DROPSHADOWFILTER extends FILTER { } return Objects.equals(this.dropShadowColor, other.dropShadowColor); } - - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/FILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/FILTER.java index e2b64b052..742420c06 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/FILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/FILTER.java @@ -16,20 +16,13 @@ */ package com.jpexs.decompiler.flash.types.filters; -import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.ConcreteClasses; -import com.jpexs.helpers.GradientUtil; import com.jpexs.helpers.SerializableImage; -import java.awt.Color; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -101,293 +94,6 @@ public abstract class FILTER implements Serializable { */ public abstract String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in); - /** - * Converts drop shadow to SVG. - * - * @param distance Distance - * @param angle Angle - * @param dropShadowColor Drop shadow color - * @param innerShadow Inner shadow - * @param knockout Knockout - * @param compositeSource Composite source - * @param blurX Blur X - * @param blurY Blur Y - * @param strength Strength - * @param iterations Iterations - * @param document Document - * @param filtersElement Filters element - * @param exporter SVG exporter - * @param in Input - * @return SVG id of the drop shadow - */ - protected String dropShadowSvg( - double distance, - double angle, - RGBA dropShadowColor, - boolean innerShadow, - boolean knockout, - boolean compositeSource, - double blurX, - double blurY, - double strength, - int iterations, - Document document, - Element filtersElement, - SVGExporter exporter, - String in - ) { - return dropShadowSvg( - distance, - angle, - new RGBA[]{dropShadowColor}, - new int[0], - innerShadow, - knockout, - compositeSource, - blurX, - blurY, - strength, - iterations, - document, - filtersElement, - exporter, - in - ); - } - - /** - * Converts drop shadow to SVG. - * - * @param distance Distance - * @param angle Angle - * @param dropShadowColor Drop shadow color - * @param innerShadow Inner shadow - * @param knockout Knockout - * @param compositeSource Composite source - * @param blurX Blur X - * @param blurY Blur Y - * @param strength Strength - * @param iterations Iterations - * @param document Document - * @param filtersElement Filters element - * @param exporter SVG exporter - * @param in Input - * @return SVG id of the drop shadow - */ - protected String dropShadowSvg( - double distance, - double angle, - RGBA[] gradientColors, - int[] gradientRatio, - boolean innerShadow, - boolean knockout, - boolean compositeSource, - double blurX, - double blurY, - double strength, - int iterations, - Document document, - Element filtersElement, - SVGExporter exporter, - String in - ) { - double dx = distance * Math.cos(angle); - double dy = distance * Math.sin(angle); - - RGBA dropShadowColor = gradientColors.length == 1 ? gradientColors[0] : new RGBA(0, 0, 0, 255); - - if (innerShadow) { - - Element feFlood = document.createElement("feFlood"); - feFlood.setAttribute("flood-color", dropShadowColor.toHexRGB()); - feFlood.setAttribute("flood-opacity", "" + dropShadowColor.getAlphaFloat()); - filtersElement.appendChild(feFlood); - String feFloodResult = exporter.getUniqueId("filterResult"); - feFlood.setAttribute("result", feFloodResult); - - Element feOffset = document.createElement("feOffset"); - feOffset.setAttribute("dx", "" + dx); - feOffset.setAttribute("dy", "" + dy); - feOffset.setAttribute("in", in); - String feOffsetResult = exporter.getUniqueId("filterResult"); - feOffset.setAttribute("result", feOffsetResult); - filtersElement.appendChild(feOffset); - - Element feComposite1 = document.createElement("feComposite"); - feComposite1.setAttribute("in", feFloodResult); - feComposite1.setAttribute("in2", feOffsetResult); - feComposite1.setAttribute("operator", "out"); - String feComposite1Result = exporter.getUniqueId("filterResult"); - feComposite1.setAttribute("result", feComposite1Result); - filtersElement.appendChild(feComposite1); - - String blurResult = blurSvg(blurX, blurY, iterations, document, filtersElement, exporter, feComposite1Result); - - Element feComposite2 = document.createElement("feComposite"); - feComposite2.setAttribute("in", blurResult); - feComposite2.setAttribute("in2", in); - feComposite2.setAttribute("operator", "in"); - - String result = exporter.getUniqueId("filterResult"); - feComposite2.setAttribute("result", result); - filtersElement.appendChild(feComposite2); - - if (Double.compare(strength, 1.0) != 0) { - Element feColorMatrix2 = document.createElement("feColorMatrix"); - feColorMatrix2.setAttribute("type", "matrix"); - feColorMatrix2.setAttribute("in", result); - feColorMatrix2.setAttribute("values", - "1 0 0 0 0," - + "0 1 0 0 0," - + "0 0 1 0 0," - + "0 0 0 " + strength + " 0" - ); - result = exporter.getUniqueId("filterResult"); - feColorMatrix2.setAttribute("result", result); - filtersElement.appendChild(feColorMatrix2); - } - - if (knockout || !compositeSource) { - return result; - } - Element feComposite3 = document.createElement("feComposite"); - feComposite3.setAttribute("in", result); - feComposite3.setAttribute("in2", in); - feComposite3.setAttribute("operator", "over"); - - result = exporter.getUniqueId("filterResult"); - feComposite3.setAttribute("result", result); - filtersElement.appendChild(feComposite3); - return result; - } else { - Element feOffset = document.createElement("feOffset"); - feOffset.setAttribute("dx", "" + dx); - feOffset.setAttribute("dy", "" + dy); - feOffset.setAttribute("in", in); - String feOffsetResult = exporter.getUniqueId("filterResult"); - feOffset.setAttribute("result", feOffsetResult); - filtersElement.appendChild(feOffset); - - Element feColorMatrix = document.createElement("feColorMatrix"); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute("in", feOffsetResult); - feColorMatrix.setAttribute("values", - "0 0 0 0 " + (dropShadowColor.red / 255f) + "," - + "0 0 0 0 " + (dropShadowColor.green / 255f) + "," - + "0 0 0 0 " + (dropShadowColor.blue / 255f) + "," - + "0 0 0 1 0" - ); - String feColorMatrixResult = exporter.getUniqueId("filterResult"); - feColorMatrix.setAttribute("result", feColorMatrixResult); - filtersElement.appendChild(feColorMatrix); - - String result = blurSvg(blurX, blurY, iterations, document, filtersElement, exporter, feColorMatrixResult); - - if (Double.compare(strength, 1.0) != 0) { - Element feColorMatrix2 = document.createElement("feColorMatrix"); - feColorMatrix2.setAttribute("type", "matrix"); - feColorMatrix2.setAttribute("in", result); - feColorMatrix2.setAttribute("values", - "1 0 0 0 0," - + "0 1 0 0 0," - + "0 0 1 0 0," - + "0 0 0 " + strength + " 0" - ); - result = exporter.getUniqueId("filterResult"); - feColorMatrix2.setAttribute("result", result); - filtersElement.appendChild(feColorMatrix2); - } - - if (!knockout && !compositeSource) { - return result; - } - - Element feComposite = document.createElement("feComposite"); - if (knockout) { - feComposite.setAttribute("in", result); - feComposite.setAttribute("in2", in); - feComposite.setAttribute("operator", "out"); - } else { - feComposite.setAttribute("in", in); - feComposite.setAttribute("in2", result); - feComposite.setAttribute("operator", "over"); - } - - result = exporter.getUniqueId("filterResult"); - feComposite.setAttribute("result", result); - filtersElement.appendChild(feComposite); - return result; - } - } - - /** - * Converts blur to SVG. - * - * @param blurX Blur X - * @param blurY Blur Y - * @param iterations Iterations - * @param document Document - * @param filtersElement Filters element - * @param exporter SVG exporter - * @param in Input - * @return SVG id of the blur - */ - protected String blurSvg(double blurX, double blurY, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { - if (iterations == 0) { - return in; - } - if (Double.compare(blurX, 0.0) == 0 && Double.compare(blurY, 0.0) == 0) { - return in; - } - Element element; - if (Configuration.svgExportGaussianBlur.get()) { - double blurXScaled = blurX * exporter.getZoom(); - double blurYScaled = blurY * exporter.getZoom(); - - double deviationX = Math.sqrt((blurXScaled * blurXScaled - 1) / 12); - double deviationY = Math.sqrt((blurYScaled * blurYScaled - 1) / 12); - - element = document.createElement("feGaussianBlur"); - - element.setAttribute("stdDeviation", "" + deviationX + " " + deviationY); - } else { - element = document.createElement("feConvolveMatrix"); - - List parts = new ArrayList<>(); - - int orderX = (int) Math.ceil(blurX * exporter.getZoom()); - int orderY = (int) Math.ceil(blurY * exporter.getZoom()); - - if (orderX % 2 == 0) { - orderX++; - } - - if (orderY % 2 == 0) { - orderY++; - } - - double divisor = orderX * orderY; - element.setAttribute("order", "" + orderX + " " + orderY); - - for (int i = 0; i < divisor; i++) { - parts.add("1"); - } - - element.setAttribute("divisor", "" + divisor); - - element.setAttribute("kernelMatrix", String.join(" ", parts)); - element.setAttribute("kernelUnitLength", "1"); - } - element.setAttribute("in", in); - - String result = exporter.getUniqueId("filterResult"); - element.setAttribute("result", result); - - filtersElement.appendChild(element); - - return blurSvg(blurX, blurY, iterations - 1, document, filtersElement, exporter, result); - } - /** * Converts gradient ratios to Java format float ratios. * @@ -431,226 +137,4 @@ public abstract class FILTER implements Serializable { return output; } - - protected String bevelSvg(double distance, double angle, RGBA[] gradientColors, int[] gradientRatio, boolean knockout, boolean onTop, boolean innerShadow, double blurX, double blurY, double strength, int passes, Document document, Element filtersElement, SVGExporter exporter, String in) { - RGBA highlightColor = new RGBA(255, 0, 0, 255); - RGBA shadowColor = new RGBA(0, 0, 255, 255); - - int type = Filtering.INNER; - if (onTop && !innerShadow) { - type = Filtering.FULL; - } else if (!innerShadow) { - type = Filtering.OUTER; - } - - String shadowInner = null; - String hilightInner = null; - if (type != Filtering.OUTER) { - String hilight = dropShadowSvg(distance, angle, highlightColor, true, true, true, 0, 0, strength, passes, document, filtersElement, exporter, in); - String shadow = dropShadowSvg(distance, angle + Math.PI, shadowColor, true, true, true, 0, 0, strength, passes, document, filtersElement, exporter, in); - - Element feComposite1 = document.createElement("feComposite"); - feComposite1.setAttribute("in", hilight); - feComposite1.setAttribute("in2", shadow); - feComposite1.setAttribute("operator", "out"); - hilightInner = exporter.getUniqueId("filterResult"); - feComposite1.setAttribute("result", hilightInner); - filtersElement.appendChild(feComposite1); - - Element feComposite2 = document.createElement("feComposite"); - feComposite2.setAttribute("in", shadow); - feComposite2.setAttribute("in2", hilight); - feComposite2.setAttribute("operator", "out"); - shadowInner = exporter.getUniqueId("filterResult"); - feComposite2.setAttribute("result", shadowInner); - filtersElement.appendChild(feComposite2); - } - - String shadowOuter = null; - String hilightOuter = null; - - if (type != Filtering.INNER) { - String hilight = dropShadowSvg(distance, angle + Math.PI, highlightColor, false, true, true, 0, 0, strength, passes, document, filtersElement, exporter, in); - String shadow = dropShadowSvg(distance, angle, shadowColor, false, true, true, 0, 0, strength, passes, document, filtersElement, exporter, in); - - Element feComposite1 = document.createElement("feComposite"); - feComposite1.setAttribute("in", hilight); - feComposite1.setAttribute("in2", shadow); - feComposite1.setAttribute("operator", "out"); - hilightOuter = exporter.getUniqueId("filterResult"); - feComposite1.setAttribute("result", hilightOuter); - filtersElement.appendChild(feComposite1); - - Element feComposite2 = document.createElement("feComposite"); - feComposite2.setAttribute("in", shadow); - feComposite2.setAttribute("in2", hilight); - feComposite2.setAttribute("operator", "out"); - shadowOuter = exporter.getUniqueId("filterResult"); - feComposite2.setAttribute("result", shadowOuter); - filtersElement.appendChild(feComposite2); - } - - String hilight = null; - String shadow = null; - - switch (type) { - case Filtering.OUTER: - hilight = hilightOuter; - shadow = shadowOuter; - break; - case Filtering.INNER: - hilight = hilightInner; - shadow = shadowInner; - break; - case Filtering.FULL: - Element feComposite1 = document.createElement("feComposite"); - feComposite1.setAttribute("in", hilightInner); - feComposite1.setAttribute("in2", hilightOuter); - feComposite1.setAttribute("operator", "over"); - hilight = exporter.getUniqueId("filterResult"); - feComposite1.setAttribute("result", hilight); - filtersElement.appendChild(feComposite1); - - Element feComposite2 = document.createElement("feComposite"); - feComposite2.setAttribute("in", shadowInner); - feComposite2.setAttribute("in2", shadowOuter); - feComposite2.setAttribute("operator", "over"); - shadow = exporter.getUniqueId("filterResult"); - feComposite2.setAttribute("result", shadow); - filtersElement.appendChild(feComposite2); - break; - } - - Element feFlood = document.createElement("feFlood"); - feFlood.setAttribute("flood-color", "black"); - feFlood.setAttribute("flood-opacity", "1"); - String black = exporter.getUniqueId("filterResult"); - feFlood.setAttribute("result", black); - filtersElement.appendChild(feFlood); - - String result; - - Element feComposite4 = document.createElement("feComposite"); - feComposite4.setAttribute("in", shadow); - feComposite4.setAttribute("in2", black); - feComposite4.setAttribute("operator", "over"); - result = exporter.getUniqueId("filterResult"); - feComposite4.setAttribute("result", result); - filtersElement.appendChild(feComposite4); - - Element feComposite5 = document.createElement("feComposite"); - feComposite5.setAttribute("in", hilight); - feComposite5.setAttribute("in2", result); - feComposite5.setAttribute("operator", "over"); - result = exporter.getUniqueId("filterResult"); - feComposite5.setAttribute("result", result); - filtersElement.appendChild(feComposite5); - - result = blurSvg(blurX, blurY, passes, document, filtersElement, exporter, result); - - Element feColorMatrix = document.createElement("feColorMatrix"); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute("in", result); - double halfStrength = strength / 2; - String matrixRow = "" + halfStrength + " 0 " + (-halfStrength) + " 0 0.5"; - feColorMatrix.setAttribute("values", - matrixRow + " " - + matrixRow + " " - + matrixRow + " " - + matrixRow - ); - result = exporter.getUniqueId("filterResult"); - feColorMatrix.setAttribute("result", result); - filtersElement.appendChild(feColorMatrix); - - result = prepareFeComponentTransfer(gradientColors, gradientRatio, document, filtersElement, exporter, result); - - if (type == Filtering.INNER) { - Element feComposite6 = document.createElement("feComposite"); - feComposite6.setAttribute("in", result); - feComposite6.setAttribute("in2", in); - feComposite6.setAttribute("operator", "in"); - result = exporter.getUniqueId("filterResult"); - feComposite6.setAttribute("result", result); - filtersElement.appendChild(feComposite6); - } - if (type == Filtering.OUTER) { - Element feComposite6 = document.createElement("feComposite"); - feComposite6.setAttribute("in", result); - feComposite6.setAttribute("in2", in); - feComposite6.setAttribute("operator", "out"); - result = exporter.getUniqueId("filterResult"); - feComposite6.setAttribute("result", result); - filtersElement.appendChild(feComposite6); - } - - if (!knockout) { - Element feComposite7 = document.createElement("feComposite"); - feComposite7.setAttribute("in", result); - feComposite7.setAttribute("in2", in); - feComposite7.setAttribute("operator", "over"); - result = exporter.getUniqueId("filterResult"); - feComposite7.setAttribute("result", result); - filtersElement.appendChild(feComposite7); - } - return result; - } - - private String prepareFeComponentTransfer(RGBA[] gradientColors, int[] gradientRatio, Document document, Element filtersElement, SVGExporter exporter, String in) { - Element feComponentTransfer = document.createElement("feComponentTransfer"); - feComponentTransfer.setAttribute("in", in); - - List redValues = new ArrayList<>(); - List greenValues = new ArrayList<>(); - List blueValues = new ArrayList<>(); - List alphaValues = new ArrayList<>(); - - for (int i = 0; i < 256; i++) { - RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i, GradientUtil.ColorInterpolation.SRGB); - redValues.add("" + (color.red / 255f)); - greenValues.add("" + (color.green / 255f)); - blueValues.add("" + (color.blue / 255f)); - alphaValues.add("" + color.getAlphaFloat()); - } - - /* - //In case we want to map 128 to center - - for (int i = 0; i < 126; i++) { //126 colors - RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i * 127f / 125f, GradientUtil.ColorInterpolation.SRGB); - redValues.add("" + (color.red / 255f)); - greenValues.add("" + (color.green / 255f)); - blueValues.add("" + (color.blue / 255f)); - alphaValues.add("" + color.getAlphaFloat()); - } - for (int i = 128; i < 256; i++) { //1 center + 126 colors - RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i, GradientUtil.ColorInterpolation.SRGB); - redValues.add("" + (color.red / 255f)); - greenValues.add("" + (color.green / 255f)); - blueValues.add("" + (color.blue / 255f)); - alphaValues.add("" + color.getAlphaFloat()); - } - */ - Element feFuncR = document.createElement("feFuncR"); - feFuncR.setAttribute("type", "table"); - feFuncR.setAttribute("tableValues", String.join(" ", redValues)); - Element feFuncG = document.createElement("feFuncG"); - feFuncG.setAttribute("type", "table"); - feFuncG.setAttribute("tableValues", String.join(" ", greenValues)); - Element feFuncB = document.createElement("feFuncB"); - feFuncB.setAttribute("type", "table"); - feFuncB.setAttribute("tableValues", String.join(" ", blueValues)); - Element feFuncA = document.createElement("feFuncA"); - feFuncA.setAttribute("type", "table"); - feFuncA.setAttribute("tableValues", String.join(" ", alphaValues)); - feComponentTransfer.appendChild(feFuncR); - feComponentTransfer.appendChild(feFuncG); - feComponentTransfer.appendChild(feFuncB); - feComponentTransfer.appendChild(feFuncA); - - String result = exporter.getUniqueId("filterResult"); - feComponentTransfer.setAttribute("result", result); - filtersElement.appendChild(feComponentTransfer); - return result; - } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java index e9ca0b22b..c173df271 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java @@ -101,101 +101,7 @@ public class GLOWFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - /*Element element = document.createElement("feDropShadow"); - element.setAttribute("dx", "0"); - element.setAttribute("dy", "0"); - - double blur = ((blurX + blurY) / 2); - element.setAttribute("stdDeviation", "" + blur); - - element.setAttribute("flood-color", glowColor.toHexRGB()); - element.setAttribute("flood-opacity", "" + glowColor.getAlphaFloat()); - - element.setAttribute("in", in); - - String result = exporter.getUniqueId("filterResult"); - element.setAttribute("result", result); - - filtersElement.appendChild(element); - - return result;*/ - - if (innerGlow) { - Element feFlood = document.createElement("feFlood"); - feFlood.setAttribute("flood-color", glowColor.toHexRGB()); - feFlood.setAttribute("flood-opacity", "" + glowColor.getAlphaFloat()); - filtersElement.appendChild(feFlood); - String feFloodResult = exporter.getUniqueId("filterResult"); - feFlood.setAttribute("result", feFloodResult); - - Element feComposite1 = document.createElement("feComposite"); - feComposite1.setAttribute("in", feFloodResult); - feComposite1.setAttribute("in2", in); - feComposite1.setAttribute("operator", "out"); - String feComposite1Result = exporter.getUniqueId("filterResult"); - feComposite1.setAttribute("result", feComposite1Result); - filtersElement.appendChild(feComposite1); - - String blurResult = blurSvg(blurX, blurY, passes, document, filtersElement, exporter, feComposite1Result); - - Element feComposite2 = document.createElement("feComposite"); - feComposite2.setAttribute("in", blurResult); - feComposite2.setAttribute("in2", in); - feComposite2.setAttribute("operator", "in"); - - String feComposite2Result = exporter.getUniqueId("filterResult"); - feComposite2.setAttribute("result", feComposite2Result); - filtersElement.appendChild(feComposite2); - - if (knockout || !compositeSource) { - return feComposite2Result; - } - Element feComposite3 = document.createElement("feComposite"); - feComposite3.setAttribute("in", feComposite2Result); - feComposite3.setAttribute("in2", in); - feComposite3.setAttribute("operator", "over"); - - String feComposite3Result = exporter.getUniqueId("filterResult"); - feComposite3.setAttribute("result", feComposite3Result); - filtersElement.appendChild(feComposite3); - return feComposite3Result; - } else { - - Element feColorMatrix = document.createElement("feColorMatrix"); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute("in", in); - feColorMatrix.setAttribute("values", - "0 0 0 0 " + (glowColor.red / 255f) + "," - + "0 0 0 0 " + (glowColor.green / 255f) + "," - + "0 0 0 0 " + (glowColor.blue / 255f) + "," - + "0 0 0 1 0" - ); - String feColorMatrixResult = exporter.getUniqueId("filterResult"); - feColorMatrix.setAttribute("result", feColorMatrixResult); - filtersElement.appendChild(feColorMatrix); - - String blurResult = blurSvg(blurX, blurY, passes, document, filtersElement, exporter, feColorMatrixResult); - - if (!knockout && !compositeSource) { - return blurResult; - } - - Element feComposite = document.createElement("feComposite"); - if (knockout) { - feComposite.setAttribute("in", blurResult); - feComposite.setAttribute("in2", in); - feComposite.setAttribute("operator", "out"); - } else { - feComposite.setAttribute("in", in); - feComposite.setAttribute("in2", blurResult); - feComposite.setAttribute("operator", "over"); - } - - String feCompositeResult = exporter.getUniqueId("filterResult"); - feComposite.setAttribute("result", feCompositeResult); - filtersElement.appendChild(feComposite); - return feCompositeResult; - } + return SvgFiltering.glow(glowColor, knockout, innerGlow, compositeSource, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } @Override @@ -247,6 +153,5 @@ public class GLOWFILTER extends FILTER { } return Objects.equals(this.glowColor, other.glowColor); } - - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java index c63afe857..c445b8e9c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java @@ -143,7 +143,13 @@ public class GRADIENTBEVELFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - return bevelSvg(distance, angle, gradientColors, gradientRatio, knockout, onTop, innerShadow, blurX, blurY, strength, passes, document, filtersElement, exporter, in); + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + return SvgFiltering.gradientBevel(distance, angle, gradientColors, gradientRatio, knockout, compositeSource, type, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java index 75e274d94..686f8ad6c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java @@ -122,7 +122,7 @@ public class GRADIENTGLOWFILTER extends FILTER { colorsArr[i] = gradientColors[i].toColor(); } float[] ratiosArr = convertRatiosToJavaGradient(gradientRatio); - + int type = Filtering.INNER; if (onTop && !innerShadow) { type = Filtering.FULL; @@ -145,7 +145,13 @@ public class GRADIENTGLOWFILTER extends FILTER { @Override public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { - return null; //NOT SUPPORTED + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + return SvgFiltering.gradientGlow(distance, angle, gradientColors, gradientRatio, knockout, type, compositeSource, innerShadow, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } @Override @@ -213,6 +219,5 @@ public class GRADIENTGLOWFILTER extends FILTER { } return Arrays.equals(this.gradientRatio, other.gradientRatio); } - - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/SvgFiltering.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/SvgFiltering.java new file mode 100644 index 000000000..9765d3ae9 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/SvgFiltering.java @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2010-2026 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.types.filters; + +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.helpers.GradientUtil; +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * SVG filtering + * + * @author JPEXS + */ +public class SvgFiltering { + + /** + * Blur filter + * + * @param blurX Blur X + * @param blurY Blur Y + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter SVG exporter + * @param in Input + * @return SVG id of the blur + */ + public static String blur(double blurX, double blurY, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { + if (iterations == 0) { + return in; + } + if (Double.compare(blurX, 0.0) == 0 && Double.compare(blurY, 0.0) == 0) { + return in; + } + Element element; + if (Configuration.svgExportGaussianBlur.get()) { + double blurXScaled = blurX * exporter.getZoom(); + double blurYScaled = blurY * exporter.getZoom(); + + double deviationX = Math.sqrt((blurXScaled * blurXScaled - 1) / 12); + double deviationY = Math.sqrt((blurYScaled * blurYScaled - 1) / 12); + + element = document.createElement("feGaussianBlur"); + + element.setAttribute("stdDeviation", "" + deviationX + " " + deviationY); + } else { + element = document.createElement("feConvolveMatrix"); + + List parts = new ArrayList<>(); + + int orderX = (int) Math.ceil(blurX * exporter.getZoom()); + int orderY = (int) Math.ceil(blurY * exporter.getZoom()); + + if (orderX % 2 == 0) { + orderX++; + } + + if (orderY % 2 == 0) { + orderY++; + } + + double divisor = orderX * orderY; + element.setAttribute("order", "" + orderX + " " + orderY); + + for (int i = 0; i < divisor; i++) { + parts.add("1"); + } + + element.setAttribute("divisor", "" + divisor); + + element.setAttribute("kernelMatrix", String.join(" ", parts)); + element.setAttribute("kernelUnitLength", "1"); + } + element.setAttribute("in", in); + + String result = exporter.getUniqueId("filterResult"); + element.setAttribute("result", result); + + filtersElement.appendChild(element); + + return blur(blurX, blurY, iterations - 1, document, filtersElement, exporter, result); + } + + /** + * Compose result according to INNER/OUTER/FULL + * + * @param src Source + * @param result Result + * @param type Type + * @param knockout Knockout + * @param compositeSource Composite source + * @param document Document + * @param filtersElement Filters element + * @param exporter Exporter + * @return Result + */ + private static String compose( + String src, + String result, + int type, + boolean knockout, + boolean compositeSource, + Document document, + Element filtersElement, + SVGExporter exporter + ) { + String in = ""; + String in2 = ""; + String operator = ""; + String dst = result; + + if (type == Filtering.FULL && !knockout && compositeSource) { + operator = "over"; + in = dst; + in2 = src; + //dstover + } else if (type == Filtering.INNER) { + if (knockout || !compositeSource) { + operator = "in"; + //dstin + } else { + operator = "atop"; + //dstatop + } + in = dst; + in2 = src; + } else if (type == Filtering.OUTER) { + if (knockout) { + in = dst; + in2 = src; + operator = "out"; + //dstout + } else if (compositeSource) { + in = src; + in2 = dst; + operator = "over"; + //srcover + } + } else { + return result; + } + + Element feComposite = document.createElement("feComposite"); + feComposite.setAttribute("in", in); + feComposite.setAttribute("in2", in2); + feComposite.setAttribute("operator", operator); + result = exporter.getUniqueId("filterResult"); + feComposite.setAttribute("result", result); + filtersElement.appendChild(feComposite); + return result; + } + + /** + * Glow filter + * + * @param glowColor Glow color + * @param knockout Knockout + * @param innerGlow Inner glow + * @param compositeSource Composite source + * @param blurX Blur X + * @param blurY Blur Y + * @param strength Strength + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter Exporter + * @param in In + * @return Result + */ + public static String glow(RGBA glowColor, boolean knockout, boolean innerGlow, boolean compositeSource, double blurX, double blurY, double strength, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { + return dropShadow(0, 45, glowColor, innerGlow, knockout, true, blurX, blurY, strength, iterations, document, filtersElement, exporter, in); + } + + /** + * Gradient glow + * + * @param distance Distance + * @param angle Angle + * @param gradientColors Gradient colors + * @param gradientRatio Gradient ratio + * @param knockout Knockout + * @param type Type + * @param compositeSource Composite source + * @param innerGlow Inner glow + * @param blurX Blur X + * @param blurY Blur Y + * @param strength Strength + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter Exporter + * @param in In + * @return Result + */ + public static String gradientGlow(double distance, double angle, RGBA[] gradientColors, int[] gradientRatio, boolean knockout, int type, boolean compositeSource, boolean innerGlow, double blurX, double blurY, double strength, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { + RGBA glowColor = new RGBA(255, 0, 0, 255); + + double dx = distance * exporter.getZoom() * Math.cos(angle); + double dy = distance * exporter.getZoom() * Math.sin(angle); + + Element feFloodBlack = document.createElement("feFlood"); + feFloodBlack.setAttribute("flood-color", "black"); + feFloodBlack.setAttribute("flood-opacity", "1"); + String black = exporter.getUniqueId("filterResult"); + feFloodBlack.setAttribute("result", black); + filtersElement.appendChild(feFloodBlack); + + Element feOffset = document.createElement("feOffset"); + feOffset.setAttribute("dx", "" + dx); + feOffset.setAttribute("dy", "" + dy); + feOffset.setAttribute("in", in); + String feOffsetResult = exporter.getUniqueId("filterResult"); + feOffset.setAttribute("result", feOffsetResult); + filtersElement.appendChild(feOffset); + + String result; + + Element feColorMatrix = document.createElement("feColorMatrix"); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute("in", feOffsetResult); + feColorMatrix.setAttribute("values", + "0 0 0 0 " + (glowColor.red / 255f) + "," + + "0 0 0 0 " + (glowColor.green / 255f) + "," + + "0 0 0 0 " + (glowColor.blue / 255f) + "," + + "0 0 0 " + (glowColor.alpha / 255f) + " 0" //Note: it is not last one here + ); + result = exporter.getUniqueId("filterResult"); + feColorMatrix.setAttribute("result", result); + filtersElement.appendChild(feColorMatrix); + + Element feComposite = document.createElement("feComposite"); + feComposite.setAttribute("in", result); + feComposite.setAttribute("in2", black); + feComposite.setAttribute("operator", "over"); + result = exporter.getUniqueId("filterResult"); + feComposite.setAttribute("result", result); + filtersElement.appendChild(feComposite); + + if (Double.compare(blurX, 0.0) != 0 || Double.compare(blurY, 0.0) != 0) { + result = blur(blurX, blurY, iterations, document, filtersElement, exporter, result); + } + + Element feColorMatrix2 = document.createElement("feColorMatrix"); + feColorMatrix2.setAttribute("type", "matrix"); + feColorMatrix2.setAttribute("in", result); + String matrixRow = "" + strength + " 0 0 0 0"; + feColorMatrix2.setAttribute("values", + matrixRow + " " + + matrixRow + " " + + matrixRow + " " + + matrixRow + ); + result = exporter.getUniqueId("filterResult"); + feColorMatrix2.setAttribute("result", result); + filtersElement.appendChild(feColorMatrix2); + + result = prepareFeComponentTransfer(gradientColors, gradientRatio, document, filtersElement, exporter, result); + + return compose(in, result, type, knockout, compositeSource, document, filtersElement, exporter); + } + + /** + * Bevel filter + * + * @param distance Distance + * @param angle Angle + * @param highlightColor Highlight color + * @param shadowColor Shadow color + * @param knockout Knockout + * @param compositeSource Composite source + * @param type Type + * @param blurX Blur X + * @param blurY Blur Y + * @param strength Strength + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter Exporter + * @param in In + * @return Result + */ + public static String bevel(double distance, double angle, RGBA highlightColor, RGBA shadowColor, boolean knockout, boolean compositeSource, int type, double blurX, double blurY, double strength, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { + RGBA shadowColorTransparent = new RGBA(shadowColor); + shadowColorTransparent.alpha = 0; + RGBA highlightColorTransparent = new RGBA(highlightColor); + highlightColorTransparent.alpha = 0; + + return SvgFiltering.gradientBevel(distance, angle, new RGBA[]{shadowColor, shadowColorTransparent, highlightColorTransparent, highlightColor}, new int[]{0, 127, 128, 255}, knockout, compositeSource, type, blurX, blurY, strength, iterations, document, filtersElement, exporter, in); + + } + + /** + * Gradient bevel + * + * @param distance Distance + * @param angle Angle + * @param gradientColors Gradient colors + * @param gradientRatio Gradient ratio + * @param knockout Knockout + * @param compositeSource Composite source + * @param type Type + * @param blurX Blur X + * @param blurY Blur Y + * @param strength Strength + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter Exporter + * @param in In + * @return Result + */ + public static String gradientBevel(double distance, double angle, RGBA[] gradientColors, int[] gradientRatio, boolean knockout, boolean compositeSource, int type, double blurX, double blurY, double strength, int iterations, Document document, Element filtersElement, SVGExporter exporter, String in) { + RGBA highlightColor = new RGBA(255, 0, 0, 255); + RGBA shadowColor = new RGBA(0, 0, 255, 255); + + String shadowInner = null; + String hilightInner = null; + if (type != Filtering.OUTER) { + String hilight = dropShadow(distance, angle, highlightColor, true, true, true, 0, 0, strength, iterations, document, filtersElement, exporter, in); + String shadow = dropShadow(distance, angle + Math.PI, shadowColor, true, true, true, 0, 0, strength, iterations, document, filtersElement, exporter, in); + + Element feComposite1 = document.createElement("feComposite"); + feComposite1.setAttribute("in", hilight); + feComposite1.setAttribute("in2", shadow); + feComposite1.setAttribute("operator", "out"); + hilightInner = exporter.getUniqueId("filterResult"); + feComposite1.setAttribute("result", hilightInner); + filtersElement.appendChild(feComposite1); + + Element feComposite2 = document.createElement("feComposite"); + feComposite2.setAttribute("in", shadow); + feComposite2.setAttribute("in2", hilight); + feComposite2.setAttribute("operator", "out"); + shadowInner = exporter.getUniqueId("filterResult"); + feComposite2.setAttribute("result", shadowInner); + filtersElement.appendChild(feComposite2); + } + + String shadowOuter = null; + String hilightOuter = null; + + if (type != Filtering.INNER) { + String hilight = dropShadow(distance, angle + Math.PI, highlightColor, false, true, true, 0, 0, strength, iterations, document, filtersElement, exporter, in); + String shadow = dropShadow(distance, angle, shadowColor, false, true, true, 0, 0, strength, iterations, document, filtersElement, exporter, in); + + Element feComposite1 = document.createElement("feComposite"); + feComposite1.setAttribute("in", hilight); + feComposite1.setAttribute("in2", shadow); + feComposite1.setAttribute("operator", "out"); + hilightOuter = exporter.getUniqueId("filterResult"); + feComposite1.setAttribute("result", hilightOuter); + filtersElement.appendChild(feComposite1); + + Element feComposite2 = document.createElement("feComposite"); + feComposite2.setAttribute("in", shadow); + feComposite2.setAttribute("in2", hilight); + feComposite2.setAttribute("operator", "out"); + shadowOuter = exporter.getUniqueId("filterResult"); + feComposite2.setAttribute("result", shadowOuter); + filtersElement.appendChild(feComposite2); + } + + String hilight = null; + String shadow = null; + + switch (type) { + case Filtering.OUTER: + hilight = hilightOuter; + shadow = shadowOuter; + break; + case Filtering.INNER: + hilight = hilightInner; + shadow = shadowInner; + break; + case Filtering.FULL: + Element feComposite1 = document.createElement("feComposite"); + feComposite1.setAttribute("in", hilightInner); + feComposite1.setAttribute("in2", hilightOuter); + feComposite1.setAttribute("operator", "over"); + hilight = exporter.getUniqueId("filterResult"); + feComposite1.setAttribute("result", hilight); + filtersElement.appendChild(feComposite1); + + Element feComposite2 = document.createElement("feComposite"); + feComposite2.setAttribute("in", shadowInner); + feComposite2.setAttribute("in2", shadowOuter); + feComposite2.setAttribute("operator", "over"); + shadow = exporter.getUniqueId("filterResult"); + feComposite2.setAttribute("result", shadow); + filtersElement.appendChild(feComposite2); + break; + } + + Element feFlood = document.createElement("feFlood"); + feFlood.setAttribute("flood-color", "black"); + feFlood.setAttribute("flood-opacity", "1"); + String black = exporter.getUniqueId("filterResult"); + feFlood.setAttribute("result", black); + filtersElement.appendChild(feFlood); + + String result; + + Element feComposite4 = document.createElement("feComposite"); + feComposite4.setAttribute("in", shadow); + feComposite4.setAttribute("in2", black); + feComposite4.setAttribute("operator", "over"); + result = exporter.getUniqueId("filterResult"); + feComposite4.setAttribute("result", result); + filtersElement.appendChild(feComposite4); + + Element feComposite5 = document.createElement("feComposite"); + feComposite5.setAttribute("in", hilight); + feComposite5.setAttribute("in2", result); + feComposite5.setAttribute("operator", "over"); + result = exporter.getUniqueId("filterResult"); + feComposite5.setAttribute("result", result); + filtersElement.appendChild(feComposite5); + + result = blur(blurX, blurY, iterations, document, filtersElement, exporter, result); + + Element feColorMatrix = document.createElement("feColorMatrix"); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute("in", result); + double halfStrength = strength / 2; + String matrixRow = "" + halfStrength + " 0 " + (-halfStrength) + " 0 0.5"; + feColorMatrix.setAttribute("values", + matrixRow + " " + + matrixRow + " " + + matrixRow + " " + + matrixRow + ); + result = exporter.getUniqueId("filterResult"); + feColorMatrix.setAttribute("result", result); + filtersElement.appendChild(feColorMatrix); + + result = prepareFeComponentTransfer(gradientColors, gradientRatio, document, filtersElement, exporter, result); + + return compose(in, result, type, knockout, compositeSource, document, filtersElement, exporter); + } + + private static String prepareFeComponentTransfer(RGBA[] gradientColors, int[] gradientRatio, Document document, Element filtersElement, SVGExporter exporter, String in) { + Element feComponentTransfer = document.createElement("feComponentTransfer"); + feComponentTransfer.setAttribute("in", in); + + List redValues = new ArrayList<>(); + List greenValues = new ArrayList<>(); + List blueValues = new ArrayList<>(); + List alphaValues = new ArrayList<>(); + + for (int i = 0; i < 256; i++) { + RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i, GradientUtil.ColorInterpolation.SRGB); + redValues.add("" + (color.red / 255f)); + greenValues.add("" + (color.green / 255f)); + blueValues.add("" + (color.blue / 255f)); + alphaValues.add("" + color.getAlphaFloat()); + } + + /* + //In case we want to map 128 to center + + for (int i = 0; i < 126; i++) { //126 colors + RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i * 127f / 125f, GradientUtil.ColorInterpolation.SRGB); + redValues.add("" + (color.red / 255f)); + greenValues.add("" + (color.green / 255f)); + blueValues.add("" + (color.blue / 255f)); + alphaValues.add("" + color.getAlphaFloat()); + } + for (int i = 128; i < 256; i++) { //1 center + 126 colors + RGBA color = GradientUtil.colorAt(gradientColors, gradientRatio, i, GradientUtil.ColorInterpolation.SRGB); + redValues.add("" + (color.red / 255f)); + greenValues.add("" + (color.green / 255f)); + blueValues.add("" + (color.blue / 255f)); + alphaValues.add("" + color.getAlphaFloat()); + } + */ + Element feFuncR = document.createElement("feFuncR"); + feFuncR.setAttribute("type", "table"); + feFuncR.setAttribute("tableValues", String.join(" ", redValues)); + Element feFuncG = document.createElement("feFuncG"); + feFuncG.setAttribute("type", "table"); + feFuncG.setAttribute("tableValues", String.join(" ", greenValues)); + Element feFuncB = document.createElement("feFuncB"); + feFuncB.setAttribute("type", "table"); + feFuncB.setAttribute("tableValues", String.join(" ", blueValues)); + Element feFuncA = document.createElement("feFuncA"); + feFuncA.setAttribute("type", "table"); + feFuncA.setAttribute("tableValues", String.join(" ", alphaValues)); + feComponentTransfer.appendChild(feFuncR); + feComponentTransfer.appendChild(feFuncG); + feComponentTransfer.appendChild(feFuncB); + feComponentTransfer.appendChild(feFuncA); + + String result = exporter.getUniqueId("filterResult"); + feComponentTransfer.setAttribute("result", result); + filtersElement.appendChild(feComponentTransfer); + return result; + } + + /** + * Drop shadow + * + * @param distance Distance + * @param angle Angle + * @param dropShadowColor Drop shadow color + * @param innerShadow Inner shadow + * @param knockout Knockout + * @param compositeSource Composite source + * @param blurX Blur X + * @param blurY Blur Y + * @param strength Strength + * @param iterations Iterations + * @param document Document + * @param filtersElement Filters element + * @param exporter SVG exporter + * @param in Input + * @return SVG id of the drop shadow + */ + public static String dropShadow( + double distance, + double angle, + RGBA dropShadowColor, + boolean innerShadow, + boolean knockout, + boolean compositeSource, + double blurX, + double blurY, + double strength, + int iterations, + Document document, + Element filtersElement, + SVGExporter exporter, + String in + ) { + double dx = distance * exporter.getZoom() * Math.cos(angle); + double dy = distance * exporter.getZoom() * Math.sin(angle); + + Element feOffset = document.createElement("feOffset"); + feOffset.setAttribute("dx", "" + dx); + feOffset.setAttribute("dy", "" + dy); + feOffset.setAttribute("in", in); + String feOffsetResult = exporter.getUniqueId("filterResult"); + feOffset.setAttribute("result", feOffsetResult); + filtersElement.appendChild(feOffset); + + String result; + if (innerShadow) { + Element feFlood = document.createElement("feFlood"); + feFlood.setAttribute("flood-color", dropShadowColor.toHexRGB()); + feFlood.setAttribute("flood-opacity", "" + dropShadowColor.getAlphaFloat()); + filtersElement.appendChild(feFlood); + String feFloodResult = exporter.getUniqueId("filterResult"); + feFlood.setAttribute("result", feFloodResult); + + Element feComposite1 = document.createElement("feComposite"); + feComposite1.setAttribute("in", feFloodResult); + feComposite1.setAttribute("in2", feOffsetResult); + feComposite1.setAttribute("operator", "out"); + result = exporter.getUniqueId("filterResult"); + feComposite1.setAttribute("result", result); + filtersElement.appendChild(feComposite1); + } else { + Element feColorMatrix = document.createElement("feColorMatrix"); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute("in", feOffsetResult); + feColorMatrix.setAttribute("values", + "0 0 0 0 " + (dropShadowColor.red / 255f) + "," + + "0 0 0 0 " + (dropShadowColor.green / 255f) + "," + + "0 0 0 0 " + (dropShadowColor.blue / 255f) + "," + + "0 0 0 " + (dropShadowColor.alpha / 255f) + " 0" //Note: it is not last one here + ); + result = exporter.getUniqueId("filterResult"); + feColorMatrix.setAttribute("result", result); + filtersElement.appendChild(feColorMatrix); + } + + if (Double.compare(blurX, 0.0) != 0 || Double.compare(blurY, 0.0) != 0) { + result = blur(blurX, blurY, iterations, document, filtersElement, exporter, result); + } + + if (Double.compare(strength, 1.0) != 0) { + Element feColorMatrix2 = document.createElement("feColorMatrix"); + feColorMatrix2.setAttribute("type", "matrix"); + feColorMatrix2.setAttribute("in", result); + feColorMatrix2.setAttribute("values", + "1 0 0 0 0," + + "0 1 0 0 0," + + "0 0 1 0 0," + + "0 0 0 " + strength + " 0" + ); + result = exporter.getUniqueId("filterResult"); + feColorMatrix2.setAttribute("result", result); + filtersElement.appendChild(feColorMatrix2); + } + return compose(in, result, innerShadow ? Filtering.INNER : Filtering.OUTER, knockout, compositeSource, document, filtersElement, exporter); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/GradientUtil.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/GradientUtil.java index a219379a8..d3b16b483 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/GradientUtil.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/GradientUtil.java @@ -16,7 +16,6 @@ */ package com.jpexs.helpers; - import com.jpexs.decompiler.flash.types.RGBA; import java.util.ArrayList; import java.util.List; @@ -107,8 +106,9 @@ public final class GradientUtil { ); } } - + public static final class SplitResult { + public final RGBA[] colorsA; public final int[] ratioA; public final RGBA[] colorsB; @@ -123,14 +123,12 @@ public final class GradientUtil { } /** - * Splits a gradient into two halves: - * - A: original positions 0..127 mapped to 0..255 - * - B: original positions 128..255 mapped to 0..255 + * Splits a gradient into two halves: - A: original positions 0..127 mapped + * to 0..255 - B: original positions 128..255 mapped to 0..255 * - * Notes: - * - Stop lookup is based on original ratio[]. - * - Boundary stops at 127 (A) and 128 (B) are ensured (interpolated if missing). - * - ratio[] is assumed sorted ascending. + * Notes: - Stop lookup is based on original ratio[]. - Boundary stops at + * 127 (A) and 128 (B) are ensured (interpolated if missing). - ratio[] is + * assumed sorted ascending. */ public static SplitResult splitIntoHalves( RGBA[] colors, int[] ratio, ColorInterpolation mode @@ -152,18 +150,19 @@ public final class GradientUtil { } // ----- Internal representation of stops ----- - private static final class Stops { + final List colors = new ArrayList<>(); final List ratio = new ArrayList<>(); } /** - * Extracts all stops within [from..to] (inclusive), and ensures stops at both ends exist. - * If an endpoint stop is missing, it is computed via SvgGradient.colorAt(...). + * Extracts all stops within [from..to] (inclusive), and ensures stops at + * both ends exist. If an endpoint stop is missing, it is computed via + * SvgGradient.colorAt(...). */ private static Stops extractRange(RGBA[] colors, int[] ratio, int from, int to, - ColorInterpolation mode) { + ColorInterpolation mode) { Stops out = new Stops(); // Ensure start stop @@ -184,19 +183,22 @@ public final class GradientUtil { } /** - * If there is an existing stop exactly at pos, returns its color; - * otherwise computes the color by interpolation at that position. + * If there is an existing stop exactly at pos, returns its color; otherwise + * computes the color by interpolation at that position. */ private static RGBA stopColorAtOrExisting(RGBA[] colors, int[] ratio, int pos, - ColorInterpolation mode) { + ColorInterpolation mode) { for (int i = 0; i < ratio.length; i++) { - if (ratio[i] == pos) return colors[i]; + if (ratio[i] == pos) { + return colors[i]; + } } return colorAt(colors, ratio, pos, mode); } /** - * Adds a stop keeping order; if the same position already exists, it overwrites the color. + * Adds a stop keeping order; if the same position already exists, it + * overwrites the color. */ private static void addStop(Stops stops, int pos, RGBA color) { // Insert in ascending order (ratio is small, linear insert is fine) @@ -217,8 +219,8 @@ public final class GradientUtil { } /** - * Remaps original integer positions in [from..to] to [0..255]. - * Uses rounding and clamps, guaranteeing endpoints map to 0 and 255. + * Remaps original integer positions in [from..to] to [0..255]. Uses + * rounding and clamps, guaranteeing endpoints map to 0 and 255. */ private static int[] remap(List original, int from, int to) { int span = to - from; // for 0..127 and 128..255 span is 127 diff --git a/libsrc/ffdec_lib/testdata/graphics/graphics.swf b/libsrc/ffdec_lib/testdata/graphics/graphics.swf index 95c1e79bf..97e2bceb3 100644 Binary files a/libsrc/ffdec_lib/testdata/graphics/graphics.swf and b/libsrc/ffdec_lib/testdata/graphics/graphics.swf differ diff --git a/libsrc/ffdec_lib/testdata/graphics/graphics/DOMDocument.xml b/libsrc/ffdec_lib/testdata/graphics/graphics/DOMDocument.xml index d38e0db1d..1c573cfc5 100644 --- a/libsrc/ffdec_lib/testdata/graphics/graphics/DOMDocument.xml +++ b/libsrc/ffdec_lib/testdata/graphics/graphics/DOMDocument.xml @@ -1,7 +1,7 @@ - + @@ -865,10 +865,10 @@ stop();]]> !3980 1990|2980 1990!2980 1990|2980 990!2980 990|3980 990!3980 990|3980 1990"/> - + @@ -1467,18 +1467,18 @@ stop();]]> - + - - + + @@ -2020,7 +2020,7 @@ stop();]]> - + @@ -4172,6 +4172,7 @@ stop();]]> + @@ -4191,6 +4192,5 @@ stop();]]> - \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/graphics/graphics/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/graphics/graphics/META-INF/metadata.xml index 87cfd8c88..8ce4d4815 100644 --- a/libsrc/ffdec_lib/testdata/graphics/graphics/META-INF/metadata.xml +++ b/libsrc/ffdec_lib/testdata/graphics/graphics/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 481 2021-03-14T08:29:20+01:00 - 2026-03-02T13:36:50-08:00 - 2026-03-02T13:36:50-08:00 + 2026-03-03T11:35:43-08:00 + 2026-03-03T11:35:43-08:00 @@ -15,7 +15,7 @@ - xmp.iid:97E446EC7F16F1119B53F77A0E0EAFD1 + xmp.iid:D00C382B3817F111A8D794A495DC263E xmp.did:D6D3FE199784EB1187FEAE6972EC5115 xmp.did:D6D3FE199784EB1187FEAE6972EC5115 @@ -194,6 +194,12 @@ 2021-03-14T08:29:20+01:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:D00C382B3817F111A8D794A495DC263E + 2021-03-14T08:29:20+01:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/ffdec_lib/testdata/graphics/graphics/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/graphics/graphics/bin/SymDepend.cache index ff8795d6d..f2e3d0d04 100644 Binary files a/libsrc/ffdec_lib/testdata/graphics/graphics/bin/SymDepend.cache and b/libsrc/ffdec_lib/testdata/graphics/graphics/bin/SymDepend.cache differ