godot/thirdparty/thorvg/patches/pr2702-sw_engine-handle-small-cubics.patch

From ac7d208ed8e4651c93ce1b2384070fccac9b6cb6 Mon Sep 17 00:00:00 2001
From: Mira Grudzinska <[email protected]>
Date: Sun, 1 Sep 2024 22:36:18 +0200
Subject: [PATCH] sw_engine: handle small cubics

During the stroke's outline calculation, the function
handling small cubics set all angles to zero. Such cases
should be ignored, as further processing caused errors -
when the cubic was small but not zero, setting the angles
to zero resulted in incorrect outlines.

@Issue: https://github.com/godotengine/godot/issues/96262
---
 src/renderer/sw_engine/tvgSwCommon.h   |  3 ++-
 src/renderer/sw_engine/tvgSwMath.cpp   | 19 ++++++++++++-------
 src/renderer/sw_engine/tvgSwStroke.cpp | 16 +++++++++++-----
 3 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h
index 893e9beca..158fe8ecd 100644
--- a/src/renderer/sw_engine/tvgSwCommon.h
+++ b/src/renderer/sw_engine/tvgSwCommon.h
@@ -491,7 +491,8 @@ SwFixed mathSin(SwFixed angle);
 void mathSplitCubic(SwPoint* base);
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
 SwFixed mathLength(const SwPoint& pt);
-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
+bool mathSmallCubic(const SwPoint* base);
+bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
 SwFixed mathMean(SwFixed angle1, SwFixed angle2);
 SwPoint mathTransform(const Point* to, const Matrix& transform);
 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
diff --git a/src/renderer/sw_engine/tvgSwMath.cpp b/src/renderer/sw_engine/tvgSwMath.cpp
index 1093edd62..b311be05f 100644
--- a/src/renderer/sw_engine/tvgSwMath.cpp
+++ b/src/renderer/sw_engine/tvgSwMath.cpp
@@ -44,7 +44,17 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2)
 }
 
 
-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
+bool mathSmallCubic(const SwPoint* base)
+{
+    auto d1 = base[2] - base[3];
+    auto d2 = base[1] - base[2];
+    auto d3 = base[0] - base[1];
+
+    return d1.small() && d2.small() && d3.small();
+}
+
+
+bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
 {
     auto d1 = base[2] - base[3];
     auto d2 = base[1] - base[2];
@@ -52,12 +62,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
 
     if (d1.small()) {
         if (d2.small()) {
-            if (d3.small()) {
-                angleIn = angleMid = angleOut = 0;
-                return true;
-            } else {
-                angleIn = angleMid = angleOut = mathAtan(d3);
-            }
+            angleIn = angleMid = angleOut = mathAtan(d3);
         } else {
             if (d3.small()) {
                 angleIn = angleMid = angleOut = mathAtan(d2);
diff --git a/src/renderer/sw_engine/tvgSwStroke.cpp b/src/renderer/sw_engine/tvgSwStroke.cpp
index 575d12951..4679b72cc 100644
--- a/src/renderer/sw_engine/tvgSwStroke.cpp
+++ b/src/renderer/sw_engine/tvgSwStroke.cpp
@@ -441,11 +441,17 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
         //initialize with current direction
         angleIn = angleOut = angleMid = stroke.angleIn;
 
-        if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
-            if (stroke.firstPt) stroke.angleIn = angleIn;
-            mathSplitCubic(arc);
-            arc += 3;
-            continue;
+        if (arc < limit) {
+            if (mathSmallCubic(arc)) {
+                arc -= 3;
+                continue;
+            }
+            if (!mathFlatCubic(arc, angleIn, angleMid, angleOut)) {
+                if (stroke.firstPt) stroke.angleIn = angleIn;
+                mathSplitCubic(arc);
+                arc += 3;
+                continue;
+            }
         }
 
         if (firstArc) {