Browse Source

thorvg: Update to 0.10.7

Fixes #81618.
Rémi Verschelde 2 years ago
parent
commit
81949c2cd2
52 changed files with 1911 additions and 1521 deletions
  1. 1 1
      COPYRIGHT.txt
  2. 4 4
      modules/svg/SCsub
  3. 3 1
      modules/text_server_adv/SCsub
  4. 11 5
      modules/text_server_adv/gdextension_build/SConstruct
  5. 3 1
      modules/text_server_fb/SCsub
  6. 11 5
      modules/text_server_fb/gdextension_build/SConstruct
  7. 1 1
      thirdparty/README.md
  8. 1 1
      thirdparty/thorvg/inc/config.h
  9. 2 1
      thirdparty/thorvg/inc/thorvg.h
  10. 46 76
      thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
  11. 318 79
      thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
  12. 1 1
      thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
  13. 319 376
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
  14. 48 197
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
  15. 28 20
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
  16. 2 2
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
  17. 151 93
      thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
  18. 1 11
      thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
  19. 21 9
      thirdparty/thorvg/src/lib/tvgAnimation.cpp
  20. 3 2
      thirdparty/thorvg/src/lib/tvgCanvasImpl.h
  21. 6 2
      thirdparty/thorvg/src/lib/tvgCommon.h
  22. 134 0
      thirdparty/thorvg/src/lib/tvgFill.cpp
  23. 23 0
      thirdparty/thorvg/src/lib/tvgFill.h
  24. 0 100
      thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
  25. 19 17
      thirdparty/thorvg/src/lib/tvgLoader.cpp
  26. 13 16
      thirdparty/thorvg/src/lib/tvgPaint.cpp
  27. 18 5
      thirdparty/thorvg/src/lib/tvgPaint.h
  28. 4 7
      thirdparty/thorvg/src/lib/tvgPictureImpl.h
  29. 0 98
      thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
  30. 28 11
      thirdparty/thorvg/src/lib/tvgRender.h
  31. 6 6
      thirdparty/thorvg/src/lib/tvgSceneImpl.h
  32. 7 16
      thirdparty/thorvg/src/lib/tvgShape.cpp
  33. 35 19
      thirdparty/thorvg/src/lib/tvgShapeImpl.h
  34. 12 1
      thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
  35. 1 0
      thirdparty/thorvg/src/lib/tvgTaskScheduler.h
  36. 2 2
      thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
  37. 66 22
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
  38. 9 18
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
  39. 4 4
      thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
  40. 124 39
      thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
  41. 0 223
      thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
  42. 0 5
      thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
  43. 3 3
      thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
  44. 26 1
      thirdparty/thorvg/src/utils/tvgArray.h
  45. 1 1
      thirdparty/thorvg/src/utils/tvgBezier.cpp
  46. 0 0
      thirdparty/thorvg/src/utils/tvgBezier.h
  47. 61 14
      thirdparty/thorvg/src/utils/tvgCompressor.cpp
  48. 4 3
      thirdparty/thorvg/src/utils/tvgCompressor.h
  49. 52 0
      thirdparty/thorvg/src/utils/tvgMath.h
  50. 239 0
      thirdparty/thorvg/src/utils/tvgStr.cpp
  51. 37 0
      thirdparty/thorvg/src/utils/tvgStr.h
  52. 2 2
      thirdparty/thorvg/update-thorvg.sh

+ 1 - 1
COPYRIGHT.txt

@@ -466,7 +466,7 @@ License: Expat
 
 Files: ./thirdparty/thorvg/
 Comment: ThorVG
-Copyright: 2020-2022, Samsung Electronics Co., Ltd.
+Copyright: 2020-2023, The ThorVG Project
 License: Expat
 
 Files: ./thirdparty/tinyexr/

+ 4 - 4
modules/svg/SCsub

@@ -21,23 +21,22 @@ thirdparty_sources = [
     "src/lib/sw_engine/tvgSwShape.cpp",
     "src/lib/sw_engine/tvgSwStroke.cpp",
     "src/lib/tvgAccessor.cpp",
-    "src/lib/tvgBezier.cpp",
     "src/lib/tvgCanvas.cpp",
     "src/lib/tvgFill.cpp",
     "src/lib/tvgGlCanvas.cpp",
     "src/lib/tvgInitializer.cpp",
-    "src/lib/tvgLinearGradient.cpp",
     "src/lib/tvgLoader.cpp",
-    "src/lib/tvgLzw.cpp",
     "src/lib/tvgPaint.cpp",
     "src/lib/tvgPicture.cpp",
-    "src/lib/tvgRadialGradient.cpp",
     "src/lib/tvgRender.cpp",
     "src/lib/tvgSaver.cpp",
     "src/lib/tvgScene.cpp",
     "src/lib/tvgShape.cpp",
     "src/lib/tvgSwCanvas.cpp",
     "src/lib/tvgTaskScheduler.cpp",
+    "src/utils/tvgBezier.cpp",
+    "src/utils/tvgCompressor.cpp",
+    "src/utils/tvgStr.cpp",
     "src/loaders/raw/tvgRawLoader.cpp",
     "src/loaders/svg/tvgSvgCssStyle.cpp",
     "src/loaders/svg/tvgSvgLoader.cpp",
@@ -62,6 +61,7 @@ env_thirdparty.Prepend(
         thirdparty_dir + "src/lib/sw_engine",
         thirdparty_dir + "src/loaders/raw",
         thirdparty_dir + "src/loaders/svg",
+        thirdparty_dir + "src/utils",
     ]
 )
 # Also requires libpng headers

+ 3 - 1
modules/text_server_adv/SCsub

@@ -39,7 +39,9 @@ freetype_enabled = "freetype" in env.module_list
 msdfgen_enabled = "msdfgen" in env.module_list
 
 if "svg" in env.module_list:
-    env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+    env_text_server_adv.Prepend(
+        CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+    )
     # Enable ThorVG static object linking.
     env_text_server_adv.Append(CPPDEFINES=["TVG_STATIC"])
 

+ 11 - 5
modules/text_server_adv/gdextension_build/SConstruct

@@ -52,23 +52,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
         "src/lib/sw_engine/tvgSwShape.cpp",
         "src/lib/sw_engine/tvgSwStroke.cpp",
         "src/lib/tvgAccessor.cpp",
-        "src/lib/tvgBezier.cpp",
         "src/lib/tvgCanvas.cpp",
         "src/lib/tvgFill.cpp",
         "src/lib/tvgGlCanvas.cpp",
         "src/lib/tvgInitializer.cpp",
-        "src/lib/tvgLinearGradient.cpp",
         "src/lib/tvgLoader.cpp",
-        "src/lib/tvgLzw.cpp",
         "src/lib/tvgPaint.cpp",
         "src/lib/tvgPicture.cpp",
-        "src/lib/tvgRadialGradient.cpp",
         "src/lib/tvgRender.cpp",
         "src/lib/tvgSaver.cpp",
         "src/lib/tvgScene.cpp",
         "src/lib/tvgShape.cpp",
         "src/lib/tvgSwCanvas.cpp",
         "src/lib/tvgTaskScheduler.cpp",
+        "src/utils/tvgBezier.cpp",
+        "src/utils/tvgCompressor.cpp",
+        "src/utils/tvgStr.cpp",
         "src/loaders/raw/tvgRawLoader.cpp",
         "src/loaders/svg/tvgSvgCssStyle.cpp",
         "src/loaders/svg/tvgSvgLoader.cpp",
@@ -86,6 +85,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
             "../../../thirdparty/thorvg/src/lib/sw_engine",
             "../../../thirdparty/thorvg/src/loaders/raw",
             "../../../thirdparty/thorvg/src/loaders/svg",
+            "../../../thirdparty/thorvg/src/utils",
             "../../../thirdparty/libpng",
         ]
     )
@@ -93,7 +93,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
     # Enable ThorVG static object linking.
     env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
 
-    env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+    env.Append(
+        CPPPATH=[
+            "../../../thirdparty/thorvg/inc",
+            "../../../thirdparty/thorvg/src/lib",
+            "../../../thirdparty/thorvg/src/utils",
+        ]
+    )
     env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
 
     lib = env_tvg.Library(

+ 3 - 1
modules/text_server_fb/SCsub

@@ -9,7 +9,9 @@ msdfgen_enabled = "msdfgen" in env.module_list
 env_text_server_fb = env_modules.Clone()
 
 if "svg" in env.module_list:
-    env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+    env_text_server_fb.Prepend(
+        CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+    )
     # Enable ThorVG static object linking.
     env_text_server_fb.Append(CPPDEFINES=["TVG_STATIC"])
 

+ 11 - 5
modules/text_server_fb/gdextension_build/SConstruct

@@ -47,23 +47,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
         "src/lib/sw_engine/tvgSwShape.cpp",
         "src/lib/sw_engine/tvgSwStroke.cpp",
         "src/lib/tvgAccessor.cpp",
-        "src/lib/tvgBezier.cpp",
         "src/lib/tvgCanvas.cpp",
         "src/lib/tvgFill.cpp",
         "src/lib/tvgGlCanvas.cpp",
         "src/lib/tvgInitializer.cpp",
-        "src/lib/tvgLinearGradient.cpp",
         "src/lib/tvgLoader.cpp",
-        "src/lib/tvgLzw.cpp",
         "src/lib/tvgPaint.cpp",
         "src/lib/tvgPicture.cpp",
-        "src/lib/tvgRadialGradient.cpp",
         "src/lib/tvgRender.cpp",
         "src/lib/tvgSaver.cpp",
         "src/lib/tvgScene.cpp",
         "src/lib/tvgShape.cpp",
         "src/lib/tvgSwCanvas.cpp",
         "src/lib/tvgTaskScheduler.cpp",
+        "src/utils/tvgBezier.cpp",
+        "src/utils/tvgCompressor.cpp",
+        "src/utils/tvgStr.cpp",
         "src/loaders/raw/tvgRawLoader.cpp",
         "src/loaders/svg/tvgSvgCssStyle.cpp",
         "src/loaders/svg/tvgSvgLoader.cpp",
@@ -81,6 +80,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
             "../../../thirdparty/thorvg/src/lib/sw_engine",
             "../../../thirdparty/thorvg/src/loaders/raw",
             "../../../thirdparty/thorvg/src/loaders/svg",
+            "../../../thirdparty/thorvg/src/utils",
             "../../../thirdparty/libpng",
         ]
     )
@@ -88,7 +88,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
     # Enable ThorVG static object linking.
     env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
 
-    env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+    env.Append(
+        CPPPATH=[
+            "../../../thirdparty/thorvg/inc",
+            "../../../thirdparty/thorvg/src/lib",
+            "../../../thirdparty/thorvg/src/utils",
+        ]
+    )
     env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
 
     lib = env_tvg.Library(

+ 1 - 1
thirdparty/README.md

@@ -815,7 +815,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.10.0 (b8c605583fd7de73209a93a1238e1ba72cce2e8f, 2023)
+- Version: 0.10.7 (026ff4ce7eda10dd0cf80eeaef56fe3a5ed89f93, 2023)
 - License: MIT
 
 Files extracted from upstream source:

+ 1 - 1
thirdparty/thorvg/inc/config.h

@@ -5,5 +5,5 @@
 
 #define THORVG_SVG_LOADER_SUPPORT
 
-#define THORVG_VERSION_STRING "0.10.0"
+#define THORVG_VERSION_STRING "0.10.7"
 #endif

+ 2 - 1
thirdparty/thorvg/inc/thorvg.h

@@ -1268,7 +1268,7 @@ public:
      *
      * @param[in] data A pointer to a memory location where the content of the picture file is stored.
      * @param[in] size The size in bytes of the memory occupied by the @p data.
-     * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
+     * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      *
      * @retval Result::Success When succeed.
@@ -1278,6 +1278,7 @@ public:
      *
      * @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true.
      *
+     * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out.
      * @since 0.5
      */
     Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept;

+ 46 - 76
thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h

@@ -26,6 +26,8 @@
 #include "tvgCommon.h"
 #include "tvgRender.h"
 
+#include <algorithm>
+
 #if 0
 #include <sys/time.h>
 static double timeStamp()
@@ -139,10 +141,11 @@ struct SwFill
     };
 
     struct SwRadial {
-        float a11, a12, shiftX;
-        float a21, a22, shiftY;
-        float detSecDeriv;
-        float a;
+        float a11, a12, a13;
+        float a21, a22, a23;
+        float fx, fy, fr;
+        float dx, dy, dr;
+        float invA, a;
     };
 
     union {
@@ -194,14 +197,14 @@ struct SwStroke
 
 struct SwDashStroke
 {
-    SwOutline* outline;
-    float curLen;
-    int32_t curIdx;
-    Point ptStart;
-    Point ptCur;
-    float* pattern;
-    uint32_t cnt;
-    bool curOpGap;
+    SwOutline* outline = nullptr;
+    float curLen = 0;
+    int32_t curIdx = 0;
+    Point ptStart = {0, 0};
+    Point ptCur = {0, 0};
+    float* pattern = nullptr;
+    uint32_t cnt = 0;
+    bool curOpGap = false;
 };
 
 struct SwShape
@@ -235,6 +238,7 @@ struct SwImage
     bool         scaled = false;  //draw scaled image
 };
 
+typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a);                  //src, dst, alpha
 typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a);            //src, dst, alpha
 typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);      //color channel join
 typedef uint8_t(*SwAlpha)(uint8_t*);                                        //blending alpha
@@ -295,7 +299,7 @@ static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
 
 static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
 {
-    return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8);
+    return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8);
 }
 
 static inline SwCoord HALF_STROKE(float width)
@@ -363,18 +367,18 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
 static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     //A + B - 2AB
-    auto c1 = min(255, C1(s) + C1(d) - min(255, (C1(s) * C1(d)) << 1));
-    auto c2 = min(255, C2(s) + C2(d) - min(255, (C2(s) * C2(d)) << 1));
-    auto c3 = min(255, C3(s) + C3(d) - min(255, (C3(s) * C3(d)) << 1));
+    auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
+    auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
+    auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
     return JOIN(255, c1, c2, c3);
 }
 
 static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // s + d
-    auto c1 = min(C1(s) + C1(d), 255);
-    auto c2 = min(C2(s) + C2(d), 255);
-    auto c3 = min(C3(s) + C3(d), 255);
+    auto c1 = std::min(C1(s) + C1(d), 255);
+    auto c2 = std::min(C2(s) + C2(d), 255);
+    auto c3 = std::min(C3(s) + C3(d), 255);
     return JOIN(255, c1, c2, c3);
 }
 
@@ -402,27 +406,27 @@ static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
 {
     // if (2 * d < da) => 2 * s * d,
     // else => 1 - 2 * (1 - s) * (1 - d)
-    auto c1 = (C1(d) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
-    auto c2 = (C2(d) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
-    auto c3 = (C3(d) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+    auto c1 = (C1(d) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+    auto c2 = (C2(d) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+    auto c3 = (C3(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
     return JOIN(255, c1, c2, c3);
 }
 
 static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // min(s, d)
-    auto c1 = min(C1(s), C1(d));
-    auto c2 = min(C2(s), C2(d));
-    auto c3 = min(C3(s), C3(d));
+    auto c1 = std::min(C1(s), C1(d));
+    auto c2 = std::min(C2(s), C2(d));
+    auto c3 = std::min(C3(s), C3(d));
     return JOIN(255, c1, c2, c3);
 }
 
 static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // max(s, d)
-    auto c1 = max(C1(s), C1(d));
-    auto c2 = max(C2(s), C2(d));
-    auto c3 = max(C3(s), C3(d));
+    auto c1 = std::max(C1(s), C1(d));
+    auto c2 = std::max(C2(s), C2(d));
+    auto c3 = std::max(C3(s), C3(d));
     return JOIN(255, c1, c2, c3);
 }
 
@@ -448,61 +452,21 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
 
 static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
-    auto c1 = (C1(s) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
-    auto c2 = (C2(s) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
-    auto c3 = (C3(s) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+    auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+    auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+    auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
     return JOIN(255, c1, c2, c3);
 }
 
 static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     //(255 - 2 * s) * (d * d) + (2 * s * b)
-    auto c1 = min(255, MULTIPLY(255 - min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
-    auto c2 = min(255, MULTIPLY(255 - min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
-    auto c3 = min(255, MULTIPLY(255 - min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
+    auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
+    auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
+    auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
     return JOIN(255, c1, c2, c3);
 }
 
-static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
-{
-    return opBlendNormal(s, d, a);
-}
-
-static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a)
-{
-    return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
-}
-
-static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
-{
-    auto t = ALPHA_BLEND(s, a);
-    return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
-}
-
-static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
-{
-   return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
-}
-
-static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
-    return opBlendPreNormal(s, d, a);
-}
-
-static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
-    return ALPHA_BLEND(d, IA(s));
-}
-
-static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
-   return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s));
-}
-
-static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
-   return ALPHA_BLEND(d, MULTIPLY(a, IA(s)));
-}
 
 int64_t mathMultiply(int64_t a, int64_t b);
 int64_t mathDivide(int64_t a, int64_t b);
@@ -551,13 +515,19 @@ void imageFree(SwImage* image);
 bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
 void fillReset(SwFill* fill);
 void fillFree(SwFill* fill);
+
 //OPTIMIZE_ME: Skip the function pointer access
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity);                                   //composite masking ver.
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity);                     //direct masking ver.
 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a);                                         //blending ver.
 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a);                          //blending + BlendingMethod(op2) ver.
-void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //masking ver.
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //matting ver.
+
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a);                                             //composite masking ver.
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ;                              //direct masking ver.
 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a);                                         //blending ver.
 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a);                          //blending + BlendingMethod(op2) ver.
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //masking ver.
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //matting ver.
 
 SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
 SwRleData* rleRender(const SwBBox* bbox);

+ 318 - 79
thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp

@@ -22,16 +22,46 @@
 
 #include "tvgMath.h"
 #include "tvgSwCommon.h"
-
+#include "tvgFill.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
+#define RADIAL_A_THRESHOLD 0.0005f
 #define GRADIENT_STOP_SIZE 1024
 #define FIXPT_BITS 8
 #define FIXPT_SIZE (1<<FIXPT_BITS)
 
+/*
+ * quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
+ * A = a  // fill->radial.a
+ * B = 2 * (dr * fr + rx * dx + ry * dy)
+ * C = fr^2 - rx^2 - ry^2
+ * Derivatives are computed with respect to dx.
+ * This procedure aims to optimize and eliminate the need to calculate all values from the beginning
+ * for consecutive x values with a constant y. The Taylor series expansions are computed as long as
+ * its terms are non-zero.
+ */
+static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
+{
+    auto radial = &fill->radial;
+
+    auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+    auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+    b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
+    deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
+
+    auto rr = rx * rx + ry * ry;
+    auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
+    auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
+
+    det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
+    deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
+    deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
+}
+
 
 static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
 {
@@ -146,46 +176,62 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
 
 bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
 {
-    float radius, cx, cy;
-    if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
-    if (radius < FLT_EPSILON) return true;
+    auto cx = P(radial)->cx;
+    auto cy = P(radial)->cy;
+    auto r = P(radial)->r;
+    auto fx = P(radial)->fx;
+    auto fy = P(radial)->fy;
+    auto fr = P(radial)->fr;
+
+    if (r < FLT_EPSILON) return true;
+
+    fill->radial.dr = r - fr;
+    fill->radial.dx = cx - fx;
+    fill->radial.dy = cy - fy;
+    fill->radial.fr = fr;
+    fill->radial.fx = fx;
+    fill->radial.fy = fy;
+    fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
+
+    //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) {
+        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;
+        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;
+    }
 
-    float invR = 1.0f / radius;
-    fill->radial.shiftX = -cx;
-    fill->radial.shiftY = -cy;
-    fill->radial.a = radius;
+    if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
 
     auto gradTransform = radial->transform();
     bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
 
-    if (isTransformation) {
-        if (transform) gradTransform = mathMultiply(transform, &gradTransform);
-    } else if (transform) {
-        gradTransform = *transform;
-        isTransformation = true;
+    if (transform) {
+        if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
+        else {
+            gradTransform = *transform;
+            isTransformation = true;
+        }
     }
 
     if (isTransformation) {
         Matrix invTransform;
         if (!mathInverse(&gradTransform, &invTransform)) return false;
-
-        fill->radial.a11 = invTransform.e11 * invR;
-        fill->radial.a12 = invTransform.e12 * invR;
-        fill->radial.shiftX += invTransform.e13;
-        fill->radial.a21 = invTransform.e21 * invR;
-        fill->radial.a22 = invTransform.e22 * invR;
-        fill->radial.shiftY += invTransform.e23;
-        fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
-
-        fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
+        fill->radial.a11 = invTransform.e11;
+        fill->radial.a12 = invTransform.e12;
+        fill->radial.a13 = invTransform.e13;
+        fill->radial.a21 = invTransform.e21;
+        fill->radial.a22 = invTransform.e22;
+        fill->radial.a23 = invTransform.e23;
     } else {
-        fill->radial.a11 = fill->radial.a22 = invR;
-        fill->radial.a12 = fill->radial.a21 = 0.0f;
-        fill->radial.detSecDeriv = 2.0f * invR * invR;
+        fill->radial.a11 = fill->radial.a22 = 1.0f;
+        fill->radial.a12 = fill->radial.a13 = 0.0f;
+        fill->radial.a21 = fill->radial.a23 = 0.0f;
     }
-    fill->radial.shiftX *= invR;
-    fill->radial.shiftY *= invR;
-
     return true;
 }
 
@@ -233,77 +279,181 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
 /* External Class Implementation                                        */
 /************************************************************************/
 
+
 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
 {
-    auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
-    auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+    //edge case
+    if (fill->radial.a < RADIAL_A_THRESHOLD) {
+        auto radial = &fill->radial;
+        auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+        auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+        if (opacity == 255) {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+                auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+                *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
+                rx += radial->a11;
+                ry += radial->a21;
+            }
+        } else {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+                auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+                *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
+                rx += radial->a11;
+                ry += radial->a21;
+            }
+        }
+    } else {
+        float b, deltaB, det, deltaDet, deltaDeltaDet;
+        _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+        if (opacity == 255) {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+                *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
+                det += deltaDet;
+                deltaDet += deltaDeltaDet;
+                b += deltaB;
+            }
+        } else {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+                *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
+                det += deltaDet;
+                deltaDet += deltaDeltaDet;
+                b += deltaB;
+            }
+        }
+    }
+}
 
-    // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
-    auto detSecondDerivative = fill->radial.detSecDeriv;
-    // detFirstDerivative = d(det)/dx
-    auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
-    auto det = rx * rx + ry * ry;
 
-    if (opacity == 255) {
-        for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
-            *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp));
-            det += detFirstDerivative;
-            detFirstDerivative += detSecondDerivative;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+{
+    if (fill->radial.a < RADIAL_A_THRESHOLD) {
+        auto radial = &fill->radial;
+        auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+        auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+        for (uint32_t i = 0; i < len; ++i, ++dst) {
+            auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+            *dst = op(_pixel(fill, x0), *dst, a);
+            rx += radial->a11;
+            ry += radial->a21;
         }
     } else {
-        for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
-            *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp)));
-            det += detFirstDerivative;
-            detFirstDerivative += detSecondDerivative;
+        float b, deltaB, det, deltaDet, deltaDeltaDet;
+        _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+        for (uint32_t i = 0; i < len; ++i, ++dst) {
+            *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
+            det += deltaDet;
+            deltaDet += deltaDeltaDet;
+            b += deltaB;
         }
     }
 }
 
 
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
 {
-    auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
-    auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
-
-    // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
-    auto detSecondDerivative = fill->radial.detSecDeriv;
-    // detFirstDerivative = d(det)/dx
-    auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
-    auto det = rx * rx + ry * ry;
-
-    for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
-        *dst = op(_pixel(fill, sqrtf(det)), *dst, a);
-        det += detFirstDerivative;
-        detFirstDerivative += detSecondDerivative;
+    if (fill->radial.a < RADIAL_A_THRESHOLD) {
+        auto radial = &fill->radial;
+        auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+        auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+            auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+            auto src = MULTIPLY(a, A(_pixel(fill, x0)));
+            *dst = maskOp(src, *dst, ~src);
+            rx += radial->a11;
+            ry += radial->a21;
+        }
+    } else {
+        float b, deltaB, det, deltaDet, deltaDeltaDet;
+        _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+            auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
+            *dst = maskOp(src, *dst, ~src);
+            det += deltaDet;
+            deltaDet += deltaDeltaDet;
+            b += deltaB;
+        }
     }
 }
 
 
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
 {
-    auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
-    auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+    if (fill->radial.a < RADIAL_A_THRESHOLD) {
+        auto radial = &fill->radial;
+        auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+        auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+            auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+            auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
+            auto tmp = maskOp(src, *cmp, 0);
+            *dst = tmp + MULTIPLY(*dst, ~tmp);
+            rx += radial->a11;
+            ry += radial->a21;
+        }
+    } else {
+        float b, deltaB, det, deltaDet, deltaDeltaDet;
+        _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+            auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
+            auto tmp = maskOp(src, *cmp, 0);
+            *dst = tmp + MULTIPLY(*dst, ~tmp);
+            deltaDet += deltaDeltaDet;
+            b += deltaB;
+        }
+    }
+}
 
-    // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
-    auto detSecondDerivative = fill->radial.detSecDeriv;
-    // detFirstDerivative = d(det)/dx
-    auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
-    auto det = rx * rx + ry * ry;
 
-    if (a == 255) {
-        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
-            auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
-            *dst = op2(tmp, *dst, 255);
-            det += detFirstDerivative;
-            detFirstDerivative += detSecondDerivative;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+{
+    if (fill->radial.a < RADIAL_A_THRESHOLD) {
+        auto radial = &fill->radial;
+        auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+        auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+        if (a == 255) {
+            for (uint32_t i = 0; i < len; ++i, ++dst) {
+                auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+                auto tmp = op(_pixel(fill, x0), *dst, 255);
+                *dst = op2(tmp, *dst, 255);
+                rx += radial->a11;
+                ry += radial->a21;
+            }
+        } else {
+            for (uint32_t i = 0; i < len; ++i, ++dst) {
+                auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+                auto tmp = op(_pixel(fill, x0), *dst, 255);
+                auto tmp2 = op2(tmp, *dst, 255);
+                *dst = INTERPOLATE(tmp2, *dst, a);
+                rx += radial->a11;
+                ry += radial->a21;
+            }
         }
     } else {
-        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
-            auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
-            auto tmp2 = op2(tmp, *dst, 255);
-            *dst = INTERPOLATE(tmp2, *dst, a);
-            det += detFirstDerivative;
-            detFirstDerivative += detSecondDerivative;
+        float b, deltaB, det, deltaDet, deltaDeltaDet;
+        _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+        if (a == 255) {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+                auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+                *dst = op2(tmp, *dst, 255);
+                det += deltaDet;
+                deltaDet += deltaDeltaDet;
+                b += deltaB;
+            }
+        } else {
+            for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+                auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+                auto tmp2 = op2(tmp, *dst, 255);
+                *dst = INTERPOLATE(tmp2, *dst, a);
+                det += deltaDet;
+                deltaDet += deltaDeltaDet;
+                b += deltaB;
+            }
         }
     }
 }
@@ -383,6 +533,95 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
 }
 
 
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
+{
+    //Rotation
+    float rx = x + 0.5f;
+    float ry = y + 0.5f;
+    float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+    float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+    if (mathZero(inc)) {
+        auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
+        for (uint32_t i = 0; i < len; ++i, ++dst) {
+            *dst = maskOp(src, *dst, ~src);
+        }
+        return;
+    }
+
+    auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+    auto vMin = -vMax;
+    auto v = t + (inc * len);
+
+    //we can use fixed point math
+    if (v < vMax && v > vMin) {
+        auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+        auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+        for (uint32_t j = 0; j < len; ++j, ++dst) {
+            auto src = MULTIPLY(_fixedPixel(fill, t2), a);
+            *dst = maskOp(src, *dst, ~src);
+            t2 += inc2;
+        }
+    //we have to fallback to float math
+    } else {
+        uint32_t counter = 0;
+        while (counter++ < len) {
+            auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
+            *dst = maskOp(src, *dst, ~src);
+            ++dst;
+            t += inc;
+        }
+    }
+}
+
+
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
+{
+    //Rotation
+    float rx = x + 0.5f;
+    float ry = y + 0.5f;
+    float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+    float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+    if (mathZero(inc)) {
+        auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
+        src = MULTIPLY(src, a);
+        for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
+            auto tmp = maskOp(src, *cmp, 0);
+            *dst = tmp + MULTIPLY(*dst, ~tmp);
+        }
+        return;
+    }
+
+    auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+    auto vMin = -vMax;
+    auto v = t + (inc * len);
+
+    //we can use fixed point math
+    if (v < vMax && v > vMin) {
+        auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+        auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+        for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) {
+            auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
+            auto tmp = maskOp(src, *cmp, 0);
+            *dst = tmp + MULTIPLY(*dst, ~tmp);
+            t2 += inc2;
+        }
+    //we have to fallback to float math
+    } else {
+        uint32_t counter = 0;
+        while (counter++ < len) {
+            auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
+            auto tmp = maskOp(src, *cmp, 0);
+            *dst = tmp + MULTIPLY(*dst, ~tmp);
+            ++dst;
+            ++cmp;
+            t += inc;
+        }
+    }
+}
+
+
 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
 {
     //Rotation

+ 1 - 1
thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp

@@ -93,7 +93,7 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
         outline->types.push(SW_CURVE_TYPE_POINT);
     }
 
-    outline->pts.push(outline->pts.data[0]);
+    outline->pts.push(outline->pts[0]);
     outline->types.push(SW_CURVE_TYPE_POINT);
     outline->cntrs.push(outline->pts.count - 1);
     outline->closed.push(true);

File diff suppressed because it is too large
+ 319 - 376
thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp


+ 48 - 197
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h

@@ -70,190 +70,17 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
 }
 
 
-static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag)
+static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
 {
-    float _dudx = dudx, _dvdx = dvdx;
-    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
-    float _xa = xa, _xb = xb, _ua = ua, _va = va;
-    auto sbuf = image->buf32;
-    int32_t sw = static_cast<int32_t>(image->stride);
-    int32_t sh = image->h;
-    int32_t x1, x2, ar, ab, iru, irv, px, ay;
-    int32_t vv = 0, uu = 0;
-    int32_t minx = INT32_MAX, maxx = INT32_MIN;
-    float dx, u, v, iptr;
-    auto cbuffer = surface->compositor->image.buf32;
-    SwSpan* span = nullptr;         //used only when rle based.
-
-    if (!_arrange(image, region, yStart, yEnd)) return;
-
-    //Clear out of the Polygon vertical ranges
-    auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x;
-    if (dirFlag == 1) {     //left top case.
-        for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) {
-            rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
-        }
-    }
-    if (dirFlag == 4) {     //right bottom case.
-        for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) {
-            rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
-        }
-    }
-
-    //Loop through all lines in the segment
-    uint32_t spanIdx = 0;
-
-    if (region) {
-        minx = region->min.x;
-        maxx = region->max.x;
-    } else {
-        span = image->rle->spans;
-        while (span->y < yStart) {
-            ++span;
-            ++spanIdx;
-        }
-    }
-
-    for (int32_t y = yStart; y < yEnd; ++y) {
-        auto cmp = &cbuffer[y * surface->compositor->image.stride];
-        x1 = (int32_t)_xa;
-        x2 = (int32_t)_xb;
-
-        if (!region) {
-            minx = INT32_MAX;
-            maxx = INT32_MIN;
-            //one single row, could be consisted of multiple spans.
-            while (span->y == y && spanIdx < image->rle->size) {
-                if (minx > span->x) minx = span->x;
-                if (maxx < span->x + span->len) maxx = span->x + span->len;
-                ++span;
-                ++spanIdx;
-            }
-        }
-
-        if (x1 < minx) x1 = minx;
-        if (x2 > maxx) x2 = maxx;
-
-        //Anti-Aliasing frames
-        //FIXME: this aa must be applied before masking op
-        ay = y - aaSpans->yStart;
-        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
-        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
-
-        //Range allowed
-        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
-            for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
-                //Range allowed
-                if (x >= x1 && x < x2) {
-                    //Perform subtexel pre-stepping on UV
-                    dx = 1 - (_xa - x1);
-                    u = _ua + dx * _dudx;
-                    v = _va + dx * _dvdx;
-                    if ((uint32_t)v >= image->h) {
-                        cmp[x] = 0;
-                    } else {
-                        if (opacity == 255) {
-                            uu = (int) u;
-                            if (uu >= sw) continue;
-                            vv = (int) v;
-                            if (vv >= sh) continue;
-
-                            ar = (int)(255 * (1 - modff(u, &iptr)));
-                            ab = (int)(255 * (1 - modff(v, &iptr)));
-                            iru = uu + 1;
-                            irv = vv + 1;
-
-                            px = *(sbuf + (vv * sw) + uu);
-
-                            /* horizontal interpolate */
-                            if (iru < sw) {
-                                /* right pixel */
-                                int px2 = *(sbuf + (vv * sw) + iru);
-                                px = INTERPOLATE(px, px2, ar);
-                            }
-                            /* vertical interpolate */
-                            if (irv < sh) {
-                                /* bottom pixel */
-                                int px2 = *(sbuf + (irv * sw) + uu);
-
-                                /* horizontal interpolate */
-                                if (iru < sw) {
-                                    /* bottom right pixel */
-                                    int px3 = *(sbuf + (irv * sw) + iru);
-                                    px2 = INTERPOLATE(px2, px3, ar);
-                                }
-                                px = INTERPOLATE(px, px2, ab);
-                            }
-                            cmp[x] = ALPHA_BLEND(cmp[x], A(px));
-
-                            //Step UV horizontally
-                            u += _dudx;
-                            v += _dvdx;
-                        } else {
-                            uu = (int) u;
-                            if (uu >= sw) continue;
-                            vv = (int) v;
-                            if (vv >= sh) continue;
-
-                            ar = (int)(255 * (1 - modff(u, &iptr)));
-                            ab = (int)(255 * (1 - modff(v, &iptr)));
-                            iru = uu + 1;
-                            irv = vv + 1;
-
-                            px = *(sbuf + (vv * sw) + uu);
-
-                            /* horizontal interpolate */
-                            if (iru < sw) {
-                                /* right pixel */
-                                int px2 = *(sbuf + (vv * sw) + iru);
-                                px = INTERPOLATE(px, px2, ar);
-                            }
-                            /* vertical interpolate */
-                            if (irv < sh) {
-                                /* bottom pixel */
-                                int px2 = *(sbuf + (irv * sw) + uu);
-
-                                /* horizontal interpolate */
-                                if (iru < sw) {
-                                    /* bottom right pixel */
-                                    int px3 = *(sbuf + (irv * sw) + iru);
-                                    px2 = INTERPOLATE(px2, px3, ar);
-                                }
-                                px = INTERPOLATE(px, px2, ab);
-                            }
-                            cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity));
-
-                            //Step UV horizontally
-                            u += _dudx;
-                            v += _dvdx;
-                        }
-                    }
-                } else {
-                    //Clear out of polygon horizontal range
-                    if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0;
-                    else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0;
-                }
-            }
-        }
-        //Step along both edges
-        _xa += _dxdya;
-        _xb += _dxdyb;
-        _ua += _dudya;
-        _va += _dvdya;
-    }
-    xa = _xa;
-    xb = _xb;
-    ua = _ua;
-    va = _va;
-}
-
+    return false;
 
-static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
-{
+#if 0 //Enable it when GRAYSCALE image is supported
+    auto maskOp = _getMaskOp(surface->compositor->method);
+    auto direct = _direct(surface->compositor->method);
     float _dudx = dudx, _dvdx = dvdx;
     float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
     float _xa = xa, _xb = xb, _ua = ua, _va = va;
-    auto sbuf = image->buf32;
+    auto sbuf = image->buf8;
     int32_t sw = static_cast<int32_t>(image->stride);
     int32_t sh = image->h;
     int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
@@ -262,7 +89,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
     float dx, u, v, iptr;
     SwSpan* span = nullptr;         //used only when rle based.
 
-    if (!_arrange(image, region, yStart, yEnd)) return;
+    if (!_arrange(image, region, yStart, yEnd)) return false;
 
     //Loop through all lines in the segment
     uint32_t spanIdx = 0;
@@ -313,7 +140,8 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
 
             x = x1;
 
-            auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
+            auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
+            auto dst = &surface->buf8[y * surface->stride + x1];
 
             if (opacity == 255) {
                 //Draw horizontal line
@@ -349,7 +177,13 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
                         }
                         px = INTERPOLATE(px, px2, ab);
                     }
-                    *cmp = maskOp(px, *cmp, IA(px));
+                    if (direct) {
+                        auto tmp = maskOp(px, *cmp, 0);  //not use alpha
+                        *dst = tmp + MULTIPLY(*dst, ~tmp);
+                        ++dst;
+                    } else {
+                        *cmp = maskOp(px, *cmp, ~px);
+                    }
                     ++cmp;
 
                     //Step UV horizontally
@@ -392,7 +226,15 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
                         }
                         px = INTERPOLATE(px, px2, ab);
                     }
-                    *cmp = amaskOp(px, *cmp, opacity);
+
+                    if (direct) {
+                        auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
+                        *dst = tmp + MULTIPLY(*dst, ~tmp);
+                        ++dst;
+                    } else {
+                        auto tmp = MULTIPLY(px, opacity);
+                        *cmp = maskOp(tmp, *cmp, ~px);
+                    }
                     ++cmp;
 
                     //Step UV horizontally
@@ -418,17 +260,9 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
     xb = _xb;
     ua = _ua;
     va = _va;
-}
 
-
-static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
-{
-    if (surface->compositor->method == CompositeMethod::IntersectMask) {
-        _rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag);
-    } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
-        //Other Masking operations: Add, Subtract, Difference ...
-        _rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity);
-    }
+    return true;
+#endif
 }
 
 
@@ -1256,6 +1090,11 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
 */
 static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
 {
+    if (surface->channelSize == sizeof(uint8_t)) {
+        TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
+        return false;
+    }
+
     //Exceptions: No dedicated drawing area?
     if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
 
@@ -1294,6 +1133,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
 
     _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
 
+#if 0
+    if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+        _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+    }
+#endif
     return _apply(surface, aaSpans);
 }
 
@@ -1313,6 +1157,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
 */
 static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
 {
+    if (surface->channelSize == sizeof(uint8_t)) {
+        TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+        return false;
+    }
+
     //Exceptions: No dedicated drawing area?
     if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
 
@@ -1342,15 +1191,17 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
     }
 
     // Get AA spans and step polygons again to draw
-    auto aaSpans = _AASpans(ys, ye, image, region);
-    if (aaSpans) {
+    if (auto aaSpans = _AASpans(ys, ye, image, region)) {
         for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
             _rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
         }
-        // Apply to surface (note: frees the AA spans)
+#if 0
+        if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+            _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+        }
+#endif
         _apply(surface, aaSpans);
     }
     free(transformedTris);
-
     return true;
 }

+ 28 - 20
thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp

@@ -78,6 +78,30 @@ struct SwShapeTask : SwTask
     bool cmpStroking = false;
     bool clipper = false;
 
+    /* We assume that if the stroke width is greater than 2,
+       the shape's outline beneath the stroke could be adequately covered by the stroke drawing.
+       Therefore, antialiasing is disabled under this condition.
+       Additionally, the stroke style should not be dashed. */
+    bool antialiasing(float strokeWidth)
+    {
+        return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst;
+    }
+
+    float validStrokeWidth()
+    {
+        if (!rshape->stroke) return 0.0f;
+
+        auto width = rshape->stroke->width;
+        if (mathZero(width)) return 0.0f;
+
+        if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
+        if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
+
+        if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
+        else return width;
+    }
+
+
     bool clip(SwRleData* target) override
     {
         if (shape.fastTrack) rleClipRect(target, &bbox);
@@ -99,16 +123,10 @@ struct SwShapeTask : SwTask
     {
         if (opacity == 0 && !clipper) return;  //Invisible
 
-        uint8_t strokeAlpha = 0;
-        auto visibleStroke = false;
+        auto strokeWidth = validStrokeWidth();
         bool visibleFill = false;
         auto clipRegion = bbox;
 
-        if (HALF_STROKE(rshape->strokeWidth()) > 0) {
-            rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
-            visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
-        }
-
         //This checks also for the case, if the invisible shape turned to visible by alpha.
         auto prepareShape = false;
         if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
@@ -119,22 +137,15 @@ struct SwShapeTask : SwTask
             rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
             alpha = MULTIPLY(alpha, opacity);
             visibleFill = (alpha > 0 || rshape->fill);
-            if (visibleFill || visibleStroke || clipper) {
+            if (visibleFill || clipper) {
                 shapeReset(&shape);
                 if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
             }
         }
-
         //Fill
         if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
             if (visibleFill || clipper) {
-                /* We assume that if stroke width is bigger than 2,
-                   shape outline below stroke could be full covered by stroke drawing.
-                   Thus it turns off antialising in that condition.
-                   Also, it shouldn't be dash style. */
-                auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
-
-                if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
+                if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
             }
             if (auto fill = rshape->fill) {
                 auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
@@ -144,10 +155,9 @@ struct SwShapeTask : SwTask
                 shapeDelFill(&shape);
             }
         }
-
         //Stroke
         if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
-            if (visibleStroke) {
+            if (strokeWidth > 0.0f) {
                 shapeResetStroke(&shape, rshape, transform);
                 if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
 
@@ -641,8 +651,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
     if (x + w > sw) w = (sw - x);
     if (y + h > sh) h = (sh - y);
 
-    TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
-
     cmp->compositor->recoverSfc = surface;
     cmp->compositor->recoverCmp = surface->compositor;
     cmp->compositor->valid = false;

+ 2 - 2
thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp

@@ -716,11 +716,11 @@ static void _decomposeOutline(RleWorker& rw)
     for (auto cntr = outline->cntrs.data; cntr < outline->cntrs.end(); ++cntr) {
         auto last = *cntr;
         auto limit = outline->pts.data + last;
-        auto start = UPSCALE(outline->pts.data[first]);
+        auto start = UPSCALE(outline->pts[first]);
         auto pt = outline->pts.data + first;
         auto types = outline->types.data + first;
 
-        _moveTo(rw, UPSCALE(outline->pts.data[first]));
+        _moveTo(rw, UPSCALE(outline->pts[first]));
 
         while (pt < limit) {
             ++pt;

+ 151 - 93
thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp

@@ -21,9 +21,8 @@
  */
 
 #include "tvgSwCommon.h"
+#include "tvgMath.h"
 #include "tvgBezier.h"
-#include <float.h>
-#include <math.h>
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -108,7 +107,7 @@ static void _outlineClose(SwOutline& outline)
     if (outline.pts.count == i) return;
 
     //Close the path
-    outline.pts.push(outline.pts.data[i]);
+    outline.pts.push(outline.pts[i]);
     outline.types.push(SW_CURVE_TYPE_POINT);
     outline.closed.push(true);
 }
@@ -127,14 +126,18 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
         }
     } else {
         while (len > dash.curLen) {
-            len -= dash.curLen;
             Line left, right;
-            _lineSplitAt(cur, dash.curLen, left, right);;
-            dash.curIdx = (dash.curIdx + 1) % dash.cnt;
-            if (!dash.curOpGap) {
-                _outlineMoveTo(*dash.outline, &left.pt1, transform);
-                _outlineLineTo(*dash.outline, &left.pt2, transform);
+            if (dash.curLen > 0) {
+                len -= dash.curLen;
+                _lineSplitAt(cur, dash.curLen, left, right);
+                if (!dash.curOpGap) {
+                    _outlineMoveTo(*dash.outline, &left.pt1, transform);
+                    _outlineLineTo(*dash.outline, &left.pt2, transform);
+                }
+            } else {
+                right = cur;
             }
+            dash.curIdx = (dash.curIdx + 1) % dash.cnt;
             dash.curLen = dash.pattern[dash.curIdx];
             dash.curOpGap = !dash.curOpGap;
             cur = right;
@@ -169,16 +172,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
         }
     } else {
+        bool begin = true;          //starting with move_to
         while (len > dash.curLen) {
             Bezier left, right;
-            len -= dash.curLen;
-            bezSplitAt(cur, dash.curLen, left, right);
-            if (!dash.curOpGap) {
-                // leftovers from a previous command don't require moveTo
-                if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
-                    _outlineMoveTo(*dash.outline, &left.start, transform);
+            if (dash.curLen > 0) {
+                len -= dash.curLen;
+                bezSplitAt(cur, dash.curLen, left, right);
+                if (!dash.curOpGap) {
+                    // leftovers from a previous command don't require moveTo
+                    if (begin || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
+                        _outlineMoveTo(*dash.outline, &left.start, transform);
+                        begin = false;
+                    }
+                    _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
                 }
-                _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
+            } else {
+                right = cur;
             }
             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
             dash.curLen = dash.pattern[dash.curIdx];
@@ -203,11 +212,10 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
 }
 
 
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length)
 {
     const PathCommand* cmds = rshape->path.cmds.data;
     auto cmdCnt = rshape->path.cmds.count;
-
     const Point* pts = rshape->path.pts.data;
     auto ptsCnt = rshape->path.pts.count;
 
@@ -215,86 +223,161 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
     if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
 
     SwDashStroke dash;
-    dash.curIdx = 0;
-    dash.curLen = 0;
-    dash.ptStart = {0, 0};
-    dash.ptCur = {0, 0};
-    dash.curOpGap = false;
+    auto offset = 0.0f;
+    auto trimmed = false;
+
+    dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
+
+    //dash by trimming.
+    if (length > 0.0f && dash.cnt == 0) {
+        auto begin = length * rshape->stroke->trim.begin;
+        auto end = length * rshape->stroke->trim.end;
+
+        //TODO: mix trimming + dash style
+
+        //default
+        if (end > begin) {
+            if (begin > 0) dash.cnt += 4;
+            else dash.cnt += 2;
+        //looping
+        } else dash.cnt += 3;
+
+        dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
+
+        if (dash.cnt == 2) {
+            dash.pattern[0] = end - begin;
+            dash.pattern[1] = length - (end - begin);
+        } else if (dash.cnt == 3) {
+            dash.pattern[0] = end;
+            dash.pattern[1] = (begin - end);
+            dash.pattern[2] = length - begin;
+        } else {
+            dash.pattern[0] = 0;     //zero dash to start with a space.
+            dash.pattern[1] = begin;
+            dash.pattern[2] = end - begin;
+            dash.pattern[3] = length - (end - begin);
+        }
+
+        trimmed = true;
+    //just a dasy style.
+    } else {
+
+        if (dash.cnt == 0) return nullptr;
+    }
 
-    const float* pattern;
-    dash.cnt = rshape->strokeDash(&pattern);
-    if (dash.cnt == 0) return nullptr;
+    //offset?
+    auto patternLength = 0.0f;
+    uint32_t offIdx = 0;
+    if (!mathZero(offset)) {
+        for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
+        bool isOdd = dash.cnt % 2;
+        if (isOdd) patternLength *= 2;
+
+        offset = fmod(offset, patternLength);
+        if (offset < 0) offset += patternLength;
+
+        for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) {
+            auto curPattern = dash.pattern[i % dash.cnt];
+            if (offset < curPattern) break;
+            offset -= curPattern;
+        }
+    }
 
     //OPTMIZE ME: Use mempool???
-    dash.pattern = const_cast<float*>(pattern);
     dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
 
     //smart reservation
-    auto outlinePtsCnt = 0;
-    auto outlineCntrsCnt = 0;
+    auto closeCnt = 0;
+    auto moveCnt = 0;
+
+    for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+        if (*cmd == PathCommand::Close) ++closeCnt;
+        else if (*cmd == PathCommand::MoveTo) ++moveCnt;
+    }
 
-    for (uint32_t i = 0; i < cmdCnt; ++i) {
-        switch (*(cmds + i)) {
+    //No idea exact count.... Reserve Approximitely 20x...
+    //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+    dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1));
+    dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1));
+    dash.outline->cntrs.grow(20 * (moveCnt + 1));
+
+    while (cmdCnt-- > 0) {
+        switch (*cmds) {
             case PathCommand::Close: {
-                ++outlinePtsCnt;
+                _dashLineTo(dash, &dash.ptStart, transform);
                 break;
             }
             case PathCommand::MoveTo: {
-                ++outlineCntrsCnt;
-                ++outlinePtsCnt;
+                //reset the dash
+                dash.curIdx = offIdx % dash.cnt;
+                dash.curLen = dash.pattern[dash.curIdx] - offset;
+                dash.curOpGap = offIdx % 2;
+                dash.ptStart = dash.ptCur = *pts;
+                ++pts;
                 break;
             }
             case PathCommand::LineTo: {
-                ++outlinePtsCnt;
+                _dashLineTo(dash, pts, transform);
+                ++pts;
                 break;
             }
             case PathCommand::CubicTo: {
-                outlinePtsCnt += 3;
+                _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+                pts += 3;
                 break;
             }
         }
+        ++cmds;
     }
 
-    ++outlinePtsCnt;    //for close
-    ++outlineCntrsCnt;  //for end
+    _outlineEnd(*dash.outline);
 
-    //No idea exact count.... Reserve Approximitely 20x...
-    dash.outline->pts.grow(20 * outlinePtsCnt);
-    dash.outline->types.grow(20 * outlinePtsCnt);
-    dash.outline->cntrs.grow(20 * outlineCntrsCnt);
+    if (trimmed) free(dash.pattern);
+
+    return dash.outline;
+}
+
+
+static float _outlineLength(const RenderShape* rshape)
+{
+    const PathCommand* cmds = rshape->path.cmds.data;
+    auto cmdCnt = rshape->path.cmds.count;
+    const Point* pts = rshape->path.pts.data;
+    auto ptsCnt = rshape->path.pts.count;
 
+    //No actual shape data
+    if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
+
+    const Point* close = nullptr;
+    auto length = 0.0f;
+
+    //Compute the whole length
     while (cmdCnt-- > 0) {
         switch (*cmds) {
             case PathCommand::Close: {
-                _dashLineTo(dash, &dash.ptStart, transform);
+                length += mathLength(pts - 1, close);
+                ++pts;
                 break;
             }
             case PathCommand::MoveTo: {
-                //reset the dash
-                dash.curIdx = 0;
-                dash.curLen = *dash.pattern;
-                dash.curOpGap = false;
-                dash.ptStart = dash.ptCur = *pts;
+                close = pts;
                 ++pts;
                 break;
             }
             case PathCommand::LineTo: {
-                _dashLineTo(dash, pts, transform);
+                length += mathLength(pts - 1, pts);
                 ++pts;
                 break;
             }
             case PathCommand::CubicTo: {
-                _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+                length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
                 pts += 3;
                 break;
             }
         }
         ++cmds;
     }
-
-    _outlineEnd(*dash.outline);
-
-    return dash.outline;
+    return length;
 }
 
 
@@ -321,7 +404,6 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
 {
     const PathCommand* cmds = rshape->path.cmds.data;
     auto cmdCnt = rshape->path.cmds.count;
-
     const Point* pts = rshape->path.pts.data;
     auto ptsCnt = rshape->path.pts.count;
 
@@ -329,47 +411,21 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
     if (cmdCnt == 0 || ptsCnt == 0) return false;
 
     //smart reservation
-    auto outlinePtsCnt = 0;
-    auto outlineCntrsCnt = 0;
+    auto moveCnt = 0;
     auto closeCnt = 0;
 
-    for (uint32_t i = 0; i < cmdCnt; ++i) {
-        switch (*(cmds + i)) {
-            case PathCommand::Close: {
-                ++outlinePtsCnt;
-                ++closeCnt;
-                break;
-            }
-            case PathCommand::MoveTo: {
-                ++outlineCntrsCnt;
-                ++outlinePtsCnt;
-                break;
-            }
-            case PathCommand::LineTo: {
-                ++outlinePtsCnt;
-                break;
-            }
-            case PathCommand::CubicTo: {
-                outlinePtsCnt += 3;
-                break;
-            }
-        }
+    for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+        if (*cmd == PathCommand::Close) ++closeCnt;
+        else if (*cmd == PathCommand::MoveTo) ++moveCnt;
     }
 
-    if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
-        TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
-        return false;
-    }
-
-    ++outlinePtsCnt;    //for close
-    ++outlineCntrsCnt;  //for end
-
     shape->outline = mpoolReqOutline(mpool, tid);
     auto outline = shape->outline;
 
-    outline->pts.grow(outlinePtsCnt);
-    outline->types.grow(outlinePtsCnt);
-    outline->cntrs.grow(outlineCntrsCnt);
+    //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+    outline->pts.grow(ptsCnt + closeCnt + 1);
+    outline->types.grow(ptsCnt + closeCnt + 1);
+    outline->cntrs.grow(moveCnt + 1);
 
     //Dash outlines are always opened.
     //Only normal outlines use this information, it sholud be same to their contour counts.
@@ -514,12 +570,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
     bool freeOutline = false;
     bool ret = true;
 
-    //Dash Style Stroke
-    if (rshape->strokeDash(nullptr) > 0) {
-        shapeOutline = _genDashOutline(rshape, transform);
+    auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
+
+    //Dash style (+trimming)
+    if (rshape->stroke->dashCnt > 0 || length > 0) {
+        shapeOutline = _genDashOutline(rshape, transform, length);
         if (!shapeOutline) return false;
         freeOutline = true;
-    //Normal Style stroke
+    //Normal style
     } else {
         if (!shape->outline) {
             if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;

+ 1 - 11
thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp

@@ -373,10 +373,6 @@ void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
 static void _lineTo(SwStroke& stroke, const SwPoint& to)
 {
     auto delta = to - stroke.center;
-
-    //a zero-length lineto is a no-op; avoid creating a spurious corner
-    if (delta.zero()) return;
-
     //compute length of line
     auto angle = mathAtan(delta);
 
@@ -428,12 +424,6 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
 
 static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
 {
-    //if all control points are coincident, this is a no-op; avoid creating a spurious corner
-    if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
-        stroke.center = to;
-        return;
-    }
-
     SwPoint bezStack[37];   //TODO: static?
     auto limit = bezStack + 32;
     auto arc = bezStack;
@@ -852,7 +842,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
             continue;
         }
 
-        auto start = outline.pts.data[first];
+        auto start = outline.pts[first];
         auto pt = outline.pts.data + first;
         auto types = outline.types.data + first;
         auto type = types[0];

+ 21 - 9
thirdparty/thorvg/src/lib/tvgAnimation.cpp

@@ -23,6 +23,7 @@
 //#include "tvgAnimationImpl.h"
 #include "tvgCommon.h"
 #include "tvgFrameModule.h"
+#include "tvgPaint.h"
 #include "tvgPictureImpl.h"
 
 /************************************************************************/
@@ -31,8 +32,20 @@
 
 struct Animation::Impl
 {
-    //TODO: Memory Safety
-    Picture picture;
+    Picture* picture = nullptr;
+
+    Impl()
+    {
+        picture = Picture::gen().release();
+        static_cast<Paint*>(picture)->pImpl->ref();
+    }
+
+    ~Impl()
+    {
+        if (static_cast<Paint*>(picture)->pImpl->unref() == 0) {
+            delete(picture);
+        }
+    }
 };
 
 /************************************************************************/
@@ -41,19 +54,18 @@ struct Animation::Impl
 
 Animation::~Animation()
 {
-
+    delete(pImpl);
 }
 
 
 Animation::Animation() : pImpl(new Impl)
 {
-    pImpl->picture.pImpl->animated = true;
 }
 
 
 Result Animation::frame(uint32_t no) noexcept
 {
-    auto loader = pImpl->picture.pImpl->loader.get();
+    auto loader = pImpl->picture->pImpl->loader.get();
 
     if (!loader) return Result::InsufficientCondition;
     if (!loader->animatable()) return Result::NonSupport;
@@ -65,13 +77,13 @@ Result Animation::frame(uint32_t no) noexcept
 
 Picture* Animation::picture() const noexcept
 {
-    return &pImpl->picture;
+    return pImpl->picture;
 }
 
 
 uint32_t Animation::curFrame() const noexcept
 {
-    auto loader = pImpl->picture.pImpl->loader.get();
+    auto loader = pImpl->picture->pImpl->loader.get();
 
     if (!loader) return 0;
     if (!loader->animatable()) return 0;
@@ -82,7 +94,7 @@ uint32_t Animation::curFrame() const noexcept
 
 uint32_t Animation::totalFrame() const noexcept
 {
-    auto loader = pImpl->picture.pImpl->loader.get();
+    auto loader = pImpl->picture->pImpl->loader.get();
 
     if (!loader) return 0;
     if (!loader->animatable()) return 0;
@@ -93,7 +105,7 @@ uint32_t Animation::totalFrame() const noexcept
 
 float Animation::duration() const noexcept
 {
-    auto loader = pImpl->picture.pImpl->loader.get();
+    auto loader = pImpl->picture->pImpl->loader.get();
 
     if (!loader) return 0;
     if (!loader->animatable()) return 0;

+ 3 - 2
thirdparty/thorvg/src/lib/tvgCanvasImpl.h

@@ -65,8 +65,9 @@ struct Canvas::Impl
 
         //Free paints
         for (auto paint : paints) {
-            paint->pImpl->dispose(*renderer);
-            if (free) delete(paint);
+            if (paint->pImpl->dispose(*renderer)) {
+            	if (free && paint->pImpl->unref() == 0) delete(paint);
+	    }
         }
 
         paints.clear();

+ 6 - 2
thirdparty/thorvg/src/lib/tvgCommon.h

@@ -76,10 +76,14 @@ using Size = Point;
     #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
     #define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
 #else
-    #define TVGERR(...)
-    #define TVGLOG(...)
+    #define TVGERR(...) do {} while(0)
+    #define TVGLOG(...) do {} while(0)
 #endif
 
 uint16_t THORVG_VERSION_NUMBER();
 
+
+#define P(A) ((A)->pImpl)              //Access to pimpl.
+#define PP(A) (((Paint*)(A))->pImpl)   //Access to pimpl.
+
 #endif //_TVG_COMMON_H_

+ 134 - 0
thirdparty/thorvg/src/lib/tvgFill.cpp

@@ -26,6 +26,50 @@
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
+Fill* RadialGradient::Impl::duplicate()
+{
+    auto ret = RadialGradient::gen();
+    if (!ret) return nullptr;
+
+    ret->pImpl->cx = cx;
+    ret->pImpl->cy = cy;
+    ret->pImpl->r = r;
+    ret->pImpl->fx = fx;
+    ret->pImpl->fy = fy;
+    ret->pImpl->fr = fr;
+
+    return ret.release();
+}
+
+
+Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
+{
+    if (r < 0 || fr < 0) return Result::InvalidArguments;
+
+    this->cx = cx;
+    this->cy = cy;
+    this->r = r;
+    this->fx = fx;
+    this->fy = fy;
+    this->fr = fr;
+
+    return Result::Success;
+};
+
+
+Fill* LinearGradient::Impl::duplicate()
+{
+    auto ret = LinearGradient::gen();
+    if (!ret) return nullptr;
+
+    ret->pImpl->x1 = x1;
+    ret->pImpl->y1 = y1;
+    ret->pImpl->x2 = x2;
+    ret->pImpl->y2 = y2;
+
+    return ret.release();
+};
+
 
 /************************************************************************/
 /* External Class Implementation                                        */
@@ -110,7 +154,97 @@ Fill* Fill::duplicate() const noexcept
     return pImpl->duplicate();
 }
 
+
 uint32_t Fill::identifier() const noexcept
 {
     return pImpl->id;
 }
+
+
+RadialGradient::RadialGradient():pImpl(new Impl())
+{
+    Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
+    Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
+}
+
+
+RadialGradient::~RadialGradient()
+{
+    delete(pImpl);
+}
+
+
+Result RadialGradient::radial(float cx, float cy, float r) noexcept
+{
+    return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
+}
+
+
+Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
+{
+    if (cx) *cx = pImpl->cx;
+    if (cy) *cy = pImpl->cy;
+    if (r) *r = pImpl->r;
+
+    return Result::Success;
+}
+
+
+unique_ptr<RadialGradient> RadialGradient::gen() noexcept
+{
+    return unique_ptr<RadialGradient>(new RadialGradient);
+}
+
+
+uint32_t RadialGradient::identifier() noexcept
+{
+    return TVG_CLASS_ID_RADIAL;
+}
+
+
+LinearGradient::LinearGradient():pImpl(new Impl())
+{
+    Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
+    Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
+}
+
+
+LinearGradient::~LinearGradient()
+{
+    delete(pImpl);
+}
+
+
+Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
+{
+    pImpl->x1 = x1;
+    pImpl->y1 = y1;
+    pImpl->x2 = x2;
+    pImpl->y2 = y2;
+
+    return Result::Success;
+}
+
+
+Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
+{
+    if (x1) *x1 = pImpl->x1;
+    if (x2) *x2 = pImpl->x2;
+    if (y1) *y1 = pImpl->y1;
+    if (y2) *y2 = pImpl->y2;
+
+    return Result::Success;
+}
+
+
+unique_ptr<LinearGradient> LinearGradient::gen() noexcept
+{
+    return unique_ptr<LinearGradient>(new LinearGradient);
+}
+
+
+uint32_t LinearGradient::identifier() noexcept
+{
+    return TVG_CLASS_ID_LINEAR;
+}
+

+ 23 - 0
thirdparty/thorvg/src/lib/tvgFill.h

@@ -86,4 +86,27 @@ struct Fill::Impl
     }
 };
 
+
+struct RadialGradient::Impl
+{
+    float cx = 0.0f, cy = 0.0f;
+    float fx = 0.0f, fy = 0.0f;
+    float r = 0.0f, fr = 0.0f;
+
+    Fill* duplicate();
+    Result radial(float cx, float cy, float r, float fx, float fy, float fr);
+};
+
+
+struct LinearGradient::Impl
+{
+    float x1 = 0.0f;
+    float y1 = 0.0f;
+    float x2 = 0.0f;
+    float y2 = 0.0f;
+
+    Fill* duplicate();
+};
+
+
 #endif  //_TVG_FILL_H_

+ 0 - 100
thirdparty/thorvg/src/lib/tvgLinearGradient.cpp

@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
-
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <float.h>
-#include <math.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-struct LinearGradient::Impl
-{
-    float x1 = 0;
-    float y1 = 0;
-    float x2 = 0;
-    float y2 = 0;
-
-    Fill* duplicate()
-    {
-        auto ret = LinearGradient::gen();
-        if (!ret) return nullptr;
-
-        ret->pImpl->x1 = x1;
-        ret->pImpl->y1 = y1;
-        ret->pImpl->x2 = x2;
-        ret->pImpl->y2 = y2;
-
-        return ret.release();
-    }
-};
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-LinearGradient::LinearGradient():pImpl(new Impl())
-{
-    Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
-    Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
-}
-
-
-LinearGradient::~LinearGradient()
-{
-    delete(pImpl);
-}
-
-
-Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
-{
-    pImpl->x1 = x1;
-    pImpl->y1 = y1;
-    pImpl->x2 = x2;
-    pImpl->y2 = y2;
-
-    return Result::Success;
-}
-
-
-Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
-{
-    if (x1) *x1 = pImpl->x1;
-    if (x2) *x2 = pImpl->x2;
-    if (y1) *y1 = pImpl->y1;
-    if (y2) *y2 = pImpl->y2;
-
-    return Result::Success;
-}
-
-
-unique_ptr<LinearGradient> LinearGradient::gen() noexcept
-{
-    return unique_ptr<LinearGradient>(new LinearGradient);
-}
-
-
-uint32_t LinearGradient::identifier() noexcept
-{
-    return TVG_CLASS_ID_LINEAR;
-}

+ 19 - 17
thirdparty/thorvg/src/lib/tvgLoader.cpp

@@ -164,7 +164,7 @@ static LoadModule* _findByType(const string& mimeType)
 
     if (mimeType == "tvg") type = FileType::Tvg;
     else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
-    else if (mimeType == "lottie" || mimeType == "json") type = FileType::Lottie;
+    else if (mimeType == "lottie") type = FileType::Lottie;
     else if (mimeType == "raw") type = FileType::Raw;
     else if (mimeType == "png") type = FileType::Png;
     else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
@@ -214,22 +214,24 @@ shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
 
 shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
 {
-    //Try first with the given MimeType
-    if (auto loader = _findByType(mimeType)) {
-        if (loader->open(data, size, copy)) {
-            return shared_ptr<LoadModule>(loader);
-        } else {
-            TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
-            delete(loader);
-        }
-    }
-
-    //Abnormal MimeType. Try with the candidates in the order
-    for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
-        auto loader = _find(static_cast<FileType>(i));
-        if (loader) {
-            if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
-            else delete(loader);
+    //Try with the given MimeType
+    if (!mimeType.empty()) {
+        if (auto loader = _findByType(mimeType)) {
+            if (loader->open(data, size, copy)) {
+                return shared_ptr<LoadModule>(loader);
+            } else {
+                TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
+                delete(loader);
+            }
+        }
+    //Unkown MimeType. Try with the candidates in the order
+    } else {
+        for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
+            auto loader = _find(static_cast<FileType>(i));
+            if (loader) {
+                if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
+                else delete(loader);
+            }
         }
     }
     return nullptr;

+ 13 - 16
thirdparty/thorvg/src/lib/tvgPaint.cpp

@@ -166,7 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
         Create a composition image. */
     if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
         auto region = smethod->bounds(renderer);
-        if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
+        if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
         if (region.w == 0 || region.h == 0) return true;
         cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
         if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@@ -206,23 +206,20 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
         auto method = compData->method;
         target->pImpl->ctxFlag &= ~ContextFlag::FastTrack;   //reset
 
-        /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
-           we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
+        /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
+           we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
         auto tryFastTrack = false;
         if (target->identifier() == TVG_CLASS_ID_SHAPE) {
             if (method == CompositeMethod::ClipPath) tryFastTrack = true;
-            //OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it?
-            else if (method == CompositeMethod::AlphaMask) {
+            else {
                 auto shape = static_cast<Shape*>(target);
                 uint8_t a;
                 shape->fillColor(nullptr, nullptr, nullptr, &a);
-                if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
-            //OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it?
-            } else if (method == CompositeMethod::InvAlphaMask) {
-                auto shape = static_cast<Shape*>(target);
-                uint8_t a;
-                shape->fillColor(nullptr, nullptr, nullptr, &a);
-                if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true;
+                //no gradient fill & no compositions of the composition target.
+                if (!shape->fill() && !(PP(shape)->compData)) {
+                    if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
+                    else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
+                }
             }
             if (tryFastTrack) {
                 RenderRegion viewport2;
@@ -263,12 +260,12 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
 }
 
 
-bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
+bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
 {
     Matrix* m = nullptr;
 
     //Case: No transformed, quick return!
-    if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
+    if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
 
     //Case: Transformed
     auto tx = 0.0f;
@@ -276,7 +273,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
     auto tw = 0.0f;
     auto th = 0.0f;
 
-    auto ret = smethod->bounds(&tx, &ty, &tw, &th);
+    auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
 
     //Get vertices
     Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
@@ -365,7 +362,7 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
 
 Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
 {
-    if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
+    if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
     return Result::InsufficientCondition;
 }
 

+ 18 - 5
thirdparty/thorvg/src/lib/tvgPaint.h

@@ -42,10 +42,10 @@ namespace tvg
     {
         virtual ~StrategyMethod() {}
 
-        virtual bool dispose(RenderMethod& renderer) = 0;
+        virtual bool dispose(RenderMethod& renderer) = 0;     //return true if the deletion is allowed.
         virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0;   //Return engine data if it has.
         virtual bool render(RenderMethod& renderer) = 0;
-        virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
+        virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
         virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
         virtual Paint* duplicate() = 0;
         virtual Iterator* iterator() = 0;
@@ -68,6 +68,7 @@ namespace tvg
         uint8_t ctxFlag = ContextFlag::Invalid;
         uint8_t id;
         uint8_t opacity = 255;
+        uint8_t refCnt = 1;
 
         ~Impl()
         {
@@ -79,6 +80,18 @@ namespace tvg
             delete(rTransform);
         }
 
+        uint8_t ref()
+        {
+            if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
+            return (++refCnt);
+        }
+
+        uint8_t unref()
+        {
+            if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
+            return (--refCnt);
+        }
+
         void method(StrategyMethod* method)
         {
             smethod = method;
@@ -147,7 +160,7 @@ namespace tvg
         bool rotate(float degree);
         bool scale(float factor);
         bool translate(float x, float y);
-        bool bounds(float* x, float* y, float* w, float* h, bool transformed);
+        bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
         RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
         bool render(RenderMethod& renderer);
         Paint* duplicate();
@@ -162,9 +175,9 @@ namespace tvg
         PaintMethod(T* _inst) : inst(_inst) {}
         ~PaintMethod() {}
 
-        bool bounds(float* x, float* y, float* w, float* h) override
+        bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
         {
-            return inst->bounds(x, y, w, h);
+            return inst->bounds(x, y, w, h, stroking);
         }
 
         RenderRegion bounds(RenderMethod& renderer) const override

+ 4 - 7
thirdparty/thorvg/src/lib/tvgPictureImpl.h

@@ -70,7 +70,6 @@ struct Picture::Impl
     Picture* picture = nullptr;
     bool resizing = false;
     bool needComp = false;            //need composition
-    bool animated = false;            //picture is belonged to Animation
 
     Impl(Picture* p) : picture(p)
     {
@@ -84,12 +83,10 @@ struct Picture::Impl
 
     bool dispose(RenderMethod& renderer)
     {
-        bool ret = true;
-        if (paint) ret = paint->pImpl->dispose(renderer);
-        else if (surface) ret =  renderer.dispose(rd);
+        if (paint) paint->pImpl->dispose(renderer);
+        else if (surface) renderer.dispose(rd);
         rd = nullptr;
-
-        return ret;
+        return true;
     }
 
     RenderUpdateFlag load()
@@ -191,7 +188,7 @@ struct Picture::Impl
         return true;
     }
 
-    bool bounds(float* x, float* y, float* w, float* h)
+    bool bounds(float* x, float* y, float* w, float* h, bool stroking)
     {
         if (rm.triangleCnt > 0) {
             auto triangles = rm.triangles;

+ 0 - 98
thirdparty/thorvg/src/lib/tvgRadialGradient.cpp

@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
-
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <float.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-struct RadialGradient::Impl
-{
-    float cx = 0;
-    float cy = 0;
-    float radius = 0;
-
-    Fill* duplicate()
-    {
-        auto ret = RadialGradient::gen();
-        if (!ret) return nullptr;
-
-        ret->pImpl->cx = cx;
-        ret->pImpl->cy = cy;
-        ret->pImpl->radius = radius;
-
-        return ret.release();
-    }
-};
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-RadialGradient::RadialGradient():pImpl(new Impl())
-{
-    Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
-    Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
-}
-
-
-RadialGradient::~RadialGradient()
-{
-    delete(pImpl);
-}
-
-
-Result RadialGradient::radial(float cx, float cy, float radius) noexcept
-{
-    if (radius < 0) return Result::InvalidArguments;
-
-    pImpl->cx = cx;
-    pImpl->cy = cy;
-    pImpl->radius = radius;
-
-    return Result::Success;
-}
-
-
-Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
-{
-    if (cx) *cx = pImpl->cx;
-    if (cy) *cy = pImpl->cy;
-    if (radius) *radius = pImpl->radius;
-
-    return Result::Success;
-}
-
-
-unique_ptr<RadialGradient> RadialGradient::gen() noexcept
-{
-    return unique_ptr<RadialGradient>(new RadialGradient);
-}
-
-
-uint32_t RadialGradient::identifier() noexcept
-{
-    return TVG_CLASS_ID_RADIAL;
-}

+ 28 - 11
thirdparty/thorvg/src/lib/tvgRender.h

@@ -137,11 +137,17 @@ struct RenderStroke
     Fill *fill = nullptr;
     float* dashPattern = nullptr;
     uint32_t dashCnt = 0;
+    float dashOffset = 0.0f;
     StrokeCap cap = StrokeCap::Square;
     StrokeJoin join = StrokeJoin::Bevel;
     float miterlimit = 4.0f;
     bool strokeFirst = false;
 
+    struct {
+        float begin = 0.0f;
+        float end = 1.0f;
+    } trim;
+
     ~RenderStroke()
     {
         free(dashPattern);
@@ -182,6 +188,14 @@ struct RenderShape
         return stroke->width;
     }
 
+    bool strokeTrim() const
+    {
+        if (!stroke) return false;
+        if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
+        if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
+        return true;
+    }
+
     bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
     {
         if (!stroke) return false;
@@ -200,10 +214,11 @@ struct RenderShape
         return stroke->fill;
     }
 
-    uint32_t strokeDash(const float** dashPattern) const
+    uint32_t strokeDash(const float** dashPattern, float* offset) const
     {
         if (!stroke) return 0;
         if (dashPattern) *dashPattern = stroke->dashPattern;
+        if (offset) *offset = stroke->dashOffset;
         return stroke->dashCnt;
     }
 
@@ -253,21 +268,22 @@ public:
     virtual bool endComposite(Compositor* cmp) = 0;
 };
 
-static inline bool MASK_OPERATION(CompositeMethod method)
+static inline bool MASK_REGION_MERGING(CompositeMethod method)
 {
     switch(method) {
         case CompositeMethod::AlphaMask:
         case CompositeMethod::InvAlphaMask:
         case CompositeMethod::LumaMask:
         case CompositeMethod::InvLumaMask:
-            return false;
-        case CompositeMethod::AddMask:
         case CompositeMethod::SubtractMask:
         case CompositeMethod::IntersectMask:
+            return false;
+        //these might expand the rendering region
+        case CompositeMethod::AddMask:
         case CompositeMethod::DifferenceMask:
             return true;
         default:
-            TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+            TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
             return false;
     }
 }
@@ -284,7 +300,7 @@ static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
             return sizeof(uint8_t);
         case ColorSpace::Unsupported:
         default:
-            TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs);
+            TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs);
             return 0;
     }
 }
@@ -294,16 +310,17 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
     switch(method) {
         case CompositeMethod::AlphaMask:
         case CompositeMethod::InvAlphaMask:
-            return ColorSpace::Grayscale8;
-        case CompositeMethod::LumaMask:
-        case CompositeMethod::InvLumaMask:
         case CompositeMethod::AddMask:
+        case CompositeMethod::DifferenceMask:
         case CompositeMethod::SubtractMask:
         case CompositeMethod::IntersectMask:
-        case CompositeMethod::DifferenceMask:
+            return ColorSpace::Grayscale8;
+        //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
+        case CompositeMethod::LumaMask:
+        case CompositeMethod::InvLumaMask:
             return renderer.colorSpace();
         default:
-            TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+            TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
             return ColorSpace::Unsupported;
     }
 }

+ 6 - 6
thirdparty/thorvg/src/lib/tvgSceneImpl.h

@@ -75,7 +75,7 @@ struct Scene::Impl
     ~Impl()
     {
         for (auto paint : paints) {
-            delete(paint);
+            if (paint->pImpl->unref() == 0) delete(paint);
         }
     }
 
@@ -85,11 +85,11 @@ struct Scene::Impl
             paint->pImpl->dispose(renderer);
         }
 
-        auto ret = renderer.dispose(rd);
+        renderer.dispose(rd);
         this->renderer = nullptr;
         this->rd = nullptr;
 
-        return ret;
+        return true;
     }
 
     bool needComposition(uint8_t opacity)
@@ -181,7 +181,7 @@ struct Scene::Impl
         return {x1, y1, (x2 - x1), (y2 - y1)};
     }
 
-    bool bounds(float* px, float* py, float* pw, float* ph)
+    bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
     {
         if (paints.empty()) return false;
 
@@ -196,7 +196,7 @@ struct Scene::Impl
             auto w = 0.0f;
             auto h = 0.0f;
 
-            if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+            if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
 
             //Merge regions
             if (x < x1) x1 = x;
@@ -231,7 +231,7 @@ struct Scene::Impl
         auto dispose = renderer ? true : false;
 
         for (auto paint : paints) {
-            if (dispose) paint->pImpl->dispose(*renderer);
+            if (dispose) free &= paint->pImpl->dispose(*renderer);
             if (free) delete(paint);
         }
         paints.clear();

+ 7 - 16
thirdparty/thorvg/src/lib/tvgShape.cpp

@@ -150,13 +150,13 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
     //just circle
     if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
 
-    startAngle = (startAngle * M_PI) / 180.0f;
-    sweep = sweep * M_PI / 180.0f;
+    startAngle = (startAngle * MATH_PI) / 180.0f;
+    sweep = sweep * MATH_PI / 180.0f;
 
-    auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
+    auto nCurves = ceil(fabsf(sweep / MATH_PI2));
     auto sweepSign = (sweep < 0 ? -1 : 1);
-    auto fract = fmodf(sweep, float(M_PI_2));
-    fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
+    auto fract = fmodf(sweep, MATH_PI2);
+    fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
 
     //Start from here
     Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
@@ -342,22 +342,13 @@ const Fill* Shape::strokeFill() const noexcept
 
 Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
 {
-    if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
-        return Result::InvalidArguments;
-    }
-
-    for (uint32_t i = 0; i < cnt; i++)
-        if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
-
-    if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
-
-    return Result::Success;
+    return pImpl->strokeDash(dashPattern, cnt, 0);
 }
 
 
 uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
 {
-    return pImpl->rs.strokeDash(dashPattern);
+    return pImpl->rs.strokeDash(dashPattern, nullptr);
 }
 
 

+ 35 - 19
thirdparty/thorvg/src/lib/tvgShapeImpl.h

@@ -46,9 +46,9 @@ struct Shape::Impl
 
     bool dispose(RenderMethod& renderer)
     {
-        auto ret = renderer.dispose(rd);
+        renderer.dispose(rd);
         rd = nullptr;
-        return ret;
+        return true;
     }
 
     bool render(RenderMethod& renderer)
@@ -70,7 +70,7 @@ struct Shape::Impl
         if (opacity == 0) return false;
 
         //Shape composition is only necessary when stroking & fill are valid.
-        if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
+        if (!rs.stroke || rs.stroke->width < FLT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
         if (!rs.fill && rs.color[3] == 0) return false;
 
         //translucent fill & stroke
@@ -104,7 +104,7 @@ struct Shape::Impl
         return renderer.region(rd);
     }
 
-    bool bounds(float* x, float* y, float* w, float* h)
+    bool bounds(float* x, float* y, float* w, float* h, bool stroking)
     {
         //Path bounding size
         if (rs.path.pts.count > 0 ) {
@@ -126,7 +126,7 @@ struct Shape::Impl
         }
 
         //Stroke feathering
-        if (rs.stroke) {
+        if (stroking && rs.stroke) {
             if (x) *x -= rs.stroke->width * 0.5f;
             if (y) *y -= rs.stroke->width * 0.5f;
             if (w) *w += rs.stroke->width;
@@ -199,8 +199,6 @@ struct Shape::Impl
 
     bool strokeWidth(float width)
     {
-        //TODO: Size Exception?
-
         if (!rs.stroke) rs.stroke = new RenderStroke();
         rs.stroke->width = width;
         flag |= RenderUpdateFlag::Stroke;
@@ -208,6 +206,22 @@ struct Shape::Impl
         return true;
     }
 
+    bool strokeTrim(float begin, float end)
+    {
+        if (!rs.stroke) {
+            if (begin == 0.0f && end == 1.0f) return true;
+            rs.stroke = new RenderStroke();
+        }
+
+        if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
+
+        rs.stroke->trim.begin = begin;
+        rs.stroke->trim.end = end;
+        flag |= RenderUpdateFlag::Stroke;
+
+        return true;
+    }
+
     bool strokeCap(StrokeCap cap)
     {
         if (!rs.stroke) rs.stroke = new RenderStroke();
@@ -269,8 +283,16 @@ struct Shape::Impl
         return Result::Success;
     }
 
-    bool strokeDash(const float* pattern, uint32_t cnt)
+    Result strokeDash(const float* pattern, uint32_t cnt, float offset)
     {
+        if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
+            return Result::InvalidArguments;
+        }
+
+        for (uint32_t i = 0; i < cnt; i++) {
+            if (pattern[i] < FLT_EPSILON) return Result::InvalidArguments;
+        }
+
         //Reset dash
         if (!pattern && cnt == 0) {
             free(rs.stroke->dashPattern);
@@ -283,16 +305,17 @@ struct Shape::Impl
             }
             if (!rs.stroke->dashPattern) {
                 rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
-                if (!rs.stroke->dashPattern) return false;
+                if (!rs.stroke->dashPattern) return Result::FailedAllocation;
             }
             for (uint32_t i = 0; i < cnt; ++i) {
                 rs.stroke->dashPattern[i] = pattern[i];
             }
         }
         rs.stroke->dashCnt = cnt;
+        rs.stroke->dashOffset = offset;
         flag |= RenderUpdateFlag::Stroke;
 
-        return true;
+        return Result::Success;
     }
 
     bool strokeFirst()
@@ -336,24 +359,17 @@ struct Shape::Impl
         //Stroke
         if (rs.stroke) {
             dup->rs.stroke = new RenderStroke();
-            dup->rs.stroke->width = rs.stroke->width;
-            dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
-            dup->rs.stroke->cap = rs.stroke->cap;
-            dup->rs.stroke->join = rs.stroke->join;
-            dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
+            *dup->rs.stroke = *rs.stroke;
             memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
-
             if (rs.stroke->dashCnt > 0) {
                 dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
                 memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
             }
-
-            dup->flag |= RenderUpdateFlag::Stroke;
-
             if (rs.stroke->fill) {
                 dup->rs.stroke->fill = rs.stroke->fill->duplicate();
                 dup->flag |= RenderUpdateFlag::GradientStroke;
             }
+            dup->flag |= RenderUpdateFlag::Stroke;
         }
 
         //Fill

+ 12 - 1
thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp

@@ -100,6 +100,9 @@ struct TaskQueue {
 };
 
 
+static thread_local bool _async = true;  //toggle async tasking for each thread on/off
+
+
 struct TaskSchedulerImpl
 {
     uint32_t                       threadCnt;
@@ -109,6 +112,8 @@ struct TaskSchedulerImpl
 
     TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
     {
+        threads.reserve(threadCnt);
+
         for (unsigned i = 0; i < threadCnt; ++i) {
             threads.emplace_back([&, i] { run(i); });
         }
@@ -142,7 +147,7 @@ struct TaskSchedulerImpl
     void request(Task* task)
     {
         //Async
-        if (threadCnt > 0) {
+        if (threadCnt > 0 && _async) {
             task->prepare();
             auto i = idx++;
             for (unsigned n = 0; n < threadCnt; ++n) {
@@ -190,3 +195,9 @@ unsigned TaskScheduler::threads()
     if (inst) return inst->threadCnt;
     return 0;
 }
+
+
+void TaskScheduler::async(bool on)
+{
+    _async = on;
+}

+ 1 - 0
thirdparty/thorvg/src/lib/tvgTaskScheduler.h

@@ -38,6 +38,7 @@ struct TaskScheduler
     static void init(unsigned threads);
     static void term();
     static void request(Task* task);
+    static void async(bool on);
 };
 
 struct Task

+ 2 - 2
thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp

@@ -123,7 +123,7 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
             to->stroke.dash.array.clear();
             to->stroke.dash.array.reserve(from->stroke.dash.array.count);
             for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
-                to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+                to->stroke.dash.array.push(from->stroke.dash.array[i]);
             }
             to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
             to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
@@ -236,7 +236,7 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
 void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
 {
     for (uint32_t i = 0; i < postponeds.count; ++i) {
-        auto nodeIdPair = postponeds.data[i];
+        auto nodeIdPair = postponeds[i];
 
         //css styling: tag.name has higher priority than .name
         if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {

+ 66 - 22
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -59,7 +59,7 @@
 #include "tvgXmlParser.h"
 #include "tvgSvgLoader.h"
 #include "tvgSvgSceneBuilder.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
 #include "tvgSvgCssStyle.h"
 #include "tvgMath.h"
 
@@ -110,7 +110,7 @@ static bool _parseNumber(const char** content, float* number)
 {
     char* end = nullptr;
 
-    *number = svgUtilStrtof(*content, &end);
+    *number = strToFloat(*content, &end);
     //If the start of string is not number
     if ((*content) == end) return false;
     //Skip comma if any
@@ -166,7 +166,7 @@ static void _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
  */
 static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
 {
-    float parsedValue = svgUtilStrtof(str, nullptr);
+    float parsedValue = strToFloat(str, nullptr);
 
     if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
     else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
@@ -194,7 +194,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
 {
     char* end = nullptr;
 
-    float parsedValue = svgUtilStrtof(str, &end);
+    float parsedValue = strToFloat(str, &end);
     isPercentage = false;
 
     if (strstr(str, "%")) {
@@ -217,7 +217,7 @@ static float _toOffset(const char* str)
     char* end = nullptr;
     auto strEnd = str + strlen(str);
 
-    float parsedValue = svgUtilStrtof(str, &end);
+    float parsedValue = strToFloat(str, &end);
 
     end = _skipSpace(end, nullptr);
     auto ptr = strstr(str, "%");
@@ -234,7 +234,7 @@ static float _toOffset(const char* str)
 static int _toOpacity(const char* str)
 {
     char* end = nullptr;
-    float opacity = svgUtilStrtof(str, &end);
+    float opacity = strToFloat(str, &end);
 
     if (end) {
         if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
@@ -362,7 +362,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
 
     while (*str) {
         str = _skipComma(str);
-        float parsedValue = svgUtilStrtof(str, &end);
+        float parsedValue = strToFloat(str, &end);
         if (str == end) break;
         if (parsedValue <= 0.0f) break;
         if (*end == '%') {
@@ -375,7 +375,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
         str = end;
     }
     //If dash array size is 1, it means that dash and gap size are the same.
-    if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
+    if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
 }
 
 
@@ -393,7 +393,7 @@ static char* _idFromUrl(const char* url)
     int i = 0;
     while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
     
-    return svgUtilStrndup(url, i);
+    return strDuplicate(url, i);
 }
 
 
@@ -401,7 +401,7 @@ static unsigned char _parseColor(const char* value, char** end)
 {
     float r;
 
-    r = svgUtilStrtof(value, end);
+    r = strToFloat(value, end);
     *end = _skipSpace(*end, nullptr);
     if (**end == '%') {
         r = 255 * r / 100;
@@ -643,7 +643,7 @@ static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
 
     str = _skipSpace(str, nullptr);
     while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
-        points[count++] = svgUtilStrtof(str, &end);
+        points[count++] = strToFloat(str, &end);
         str = end;
         str = _skipSpace(str, nullptr);
         if (*str == ',') ++str;
@@ -893,7 +893,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
     } else if (!strcmp(key, "style")) {
         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
 #ifdef THORVG_LOG_ENABLED
-    } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
+    } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) {
         TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
 #endif
     } else {
@@ -956,6 +956,12 @@ static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, con
     _parseDashArray(loader, value, &node->style->stroke.dash);
 }
 
+static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+    node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
+    node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+}
+
 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
 {
     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
@@ -979,7 +985,7 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
 static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
 {
     char* end = nullptr;
-    const float miterlimit = svgUtilStrtof(value, &end);
+    const float miterlimit = strToFloat(value, &end);
 
     // https://www.w3.org/TR/SVG2/painting.html#LineJoin
     // - A negative value for stroke-miterlimit must be treated as an illegal value.
@@ -1112,6 +1118,7 @@ static constexpr struct
     STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
     STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
     STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
+    STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
     STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
     STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
     STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
@@ -1141,7 +1148,7 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
                 while (size > 0 && isspace(value[size - 1])) {
                     size--;
                 }
-                value = svgUtilStrndup(value, size);
+                value = strDuplicate(value, size);
                 importance = true;
             }
             if (style) {
@@ -2097,6 +2104,12 @@ static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
 }
 
 
+static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+    radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
+}
+
+
 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
 {
     radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
@@ -2127,6 +2140,13 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
 }
 
 
+static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+    // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+    if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
+}
+
+
 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
 {
     // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
@@ -2170,6 +2190,15 @@ static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradien
 }
 
 
+static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+    if (!radial->isFrPercentage) {
+        if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+        else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+    }
+}
+
+
 static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
 {
     if (!radial->isRPercentage) {
@@ -2211,6 +2240,14 @@ static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
 }
 
 
+static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+    to->radial->fr = from->radial->fr;
+    to->radial->isFrPercentage = from->radial->isFrPercentage;
+    to->flags = (to->flags | SvgGradientFlags::Fr);
+}
+
+
 static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
 {
     to->radial->r = from->radial->r;
@@ -2244,7 +2281,8 @@ static constexpr struct
     RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
     RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
     RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
-    RADIAL_DEF(r, R, SvgGradientFlags::R)
+    RADIAL_DEF(r, R, SvgGradientFlags::R),
+    RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
 };
 
 
@@ -2312,6 +2350,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
     grad->radial->isFxPercentage = true;
     grad->radial->isFyPercentage = true;
     grad->radial->isRPercentage = true;
+    grad->radial->isFrPercentage = true;
 
     loader->svgParse->gradient.parsedFx = false;
     loader->svgParse->gradient.parsedFy = false;
@@ -2619,7 +2658,7 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
 static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
 {
     for (uint32_t i = 0; i < src.count; ++i) {
-        dst.push(src.data[i]);
+        dst.push(src[i]);
     }
 }
 
@@ -2773,10 +2812,13 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
             child->stroke.dash.array.clear();
             child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
             for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
-                child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+                child->stroke.dash.array.push(parent->stroke.dash.array[i]);
             }
         }
     }
+    if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
+        child->stroke.dash.offset = parent->stroke.dash.offset;
+    }
     if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
         child->stroke.cap = parent->stroke.cap;
     }
@@ -2839,17 +2881,19 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
             to->stroke.dash.array.clear();
             to->stroke.dash.array.reserve(from->stroke.dash.array.count);
             for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
-                to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+                to->stroke.dash.array.push(from->stroke.dash.array[i]);
             }
         }
     }
+    if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
+        to->stroke.dash.offset = from->stroke.dash.offset;
+    }
     if (from->stroke.flags & SvgStrokeFlags::Cap) {
         to->stroke.cap = from->stroke.cap;
     }
     if (from->stroke.flags & SvgStrokeFlags::Join) {
         to->stroke.join = from->stroke.join;
     }
-
     if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
         to->stroke.miterlimit = from->stroke.miterlimit;
     }
@@ -2983,7 +3027,7 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
 static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
 {
     for (uint32_t i = 0; i < cloneNodes->count; ++i) {
-        auto nodeIdPair = cloneNodes->data[i];
+        auto nodeIdPair = (*cloneNodes)[i];
         auto defs = _getDefsNode(nodeIdPair.node);
         auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
         if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
@@ -3064,7 +3108,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
             loader->doc = node;
         } else {
             if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
-            if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+            if (loader->stack.count > 0) parent = loader->stack.last();
             else parent = loader->doc;
             if (!strcmp(tagName, "style")) {
                 // TODO: For now only the first style node is saved. After the css id selector
@@ -3085,7 +3129,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
             loader->stack.push(node);
         }
     } else if ((method = _findGraphicsFactory(tagName))) {
-        if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+        if (loader->stack.count > 0) parent = loader->stack.last();
         else parent = loader->doc;
         node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
     } else if ((gradientMethod = _findGradientFactory(tagName))) {

+ 9 - 18
thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h

@@ -100,7 +100,8 @@ enum class SvgStrokeFlags
     Cap = 0x20,
     Join = 0x40,
     Dash = 0x80,
-    Miterlimit = 0x100
+    Miterlimit = 0x100,
+    DashOffset = 0x200
 };
 
 constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
@@ -139,7 +140,8 @@ enum class SvgStyleFlags
     MaskType = 0x4000,
     Display = 0x8000,
     PaintOrder = 0x10000,
-    StrokeMiterlimit = 0x20000
+    StrokeMiterlimit = 0x20000,
+    StrokeDashOffset = 0x40000,
 };
 
 constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
@@ -182,7 +184,8 @@ enum class SvgGradientFlags
     Cy = 0x80,
     R = 0x100,
     Fx = 0x200,
-    Fy = 0x400
+    Fy = 0x400,
+    Fr = 0x800
 };
 
 constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
@@ -390,11 +393,13 @@ struct SvgRadialGradient
     float fx;
     float fy;
     float r;
+    float fr;
     bool isCxPercentage;
     bool isCyPercentage;
     bool isFxPercentage;
     bool isFyPercentage;
     bool isRPercentage;
+    bool isFrPercentage;
 };
 
 struct SvgComposite
@@ -423,6 +428,7 @@ struct SvgPaint
 struct SvgDash
 {
     Array<float> array;
+    float offset;
 };
 
 struct SvgStyleGradient
@@ -469,7 +475,6 @@ struct SvgStyleStroke
     StrokeJoin join;
     float miterlimit;
     SvgDash dash;
-    int dashCount;
 };
 
 struct SvgStyleProperty
@@ -561,18 +566,4 @@ struct Box
     float x, y, w, h;
 };
 
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float customStrtof(const char *nptr, char **endptr);
-
 #endif

+ 4 - 4
thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp

@@ -55,7 +55,7 @@
 #include <ctype.h>
 #include "tvgSvgLoaderCommon.h"
 #include "tvgSvgPath.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -74,7 +74,7 @@ static char* _skipComma(const char* content)
 static bool _parseNumber(char** content, float* number)
 {
     char* end = NULL;
-    *number = svgUtilStrtof(*content, &end);
+    *number = strToFloat(*content, &end);
     //If the start of string is not number
     if ((*content) == end) return false;
     //Skip comma if any
@@ -382,7 +382,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         case 's':
         case 'S': {
             Point p[3], ctrl;
-            if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+            if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
                 !(*isQuadratic)) {
                 ctrl.x = 2 * cur->x - curCtl->x;
                 ctrl.y = 2 * cur->y - curCtl->y;
@@ -423,7 +423,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         case 't':
         case 'T': {
             Point p[3], ctrl;
-            if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+            if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
                 *isQuadratic) {
                 ctrl.x = 2 * cur->x - curCtl->x;
                 ctrl.y = 2 * cur->y - curCtl->y;

+ 124 - 39
thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp

@@ -52,6 +52,11 @@
 #include "tvgMath.h" /* to include math.h before cstring */
 #include <cstring>
 #include <string>
+#include "tvgShapeImpl.h"
+#include "tvgCompressor.h"
+#include "tvgPaint.h"
+#include "tvgFill.h"
+#include "tvgStr.h"
 #include "tvgSvgLoaderCommon.h"
 #include "tvgSvgSceneBuilder.h"
 #include "tvgSvgPath.h"
@@ -62,6 +67,7 @@
 /************************************************************************/
 
 static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform);
 static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
 
 
@@ -138,7 +144,7 @@ static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient*
         if (!stops) return fillGrad;
         auto prevOffset = 0.0f;
         for (uint32_t i = 0; i < g->stops.count; ++i) {
-            auto colorStop = &g->stops.data[i];
+            auto colorStop = &g->stops[i];
             //Use premultiplied color
             stops[i].r = colorStop->r;
             stops[i].g = colorStop->g;
@@ -175,6 +181,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
         g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
         g->radial->fx = g->radial->fx * vBox.w;
         g->radial->fy = g->radial->fy * vBox.h;
+        g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
     } else {
         Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
         if (isTransform) _transformMultiply(&m, &finalTransform);
@@ -186,11 +193,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
 
     if (isTransform) fillGrad->transform(finalTransform);
 
-    //TODO: Tvg is not support to focal
-    //if (g->radial->fx != 0 && g->radial->fy != 0) {
-    //    fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
-    //}
-    fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+    P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
     fillGrad->spread(g->spread);
 
     //Update the stops
@@ -200,7 +203,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
         if (!stops) return fillGrad;
         auto prevOffset = 0.0f;
         for (uint32_t i = 0; i < g->stops.count; ++i) {
-            auto colorStop = &g->stops.data[i];
+            auto colorStop = &g->stops[i];
             //Use premultiplied color
             stops[i].r = colorStop->r;
             stops[i].g = colorStop->g;
@@ -219,20 +222,50 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
 }
 
 
-static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
+static bool _appendClipUseNode(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
 {
-    auto valid = false;
+    if (node->child.count != 1) return false;
+    auto child = *(node->child.data);
 
-    if (_appendShape(node, shape, vBox, svgPath)) valid = true;
+    Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+    if (node->transform) finalTransform = *node->transform;
+    if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
+        Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
+        finalTransform = mathMultiply(&finalTransform, &m);
+    }
+    if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
 
-    if (node->child.count > 0) {
-        auto child = node->child.data;
-        for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
-            if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
-        }
+    return _appendClipShape(child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
+}
+
+
+static bool _appendClipChild(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip)
+{
+    if (node->type == SvgNodeType::Use) {
+        return _appendClipUseNode(node, shape, vBox, svgPath);
     }
+    return _appendClipShape(node, shape, vBox, svgPath, nullptr);
+}
 
-    return valid;
+
+static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
+{
+    Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+    //The initial mask transformation ignored according to the SVG standard.
+    if (node->transform && type != SvgNodeType::Mask) {
+        m = *node->transform;
+    }
+    if (compNode->transform) {
+        m = mathMultiply(&m, compNode->transform);
+    }
+    if (!compNode->node.clip.userSpace) {
+        float x, y, w, h;
+        P(paint)->bounds(&x, &y, &w, &h, false, false);
+        Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
+        m = mathMultiply(&m, &mBBox);
+    }
+    return m;
 }
 
 
@@ -251,19 +284,18 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
             auto comp = Shape::gen();
 
             auto child = compNode->child.data;
-            auto valid = false; //Composite only when valid shapes are existed
+            auto valid = false; //Composite only when valid shapes exist
 
             for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
-                if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
+                if (_appendClipChild(*child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true;
             }
 
-            if (node->transform) {
-                auto m = comp->transform();
-                m = mathMultiply(node->transform, &m);
-                comp->transform(m);
-            }
+            if (valid) {
+                Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
+                comp->transform(finalTransform);
 
-            if (valid) paint->composite(std::move(comp), CompositeMethod::ClipPath);
+                paint->composite(std::move(comp), CompositeMethod::ClipPath);
+            }
 
             node->style->clipPath.applying = false;
         }
@@ -280,9 +312,9 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
             node->style->mask.applying = true;
 
             bool isMaskWhite = true;
-            auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite);
-            if (comp) {
-                if (node->transform) comp->transform(*node->transform);
+            if (auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite)) {
+                Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask);
+                comp->transform(finalTransform);
 
                 if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
                     paint->composite(std::move(comp), CompositeMethod::LumaMask);
@@ -297,11 +329,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
 }
 
 
-static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
+static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
 {
     SvgStyleProperty* style = node->style;
 
-    if (node->transform) vg->transform(*node->transform);
+    //Clip transformation is applied directly to the path in the _appendClipShape function
+    if (node->transform && !clip) vg->transform(*node->transform);
     if (node->type == SvgNodeType::Doc || !node->display) return;
 
     //If fill property is nullptr then do nothing
@@ -344,7 +377,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
     vg->stroke(style->stroke.join);
     vg->strokeMiterlimit(style->stroke.miterlimit);
     if (style->stroke.dash.array.count > 0) {
-        vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
+        P(vg)->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
     }
 
     //If stroke property is nullptr then do nothing
@@ -383,14 +416,13 @@ static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const
 }
 
 
-static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+static bool _recognizeShape(SvgNode* node, Shape* shape)
 {
-    Array<PathCommand> cmds;
-    Array<Point> pts;
-
     switch (node->type) {
         case SvgNodeType::Path: {
             if (node->node.path.path) {
+                Array<PathCommand> cmds;
+                Array<Point> pts;
                 if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
                     shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
                 }
@@ -437,8 +469,41 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str
             return false;
         }
     }
+    return true;
+}
+
 
-    _applyProperty(node, shape, vBox, svgPath);
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+    if (!_recognizeShape(node, shape)) return false;
+
+    _applyProperty(node, shape, vBox, svgPath, false);
+    return true;
+}
+
+
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform)
+{
+    //The 'transform' matrix has higher priority than the node->transform, since it already contains it
+    auto m = transform ? transform : (node->transform ? node->transform : nullptr);
+
+    uint32_t currentPtsCnt = 0;
+    if (m) {
+        const Point *tmp = nullptr;
+        currentPtsCnt = shape->pathCoords(&tmp);
+    }
+
+    if (!_recognizeShape(node, shape)) return false;
+
+    if (m) {
+        const Point *pts = nullptr;
+        auto ptsCnt = shape->pathCoords(&pts);
+
+        auto p = const_cast<Point*>(pts) + currentPtsCnt;
+        while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
+    }
+
+    _applyProperty(node, shape, vBox, svgPath, true);
     return true;
 }
 
@@ -514,12 +579,15 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
     return false;
 }
 
+#include "tvgTaskScheduler.h"
 
 static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
 {
     if (!node->node.image.href) return nullptr;
     auto picture = Picture::gen();
 
+    TaskScheduler::async(false);    //force to load a picture on the same thread
+
     const char* href = node->node.image.href;
     if (!strncmp(href, "data:", sizeof("data:") - 1)) {
         href += sizeof("data:") - 1;
@@ -527,11 +595,22 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
         imageMimeTypeEncoding encoding;
         if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
         if (encoding == imageMimeTypeEncoding::base64) {
-            string decoded = svgUtilBase64Decode(href);
-            if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+            char* decoded = nullptr;
+            auto size = b64Decode(href, strlen(href), &decoded);
+            //OPTIMIZE: Skip data copy.
+            if (picture->load(decoded, size, mimetype, true) != Result::Success) {
+                free(decoded);
+                TaskScheduler::async(true);
+                return nullptr;
+            }
+            free(decoded);
         } else {
             string decoded = svgUtilURLDecode(href);
-            if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+            //OPTIMIZE: Skip data copy.
+            if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) {
+                TaskScheduler::async(true);
+                return nullptr;
+            }
         }
     } else {
         if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
@@ -540,6 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
         const char *dot = strrchr(href, '.');
         if (dot && !strcmp(dot, ".svg")) {
             TVGLOG("SVG", "Embedded svg file is disabled.");
+            TaskScheduler::async(true);
             return nullptr;
         }
         string imagePath = href;
@@ -547,9 +627,14 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
             auto last = svgPath.find_last_of("/");
             imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
         }
-        if (picture->load(imagePath) != Result::Success) return nullptr;
+        if (picture->load(imagePath) != Result::Success) {
+            TaskScheduler::async(true);
+            return nullptr;
+        }
     }
 
+    TaskScheduler::async(true);
+
     float w, h;
     Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
     if (picture->size(&w, &h) == Result::Success && w  > 0 && h > 0) {

+ 0 - 223
thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp

@@ -21,19 +21,12 @@
  */
 
 #include <cstring>
-#include <math.h>
-#include <memory.h>
 #include "tvgSvgUtil.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-static inline bool _floatExact(float a, float b)
-{
-    return memcmp(&a, &b, sizeof(float)) == 0;
-}
-
 static uint8_t _hexCharToDec(const char c)
 {
     if (c >= 'a') return c - 'a' + 10;
@@ -41,181 +34,11 @@ static uint8_t _hexCharToDec(const char c)
     else return c - '0';
 }
 
-static uint8_t _base64Value(const char chr)
-{
-    if (chr >= 'A' && chr <= 'Z') return chr - 'A';
-    else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
-    else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
-    else if (chr == '+' || chr == '-') return 62;
-    else return 63;
-}
-
 
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
 
-
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float svgUtilStrtof(const char *nPtr, char **endPtr)
-{
-    if (endPtr) *endPtr = (char*)(nPtr);
-    if (!nPtr) return 0.0f;
-
-    auto a = nPtr;
-    auto iter = nPtr;
-    auto val = 0.0f;
-    unsigned long long integerPart = 0;
-    int minus = 1;
-
-    //ignore leading whitespaces
-    while (isspace(*iter)) iter++;
-
-    //signed or not
-    if (*iter == '-') {
-        minus = -1;
-        iter++;
-    } else if (*iter == '+') {
-        iter++;
-    }
-
-    if (tolower(*iter) == 'i') {
-        if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
-        else goto error;
-
-        if (tolower(*(iter)) == 'i') {
-            if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
-            else goto error;
-        }
-        if (endPtr) *endPtr = (char *)(iter);
-        return (minus == -1) ? -INFINITY : INFINITY;
-    }
-
-    if (tolower(*iter) == 'n') {
-        if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
-        else goto error;
-
-        if (endPtr) *endPtr = (char *)(iter);
-        return (minus == -1) ? -NAN : NAN;
-    }
-
-    //Optional: integer part before dot
-    if (isdigit(*iter)) {
-        for (; isdigit(*iter); iter++) {
-            integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
-        }
-        a = iter;
-    } else if (*iter != '.') {
-        goto success;
-    }
-
-    val = static_cast<float>(integerPart);
-
-    //Optional: decimal part after dot
-    if (*iter == '.') {
-        unsigned long long decimalPart = 0;
-        unsigned long long pow10 = 1;
-        int count = 0;
-
-        iter++;
-
-        if (isdigit(*iter)) {
-            for (; isdigit(*iter); iter++, count++) {
-                if (count < 19) {
-                    decimalPart = decimalPart * 10ULL +  + static_cast<unsigned long long>(*iter - '0');
-                    pow10 *= 10ULL;
-                }
-            }
-        } else if (isspace(*iter)) { //skip if there is a space after the dot.
-            a = iter;
-            goto success;
-        }
-
-        val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
-        a = iter;
-    }
-
-    //Optional: exponent
-    if (*iter == 'e' || *iter == 'E') {
-        ++iter;
-
-        //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
-        if ((*iter == 'm') || (*iter == 'M')) {
-            //TODO: We don't support font em unit now, but has to multiply val * font size later...
-            a = iter + 1;
-            goto success;
-        }
-
-        //signed or not
-        int minus_e = 1;
-
-        if (*iter == '-') {
-            minus_e = -1;
-            ++iter;
-        } else if (*iter == '+') {
-            iter++;
-        }
-
-        unsigned int exponentPart = 0;
-
-        if (isdigit(*iter)) {
-            while (*iter == '0') iter++;
-            for (; isdigit(*iter); iter++) {
-                exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
-            }
-        } else if (!isdigit(*(a - 1))) {
-            a = nPtr;
-            goto success;
-        } else if (*iter == 0) {
-            goto success;
-        }
-
-        //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
-        if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
-            //val *= 1.0e-308f;
-            val *= 1.0e-38f;
-            a = iter;
-            goto success;
-        }
-
-        a = iter;
-        auto scale = 1.0f;
-
-        while (exponentPart >= 8U) {
-            scale *= 1E8;
-            exponentPart -= 8U;
-        }
-        while (exponentPart > 0U) {
-            scale *= 10.0f;
-            exponentPart--;
-        }
-        val = (minus_e == -1) ? (val / scale) : (val * scale);
-    } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
-        a = nPtr;
-        goto success;
-    }
-
-success:
-    if (endPtr) *endPtr = (char*)(a);
-    return minus * val;
-
-error:
-    if (endPtr) *endPtr = (char*)(nPtr);
-    return 0.0f;
-}
-
-
 string svgUtilURLDecode(const char *src)
 {
     if (!src) return nullptr;
@@ -242,49 +65,3 @@ string svgUtilURLDecode(const char *src)
     }
     return decoded;
 }
-
-
-string svgUtilBase64Decode(const char *src)
-{
-    if (!src) return nullptr;
-
-    auto length = strlen(src);
-    if (length == 0) return nullptr;
-
-    string decoded;
-    decoded.reserve(3*(1+(length >> 2)));
-
-    while (*src && *(src+1)) {
-        if (*src <= 0x20) {
-            ++src;
-            continue;
-        }
-
-        auto value1 = _base64Value(src[0]);
-        auto value2 = _base64Value(src[1]);
-        decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
-
-        if (!src[2] || src[2] == '=' || src[2] == '.') break;
-        auto value3 = _base64Value(src[2]);
-        decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
-
-        if (!src[3] || src[3] == '=' || src[3] == '.') break;
-        auto value4 = _base64Value(src[3]);
-        decoded += ((value3 & 0x03) << 6) + value4;
-        src += 4;
-    }
-    return decoded;
-}
-
-
-char* svgUtilStrndup(const char* str, size_t n)
-{
-    auto len = strlen(str);
-    if (len < n) n = len;
-
-    auto ret = (char*)malloc(n + 1);
-    if (!ret) return nullptr;
-    ret[n] = '\0';
-
-    return (char*)memcpy(ret, str, n);
-}

+ 0 - 5
thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h

@@ -25,11 +25,6 @@
 
 #include "tvgCommon.h"
 
-float svgUtilStrtof(const char *nPtr, char **endPtr);
-
 string svgUtilURLDecode(const char *src);
-string svgUtilBase64Decode(const char *src);
-
-char* svgUtilStrndup(const char* str, size_t n);
 
 #endif //_TVG_SVG_UTIL_H_

+ 3 - 3
thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp

@@ -33,7 +33,7 @@
 #endif
 
 #include "tvgXmlParser.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -557,10 +557,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
     }
 
     if (p == itr) *tag = strdup("all");
-    else *tag = svgUtilStrndup(itr, p - itr);
+    else *tag = strDuplicate(itr, p - itr);
 
     if (p == itrEnd) *name = nullptr;
-    else *name = svgUtilStrndup(p + 1, itrEnd - p - 1);
+    else *name = strDuplicate(p + 1, itrEnd - p - 1);
 
     return (nextElement ? nextElement + 1 : nullptr);
 }

+ 26 - 1
thirdparty/thorvg/src/lib/tvgArray.h → thirdparty/thorvg/src/utils/tvgArray.h

@@ -73,11 +73,36 @@ struct Array
         return reserve(count + size);
     }
 
-    T* end() const
+    const T& operator[](size_t idx) const
+    {
+        return data[idx];
+    }
+
+    T& operator[](size_t idx)
+    {
+        return data[idx];
+    }
+
+    T* end()
+    {
+        return data + count;
+    }
+
+    const T* end() const
     {
         return data + count;
     }
 
+    const T& last() const
+    {
+        return data[count - 1];
+    }
+
+    const T& first() const
+    {
+        return data[0];
+    }
+
     T& last()
     {
         return data[count - 1];

+ 1 - 1
thirdparty/thorvg/src/lib/tvgBezier.cpp → thirdparty/thorvg/src/utils/tvgBezier.cpp

@@ -116,7 +116,7 @@ float bezAt(const Bezier& bz, float at, float length)
 
     //just in case to prevent an infinite loop
     if (at <= 0) return 0.0f;
-    if (at >= length) return length;
+    if (at >= length) return 1.0f;
 
     while (true) {
         auto right = bz;

+ 0 - 0
thirdparty/thorvg/src/lib/tvgBezier.h → thirdparty/thorvg/src/utils/tvgBezier.h


+ 61 - 14
thirdparty/thorvg/src/lib/tvgLzw.cpp → thirdparty/thorvg/src/utils/tvgCompressor.cpp

@@ -55,17 +55,20 @@
  */
 #include "config.h"
 
-#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
 
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
 
 #include <string>
 #include <memory.h>
-#include "tvgLzw.h"
+#include "tvgCompressor.h"
+
+namespace tvg {
+
+
+/************************************************************************/
+/* LZW Implementation                                                   */
+/************************************************************************/
+
 
-namespace {
 //LZW Dictionary helper:
 constexpr int Nil = -1;
 constexpr int MaxDictBits = 12;
@@ -334,15 +337,8 @@ static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, i
     }
     return true;
 }
-}
 
 
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-namespace tvg {
-
 uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
 {
     int code = Nil;
@@ -423,6 +419,57 @@ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes,
     return bitStream.release();
 }
 
+
+/************************************************************************/
+/* B64 Implementation                                                   */
+/************************************************************************/
+
+
+size_t b64Decode(const char* encoded, const size_t len, char** decoded)
+{
+    static constexpr const char B64_INDEX[256] =
+    {
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
+        58, 59, 60, 61, 0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  5,  6,
+        7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+        25, 0,  0,  0,  0,  63, 0,  26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+        37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+    };
+
+
+    if (!decoded || !encoded || len == 0) return 0;
+
+    auto reserved = 3 * (1 + (len >> 2)) + 1;
+    auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
+    if (!output) return 0;
+    output[reserved - 1] = '\0';
+
+    size_t idx = 0;
+
+    while (*encoded && *(encoded + 1)) {
+        if (*encoded <= 0x20) {
+            ++encoded;
+            continue;
+        }
+
+        auto value1 = B64_INDEX[(size_t)encoded[0]];
+        auto value2 = B64_INDEX[(size_t)encoded[1]];
+        output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
+
+        if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
+        auto value3 = B64_INDEX[(size_t)encoded[2]];
+        output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
+
+        if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
+        auto value4 = B64_INDEX[(size_t)encoded[3]];
+        output[idx++] = ((value3 & 0x03) << 6) + value4;
+        encoded += 4;
+    }
+    *decoded = output;
+    return reserved;
 }
 
-#endif
+
+}

+ 4 - 3
thirdparty/thorvg/src/lib/tvgLzw.h → thirdparty/thorvg/src/utils/tvgCompressor.h

@@ -20,8 +20,8 @@
  * SOFTWARE.
  */
 
-#ifndef _TVG_LZW_H_
-#define _TVG_LZW_H_
+#ifndef _TVG_COMPRESSOR_H_
+#define _TVG_COMPRESSOR_H_
 
 #include <cstdint>
 
@@ -29,6 +29,7 @@ namespace tvg
 {
     uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
     uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
+    size_t b64Decode(const char* encoded, const size_t len, char** decoded);
 }
 
-#endif  //_TVG_LZW_H
+#endif  //_TVG_COMPRESSOR_H_

+ 52 - 0
thirdparty/thorvg/src/lib/tvgMath.h → thirdparty/thorvg/src/utils/tvgMath.h

@@ -29,6 +29,8 @@
 #include <math.h>
 #include "tvgCommon.h"
 
+#define MATH_PI  3.14159265358979323846f
+#define MATH_PI2 1.57079632679489661923f
 
 #define mathMin(x, y) (((x) < (y)) ? (x) : (y))
 #define mathMax(x, y) (((x) > (y)) ? (x) : (y))
@@ -45,6 +47,7 @@ static inline bool mathEqual(float a, float b)
     return (fabsf(a - b) < FLT_EPSILON);
 }
 
+
 static inline bool mathEqual(const Matrix& a, const Matrix& b)
 {
     if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
@@ -55,6 +58,7 @@ static inline bool mathEqual(const Matrix& a, const Matrix& b)
     return true;
 }
 
+
 static inline bool mathRightAngle(const Matrix* m)
 {
    auto radian = fabsf(atan2f(m->e21, m->e11));
@@ -118,6 +122,15 @@ static inline void mathIdentity(Matrix* m)
 }
 
 
+static inline void mathTransform(Matrix* transform, Point* coord)
+{
+    auto x = coord->x;
+    auto y = coord->y;
+    coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
+    coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
+}
+
+
 static inline void mathScale(Matrix* m, float sx, float sy)
 {
     m->e11 *= sx;
@@ -125,6 +138,19 @@ static inline void mathScale(Matrix* m, float sx, float sy)
 }
 
 
+static inline void mathScaleR(Matrix* m, float x, float y)
+{
+    if (x != 1.0f) {
+        m->e11 *= x;
+        m->e21 *= x;
+    }
+    if (y != 1.0f) {
+        m->e22 *= y;
+        m->e12 *= y;
+    }
+}
+
+
 static inline void mathTranslate(Matrix* m, float x, float y)
 {
     m->e13 += x;
@@ -174,6 +200,32 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
 }
 
 
+static inline void mathTranslateR(Matrix* m, float x, float y)
+{
+    if (x == 0.0f && y == 0.0f) return;
+    m->e13 += (x * m->e11 + y * m->e12);
+    m->e23 += (x * m->e21 + y * m->e22);
+}
+
+
+static inline void mathLog(Matrix* m)
+{
+    TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
+}
+
+
+static inline float mathLength(const Point* a, const Point* b)
+{
+    auto x = b->x - a->x;
+    auto y = b->y - a->y;
+
+    if (x < 0) x = -x;
+    if (y < 0) y = -y;
+
+    return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
+}
+
+
 static inline Point operator-(const Point& lhs, const Point& rhs)
 {
     return {lhs.x - rhs.x, lhs.y - rhs.y};

+ 239 - 0
thirdparty/thorvg/src/utils/tvgStr.cpp

@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+#include <cstring>
+#include <memory.h>
+#include "tvgMath.h"
+#include "tvgStr.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation                                        */
+/************************************************************************/
+
+static inline bool _floatExact(float a, float b)
+{
+    return memcmp(&a, &b, sizeof(float)) == 0;
+}
+
+
+/************************************************************************/
+/* External Class Implementation                                        */
+/************************************************************************/
+
+namespace tvg {
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float strToFloat(const char *nPtr, char **endPtr)
+{
+    if (endPtr) *endPtr = (char *) (nPtr);
+    if (!nPtr) return 0.0f;
+
+    auto a = nPtr;
+    auto iter = nPtr;
+    auto val = 0.0f;
+    unsigned long long integerPart = 0;
+    int minus = 1;
+
+    //ignore leading whitespaces
+    while (isspace(*iter)) iter++;
+
+    //signed or not
+    if (*iter == '-') {
+        minus = -1;
+        iter++;
+    } else if (*iter == '+') {
+        iter++;
+    }
+
+    if (tolower(*iter) == 'i') {
+        if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
+        else goto error;
+
+        if (tolower(*(iter)) == 'i') {
+            if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') &&
+                (tolower(*(iter + 4)) == 'y'))
+                iter += 5;
+            else goto error;
+        }
+        if (endPtr) *endPtr = (char *) (iter);
+        return (minus == -1) ? -INFINITY : INFINITY;
+    }
+
+    if (tolower(*iter) == 'n') {
+        if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
+        else goto error;
+
+        if (endPtr) *endPtr = (char *) (iter);
+        return (minus == -1) ? -NAN : NAN;
+    }
+
+    //Optional: integer part before dot
+    if (isdigit(*iter)) {
+        for (; isdigit(*iter); iter++) {
+            integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0');
+        }
+        a = iter;
+    } else if (*iter != '.') {
+        goto success;
+    }
+
+    val = static_cast<float>(integerPart);
+
+    //Optional: decimal part after dot
+    if (*iter == '.') {
+        unsigned long long decimalPart = 0;
+        unsigned long long pow10 = 1;
+        int count = 0;
+
+        iter++;
+
+        if (isdigit(*iter)) {
+            for (; isdigit(*iter); iter++, count++) {
+                if (count < 19) {
+                    decimalPart = decimalPart * 10ULL + +static_cast<unsigned long long>(*iter - '0');
+                    pow10 *= 10ULL;
+                }
+            }
+        } else if (isspace(*iter)) { //skip if there is a space after the dot.
+            a = iter;
+            goto success;
+        }
+
+        val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
+        a = iter;
+    }
+
+    //Optional: exponent
+    if (*iter == 'e' || *iter == 'E') {
+        ++iter;
+
+        //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
+        if ((*iter == 'm') || (*iter == 'M')) {
+            //TODO: We don't support font em unit now, but has to multiply val * font size later...
+            a = iter + 1;
+            goto success;
+        }
+
+        //signed or not
+        int minus_e = 1;
+
+        if (*iter == '-') {
+            minus_e = -1;
+            ++iter;
+        } else if (*iter == '+') {
+            iter++;
+        }
+
+        unsigned int exponentPart = 0;
+
+        if (isdigit(*iter)) {
+            while (*iter == '0') iter++;
+            for (; isdigit(*iter); iter++) {
+                exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
+            }
+        } else if (!isdigit(*(a - 1))) {
+            a = nPtr;
+            goto success;
+        } else if (*iter == 0) {
+            goto success;
+        }
+
+        //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
+        if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
+            //val *= 1.0e-308f;
+            val *= 1.0e-38f;
+            a = iter;
+            goto success;
+        }
+
+        a = iter;
+        auto scale = 1.0f;
+
+        while (exponentPart >= 8U) {
+            scale *= 1E8;
+            exponentPart -= 8U;
+        }
+        while (exponentPart > 0U) {
+            scale *= 10.0f;
+            exponentPart--;
+        }
+        val = (minus_e == -1) ? (val / scale) : (val * scale);
+    } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
+        a = nPtr;
+        goto success;
+    }
+
+success:
+    if (endPtr) *endPtr = (char *)(a);
+    return minus * val;
+
+error:
+    if (endPtr) *endPtr = (char *)(nPtr);
+    return 0.0f;
+}
+
+
+int str2int(const char* str, size_t n)
+{
+    int ret = 0;
+    for(size_t i = 0; i < n; ++i) {
+        ret = ret * 10 + (str[i] - '0');
+    }
+    return ret;
+}
+
+char* strDuplicate(const char *str, size_t n)
+{
+    auto len = strlen(str);
+    if (len < n) n = len;
+
+    auto ret = (char *) malloc(n + 1);
+    if (!ret) return nullptr;
+    ret[n] = '\0';
+
+    return (char *) memcpy(ret, str, n);
+}
+
+char* strDirname(const char* path)
+{
+    const char *ptr = strrchr(path, '/');
+#ifdef _WIN32
+    if (ptr) ptr = strrchr(ptr + 1, '\\');
+#endif
+    int len = int(ptr + 1 - path);  // +1 to include '/'
+    return strDuplicate(path, len);
+}
+
+}

+ 37 - 0
thirdparty/thorvg/src/utils/tvgStr.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_STR_H_
+#define _TVG_STR_H_
+
+#include <cstddef>
+
+namespace tvg
+{
+
+float strToFloat(const char *nPtr, char **endPtr);  //convert to float
+int str2int(const char* str, size_t n);             //convert to integer
+char* strDuplicate(const char *str, size_t n);      //copy the string
+char* strDirname(const char* path);                 //return the full directory name
+
+}
+#endif //_TVG_STR_H_

+ 2 - 2
thirdparty/thorvg/update-thorvg.sh

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-VERSION=0.10.0
+VERSION=0.10.7
 
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
 
@@ -32,7 +32,7 @@ cat << EOF > ../inc/config.h
 EOF
 
 mkdir ../src
-cp -rv src/lib ../src/
+cp -rv src/lib src/utils ../src/
 # Only sw_engine is enabled.
 rm -rfv ../src/lib/gl_engine
 

Some files were not shown because too many files changed in this diff