fix(SVG importer): apply fillOpacity and strokeOpacity to gradient

This commit is contained in:
Jindra Petřík
2026-04-05 17:30:36 +02:00
parent 68cd50b0dc
commit ab0a616612

View File

@@ -107,11 +107,12 @@ public class SvgImporter {
private Rectangle2D.Double viewBox;
private double width;
private double height;
/**
* Constructor.
*
* @param st Shape tag
* @param svgXml SVG XML
* @return Imported tag
@@ -122,6 +123,7 @@ public class SvgImporter {
/**
* Constructor.
*
* @param mst Morph shape tag
* @param svgXml SVG XML
* @return Imported tag
@@ -132,6 +134,7 @@ public class SvgImporter {
/**
* Constructor.
*
* @param st Shape tag
* @param svgXml SVG XML
* @param fill Fill flag
@@ -143,6 +146,7 @@ public class SvgImporter {
/**
* Constructor.
*
* @param mst Morph shape tag
* @param svgXml SVG XML
* @param fill Fill flag
@@ -154,6 +158,7 @@ public class SvgImporter {
/**
* Constructor.
*
* @param startShape Start shape tag
* @param endShape End shape tag
* @param svgXml SVG XML
@@ -166,6 +171,7 @@ public class SvgImporter {
/**
* Constructor.
*
* @param st Start shape tag
* @param endShape End shape tag
* @param svgXml SVG XML
@@ -300,7 +306,7 @@ public class SvgImporter {
double ratioY = height / viewBox.height;
transform = transform.preConcatenate(Matrix.getScaleInstance(ratioX, ratioY));
}
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape, cachedBitmaps, false);
if (rootElement.hasAttribute("ffdec:objectType")
&& "morphshape".equals(rootElement.getAttribute("ffdec:objectType"))
@@ -337,9 +343,10 @@ public class SvgImporter {
return (Tag) st;
}
/**
* Applies animation to the element.
*
* @param element Element
* @return True if animation was applied
*/
@@ -447,8 +454,9 @@ public class SvgImporter {
}
/**
* Populates IDs.
* Generates id-element map, because getElementById does not work in some cases (namespaces?)
* Populates IDs. Generates id-element map, because getElementById does not
* work in some cases (namespaces?)
*
* @param el Element
* @param out Output map
*/
@@ -613,7 +621,7 @@ public class SvgImporter {
}
}
}
private void addStraightRecord(List<SHAPERECORD> newRecords, int deltaX, int deltaY) {
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.deltaX = deltaX;
@@ -621,29 +629,29 @@ public class SvgImporter {
ser.generalLineFlag = true;
ser.simplify();
ser.calculateBits();
//Line is too long, split into half
if (ser.numBits > StraightEdgeRecord.MAX_NUM_BITS) {
int deltaX1 = deltaX / 2;
int deltaY1 = deltaY / 2;
int deltaX2 = deltaX1;
int deltaY2 = deltaY1;
if (deltaX1 + deltaX2 < deltaX) {
deltaX2++;
}
if (deltaY1 + deltaY2 < deltaY) {
deltaY2++;
}
addStraightRecord(newRecords, deltaX1, deltaY1);
addStraightRecord(newRecords, deltaX2, deltaY2);
return;
}
newRecords.add(ser);
}
private void addCurvedRecord(List<SHAPERECORD> newRecords, int controlDeltaX, int controlDeltaY, int anchorDeltaX, int anchorDeltaY) {
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = controlDeltaX;
@@ -651,7 +659,7 @@ public class SvgImporter {
cer.anchorDeltaX = anchorDeltaX;
cer.anchorDeltaY = anchorDeltaY;
cer.calculateBits();
//Curve is too long, split into half
if (cer.numBits > CurvedEdgeRecord.MAX_NUM_BITS) {
BezierEdge be = new BezierEdge(0, 0, cer.controlDeltaX, cer.controlDeltaY, cer.controlDeltaX + cer.anchorDeltaX, cer.controlDeltaY + cer.anchorDeltaY);
@@ -668,12 +676,12 @@ public class SvgImporter {
int controlDeltaY2 = (int) Math.round(right.points.get(1).getY()) - controlDeltaY1 - anchorDeltaY1;
int anchorDeltaX2 = controlDeltaX + anchorDeltaX - controlDeltaX1 - anchorDeltaX1 - controlDeltaX2;
int anchorDeltaY2 = controlDeltaY + anchorDeltaY - controlDeltaY1 - anchorDeltaY1 - controlDeltaY2;
addCurvedRecord(newRecords, controlDeltaX1, controlDeltaY1, anchorDeltaX1, anchorDeltaY1);
addCurvedRecord(newRecords, controlDeltaX2, controlDeltaY2, anchorDeltaX2, anchorDeltaY2);
return;
}
newRecords.add(cer);
}
@@ -781,7 +789,7 @@ public class SvgImporter {
}
empty = true;
break;
case 'L':
case 'L':
x = command.params[0];
y = command.params[1];
@@ -821,8 +829,8 @@ public class SvgImporter {
p = transform2.transform(x, y);
int controlDeltaX = (int) Math.round(p.x - prevPoint.x);
int controlDeltaY = (int) Math.round(p.y - prevPoint.y);
int controlDeltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = new Point(prevPoint.x + controlDeltaX, prevPoint.y + controlDeltaY);
x = command.params[2];
@@ -832,7 +840,7 @@ public class SvgImporter {
int anchorDeltaX = (int) Math.round(p.x - prevPoint.x);
int anchorDeltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = new Point(prevPoint.x + anchorDeltaX, prevPoint.y + anchorDeltaY);
addCurvedRecord(newRecords, controlDeltaX, controlDeltaY, anchorDeltaX, anchorDeltaY);
empty = false;
break;
@@ -869,8 +877,8 @@ public class SvgImporter {
int subAnchorDeltaX = (int) Math.round(p.x - prevPoint.x);
int subAnchorDeltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = new Point(prevPoint.x + subAnchorDeltaX, prevPoint.y + subAnchorDeltaY);
addCurvedRecord(newRecords, subControlDeltaX, subControlDeltaY, subAnchorDeltaX, subAnchorDeltaY);
addCurvedRecord(newRecords, subControlDeltaX, subControlDeltaY, subAnchorDeltaX, subAnchorDeltaY);
}
empty = false;
@@ -1507,7 +1515,7 @@ public class SvgImporter {
public Rectangle2D.Double getViewBox() {
return viewBox;
}
}
//Stub for w3 test. TODO: refactor and move to test directory. It's here because of easy access - compiling single file
private static void svgTest(String name) throws IOException, InterruptedException {
@@ -1544,6 +1552,7 @@ public class SvgImporter {
/**
* Test for SVG.
*
* @param args The command line arguments
* @throws IOException On I/O error
* @throws InterruptedException On interrupt
@@ -1835,7 +1844,7 @@ public class SvgImporter {
return Math.atan2(dy, dx);
}
private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) {
private void applyFillGradients(SvgFill fill, FILLSTYLE fillStyle, RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style, double opacity) {
if (fill == null || fillStyle == null) {
return;
}
@@ -1854,7 +1863,7 @@ public class SvgImporter {
double y2 = parseCoordinate(lgfill.y2, 1);
Matrix tMatrix = new Matrix();
if (lgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) {
if (lgfill.gradientUnits == SvgGradientUnits.OBJECT_BOUNDING_BOX) {
Matrix xyMatrix = new Matrix();
xyMatrix.scaleX = (x2 - x1) * SWF.unitDivisor;
xyMatrix.rotateSkew0 = (y2 - y1) * SWF.unitDivisor;
@@ -1883,14 +1892,14 @@ public class SvgImporter {
.concatenate(scaleMatrix);
} else {
//SvgGradientUnits.USER_SPACE_ON_USE
x1 *= SWF.unitDivisor;
y1 *= SWF.unitDivisor;
x2 *= SWF.unitDivisor;
y2 *= SWF.unitDivisor;
y2 *= SWF.unitDivisor;
double L = 16384.0;
double ux = x2 - x1;
double uy = y2 - y1;
double du = Math.hypot(x2 - x1, y2 - y1);
@@ -1898,20 +1907,20 @@ public class SvgImporter {
double my = (y1 + y2) / 2.0;
double ax = ux / du;
double ay = uy / du;
double k = du / (2 * L);
double nx = -ay;
double ny = ax;
double newScaleX = k * (gradientMatrix.scaleX * ax + gradientMatrix.rotateSkew1 * ay);
double newRotateSkew1 = k * (gradientMatrix.scaleX * nx + gradientMatrix.rotateSkew1 * ny);
double newTranslateX = gradientMatrix.scaleX * mx + gradientMatrix.rotateSkew1 * my + gradientMatrix.translateX;
double newRotateSkew0 = k * (gradientMatrix.rotateSkew0 * ax + gradientMatrix.scaleY * ay);
double newScaleY = k * (gradientMatrix.rotateSkew0 * nx + gradientMatrix.scaleY * ny);
double newTranslateY = gradientMatrix.rotateSkew0 * mx + gradientMatrix.scaleY * my + gradientMatrix.translateY;
double newTranslateY = gradientMatrix.rotateSkew0 * mx + gradientMatrix.scaleY * my + gradientMatrix.translateY;
tMatrix = new Matrix(newScaleX, newScaleY, newRotateSkew0, newRotateSkew1, newTranslateX, newTranslateY);
}
fillStyle.gradientMatrix = tMatrix.toMATRIX();
@@ -1998,7 +2007,7 @@ public class SvgImporter {
for (int i = 0; i < recCount; i++) {
SvgStop stop = gfill.stops.get(i);
Color color = stop.color;
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.getOpacity()));
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * opacity));
fillStyle.gradient.gradientRecords[i] = new GRADRECORD();
fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3;
fillStyle.gradient.gradientRecords[i].color = getRGB(shapeNum, color);
@@ -2022,12 +2031,12 @@ public class SvgImporter {
private void applyStyleGradients(RECT bounds, StyleChangeRecord scr, Matrix transform, int shapeNum, SvgStyle style) {
SvgFill fill = style.getFillWithOpacity();
if (fill != null && fill != SvgTransparentFill.INSTANCE) {
applyFillGradients(fill, scr.fillStyles.fillStyles[0], bounds, scr, transform, shapeNum, style);
applyFillGradients(fill, scr.fillStyles.fillStyles[0], bounds, scr, transform, shapeNum, style, style.getOpacity() * style.getFillOpacity());
}
SvgFill strokeFill = style.getStrokeFillWithOpacity();
if (strokeFill != null) {
if (shapeNum == 4 && scr.lineStyles.lineStyles2.length > 0 && scr.lineStyles.lineStyles2[0] instanceof LINESTYLE2) {
applyFillGradients(strokeFill, ((LINESTYLE2) scr.lineStyles.lineStyles2[0]).fillType, bounds, scr, transform, shapeNum, style);
applyFillGradients(strokeFill, ((LINESTYLE2) scr.lineStyles.lineStyles2[0]).fillType, bounds, scr, transform, shapeNum, style, style.getOpacity() * style.getStrokeOpacity());
}
}
}
@@ -2077,7 +2086,7 @@ public class SvgImporter {
ILINESTYLE lineStyle = shapeNum <= 3 ? new LINESTYLE() : new LINESTYLE2();
lineStyle.setColor(getRGB(shapeNum, lineColor));
double scale = Math.sqrt(Math.abs(transform.scaleX * transform.scaleY - transform.rotateSkew0 * transform.rotateSkew1));
lineStyle.setWidth((int) Math.round(style.getStrokeWidth() * scale * SWF.unitDivisor));
SvgLineCap lineCap = style.getStrokeLineCap();
SvgLineJoin lineJoin = style.getStrokeLineJoin();
@@ -2213,6 +2222,7 @@ public class SvgImporter {
/**
* Parses a number from a string.
*
* @param value The string
* @return The number
*/
@@ -2227,6 +2237,7 @@ public class SvgImporter {
/**
* Parses a number or percent from a string.
*
* @param value The string
* @return The number
*/
@@ -2265,5 +2276,5 @@ public class SvgImporter {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, text);
shownWarnings.add(name);
}
}
}
}