diff --git a/CHANGELOG.md b/CHANGELOG.md index 653fb4209..fc11a70e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ All notable changes to this project will be documented in this file. - [#2079] Replace DefineSprite with GIF, Bulk import sprites from GIFs, also from commandline - [#116] Show invalid utf-8 bytes in Strings as `{invalid_utf8:xxx}` - [#2097] Commandline command `-header` to modify SWF header values -- SVG Sprite export - blend modes +- SVG Frames export - blend modes +- SVG Frames export - filter ### Fixed - Close action on SWF inside DefineBinaryData diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index 72e2acac9..3dcd75ed9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.BlendMode; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.filters.FILTER; import com.jpexs.helpers.Helper; import java.awt.Color; import java.io.StringWriter; @@ -239,7 +240,7 @@ public class SVGExporter { } - private void addScalingGridUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode) { + private void addScalingGridUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode, List filters) { Element image = _svg.createElement("g"); @@ -421,16 +422,41 @@ public class SVGExporter { image.setAttribute("data-characterName", characterName); } setBlendMode(image, blendMode); + handleFilters(image, filters); _svgGs.peek().appendChild(image); } public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect) { - return addUse(transform, boundRect, href, instanceName, scalingRect, null, null, BlendMode.NORMAL); + return addUse(transform, boundRect, href, instanceName, scalingRect, null, null, BlendMode.NORMAL, new ArrayList<>()); + } + + private void handleFilters(Element image, List filters) { + if (filters == null) { + return; + } + Element filtersElement = _svg.createElement("filter"); + String filterId = getUniqueId("filter"); + String in = "SourceGraphic"; + boolean empty = true; + for (FILTER filter: filters) { + String result = filter.toSvg(_svg, filtersElement, this, in); + if (result != null) { + empty = false; + in = result; + } + } + if (empty) { + return; + } + + filtersElement.setAttribute("id", filterId); + image.setAttribute("filter", "url(#" + filterId + ")"); + _svgGs.peek().appendChild(filtersElement); } - public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode) { + public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode, List filters) { if (scalingRect != null && (transform == null || (Double.compare(transform.rotateSkew0, 0.0) == 0 && Double.compare(transform.rotateSkew1, 0.0) == 0))) { - addScalingGridUse(transform, boundRect, href, instanceName, scalingRect, characterId, characterName, blendMode); + addScalingGridUse(transform, boundRect, href, instanceName, scalingRect, characterId, characterName, blendMode, filters); return null; //?? } Element image = _svg.createElement("use"); @@ -451,7 +477,9 @@ public class SVGExporter { setBlendMode(image, blendMode); - image.setAttribute("xlink:href", "#" + href); + handleFilters(image, filters); + + image.setAttribute("xlink:href", "#" + href); _svgGs.peek().appendChild(image); return image; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 6d727b9ee..bdb070e69 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -1277,7 +1277,7 @@ public class Timeline { exporter.endGroup(); } Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter, String.valueOf(drawable.getCharacterId()), String.join("___", drawable.getClassNames()), layer.blendMode); + exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter, String.valueOf(drawable.getCharacterId()), String.join("___", drawable.getClassNames()), layer.blendMode, layer.filters); } } } 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 e18839412..6e066b10a 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 @@ -16,10 +16,15 @@ */ package com.jpexs.decompiler.flash.types.filters; +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.SWFType; import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.image.BufferedImage; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * The Bevel filter creates a smooth bevel on display list objects. @@ -121,4 +126,132 @@ public class BEVELFILTER extends FILTER { public double getDeltaY() { return blurY + Math.abs(distance * Math.sin(angle)); } + + @Override + public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { + 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"); + shadowOuter = exporter.getUniqueId("filterResult"); + feComposite1.setAttribute("result", shadowOuter); + filtersElement.appendChild(feComposite1); + + Element feComposite2 = document.createElement("feComposite"); + feComposite2.setAttribute("in", shadow); + feComposite2.setAttribute("in2", hilight); + feComposite2.setAttribute("operator", "out"); + hilightOuter = exporter.getUniqueId("filterResult"); + feComposite2.setAttribute("result", hilightOuter); + 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 feComposite3 = document.createElement("feComposite"); + feComposite3.setAttribute("in", shadow); + feComposite3.setAttribute("in2", hilight); + feComposite3.setAttribute("operator", "over"); + String result = exporter.getUniqueId("filterResult"); + feComposite3.setAttribute("result", result); + filtersElement.appendChild(feComposite3); + + result = blurSvg(blurX, blurY, passes, document, filtersElement, exporter, result); + + if (type == Filtering.INNER) { + Element feComposite4 = document.createElement("feComposite"); + feComposite4.setAttribute("in", result); + feComposite4.setAttribute("in2", in); + feComposite4.setAttribute("operator", "in"); + result = exporter.getUniqueId("filterResult"); + feComposite4.setAttribute("result", result); + filtersElement.appendChild(feComposite4); + } + if (type == Filtering.OUTER) { + Element feComposite4 = document.createElement("feComposite"); + feComposite4.setAttribute("in", result); + feComposite4.setAttribute("in2", in); + feComposite4.setAttribute("operator", "out"); + result = exporter.getUniqueId("filterResult"); + feComposite4.setAttribute("result", result); + filtersElement.appendChild(feComposite4); + } + + if (!knockout) { + Element feComposite4 = document.createElement("feComposite"); + feComposite4.setAttribute("in", result); + feComposite4.setAttribute("in2", in); + feComposite4.setAttribute("operator", "over"); + result = exporter.getUniqueId("filterResult"); + feComposite4.setAttribute("result", result); + filtersElement.appendChild(feComposite4); + } + return result; + } } 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 1d22239c3..630e43561 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 @@ -16,10 +16,16 @@ */ package com.jpexs.decompiler.flash.types.filters; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.Reserved; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Blur filter based on a sub-pixel precise median filter @@ -68,4 +74,9 @@ public class BLURFILTER extends FILTER { public double getDeltaY() { return blurY; } + + @Override + public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { + return blurSvg(blurX, blurY, passes, document, filtersElement, exporter, in); + } } 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 ff091770d..6066717e1 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 @@ -16,9 +16,14 @@ */ package com.jpexs.decompiler.flash.types.filters; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Applies a color transformation on the pixels of a display list object @@ -60,4 +65,28 @@ public class COLORMATRIXFILTER extends FILTER { public double getDeltaY() { return 0; } + + @Override + public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { + Element element = document.createElement("feColorMatrix"); + element.setAttribute("type", "matrix"); + List parts = new ArrayList<>(); + for (int i = 0; i < matrix.length; i++) { + if (i % 5 == 4) { + parts.add("" + (matrix[i] / 255f)); + } else { + parts.add("" + matrix[i]); + } + } + element.setAttribute("values", String.join(" ", parts)); + element.setAttribute("in", in); + + String result = exporter.getUniqueId("filterResult"); + element.setAttribute("result", result); + + filtersElement.appendChild(element); + + return result; + } + } 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 4379b4709..95748eb3a 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 @@ -16,12 +16,17 @@ */ package com.jpexs.decompiler.flash.types.filters; +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.Reserved; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Two-dimensional discrete convolution filter. @@ -93,11 +98,41 @@ public class CONVOLUTIONFILTER extends FILTER { @Override public double getDeltaX() { - return ((matrixX-1)>>1) + 1; + return ((matrixX - 1) >> 1) + 1; } @Override public double getDeltaY() { - return ((matrixY-1)>>1) + 1; + return ((matrixY - 1) >> 1) + 1; + } + + @Override + public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { + Element element = document.createElement("feConvolveMatrix"); + element.setAttribute("order", "" + matrixX + " " + matrixY); + List parts = new ArrayList<>(); + for (int i = 0; i < matrix.length; i++) { + parts.add("" + matrix[i]); + } + element.setAttribute("kernelMatrix", String.join(" ", parts)); + if (clamp) { + element.setAttribute("edgeMode", "duplicate"); + } else { + element.setAttribute("edgeMode", "none"); + } + + element.setAttribute("preserveAlpha", preserveAlpha ? "true" : "false"); + + element.setAttribute("divisor", "" + divisor); + element.setAttribute("bias", "" + bias); + + element.setAttribute("in", in); + + String result = exporter.getUniqueId("filterResult"); + element.setAttribute("result", result); + + filtersElement.appendChild(element); + + return result; } } 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 e1c7de8d1..6dae52917 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 @@ -16,10 +16,14 @@ */ package com.jpexs.decompiler.flash.types.filters; +import com.jpexs.decompiler.flash.SWF; +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.SWFType; import com.jpexs.helpers.SerializableImage; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Drop shadow filter based on the same median filter as the blur filter @@ -105,4 +109,9 @@ public class DROPSHADOWFILTER extends FILTER { public double getDeltaY() { return blurY + Math.abs(distance * Math.sin(angle)); } + + @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); + } } 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 04e89d424..f734452ca 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,11 +16,17 @@ */ package com.jpexs.decompiler.flash.types.filters; +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.SWFType; import com.jpexs.helpers.ConcreteClasses; import com.jpexs.helpers.SerializableImage; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Bitmap filter @@ -50,4 +56,195 @@ public abstract class FILTER implements Serializable { public abstract double getDeltaX(); public abstract double getDeltaY(); + + public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { + return null; + } + + 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 + ) { + double dx = distance * Math.cos(angle); + double dy = distance * Math.sin(angle); + + 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; + } + } + + 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 = document.createElement("feConvolveMatrix"); + + List parts = new ArrayList<>(); + + double divisor; + if (Double.compare(blurX, 0.0) == 0) { + int orderX = (int) Math.ceil(blurX); + divisor = orderX; + element.setAttribute("order", "" + orderX + " 1"); + + } else if (Double.compare(blurY, 0.0) == 0) { + int orderY = (int) Math.ceil(blurY); + divisor = orderY; + element.setAttribute("order", "1 " + orderY); + } else { + int orderX = (int) Math.ceil(blurX); + int orderY = (int) Math.ceil(blurY); + 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("in", in); + + String result = exporter.getUniqueId("filterResult"); + element.setAttribute("result", result); + + filtersElement.appendChild(element); + + return blurSvg(blurX, blurY, iterations - 1, document, filtersElement, exporter, result); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java index 3c601ef2c..49b3f7f30 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java @@ -382,7 +382,7 @@ public class Filtering { public static SerializableImage glow(SerializableImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) { return new SerializableImage(dropShadow(src.getBufferedImage(), blurX, blurY, 45, 0, color, inner, iterations, strength, knockout, true)); - } + } private static Color over(Color a, Color b) { int resultA = a.getAlpha() + b.getAlpha() * (255 - a.getAlpha()) / 255; @@ -561,12 +561,12 @@ public class Filtering { public static SerializableImage convolution( SerializableImage src, float[] matrix, - int w, + int w, int h, float divisor, float bias, Color defaultColor, - boolean clamp, + boolean clamp, boolean preserveAlpha, int srcX, int srcY, @@ -576,8 +576,8 @@ public class Filtering { Kernel kernel = new Kernel(w, h, matrix); BufferedImage dst = new BufferedImage(src.getWidth() + 1, src.getHeight() + 1, src.getType()); BufferedImageOp op = new ConvolveOp( - kernel, - new RenderingHints(null), + kernel, + new RenderingHints(null), divisor, bias, defaultColor, 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 0dc6ffffc..72a50fac5 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 @@ -16,10 +16,14 @@ */ package com.jpexs.decompiler.flash.types.filters; +import com.jpexs.decompiler.flash.SWF; +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.SWFType; import com.jpexs.helpers.SerializableImage; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * Glow filter @@ -93,4 +97,103 @@ public class GLOWFILTER extends FILTER { public double getDeltaY() { return blurY; } + + @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; + } + } }