From cea221e2d56ee530c360a84af404857862344ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorc=C3=A1n=20Mc=20Donagh?= Date: Thu, 7 Mar 2024 16:13:33 +0100 Subject: [PATCH] sw_engine: fix radial gradient when focal point is outside circle on ARM/Apple Silicon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sw_engine: radial gradient [issues 2014: radial gradient](#2014) Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon. This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation, which cause loss of precision. We rely on temporary variables to prevent FMA. We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future --- AUTHORS | 1 + .../radial_gradient_focal_outside_circle.svg | 12 ++++++++++++ src/renderer/sw_engine/tvgSwFill.cpp | 8 +++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/examples/resources/radial_gradient_focal_outside_circle.svg diff --git a/AUTHORS b/AUTHORS index be3f4093..da584810 100644 --- a/AUTHORS +++ b/AUTHORS @@ -26,3 +26,4 @@ SergeyLebedkin Jinny You Nattu Adnan Gabor Kiss-Vamosi +Lorcán Mc Donagh diff --git a/src/examples/resources/radial_gradient_focal_outside_circle.svg b/src/examples/resources/radial_gradient_focal_outside_circle.svg new file mode 100644 index 00000000..8d0bd78f --- /dev/null +++ b/src/examples/resources/radial_gradient_focal_outside_circle.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/renderer/sw_engine/tvgSwFill.cpp b/src/renderer/sw_engine/tvgSwFill.cpp index 8956cd9f..60763068 100644 --- a/src/renderer/sw_engine/tvgSwFill.cpp +++ b/src/renderer/sw_engine/tvgSwFill.cpp @@ -201,7 +201,13 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr fill->radial.fy = cy + r * (fy - cy) / dist; fill->radial.dx = cx - fill->radial.fx; fill->radial.dy = cy - fill->radial.fy; - fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy; + // Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA + // https://github.com/thorvg/thorvg/issues/2014 + auto dr2 = fill->radial.dr * fill->radial.dr; + auto dx2 = fill->radial.dx * fill->radial.dx; + auto dy2 = fill->radial.dy * fill->radial.dy; + + fill->radial.a = dr2 - dx2 - dy2; } if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;