SVG export - Gradient glow filter

SVG filters refactoring
This commit is contained in:
Jindra Petřík
2026-03-03 22:18:37 +01:00
parent 85c7405a4d
commit 29ead369bf
17 changed files with 689 additions and 667 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -172,6 +172,7 @@ public final class BlendComposite implements Composite {
/**
* Gets alpha.
*
* @return Alpha
*/
public float getAlpha() {

View File

@@ -119,5 +119,4 @@ public class COLORMATRIXFILTER extends FILTER {
return Arrays.equals(this.matrix, other.matrix);
}
}

View File

@@ -194,6 +194,5 @@ public class CONVOLUTIONFILTER extends FILTER {
}
return Objects.equals(this.defaultColor, other.defaultColor);
}
}

View File

@@ -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);
}
}

View File

@@ -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<String> 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<String> redValues = new ArrayList<>();
List<String> greenValues = new ArrayList<>();
List<String> blueValues = new ArrayList<>();
List<String> 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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<String> 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<String> redValues = new ArrayList<>();
List<String> greenValues = new ArrayList<>();
List<String> blueValues = new ArrayList<>();
List<String> 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);
}
}

View File

@@ -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<RGBA> colors = new ArrayList<>();
final List<Integer> 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<Integer> original, int from, int to) {
int span = to - from; // for 0..127 and 128..255 span is 127

Binary file not shown.

View File

@@ -1,7 +1,7 @@
<DOMDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://ns.adobe.com/xfl/2008/" width="500" currentTimeline="1" xflVersion="2.2" creatorInfo="Adobe Flash Professional CS6" platform="Windows" versionInfo="Saved by Adobe Flash Windows 12.0 build 481" majorVersion="12" buildNumber="481" timelineLabelWidth="236" viewAngle3D="50.6510299754819" nextSceneIdentifier="91" playOptionsPlayLoop="false" playOptionsPlayPages="false" playOptionsPlayFrameActions="false" autoSaveEnabled="true" autoSaveHasPrompted="true">
<fonts>
<DOMFontItem name="Font 2" itemID="604e7a9e-00000444" font="TimesNewRomanPS-BoldMT" size="30" id="2" sourceLastImported="1615755934" embedRanges="1|2"/>
<DOMFontItem name="MyFont" itemID="6546c026-00000d2d" font="TimesNewRomanPSMT" size="0" id="1" sourceLastImported="1615735628" embedRanges="1|2"/>
<DOMFontItem name="Font 2" itemID="604e7a9e-00000444" font="TimesNewRomanPS-BoldMT" size="30" id="2" sourceLastImported="1615755934" embedRanges="1|2"/>
</fonts>
<media>
<DOMBitmapItem name="bitmapfill.png" itemID="604dcb84-00000229" sourceLastImported="1615711005" externalFileSize="1931" useImportedJPEGData="false" compressionType="lossless" originalCompressionType="lossless" quality="50" href="bitmapfill.png" bitmapDataHRef="M 2 1615711026.dat" frameRight="640" frameBottom="1280"/>
@@ -865,10 +865,10 @@ stop();]]></script>
!3980 1990|2980 1990!2980 1990|2980 990!2980 990|3980 990!3980 990|3980 1990"/>
<Edge strokeStyle="5" edges="
!1960 990|1960 1990!1960 1990|960 1990!960 1990|960 990!960 990|1960 990"/>
<Edge strokeStyle="6" edges="
!2980 3990|3980 3990!3980 3990|3980 4990!3980 4990|2980 4990!2980 4990|2980 3990"/>
<Edge strokeStyle="7" edges="
!1980 4990|980 4990!980 4990|980 3990!980 3990|1980 3990!1980 3990|1980 4990"/>
<Edge strokeStyle="6" edges="
!2980 3990|3980 3990!3980 3990|3980 4990!3980 4990|2980 4990!2980 4990|2980 3990"/>
<Edge strokeStyle="8" edges="
!5980 3990|5980 4990!5980 4990|4980 4990!4980 4990|4980 3990!4980 3990|5980 3990"/>
<Edge cubics="!1960 990(;1960,990 1960,1990 1960,1990q1960 990 1960 1990);"/>
@@ -1467,18 +1467,18 @@ stop();]]></script>
</StrokeStyle>
</strokes>
<edges>
<Edge strokeStyle="5" edges="
!5000 1000|5000 2000!5000 2000|4000 2000!4000 2000|4000 1000"/>
<Edge strokeStyle="2" edges="
!3500 1000|3500 2000!3500 2000|2500 2000!2500 2000|2500 1000"/>
<Edge strokeStyle="5" edges="
!5000 1000|5000 2000!5000 2000|4000 2000!4000 2000|4000 1000"/>
<Edge strokeStyle="1" edges="
!2000 1000|2000 2000!2000 2000|1000 2000!1000 2000|1000 1000"/>
<Edge strokeStyle="4" edges="
!1000 3660/1295 4670!1295 4670/1372 3660"/>
<Edge strokeStyle="3" edges="
!2553 3660/2848 4670!2848 4670/2925 3660"/>
<Edge strokeStyle="6" edges="
!4647 3660/4942 4670!4942 4670/5019 3660"/>
<Edge strokeStyle="3" edges="
!2553 3660/2848 4670!2848 4670/2925 3660"/>
<Edge strokeStyle="4" edges="
!1000 3660/1295 4670!1295 4670/1372 3660"/>
<Edge cubics="!5000 2000(;5000,2000 4000,2000 4000,2000q5000 2000 4000 2000);"/>
<Edge cubics="!4000 2000(4000,2000;4000,2000 4000,1000 4000,1000q4000 2000 4000 1000);"/>
<Edge cubics="!5000 1000(;5000,1000 5000,2000 5000,2000q5000 1000 5000 2000);"/>
@@ -2020,7 +2020,7 @@ stop();]]></script>
<AdjustColorFilter brightness="30" contrast="40" saturation="50" hue="100"/>
</filters>
</DOMSymbolInstance>
<DOMSymbolInstance libraryItemName="GreenRect" selected="true" centerPoint3DX="422.05" centerPoint3DY="165">
<DOMSymbolInstance libraryItemName="GreenRect" centerPoint3DX="422.05" centerPoint3DY="165">
<matrix>
<Matrix tx="384.55" ty="140"/>
</matrix>
@@ -4172,6 +4172,7 @@ stop();]]></script>
</timelines>
<PrinterSettings/>
<publishHistory>
<PublishItem publishSize="1433344" publishTime="1772569883"/>
<PublishItem publishSize="1433377" publishTime="1772488092"/>
<PublishItem publishSize="1433377" publishTime="1772487782"/>
<PublishItem publishSize="1433374" publishTime="1772487682"/>
@@ -4191,6 +4192,5 @@ stop();]]></script>
<PublishItem publishSize="1432355" publishTime="1699188875"/>
<PublishItem publishSize="1432352" publishTime="1699188841"/>
<PublishItem publishSize="1432352" publishTime="1699188766"/>
<PublishItem publishSize="1432347" publishTime="1699185074"/>
</publishHistory>
</DOMDocument>

View File

@@ -5,8 +5,8 @@
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<xmp:CreatorTool>Adobe Flash Professional CS6 - build 481</xmp:CreatorTool>
<xmp:CreateDate>2021-03-14T08:29:20+01:00</xmp:CreateDate>
<xmp:MetadataDate>2026-03-02T13:36:50-08:00</xmp:MetadataDate>
<xmp:ModifyDate>2026-03-02T13:36:50-08:00</xmp:ModifyDate>
<xmp:MetadataDate>2026-03-03T11:35:43-08:00</xmp:MetadataDate>
<xmp:ModifyDate>2026-03-03T11:35:43-08:00</xmp:ModifyDate>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
@@ -15,7 +15,7 @@
<rdf:Description rdf:about=""
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
<xmpMM:InstanceID>xmp.iid:97E446EC7F16F1119B53F77A0E0EAFD1</xmpMM:InstanceID>
<xmpMM:InstanceID>xmp.iid:D00C382B3817F111A8D794A495DC263E</xmpMM:InstanceID>
<xmpMM:DocumentID>xmp.did:D6D3FE199784EB1187FEAE6972EC5115</xmpMM:DocumentID>
<xmpMM:OriginalDocumentID>xmp.did:D6D3FE199784EB1187FEAE6972EC5115</xmpMM:OriginalDocumentID>
<xmpMM:History>
@@ -194,6 +194,12 @@
<stEvt:when>2021-03-14T08:29:20+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Flash Professional CS6 - build 481</stEvt:softwareAgent>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<stEvt:action>created</stEvt:action>
<stEvt:instanceID>xmp.iid:D00C382B3817F111A8D794A495DC263E</stEvt:instanceID>
<stEvt:when>2021-03-14T08:29:20+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Flash Professional CS6 - build 481</stEvt:softwareAgent>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
</rdf:Description>