sw_engine: clamp focal point to the edge

If the radial gradient's focal point lies outside
the end circle, it's projected onto the edge.
A slight inward offset is applied to avoid numerical
issues.

Note:
Focal point support in the sw and wg engines is
consistent with the SVG 1.1 standard, whereas the gl
engine aligns with the SVG 2.0 standard.
This commit is contained in:
Mira Grudzinska 2025-06-18 11:44:49 +09:00
parent 3862fa802f
commit 0c9a1d3f07

View file

@ -277,10 +277,11 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
//This condition fulfills the SVG 1.1 std:
//the focal point, if outside the end circle, is moved to be on the end circle
//See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
if (fill->radial.a < 0) {
if (fill->radial.a <= FLOAT_EPSILON) {
auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
fill->radial.fx = cx + r * (fx - cx) / dist;
fill->radial.fy = cy + r * (fy - cy) / dist;
constexpr const float precision = 0.99f; //retract focal point slightly from edge to avoid numerical errors
fill->radial.fx = cx + r * precision * (fx - cx) / dist;
fill->radial.fy = cy + r * precision * (fy - cy) / dist;
fill->radial.dx = cx - fill->radial.fx;
fill->radial.dy = cy - fill->radial.fy;
// Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA