Browse Source

thorvg: Update to 0.15.5

Jakub Marcowski 9 months ago
parent
commit
5318008ce6
61 changed files with 1936 additions and 1339 deletions
  1. 1 1
      modules/svg/SCsub
  2. 1 1
      thirdparty/README.md
  3. 5 1
      thirdparty/thorvg/AUTHORS
  4. 1 1
      thirdparty/thorvg/inc/config.h
  5. 196 96
      thirdparty/thorvg/inc/thorvg.h
  6. 1 1
      thirdparty/thorvg/src/common/tvgCompressor.cpp
  7. 0 245
      thirdparty/thorvg/src/common/tvgLines.cpp
  8. 0 61
      thirdparty/thorvg/src/common/tvgLines.h
  9. 244 11
      thirdparty/thorvg/src/common/tvgMath.cpp
  10. 77 42
      thirdparty/thorvg/src/common/tvgMath.h
  11. 0 10
      thirdparty/thorvg/src/common/tvgStr.cpp
  12. 0 1
      thirdparty/thorvg/src/common/tvgStr.h
  13. 2 2
      thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
  14. 1 1
      thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp
  15. 1 1
      thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h
  16. 1 1
      thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
  17. 1 1
      thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
  18. 10 7
      thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
  19. 37 12
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
  20. 1 0
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
  21. 6 6
      thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
  22. 12 24
      thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
  23. 4 3
      thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
  24. 29 20
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h
  25. 20 20
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
  26. 2 2
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
  27. 23 16
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
  28. 410 0
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp
  29. 127 329
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
  30. 2 2
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h
  31. 35 3
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h
  32. 1 1
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h
  33. 17 4
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
  34. 124 79
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
  35. 9 5
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
  36. 136 121
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
  37. 14 16
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
  38. 22 18
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp
  39. 1 1
      thirdparty/thorvg/src/renderer/tvgBinaryDesc.h
  40. 8 8
      thirdparty/thorvg/src/renderer/tvgCanvas.h
  41. 0 9
      thirdparty/thorvg/src/renderer/tvgCommon.h
  42. 17 8
      thirdparty/thorvg/src/renderer/tvgFill.cpp
  43. 0 1
      thirdparty/thorvg/src/renderer/tvgFill.h
  44. 2 2
      thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp
  45. 2 2
      thirdparty/thorvg/src/renderer/tvgLoadModule.h
  46. 4 4
      thirdparty/thorvg/src/renderer/tvgLoader.cpp
  47. 80 55
      thirdparty/thorvg/src/renderer/tvgPaint.cpp
  48. 19 4
      thirdparty/thorvg/src/renderer/tvgPaint.h
  49. 11 5
      thirdparty/thorvg/src/renderer/tvgPicture.cpp
  50. 1 1
      thirdparty/thorvg/src/renderer/tvgPicture.h
  51. 72 26
      thirdparty/thorvg/src/renderer/tvgRender.h
  52. 4 3
      thirdparty/thorvg/src/renderer/tvgSaver.cpp
  53. 62 4
      thirdparty/thorvg/src/renderer/tvgScene.cpp
  54. 49 11
      thirdparty/thorvg/src/renderer/tvgScene.h
  55. 10 11
      thirdparty/thorvg/src/renderer/tvgShape.cpp
  56. 5 5
      thirdparty/thorvg/src/renderer/tvgShape.h
  57. 2 2
      thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp
  58. 2 3
      thirdparty/thorvg/src/renderer/tvgText.cpp
  59. 2 2
      thirdparty/thorvg/src/renderer/tvgText.h
  60. 9 6
      thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp
  61. 1 1
      thirdparty/thorvg/update-thorvg.sh

+ 1 - 1
modules/svg/SCsub

@@ -14,7 +14,6 @@ thirdparty_dir = "#thirdparty/thorvg/"
 thirdparty_sources = [
 thirdparty_sources = [
     # common
     # common
     "src/common/tvgCompressor.cpp",
     "src/common/tvgCompressor.cpp",
-    "src/common/tvgLines.cpp",
     "src/common/tvgMath.cpp",
     "src/common/tvgMath.cpp",
     "src/common/tvgStr.cpp",
     "src/common/tvgStr.cpp",
     # SVG parser
     # SVG parser
@@ -52,6 +51,7 @@ thirdparty_sources = [
     "src/renderer/sw_engine/tvgSwImage.cpp",
     "src/renderer/sw_engine/tvgSwImage.cpp",
     "src/renderer/sw_engine/tvgSwMath.cpp",
     "src/renderer/sw_engine/tvgSwMath.cpp",
     "src/renderer/sw_engine/tvgSwMemPool.cpp",
     "src/renderer/sw_engine/tvgSwMemPool.cpp",
+    "src/renderer/sw_engine/tvgSwPostEffect.cpp",
     "src/renderer/sw_engine/tvgSwRaster.cpp",
     "src/renderer/sw_engine/tvgSwRaster.cpp",
     "src/renderer/sw_engine/tvgSwRenderer.cpp",
     "src/renderer/sw_engine/tvgSwRenderer.cpp",
     "src/renderer/sw_engine/tvgSwRle.cpp",
     "src/renderer/sw_engine/tvgSwRle.cpp",

+ 1 - 1
thirdparty/README.md

@@ -916,7 +916,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 ## thorvg
 
 
 - Upstream: https://github.com/thorvg/thorvg
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.14.10 (366dcd72850c360b49e841e568fc5a154d7cce9e, 2024)
+- Version: 0.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024)
 - License: MIT
 - License: MIT
 
 
 Files extracted from upstream source:
 Files extracted from upstream source:

+ 5 - 1
thirdparty/thorvg/AUTHORS

@@ -28,6 +28,10 @@ Nattu Adnan <[email protected]>
 Gabor Kiss-Vamosi <[email protected]>
 Gabor Kiss-Vamosi <[email protected]>
 Lorcán Mc Donagh <[email protected]>
 Lorcán Mc Donagh <[email protected]>
 Lucas Niu <[email protected]>
 Lucas Niu <[email protected]>
-Francisco Ramírez <[email protected]>
+Francisco Ramírez <[email protected]> 
 Abdelrahman Ashraf <[email protected]>
 Abdelrahman Ashraf <[email protected]>
 Neo Xu <[email protected]>
 Neo Xu <[email protected]>
+Thaddeus Crews <[email protected]>
+Josh Soref <[email protected]>
+Elliott Sales de Andrade <[email protected]>
+Łukasz Pomietło <[email protected]>

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

@@ -15,5 +15,5 @@
 // For internal debugging:
 // For internal debugging:
 //#define THORVG_LOG_ENABLED
 //#define THORVG_LOG_ENABLED
 
 
-#define THORVG_VERSION_STRING "0.14.10"
+#define THORVG_VERSION_STRING "0.15.5"
 #endif
 #endif

+ 196 - 96
thirdparty/thorvg/inc/thorvg.h

@@ -157,7 +157,7 @@ enum class FillRule
 enum class CompositeMethod
 enum class CompositeMethod
 {
 {
     None = 0,           ///< No composition is applied.
     None = 0,           ///< No composition is applied.
-    ClipPath,           ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type.
+    ClipPath,           ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type. @deprecated Use Paint::clip() instead.
     AlphaMask,          ///< Alpha Masking using the compositing target's pixels as an alpha value.
     AlphaMask,          ///< Alpha Masking using the compositing target's pixels as an alpha value.
     InvAlphaMask,       ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
     InvAlphaMask,       ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
     LumaMask,           ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
     LumaMask,           ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
@@ -178,24 +178,46 @@ enum class CompositeMethod
  *
  *
  * @see Paint::blend()
  * @see Paint::blend()
  *
  *
- * @note Experimental API
+ * @since 0.15
  */
  */
 enum class BlendMethod : uint8_t
 enum class BlendMethod : uint8_t
 {
 {
     Normal = 0,        ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D
     Normal = 0,        ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D
-    Add,               ///< Simply adds pixel values of one layer with the other. (S + D)
-    Screen,            ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D)
     Multiply,          ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D)
     Multiply,          ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D)
+    Screen,            ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D)
     Overlay,           ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
     Overlay,           ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
-    Difference,        ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
-    Exclusion,         ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d)
-    SrcOver,           ///< Replace the bottom layer with the top layer.
     Darken,            ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D)
     Darken,            ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D)
     Lighten,           ///< Only has the opposite action of Darken Only. max(S, D)
     Lighten,           ///< Only has the opposite action of Darken Only. max(S, D)
     ColorDodge,        ///< Divides the bottom layer by the inverted top layer. D / (255 - S)
     ColorDodge,        ///< Divides the bottom layer by the inverted top layer. D / (255 - S)
     ColorBurn,         ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S
     ColorBurn,         ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S
     HardLight,         ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
     HardLight,         ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
-    SoftLight          ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D)
+    SoftLight,         ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D)
+    Difference,        ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
+    Exclusion,         ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d)
+    Hue,               ///< Reserved. Not supported.
+    Saturation,        ///< Reserved. Not supported.
+    Color,             ///< Reserved. Not supported.
+    Luminosity,        ///< Reserved. Not supported.
+    Add,               ///< Simply adds pixel values of one layer with the other. (S + D)
+    HardMix            ///< Reserved. Not supported.
+};
+
+
+/**
+ * @brief Enumeration that defines methods used for Scene Effects.
+ *
+ * This enum provides options to apply various post-processing effects to a scene.
+ * Scene effects are typically applied to modify the final appearance of a rendered scene, such as blurring.
+ *
+ * @see Scene::push(SceneEffect effect, ...)
+ *
+ * @note Experimental API
+ */
+enum class SceneEffect : uint8_t
+{
+    ClearAll = 0,      ///< Reset all previously applied scene effects, restoring the scene to its original state.
+    GaussianBlur,      ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]}
+    DropShadow         ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]}
 };
 };
 
 
 
 
@@ -206,7 +228,29 @@ enum class CanvasEngine
 {
 {
     Sw = (1 << 1), ///< CPU rasterizer.
     Sw = (1 << 1), ///< CPU rasterizer.
     Gl = (1 << 2), ///< OpenGL rasterizer.
     Gl = (1 << 2), ///< OpenGL rasterizer.
-    Wg = (1 << 3), ///< WebGPU rasterizer. (Experimental API)
+    Wg = (1 << 3), ///< WebGPU rasterizer. @since 0.15
+};
+
+
+/**
+ * @brief Enumeration specifying the ThorVG class type value.
+ *
+ * ThorVG's drawing objects can return class type values, allowing you to identify the specific class of each object.
+ *
+ * @see Paint::type()
+ * @see Fill::type()
+ *
+ * @note Experimental API
+ */
+enum class Type : uint8_t
+{
+    Undefined = 0,         ///< Unkown class
+    Shape,                 ///< Shape class
+    Scene,                 ///< Scene class
+    Picture,               ///< Picture class
+    Text,                  ///< Text class
+    LinearGradient = 10,   ///< LinearGradient class
+    RadialGradient         ///< RadialGradient class
 };
 };
 
 
 
 
@@ -274,7 +318,7 @@ public:
     /**
     /**
      * @brief Sets the values by which the object is moved in a two-dimensional space.
      * @brief Sets the values by which the object is moved in a two-dimensional space.
      *
      *
-     * The origin of the coordinate system is in the upper left corner of the canvas.
+     * The origin of the coordinate system is in the upper-left corner of the canvas.
      * The horizontal and vertical axes point to the right and down, respectively.
      * The horizontal and vertical axes point to the right and down, respectively.
      *
      *
      * @param[in] x The value of the horizontal shift.
      * @param[in] x The value of the horizontal shift.
@@ -312,7 +356,6 @@ public:
      * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
      * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
      *
      *
      * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible.
      * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible.
-     * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath)
      */
      */
     Result opacity(uint8_t o) noexcept;
     Result opacity(uint8_t o) noexcept;
 
 
@@ -324,6 +367,20 @@ public:
      */
      */
     Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept;
     Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept;
 
 
+    /**
+     * @brief Clip the drawing region of the paint object.
+     *
+     * This function restricts the drawing area of the paint object to the specified shape's paths.
+     *
+     * @param[in] clipper The shape object as the clipper.
+     *
+     * @retval Result::NonSupport If the @p clipper type is not Shape.
+     *
+     * @note @p clipper only supports the Shape type.
+     * @note Experimental API
+     */
+    Result clip(std::unique_ptr<Paint> clipper) noexcept;
+
     /**
     /**
      * @brief Sets the blending method for the paint object.
      * @brief Sets the blending method for the paint object.
      *
      *
@@ -386,22 +443,15 @@ public:
     CompositeMethod composite(const Paint** target) const noexcept;
     CompositeMethod composite(const Paint** target) const noexcept;
 
 
     /**
     /**
-     * @brief Retrieves the current blending method applied to the paint object.
+     * @brief Returns the ID value of this class.
      *
      *
-     * @return The currently set blending method.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @note Experimental API
-     */
-    BlendMethod blend() const noexcept;
-
-    /**
-     * @brief Return the unique id value of the paint instance.
-     *
-     * This method can be called for checking the current concrete instance type.
+     * @return The class type ID of the Paint instance.
      *
      *
-     * @return The type id of the Paint instance.
+     * @since Experimental API
      */
      */
-    uint32_t identifier() const noexcept;
+    virtual Type type() const noexcept = 0;
 
 
     /**
     /**
      * @brief Unique ID of this instance.
      * @brief Unique ID of this instance.
@@ -412,6 +462,11 @@ public:
      */
      */
     uint32_t id = 0;
     uint32_t id = 0;
 
 
+    /**
+     * @see Paint::type()
+     */
+    TVG_DEPRECATED uint32_t identifier() const noexcept;
+
     _TVG_DECLARE_PRIVATE(Paint);
     _TVG_DECLARE_PRIVATE(Paint);
 };
 };
 
 
@@ -503,13 +558,20 @@ public:
     Fill* duplicate() const noexcept;
     Fill* duplicate() const noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of the Fill instance.
+     * @brief Returns the ID value of this class.
+     *
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * This method can be called for checking the current concrete instance type.
+     * @return The class type ID of the Fill instance.
      *
      *
-     * @return The type id of the Fill instance.
+     * @since Experimental API
+     */
+    virtual Type type() const noexcept = 0;
+
+    /**
+     * @see Fill::type()
      */
      */
-    uint32_t identifier() const noexcept;
+    TVG_DEPRECATED uint32_t identifier() const noexcept;
 
 
     _TVG_DECLARE_PRIVATE(Fill);
     _TVG_DECLARE_PRIVATE(Fill);
 };
 };
@@ -538,7 +600,7 @@ public:
      *
      *
      * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
      * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
      *
      *
-     * @warning  Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync().
+     * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync().
      * @see Canvas::sync()
      * @see Canvas::sync()
      *
      *
      * @note Experimental API
      * @note Experimental API
@@ -614,7 +676,7 @@ public:
      * @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync().
      * @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync().
      *
      *
      * @note When resetting the target, the viewport will also be reset to the target size.
      * @note When resetting the target, the viewport will also be reset to the target size.
-     * @note Experimental API
+     * @since 0.15
      */
      */
     virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept;
     virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept;
 
 
@@ -686,13 +748,20 @@ public:
     static std::unique_ptr<LinearGradient> gen() noexcept;
     static std::unique_ptr<LinearGradient> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
      *
      *
-     * This method can be referred for identifying the LinearGradient class type.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @return The type id of the LinearGradient class.
+     * @return The class type ID of the LinearGradient instance.
+     *
+     * @since Experimental API
+     */
+    Type type() const noexcept override;
+
+    /**
+     * @see LinearGradient::type()
      */
      */
-    static uint32_t identifier() noexcept;
+    TVG_DEPRECATED static uint32_t identifier() noexcept;
 
 
     _TVG_DECLARE_PRIVATE(LinearGradient);
     _TVG_DECLARE_PRIVATE(LinearGradient);
 };
 };
@@ -744,13 +813,20 @@ public:
     static std::unique_ptr<RadialGradient> gen() noexcept;
     static std::unique_ptr<RadialGradient> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
      *
      *
-     * This method can be referred for identifying the RadialGradient class type.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @return The type id of the RadialGradient class.
+     * @return The class type ID of the LinearGradient instance.
+     *
+     * @since Experimental API
+     */
+    Type type() const noexcept override;
+
+    /**
+     * @see RadialGradient::type()
      */
      */
-    static uint32_t identifier() noexcept;
+    TVG_DEPRECATED static uint32_t identifier() noexcept;
 
 
     _TVG_DECLARE_PRIVATE(RadialGradient);
     _TVG_DECLARE_PRIVATE(RadialGradient);
 };
 };
@@ -774,11 +850,11 @@ public:
     ~Shape();
     ~Shape();
 
 
     /**
     /**
-     * @brief Resets the properties of the shape path.
+     * @brief Resets the shape path.
      *
      *
-     * The transformation matrix, the color, the fill and the stroke properties are retained.
+     * The transformation matrix, color, fill, and stroke properties are retained.
      *
      *
-     * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect.
+     * @note The memory where the path data is stored is not deallocated at this stage to allow for caching.
      */
      */
     Result reset() noexcept;
     Result reset() noexcept;
 
 
@@ -836,15 +912,15 @@ public:
      * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments.
      * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments.
      * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners.
      * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners.
      *
      *
-     * The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments.
+     * The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments.
      *
      *
      * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path.
      * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path.
      *
      *
      * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater
      * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater
      * than @p w/2 the current point is set to (@p x + @p w/2, @p y)
      * than @p w/2 the current point is set to (@p x + @p w/2, @p y)
      *
      *
-     * @param[in] x The horizontal coordinate of the upper left corner of the rectangle.
-     * @param[in] y The vertical coordinate of the upper left corner of the rectangle.
+     * @param[in] x The horizontal coordinate of the upper-left corner of the rectangle.
+     * @param[in] y The vertical coordinate of the upper-left corner of the rectangle.
      * @param[in] w The width of the rectangle.
      * @param[in] w The width of the rectangle.
      * @param[in] h The height of the rectangle.
      * @param[in] h The height of the rectangle.
      * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
      * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
@@ -886,7 +962,7 @@ public:
      *
      *
      * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius).
      * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius).
      */
      */
-    Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
+    TVG_DEPRECATED Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
 
 
     /**
     /**
      * @brief Appends a given sub-path to the path.
      * @brief Appends a given sub-path to the path.
@@ -999,7 +1075,6 @@ public:
      * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
      * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
      *
      *
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
-     * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath)
      */
      */
     Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
     Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
 
 
@@ -1130,18 +1205,6 @@ public:
      */
      */
     float strokeMiterlimit() const noexcept;
     float strokeMiterlimit() const noexcept;
 
 
-    /**
-     * @brief Gets the trim of the stroke along the defined path segment.
-     *
-     * @param[out] begin The starting point of the segment to display along the path.
-     * @param[out] end Specifies the end of the segment to display along the path.
-     *
-     * @return @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
-     *
-     * @note Experimental API
-     */
-    bool strokeTrim(float* begin, float* end) const noexcept;
-
     /**
     /**
      * @brief Creates a new Shape object.
      * @brief Creates a new Shape object.
      *
      *
@@ -1150,13 +1213,20 @@ public:
     static std::unique_ptr<Shape> gen() noexcept;
     static std::unique_ptr<Shape> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
      *
      *
-     * This method can be referred for identifying the Shape class type.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @return The type id of the Shape class.
+     * @return The class type ID of the Shape instance.
+     *
+     * @since Experimental API
      */
      */
-    static uint32_t identifier() noexcept;
+    Type type() const noexcept override;
+
+    /**
+     * @see Shape::type()
+     */
+    TVG_DEPRECATED static uint32_t identifier() noexcept;
 
 
     _TVG_DECLARE_PRIVATE(Shape);
     _TVG_DECLARE_PRIVATE(Shape);
 };
 };
@@ -1213,7 +1283,7 @@ public:
      * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less.
      * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less.
      * @retval Result::NonSupport When trying to load a file with an unknown extension.
      * @retval Result::NonSupport When trying to load a file with an unknown extension.
      *
      *
-     * @warning: It's the user responsibility to release the @p data memory.
+     * @warning It's the user responsibility to release the @p data memory.
      *
      *
      * @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.
      * @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
      * @since 0.5
@@ -1251,9 +1321,9 @@ public:
      * @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
      * @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
      * @param[in] w The width of the image @p data in pixels.
      * @param[in] w The width of the image @p data in pixels.
      * @param[in] h The height of the image @p data in pixels.
      * @param[in] h The height of the image @p data in pixels.
+     * @param[in] premultiplied If @c true, the given image data is alpha-premultiplied.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      *
      *
-     * @note It expects premultiplied alpha data.
      * @since 0.9
      * @since 0.9
      */
      */
     Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
     Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
@@ -1281,13 +1351,20 @@ public:
     static std::unique_ptr<Picture> gen() noexcept;
     static std::unique_ptr<Picture> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
      *
      *
-     * This method can be referred for identifying the Picture class type.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @return The type id of the Picture class.
+     * @return The class type ID of the Picture instance.
+     *
+     * @since Experimental API
+     */
+    Type type() const noexcept override;
+
+    /**
+     * @see Picture::type()
      */
      */
-    static uint32_t identifier() noexcept;
+    TVG_DEPRECATED static uint32_t identifier() noexcept;
 
 
     _TVG_DECLARE_ACCESSOR(Animation);
     _TVG_DECLARE_ACCESSOR(Animation);
     _TVG_DECLARE_PRIVATE(Picture);
     _TVG_DECLARE_PRIVATE(Picture);
@@ -1331,9 +1408,9 @@ public:
      *
      *
      * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
      * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
      *
      *
-     * @warning  Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync().
+     * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync().
      * @see Canvas::sync()
      * @see Canvas::sync()
-     * @see Scene::push()
+     * @see Scene::push(std::unique_ptr<Paint> paint)
      * @see Scene::clear()
      * @see Scene::clear()
      *
      *
      * @note Experimental API
      * @note Experimental API
@@ -1352,6 +1429,20 @@ public:
      */
      */
     Result clear(bool free = true) noexcept;
     Result clear(bool free = true) noexcept;
 
 
+    /**
+     * @brief Apply a post-processing effect to the scene.
+     *
+     * This function adds a specified scene effect, such as clearing all effects or applying a Gaussian blur,
+     * to the scene after it has been rendered. Multiple effects can be applied in sequence.
+     *
+     * @param[in] effect The scene effect to apply. Options are defined in the SceneEffect enum.
+     *                   For example, use SceneEffect::GaussianBlur to apply a blur with specific parameters.
+     * @param[in] ... Additional variadic parameters required for certain effects (e.g., sigma and direction for GaussianBlur).
+     *
+     * @note Experimental API
+     */
+    Result push(SceneEffect effect, ...) noexcept;
+
     /**
     /**
      * @brief Creates a new Scene object.
      * @brief Creates a new Scene object.
      *
      *
@@ -1360,13 +1451,20 @@ public:
     static std::unique_ptr<Scene> gen() noexcept;
     static std::unique_ptr<Scene> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
      *
      *
-     * This method can be referred for identifying the Scene class type.
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * @return The type id of the Scene class.
+     * @return The class type ID of the Scene instance.
+     *
+     * @since Experimental API
      */
      */
-    static uint32_t identifier() noexcept;
+    Type type() const noexcept override;
+
+    /**
+     * @see Scene::type()
+     */
+    TVG_DEPRECATED static uint32_t identifier() noexcept;
 
 
     _TVG_DECLARE_PRIVATE(Scene);
     _TVG_DECLARE_PRIVATE(Scene);
 };
 };
@@ -1377,7 +1475,7 @@ public:
  *
  *
  * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text.
  * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text.
  *
  *
- * @note Experimental API
+ * @since 0.15
  */
  */
 class TVG_API Text final : public Paint
 class TVG_API Text final : public Paint
 {
 {
@@ -1422,7 +1520,7 @@ public:
      *
      *
      * @see Text::font()
      * @see Text::font()
      *
      *
-     * @note Experimental API
+     * @since 0.15
      */
      */
     Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept;
     Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept;
 
 
@@ -1434,9 +1532,9 @@ public:
      * @param[in] f The unique pointer to the gradient fill.
      * @param[in] f The unique pointer to the gradient fill.
      *
      *
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
-     * @note Experimental API
-     *
      * @see Text::font()
      * @see Text::font()
+     *
+     * @since 0.15
      */
      */
     Result fill(std::unique_ptr<Fill> f) noexcept;
     Result fill(std::unique_ptr<Fill> f) noexcept;
 
 
@@ -1452,9 +1550,9 @@ public:
      * @retval Result::InvalidArguments In case the @p path is invalid.
      * @retval Result::InvalidArguments In case the @p path is invalid.
      * @retval Result::NonSupport When trying to load a file with an unknown extension.
      * @retval Result::NonSupport When trying to load a file with an unknown extension.
      *
      *
-     * @note Experimental API
-     *
      * @see Text::unload(const std::string& path)
      * @see Text::unload(const std::string& path)
+     *
+     * @since 0.15
      */
      */
     static Result load(const std::string& path) noexcept;
     static Result load(const std::string& path) noexcept;
 
 
@@ -1475,13 +1573,13 @@ public:
      * @retval Result::NonSupport When trying to load a file with an unsupported extension.
      * @retval Result::NonSupport When trying to load a file with an unsupported extension.
      * @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded.
      * @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded.
      *
      *
-     * @warning: It's the user responsibility to release the @p data memory.
+     * @warning It's the user responsibility to release the @p data memory.
      *
      *
      * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data.
      * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data.
      * @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.
      * @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.
-     * @note Experimental API
-     *
      * @see Text::font(const char* name, float size, const char* style)
      * @see Text::font(const char* name, float size, const char* style)
+     *
+     * @note 0.15
      */
      */
     static Result load(const char* name, const char* data, uint32_t size, const std::string& mimeType = "ttf", bool copy = false) noexcept;
     static Result load(const char* name, const char* data, uint32_t size, const std::string& mimeType = "ttf", bool copy = false) noexcept;
 
 
@@ -1495,9 +1593,9 @@ public:
      * @retval Result::InsufficientCondition Fails if the loader is not initialized.
      * @retval Result::InsufficientCondition Fails if the loader is not initialized.
      *
      *
      * @note If the font data is currently in use, it will not be immediately unloaded.
      * @note If the font data is currently in use, it will not be immediately unloaded.
-     * @note Experimental API
-     *
      * @see Text::load(const std::string& path)
      * @see Text::load(const std::string& path)
+     * 
+     * @since 0.15
      */
      */
     static Result unload(const std::string& path) noexcept;
     static Result unload(const std::string& path) noexcept;
 
 
@@ -1506,18 +1604,20 @@ public:
      *
      *
      * @return A new Text object.
      * @return A new Text object.
      *
      *
-     * @note Experimental API
+     * @since 0.15
      */
      */
     static std::unique_ptr<Text> gen() noexcept;
     static std::unique_ptr<Text> gen() noexcept;
 
 
     /**
     /**
-     * @brief Return the unique id value of this class.
+     * @brief Returns the ID value of this class.
+     *
+     * This method can be used to check the current concrete instance type.
      *
      *
-     * This method can be referred for identifying the Text class type.
+     * @return The class type ID of the Text instance.
      *
      *
-     * @return The type id of the Text class.
+     * @since Experimental API
      */
      */
-    static uint32_t identifier() noexcept;
+    Type type() const noexcept override;
 
 
     _TVG_DECLARE_PRIVATE(Text);
     _TVG_DECLARE_PRIVATE(Text);
 };
 };
@@ -1616,8 +1716,6 @@ public:
  *
  *
  * @brief A class for the rendering graphic elements with a GL raster engine.
  * @brief A class for the rendering graphic elements with a GL raster engine.
  *
  *
- * @warning Please do not use it. This class is not fully supported yet.
- *
  * @since 0.14
  * @since 0.14
  */
  */
 class TVG_API GlCanvas final : public Canvas
 class TVG_API GlCanvas final : public Canvas
@@ -1666,7 +1764,7 @@ public:
  *
  *
  * @warning Please do not use it. This class is not fully supported yet.
  * @warning Please do not use it. This class is not fully supported yet.
  *
  *
- * @note Experimental API
+ * @since 0.15
  */
  */
 class TVG_API WgCanvas final : public Canvas
 class TVG_API WgCanvas final : public Canvas
 {
 {
@@ -1680,6 +1778,7 @@ public:
      * @param[in] surface WGPUSurface, handle to a presentable surface.
      * @param[in] surface WGPUSurface, handle to a presentable surface.
      * @param[in] w The width of the surface.
      * @param[in] w The width of the surface.
      * @param[in] h The height of the surface.
      * @param[in] h The height of the surface.
+     * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally.
      *
      *
      * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced.
      * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced.
      * @retval Result::NonSupport In case the wg engine is not supported.
      * @retval Result::NonSupport In case the wg engine is not supported.
@@ -1689,14 +1788,14 @@ public:
      * @see Canvas::viewport()
      * @see Canvas::viewport()
      * @see Canvas::sync()
      * @see Canvas::sync()
      */
      */
-    Result target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept;
+    Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept;
 
 
     /**
     /**
      * @brief Creates a new WgCanvas object.
      * @brief Creates a new WgCanvas object.
      *
      *
      * @return A new WgCanvas object.
      * @return A new WgCanvas object.
      *
      *
-     * @note Experimental API
+     * @since 0.15
      */
      */
     static std::unique_ptr<WgCanvas> gen() noexcept;
     static std::unique_ptr<WgCanvas> gen() noexcept;
 
 
@@ -1752,7 +1851,7 @@ public:
      *
      *
      * @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error.
      * @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error.
      *
      *
-     * @note Experimental API
+     * @since 0.15
      */
      */
     static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
     static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
 
 
@@ -1857,6 +1956,7 @@ public:
      * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin.
      * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin.
      * @note If a marker has been specified, its range will be disregarded.
      * @note If a marker has been specified, its range will be disregarded.
      * @see LottieAnimation::segment(const char* marker)
      * @see LottieAnimation::segment(const char* marker)
+     *
      * @note Experimental API
      * @note Experimental API
      */
      */
     Result segment(float begin, float end) noexcept;
     Result segment(float begin, float end) noexcept;
@@ -1895,7 +1995,7 @@ public:
  * It's useful when you need to save the composed scene or image from a paint object and recreate it later.
  * It's useful when you need to save the composed scene or image from a paint object and recreate it later.
  *
  *
  * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment.
  * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment.
- * If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result.
+ * If it doesn't support the file format, the save() method returns the @c Result::NonSupport result.
  *
  *
  * Once you export a paint to the file successfully, you can recreate it using the Picture class.
  * Once you export a paint to the file successfully, you can recreate it using the Picture class.
  *
  *

+ 1 - 1
thirdparty/thorvg/src/common/tvgCompressor.cpp

@@ -468,7 +468,7 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
         encoded += 4;
         encoded += 4;
     }
     }
     *decoded = output;
     *decoded = output;
-    return reserved;
+    return idx;
 }
 }
 
 
 
 

+ 0 - 245
thirdparty/thorvg/src/common/tvgLines.cpp

@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2020 - 2024 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 "tvgMath.h"
-#include "tvgLines.h"
-
-#define BEZIER_EPSILON 1e-2f
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-static float _lineLengthApprox(const Point& pt1, const Point& pt2)
-{
-    /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
-       With alpha = 1, beta = 3/8, giving results with the largest error less
-       than 7% compared to the exact value. */
-    Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
-    if (diff.x < 0) diff.x = -diff.x;
-    if (diff.y < 0) diff.y = -diff.y;
-    return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
-}
-
-
-static float _lineLength(const Point& pt1, const Point& pt2)
-{
-    Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
-    return sqrtf(diff.x * diff.x + diff.y * diff.y);
-}
-
-
-template<typename LengthFunc>
-float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc)
-{
-    Bezier left, right;
-    auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end);
-    auto chord = lineLengthFunc(cur.start, cur.end);
-
-    if (fabsf(len - chord) > BEZIER_EPSILON) {
-        tvg::bezSplit(cur, left, right);
-        return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc);
-    }
-    return len;
-}
-
-
-template<typename LengthFunc>
-float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc)
-{
-    auto biggest = 1.0f;
-    auto smallest = 0.0f;
-    auto t = 0.5f;
-
-    //just in case to prevent an infinite loop
-    if (at <= 0) return 0.0f;
-    if (at >= length) return 1.0f;
-
-    while (true) {
-        auto right = bz;
-        Bezier left;
-        bezSplitLeft(right, t, left);
-        length = _bezLength(left, lineLengthFunc);
-        if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
-            break;
-        }
-        if (length < at) {
-            smallest = t;
-            t = (t + biggest) * 0.5f;
-        } else {
-            biggest = t;
-            t = (smallest + t) * 0.5f;
-        }
-    }
-    return t;
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-namespace tvg
-{
-
-float lineLength(const Point& pt1, const Point& pt2)
-{
-    return _lineLength(pt1, pt2);
-}
-
-
-void lineSplitAt(const Line& cur, float at, Line& left, Line& right)
-{
-    auto len = lineLength(cur.pt1, cur.pt2);
-    auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
-    auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
-    left.pt1 = cur.pt1;
-    left.pt2.x = left.pt1.x + dx;
-    left.pt2.y = left.pt1.y + dy;
-    right.pt1 = left.pt2;
-    right.pt2 = cur.pt2;
-}
-
-
-void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
-{
-    auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
-    left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
-    right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
-    left.start.x = cur.start.x;
-    right.end.x = cur.end.x;
-    left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
-    right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
-    left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
-
-    c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
-    left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
-    right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
-    left.start.y = cur.start.y;
-    right.end.y = cur.end.y;
-    left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
-    right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
-    left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
-}
-
-
-float bezLength(const Bezier& cur)
-{
-    return _bezLength(cur, _lineLength);
-}
-
-
-float bezLengthApprox(const Bezier& cur)
-{
-    return _bezLength(cur, _lineLengthApprox);
-}
-
-
-void bezSplitLeft(Bezier& cur, float at, Bezier& left)
-{
-    left.start = cur.start;
-
-    left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
-    left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
-
-    left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
-    left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
-
-    cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
-    cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
-
-    cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
-    cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
-
-    left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
-    left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
-
-    left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
-    left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
-}
-
-
-float bezAt(const Bezier& bz, float at, float length)
-{
-    return _bezAt(bz, at, length, _lineLength);
-}
-
-
-float bezAtApprox(const Bezier& bz, float at, float length)
-{
-    return _bezAt(bz, at, length, _lineLengthApprox);
-}
-
-
-void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
-{
-    right = cur;
-    auto t = bezAt(right, at, bezLength(right));
-    bezSplitLeft(right, t, left);
-}
-
-
-Point bezPointAt(const Bezier& bz, float t)
-{
-    Point cur;
-    auto it = 1.0f - t;
-
-    auto ax = bz.start.x * it + bz.ctrl1.x * t;
-    auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t;
-    auto cx = bz.ctrl2.x * it + bz.end.x * t;
-    ax = ax * it + bx * t;
-    bx = bx * it + cx * t;
-    cur.x = ax * it + bx * t;
-
-    float ay = bz.start.y * it + bz.ctrl1.y * t;
-    float by = bz.ctrl1.y * it + bz.ctrl2.y * t;
-    float cy = bz.ctrl2.y * it + bz.end.y * t;
-    ay = ay * it + by * t;
-    by = by * it + cy * t;
-    cur.y = ay * it + by * t;
-
-    return cur;
-}
-
-
-float bezAngleAt(const Bezier& bz, float t)
-{
-    if (t < 0 || t > 1) return 0;
-
-    //derivate
-    // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
-    // t^2) * p2 + t^2 * p3)
-    float mt = 1.0f - t;
-    float d = t * t;
-    float a = -mt * mt;
-    float b = 1 - 4 * t + 3 * d;
-    float c = 2 * t - 3 * d;
-
-    Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y};
-    pt.x *= 3;
-    pt.y *= 3;
-
-    return mathRad2Deg(mathAtan2(pt.y, pt.x));
-}
-
-
-}

+ 0 - 61
thirdparty/thorvg/src/common/tvgLines.h

@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2020 - 2024 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_LINES_H_
-#define _TVG_LINES_H_
-
-#include "tvgCommon.h"
-
-namespace tvg
-{
-
-struct Line
-{
-    Point pt1;
-    Point pt2;
-};
-
-float lineLength(const Point& pt1, const Point& pt2);
-void lineSplitAt(const Line& cur, float at, Line& left, Line& right);
-
-
-struct Bezier
-{
-    Point start;
-    Point ctrl1;
-    Point ctrl2;
-    Point end;
-};
-
-void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
-float bezLength(const Bezier& cur);
-void bezSplitLeft(Bezier& cur, float at, Bezier& left);
-float bezAt(const Bezier& bz, float at, float length);
-void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
-Point bezPointAt(const Bezier& bz, float t);
-float bezAngleAt(const Bezier& bz, float t);
-
-float bezLengthApprox(const Bezier& cur);
-float bezAtApprox(const Bezier& bz, float at, float length);
-}
-
-#endif //_TVG_LINES_H_

+ 244 - 11
thirdparty/thorvg/src/common/tvgMath.cpp

@@ -22,11 +22,88 @@
 
 
 #include "tvgMath.h"
 #include "tvgMath.h"
 
 
-//see: https://en.wikipedia.org/wiki/Remez_algorithm
-float mathAtan2(float y, float x)
+#define BEZIER_EPSILON 1e-2f
+
+
+/************************************************************************/
+/* Internal Class Implementation                                        */
+/************************************************************************/
+
+static float _lineLengthApprox(const Point& pt1, const Point& pt2)
 {
 {
-    if (y == 0.0f && x == 0.0f) return 0.0f;
+    /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+       With alpha = 1, beta = 3/8, giving results with the largest error less
+       than 7% compared to the exact value. */
+    Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+    if (diff.x < 0) diff.x = -diff.x;
+    if (diff.y < 0) diff.y = -diff.y;
+    return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
+}
+
+
+static float _lineLength(const Point& pt1, const Point& pt2)
+{
+    Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+    return sqrtf(diff.x * diff.x + diff.y * diff.y);
+}
+
+
+template<typename LengthFunc>
+float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc)
+{
+    Bezier left, right;
+    auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end);
+    auto chord = lineLengthFunc(cur.start, cur.end);
+
+    if (fabsf(len - chord) > BEZIER_EPSILON) {
+        cur.split(left, right);
+        return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc);
+    }
+    return len;
+}
+
+
+template<typename LengthFunc>
+float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc)
+{
+    auto biggest = 1.0f;
+    auto smallest = 0.0f;
+    auto t = 0.5f;
+
+    //just in case to prevent an infinite loop
+    if (at <= 0) return 0.0f;
+    if (at >= length) return 1.0f;
+
+    while (true) {
+        auto right = bz;
+        Bezier left;
+        right.split(t, left);
+        length = _bezLength(left, lineLengthFunc);
+        if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) {
+            break;
+        }
+        if (length < at) {
+            smallest = t;
+            t = (t + biggest) * 0.5f;
+        } else {
+            biggest = t;
+            t = (smallest + t) * 0.5f;
+        }
+    }
+    return t;
+}
+
+
+/************************************************************************/
+/* External Class Implementation                                        */
+/************************************************************************/
 
 
+namespace tvg {
+
+//https://en.wikipedia.org/wiki/Remez_algorithm
+float atan2(float y, float x)
+{
+    if (y == 0.0f && x == 0.0f) return 0.0f;
     auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y));
     auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y));
     auto s = a * a;
     auto s = a * a;
     auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
     auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
@@ -37,7 +114,7 @@ float mathAtan2(float y, float x)
 }
 }
 
 
 
 
-bool mathInverse(const Matrix* m, Matrix* out)
+bool inverse(const Matrix* m, Matrix* out)
 {
 {
     auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
     auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
                m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
                m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
@@ -60,7 +137,7 @@ bool mathInverse(const Matrix* m, Matrix* out)
 }
 }
 
 
 
 
-bool mathIdentity(const Matrix* m)
+bool identity(const Matrix* m)
 {
 {
     if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
     if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
         m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
         m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
@@ -71,7 +148,7 @@ bool mathIdentity(const Matrix* m)
 }
 }
 
 
 
 
-void mathRotate(Matrix* m, float degree)
+void rotate(Matrix* m, float degree)
 {
 {
     if (degree == 0.0f) return;
     if (degree == 0.0f) return;
 
 
@@ -108,9 +185,9 @@ Matrix operator*(const Matrix& lhs, const Matrix& rhs)
 
 
 bool operator==(const Matrix& lhs, const Matrix& rhs)
 bool operator==(const Matrix& lhs, const Matrix& rhs)
 {
 {
-    if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
-        !mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
-        !mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
+    if (!tvg::equal(lhs.e11, rhs.e11) || !tvg::equal(lhs.e12, rhs.e12) || !tvg::equal(lhs.e13, rhs.e13) ||
+        !tvg::equal(lhs.e21, rhs.e21) || !tvg::equal(lhs.e22, rhs.e22) || !tvg::equal(lhs.e23, rhs.e23) ||
+        !tvg::equal(lhs.e31, rhs.e31) || !tvg::equal(lhs.e32, rhs.e32) || !tvg::equal(lhs.e33, rhs.e33)) {
        return false;
        return false;
     }
     }
     return true;
     return true;
@@ -133,9 +210,165 @@ Point operator*(const Point& pt, const Matrix& m)
     return {tx, ty};
     return {tx, ty};
 }
 }
 
 
-uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t)
+
+Point normal(const Point& p1, const Point& p2)
+{
+    auto dir = p2 - p1;
+    auto len = length(dir);
+    if (tvg::zero(len)) return {};
+
+    auto unitDir = dir / len;
+    return {-unitDir.y, unitDir.x};
+}
+
+
+float Line::length() const
+{
+    return _lineLength(pt1, pt2);
+}
+
+
+void Line::split(float at, Line& left, Line& right) const
+{
+    auto len = length();
+    auto dx = ((pt2.x - pt1.x) / len) * at;
+    auto dy = ((pt2.y - pt1.y) / len) * at;
+    left.pt1 = pt1;
+    left.pt2.x = left.pt1.x + dx;
+    left.pt2.y = left.pt1.y + dy;
+    right.pt1 = left.pt2;
+    right.pt2 = pt2;
+}
+
+
+void Bezier::split(Bezier& left, Bezier& right) const
+{
+    auto c = (ctrl1.x + ctrl2.x) * 0.5f;
+    left.ctrl1.x = (start.x + ctrl1.x) * 0.5f;
+    right.ctrl2.x = (ctrl2.x + end.x) * 0.5f;
+    left.start.x = start.x;
+    right.end.x = end.x;
+    left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
+    right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
+    left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
+
+    c = (ctrl1.y + ctrl2.y) * 0.5f;
+    left.ctrl1.y = (start.y + ctrl1.y) * 0.5f;
+    right.ctrl2.y = (ctrl2.y + end.y) * 0.5f;
+    left.start.y = start.y;
+    right.end.y = end.y;
+    left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
+    right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
+    left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
+}
+
+
+void Bezier::split(float at, Bezier& left, Bezier& right) const
+{
+    right = *this;
+    auto t = right.at(at, right.length());
+    right.split(t, left);
+}
+
+
+float Bezier::length() const
+{
+    return _bezLength(*this, _lineLength);
+}
+
+
+float Bezier::lengthApprox() const
+{
+    return _bezLength(*this, _lineLengthApprox);
+}
+
+
+void Bezier::split(float t, Bezier& left)
+{
+    left.start = start;
+
+    left.ctrl1.x = start.x + t * (ctrl1.x - start.x);
+    left.ctrl1.y = start.y + t * (ctrl1.y - start.y);
+
+    left.ctrl2.x = ctrl1.x + t * (ctrl2.x - ctrl1.x); //temporary holding spot
+    left.ctrl2.y = ctrl1.y + t * (ctrl2.y - ctrl1.y); //temporary holding spot
+
+    ctrl2.x = ctrl2.x + t * (end.x - ctrl2.x);
+    ctrl2.y = ctrl2.y + t * (end.y - ctrl2.y);
+
+    ctrl1.x = left.ctrl2.x + t * (ctrl2.x - left.ctrl2.x);
+    ctrl1.y = left.ctrl2.y + t * (ctrl2.y - left.ctrl2.y);
+
+    left.ctrl2.x = left.ctrl1.x + t * (left.ctrl2.x - left.ctrl1.x);
+    left.ctrl2.y = left.ctrl1.y + t * (left.ctrl2.y - left.ctrl1.y);
+
+    left.end.x = start.x = left.ctrl2.x + t * (ctrl1.x - left.ctrl2.x);
+    left.end.y = start.y = left.ctrl2.y + t * (ctrl1.y - left.ctrl2.y);
+}
+
+
+float Bezier::at(float at, float length) const
+{
+    return _bezAt(*this, at, length, _lineLength);
+}
+
+
+float Bezier::atApprox(float at, float length) const
+{
+    return _bezAt(*this, at, length, _lineLengthApprox);
+}
+
+
+Point Bezier::at(float t) const
+{
+    Point cur;
+    auto it = 1.0f - t;
+
+    auto ax = start.x * it + ctrl1.x * t;
+    auto bx = ctrl1.x * it + ctrl2.x * t;
+    auto cx = ctrl2.x * it + end.x * t;
+    ax = ax * it + bx * t;
+    bx = bx * it + cx * t;
+    cur.x = ax * it + bx * t;
+
+    float ay = start.y * it + ctrl1.y * t;
+    float by = ctrl1.y * it + ctrl2.y * t;
+    float cy = ctrl2.y * it + end.y * t;
+    ay = ay * it + by * t;
+    by = by * it + cy * t;
+    cur.y = ay * it + by * t;
+
+    return cur;
+}
+
+
+float Bezier::angle(float t) const
+{
+    if (t < 0 || t > 1) return 0;
+
+    //derivate
+    // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
+    // t^2) * p2 + t^2 * p3)
+    float mt = 1.0f - t;
+    float d = t * t;
+    float a = -mt * mt;
+    float b = 1 - 4 * t + 3 * d;
+    float c = 2 * t - 3 * d;
+
+    Point pt ={a * start.x + b * ctrl1.x + c * ctrl2.x + d * end.x, a * start.y + b * ctrl1.y + c * ctrl2.y + d * end.y};
+    pt.x *= 3;
+    pt.y *= 3;
+
+    return rad2deg(tvg::atan2(pt.y, pt.x));
+}
+
+
+uint8_t lerp(const uint8_t &start, const uint8_t &end, float t)
 {
 {
     auto result = static_cast<int>(start + (end - start) * t);
     auto result = static_cast<int>(start + (end - start) * t);
-    mathClamp(result, 0, 255);
+    tvg::clamp(result, 0, 255);
     return static_cast<uint8_t>(result);
     return static_cast<uint8_t>(result);
 }
 }
+
+}
+

+ 77 - 42
thirdparty/thorvg/src/common/tvgMath.h

@@ -29,47 +29,47 @@
 #include <cmath>
 #include <cmath>
 #include "tvgCommon.h"
 #include "tvgCommon.h"
 
 
+namespace tvg
+{
+
 #define MATH_PI  3.14159265358979323846f
 #define MATH_PI  3.14159265358979323846f
 #define MATH_PI2 1.57079632679489661923f
 #define MATH_PI2 1.57079632679489661923f
 #define FLOAT_EPSILON 1.0e-06f  //1.192092896e-07f
 #define FLOAT_EPSILON 1.0e-06f  //1.192092896e-07f
 #define PATH_KAPPA 0.552284f
 #define PATH_KAPPA 0.552284f
 
 
-#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
-#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
-
-
 /************************************************************************/
 /************************************************************************/
 /* General functions                                                    */
 /* General functions                                                    */
 /************************************************************************/
 /************************************************************************/
 
 
-float mathAtan2(float y, float x);
+float atan2(float y, float x);
 
 
-static inline float mathDeg2Rad(float degree)
+
+static inline float deg2rad(float degree)
 {
 {
      return degree * (MATH_PI / 180.0f);
      return degree * (MATH_PI / 180.0f);
 }
 }
 
 
 
 
-static inline float mathRad2Deg(float radian)
+static inline float rad2deg(float radian)
 {
 {
     return radian * (180.0f / MATH_PI);
     return radian * (180.0f / MATH_PI);
 }
 }
 
 
 
 
-static inline bool mathZero(float a)
+static inline bool zero(float a)
 {
 {
     return (fabsf(a) <= FLOAT_EPSILON) ? true : false;
     return (fabsf(a) <= FLOAT_EPSILON) ? true : false;
 }
 }
 
 
 
 
-static inline bool mathEqual(float a, float b)
+static inline bool equal(float a, float b)
 {
 {
-    return mathZero(a - b);
+    return tvg::zero(a - b);
 }
 }
 
 
 
 
 template <typename T>
 template <typename T>
-static inline void mathClamp(T& v, const T& min, const T& max)
+static inline void clamp(T& v, const T& min, const T& max)
 {
 {
     if (v < min) v = min;
     if (v < min) v = min;
     else if (v > max) v = max;
     else if (v > max) v = max;
@@ -79,27 +79,27 @@ static inline void mathClamp(T& v, const T& min, const T& max)
 /* Matrix functions                                                     */
 /* Matrix functions                                                     */
 /************************************************************************/
 /************************************************************************/
 
 
-void mathRotate(Matrix* m, float degree);
-bool mathInverse(const Matrix* m, Matrix* out);
-bool mathIdentity(const Matrix* m);
+void rotate(Matrix* m, float degree);
+bool inverse(const Matrix* m, Matrix* out);
+bool identity(const Matrix* m);
 Matrix operator*(const Matrix& lhs, const Matrix& rhs);
 Matrix operator*(const Matrix& lhs, const Matrix& rhs);
 bool operator==(const Matrix& lhs, const Matrix& rhs);
 bool operator==(const Matrix& lhs, const Matrix& rhs);
 
 
-static inline bool mathRightAngle(const Matrix& m)
+static inline bool rightAngle(const Matrix& m)
 {
 {
-   auto radian = fabsf(mathAtan2(m.e21, m.e11));
-   if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
+   auto radian = fabsf(tvg::atan2(m.e21, m.e11));
+   if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true;
    return false;
    return false;
 }
 }
 
 
 
 
-static inline bool mathSkewed(const Matrix& m)
+static inline bool skewed(const Matrix& m)
 {
 {
-    return !mathZero(m.e21 + m.e12);
+    return !tvg::zero(m.e21 + m.e12);
 }
 }
 
 
 
 
-static inline void mathIdentity(Matrix* m)
+static inline void identity(Matrix* m)
 {
 {
     m->e11 = 1.0f;
     m->e11 = 1.0f;
     m->e12 = 0.0f;
     m->e12 = 0.0f;
@@ -113,14 +113,14 @@ static inline void mathIdentity(Matrix* m)
 }
 }
 
 
 
 
-static inline void mathScale(Matrix* m, float sx, float sy)
+static inline void scale(Matrix* m, float sx, float sy)
 {
 {
     m->e11 *= sx;
     m->e11 *= sx;
     m->e22 *= sy;
     m->e22 *= sy;
 }
 }
 
 
 
 
-static inline void mathScaleR(Matrix* m, float x, float y)
+static inline void scaleR(Matrix* m, float x, float y)
 {
 {
     if (x != 1.0f) {
     if (x != 1.0f) {
         m->e11 *= x;
         m->e11 *= x;
@@ -133,14 +133,14 @@ static inline void mathScaleR(Matrix* m, float x, float y)
 }
 }
 
 
 
 
-static inline void mathTranslate(Matrix* m, float x, float y)
+static inline void translate(Matrix* m, float x, float y)
 {
 {
     m->e13 += x;
     m->e13 += x;
     m->e23 += y;
     m->e23 += y;
 }
 }
 
 
 
 
-static inline void mathTranslateR(Matrix* m, float x, float y)
+static inline void translateR(Matrix* m, float x, float y)
 {
 {
     if (x == 0.0f && y == 0.0f) return;
     if (x == 0.0f && y == 0.0f) return;
     m->e13 += (x * m->e11 + y * m->e12);
     m->e13 += (x * m->e11 + y * m->e12);
@@ -160,7 +160,7 @@ static inline void operator*=(Matrix& lhs, const Matrix& rhs)
 }
 }
 
 
 
 
-static inline void mathLog(const Matrix& m)
+static inline void log(const Matrix& m)
 {
 {
     TVGLOG("COMMON", "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);
     TVGLOG("COMMON", "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);
 }
 }
@@ -172,15 +172,21 @@ static inline void mathLog(const Matrix& m)
 
 
 void operator*=(Point& pt, const Matrix& m);
 void operator*=(Point& pt, const Matrix& m);
 Point operator*(const Point& pt, const Matrix& m);
 Point operator*(const Point& pt, const Matrix& m);
+Point normal(const Point& p1, const Point& p2);
 
 
+static inline float cross(const Point& lhs, const Point& rhs)
+{
+    return lhs.x * rhs.y - rhs.x * lhs.y;
+}
 
 
-static inline bool mathZero(const Point& p)
+
+static inline bool zero(const Point& p)
 {
 {
-    return mathZero(p.x) && mathZero(p.y);
+    return tvg::zero(p.x) && tvg::zero(p.y);
 }
 }
 
 
 
 
-static inline float mathLength(const Point* a, const Point* b)
+static inline float length(const Point* a, const Point* b)
 {
 {
     auto x = b->x - a->x;
     auto x = b->x - a->x;
     auto y = b->y - a->y;
     auto y = b->y - a->y;
@@ -192,7 +198,7 @@ static inline float mathLength(const Point* a, const Point* b)
 }
 }
 
 
 
 
-static inline float mathLength(const Point& a)
+static inline float length(const Point& a)
 {
 {
     return sqrtf(a.x * a.x + a.y * a.y);
     return sqrtf(a.x * a.x + a.y * a.y);
 }
 }
@@ -200,7 +206,7 @@ static inline float mathLength(const Point& a)
 
 
 static inline bool operator==(const Point& lhs, const Point& rhs)
 static inline bool operator==(const Point& lhs, const Point& rhs)
 {
 {
-    return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
+    return tvg::equal(lhs.x, rhs.x) && tvg::equal(lhs.y, rhs.y);
 }
 }
 
 
 
 
@@ -240,32 +246,61 @@ static inline Point operator/(const Point& lhs, const float rhs)
 }
 }
 
 
 
 
-static inline Point mathNormal(const Point& p1, const Point& p2)
+static inline void log(const Point& pt)
 {
 {
-    auto dir = p2 - p1;
-    auto len = mathLength(dir);
-    if (mathZero(len)) return {};
-
-    auto unitDir = dir / len;
-    return {-unitDir.y, unitDir.x};
+    TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);
 }
 }
 
 
 
 
-static inline void mathLog(const Point& pt)
+/************************************************************************/
+/* Line functions                                                       */
+/************************************************************************/
+
+struct Line
 {
 {
-    TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);
-}
+    Point pt1;
+    Point pt2;
+
+    void split(float at, Line& left, Line& right) const;
+    float length() const;
+};
+
+
+/************************************************************************/
+/* Bezier functions                                                     */
+/************************************************************************/
+
+struct Bezier
+{
+    Point start;
+    Point ctrl1;
+    Point ctrl2;
+    Point end;
+
+    void split(float t, Bezier& left);
+    void split(Bezier& left, Bezier& right) const;
+    void split(float at, Bezier& left, Bezier& right) const;
+    float length() const;
+    float lengthApprox() const;
+    float at(float at, float length) const;
+    float atApprox(float at, float length) const;
+    Point at(float t) const;
+    float angle(float t) const;
+};
+
 
 
 /************************************************************************/
 /************************************************************************/
 /* Interpolation functions                                              */
 /* Interpolation functions                                              */
 /************************************************************************/
 /************************************************************************/
 
 
 template <typename T>
 template <typename T>
-static inline T mathLerp(const T &start, const T &end, float t)
+static inline T lerp(const T &start, const T &end, float t)
 {
 {
     return static_cast<T>(start + (end - start) * t);
     return static_cast<T>(start + (end - start) * t);
 }
 }
 
 
-uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t);
+uint8_t lerp(const uint8_t &start, const uint8_t &end, float t);
+
+}
 
 
 #endif //_TVG_MATH_H_
 #endif //_TVG_MATH_H_

+ 0 - 10
thirdparty/thorvg/src/common/tvgStr.cpp

@@ -207,16 +207,6 @@ error:
     return 0.0f;
     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)
 char* strDuplicate(const char *str, size_t n)
 {
 {
     auto len = strlen(str);
     auto len = strlen(str);

+ 0 - 1
thirdparty/thorvg/src/common/tvgStr.h

@@ -29,7 +29,6 @@ namespace tvg
 {
 {
 
 
 float strToFloat(const char *nPtr, char **endPtr);  //convert to float
 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* strDuplicate(const char *str, size_t n);      //copy the string
 char* strAppend(char* lhs, const char* rhs, size_t n);  //append the rhs to the lhs
 char* strAppend(char* lhs, const char* rhs, size_t n);  //append the rhs to the lhs
 char* strDirname(const char* path);                 //return the full directory name
 char* strDirname(const char* path);                 //return the full directory name

+ 2 - 2
thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp

@@ -86,10 +86,10 @@ bool PngLoader::read()
 
 
     if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
     if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
         image->format = PNG_FORMAT_BGRA;
         image->format = PNG_FORMAT_BGRA;
-        surface.cs = ColorSpace::ARGB8888;
+        surface.cs = ColorSpace::ARGB8888S;
     } else {
     } else {
         image->format = PNG_FORMAT_RGBA;
         image->format = PNG_FORMAT_RGBA;
-        surface.cs = ColorSpace::ABGR8888;
+        surface.cs = ColorSpace::ABGR8888S;
     }
     }
 
 
     auto buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
     auto buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));

+ 1 - 1
thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp

@@ -134,7 +134,7 @@ bool WebpLoader::read()
 }
 }
 
 
 
 
-Surface* WebpLoader::bitmap()
+RenderSurface* WebpLoader::bitmap()
 {
 {
     this->done();
     this->done();
 
 

+ 1 - 1
thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h

@@ -36,7 +36,7 @@ public:
     bool open(const char* data, uint32_t size, bool copy) override;
     bool open(const char* data, uint32_t size, bool copy) override;
     bool read() override;
     bool read() override;
 
 
-    Surface* bitmap() override;
+    RenderSurface* bitmap() override;
 
 
 private:
 private:
     void run(unsigned tid) override;
     void run(unsigned tid) override;

+ 1 - 1
thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp

@@ -125,7 +125,7 @@ bool JpgLoader::close()
 }
 }
 
 
 
 
-Surface* JpgLoader::bitmap()
+RenderSurface* JpgLoader::bitmap()
 {
 {
     this->done();
     this->done();
     return ImageLoader::bitmap();
     return ImageLoader::bitmap();

+ 1 - 1
thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h

@@ -46,7 +46,7 @@ public:
     bool read() override;
     bool read() override;
     bool close() override;
     bool close() override;
 
 
-    Surface* bitmap() override;
+    RenderSurface* bitmap() override;
 };
 };
 
 
 #endif //_TVG_JPG_LOADER_H_
 #endif //_TVG_JPG_LOADER_H_

+ 10 - 7
thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp

@@ -36,6 +36,8 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <setjmp.h>
 #include <setjmp.h>
 #include <stdint.h>
 #include <stdint.h>
+
+#include "tvgCommon.h"
 #include "tvgJpgd.h"
 #include "tvgJpgd.h"
 
 
 #ifdef _MSC_VER
 #ifdef _MSC_VER
@@ -70,7 +72,7 @@ enum jpgd_status
     JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
     JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
     JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
     JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
     JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
     JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
-    JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
+    JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMETIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
     JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
     JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
     JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
     JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
     JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
     JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
@@ -1382,9 +1384,9 @@ int jpeg_decoder::process_markers()
                 read_dht_marker();
                 read_dht_marker();
                 break;
                 break;
             }
             }
-            // No arithmitic support - dumb patents!
+            // No arithmetic support - dumb patents!
             case M_DAC: {
             case M_DAC: {
-                stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+                stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
                 break;
                 break;
             }
             }
             case M_DQT: {
             case M_DQT: {
@@ -1466,8 +1468,8 @@ void jpeg_decoder::locate_sof_marker()
           read_sof_marker();
           read_sof_marker();
           break;
           break;
         }
         }
-        case M_SOF9: {  /* Arithmitic coding */
-          stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+        case M_SOF9: {  /* Arithmetic coding */
+          stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
           break;
           break;
         }
         }
         default: {
         default: {
@@ -1738,7 +1740,8 @@ void jpeg_decoder::transform_mcu_expand(int mcu_row)
                 DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
                 DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
                 break;
                 break;
             default:
             default:
-                JPGD_ASSERT(false);
+                TVGERR("JPG", "invalid transform_mcu_expand");
+                return;
         }
         }
         DCT_Upsample::Matrix44 a(P + Q); P -= Q;
         DCT_Upsample::Matrix44 a(P + Q); P -= Q;
         DCT_Upsample::Matrix44& b = P;
         DCT_Upsample::Matrix44& b = P;
@@ -1831,7 +1834,7 @@ void jpeg_decoder::process_restart()
     int i;
     int i;
     int c = 0;
     int c = 0;
 
 
-    // Align to a byte boundry
+    // Align to a byte boundary
     // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
     // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
     //get_bits_no_markers(m_bits_left & 7);
     //get_bits_no_markers(m_bits_left & 7);
 
 

+ 37 - 12
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -177,6 +177,7 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt
     else if (strstr(str, "%")) {
     else if (strstr(str, "%")) {
         if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h;
         if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h;
         else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w;
         else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w;
+        else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
         else //if other than it's radius
         else //if other than it's radius
         {
         {
             float max = svgParse->global.w;
             float max = svgParse->global.w;
@@ -593,15 +594,15 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re
     saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
     saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
     brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
     brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
 
 
-    if (mathZero(saturation))  _red = _green = _blue = brightness;
+    if (tvg::zero(saturation))  _red = _green = _blue = brightness;
     else {
     else {
-        if (mathEqual(hue, 360.0)) hue = 0.0f;
+        if (tvg::equal(hue, 360.0)) hue = 0.0f;
         hue /= 60.0f;
         hue /= 60.0f;
 
 
         v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
         v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
         p = brightness + brightness - v;
         p = brightness + brightness - v;
 
 
-        if (!mathZero(v)) sv = (v - p) / v;
+        if (!tvg::zero(v)) sv = (v - p) / v;
         else sv = 0;
         else sv = 0;
 
 
         i = static_cast<uint8_t>(hue);
         i = static_cast<uint8_t>(hue);
@@ -858,8 +859,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
             //Transform to signed.
             //Transform to signed.
             points[0] = fmodf(points[0], 360.0f);
             points[0] = fmodf(points[0], 360.0f);
             if (points[0] < 0) points[0] += 360.0f;
             if (points[0] < 0) points[0] += 360.0f;
-            auto c = cosf(mathDeg2Rad(points[0]));
-            auto s = sinf(mathDeg2Rad(points[0]));
+            auto c = cosf(deg2rad(points[0]));
+            auto s = sinf(deg2rad(points[0]));
             if (ptCount == 1) {
             if (ptCount == 1) {
                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
                 *matrix *= tmp;
                 *matrix *= tmp;
@@ -882,12 +883,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
             *matrix *= tmp;
             *matrix *= tmp;
         } else if (state == MatrixState::SkewX) {
         } else if (state == MatrixState::SkewX) {
             if (ptCount != 1) goto error;
             if (ptCount != 1) goto error;
-            auto deg = tanf(mathDeg2Rad(points[0]));
+            auto deg = tanf(deg2rad(points[0]));
             Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
             Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
             *matrix *= tmp;
             *matrix *= tmp;
         } else if (state == MatrixState::SkewY) {
         } else if (state == MatrixState::SkewY) {
             if (ptCount != 1) goto error;
             if (ptCount != 1) goto error;
-            auto deg = tanf(mathDeg2Rad(points[0]));
+            auto deg = tanf(deg2rad(points[0]));
             Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
             Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
             *matrix *= tmp;
             *matrix *= tmp;
         }
         }
@@ -1066,7 +1067,7 @@ static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, co
 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
 {
 {
     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
-    node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+    node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal);
 }
 }
 
 
 
 
@@ -1614,7 +1615,7 @@ static constexpr struct
 } circleTags[] = {
 } circleTags[] = {
     {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
     {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
     {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
     {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
-    {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
+    {"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)}
 };
 };
 
 
 
 
@@ -2554,6 +2555,18 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
 }
 }
 
 
 
 
+static SvgColor* _findLatestColor(const SvgLoaderData* loader)
+{
+    auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc;
+
+    while (parent != nullptr) {
+        if (parent->style->curColorSet) return &parent->style->color;
+        parent = parent->parent;
+    }
+    return nullptr;
+}
+
+
 static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
 static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
 {
 {
     SvgLoaderData* loader = (SvgLoaderData*)data;
     SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2563,7 +2576,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
         stop->a = _toOpacity(value);
         stop->a = _toOpacity(value);
         loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
         loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
     } else if (!strcmp(key, "stop-color")) {
     } else if (!strcmp(key, "stop-color")) {
-        if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
+        if (!strcmp(value, "currentColor")) {
+            if (auto latestColor = _findLatestColor(loader)) {
+                stop->r = latestColor->r;
+                stop->g = latestColor->g;
+                stop->b = latestColor->b;
+            }
+        } else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
             loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
             loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
         }
         }
     } else {
     } else {
@@ -2586,7 +2605,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
             stop->a = _toOpacity(value);
             stop->a = _toOpacity(value);
         }
         }
     } else if (!strcmp(key, "stop-color")) {
     } else if (!strcmp(key, "stop-color")) {
-        if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
+        if (!strcmp(value, "currentColor")) {
+            if (auto latestColor = _findLatestColor(loader)) {
+                stop->r = latestColor->r;
+                stop->g = latestColor->g;
+                stop->b = latestColor->b;
+            }
+        } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
             _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
             _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
         }
         }
     } else if (!strcmp(key, "style")) {
     } else if (!strcmp(key, "style")) {
@@ -3341,7 +3366,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
             else parent = loader->doc;
             else parent = loader->doc;
             if (!strcmp(tagName, "style")) {
             if (!strcmp(tagName, "style")) {
                 // TODO: For now only the first style node is saved. After the css id selector
                 // TODO: For now only the first style node is saved. After the css id selector
-                // is introduced this if condition shouldin't be necessary any more
+                // is introduced this if condition shouldn't be necessary any more
                 if (!loader->cssStyle) {
                 if (!loader->cssStyle) {
                     node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
                     node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
                     loader->cssStyle = node;
                     loader->cssStyle = node;

+ 1 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h

@@ -215,6 +215,7 @@ enum class SvgParserLengthType
 {
 {
     Vertical,
     Vertical,
     Horizontal,
     Horizontal,
+    Diagonal,
     //In case of, for example, radius of radial gradient
     //In case of, for example, radius of radial gradient
     Other
     Other
 };
 };

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

@@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
     rx = fabsf(rx);
     rx = fabsf(rx);
     ry = fabsf(ry);
     ry = fabsf(ry);
 
 
-    angle = mathDeg2Rad(angle);
+    angle = deg2rad(angle);
     cosPhi = cosf(angle);
     cosPhi = cosf(angle);
     sinPhi = sinf(angle);
     sinPhi = sinf(angle);
     dx2 = (sx - x) / 2.0f;
     dx2 = (sx - x) / 2.0f;
@@ -190,14 +190,14 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
     cx += (sx + x) / 2.0f;
     cx += (sx + x) / 2.0f;
     cy += (sy + y) / 2.0f;
     cy += (sy + y) / 2.0f;
 
 
-    //Sstep 4 (F6.5.4)
+    //Step 4 (F6.5.4)
     //We dont' use arccos (as per w3c doc), see
     //We dont' use arccos (as per w3c doc), see
     //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
     //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
     //Note: atan2 (0.0, 1.0) == 0.0
     //Note: atan2 (0.0, 1.0) == 0.0
-    at = mathAtan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
+    at = tvg::atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
     theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at;
     theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at;
 
 
-    nat = mathAtan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
+    nat = tvg::atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
     deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at;
     deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at;
 
 
     if (sweep) {
     if (sweep) {
@@ -469,12 +469,12 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         }
         }
         case 'a':
         case 'a':
         case 'A': {
         case 'A': {
-            if (mathZero(arr[0]) || mathZero(arr[1])) {
+            if (tvg::zero(arr[0]) || tvg::zero(arr[1])) {
                 Point p = {arr[5], arr[6]};
                 Point p = {arr[5], arr[6]};
                 cmds->push(PathCommand::LineTo);
                 cmds->push(PathCommand::LineTo);
                 pts->push(p);
                 pts->push(p);
                 *cur = {arr[5], arr[6]};
                 *cur = {arr[5], arr[6]};
-            } else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) {
+            } else if (!tvg::equal(cur->x, arr[5]) || !tvg::equal(cur->y, arr[6])) {
                 _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]);
                 _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]);
                 *cur = *curCtl = {arr[5], arr[6]};
                 *cur = *curCtl = {arr[5], arr[6]};
                 *isQuadratic = false;
                 *isQuadratic = false;

+ 12 - 24
thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp

@@ -153,7 +153,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
     if (isTransform) finalTransform = *g->transform;
     if (isTransform) finalTransform = *g->transform;
 
 
     if (g->userSpace) {
     if (g->userSpace) {
-        //The radius scalling is done according to the Units section:
+        //The radius scaling is done according to the Units section:
         //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
         //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
         g->radial->cx = g->radial->cx * vBox.w;
         g->radial->cx = g->radial->cx * vBox.w;
         g->radial->cy = g->radial->cy * vBox.h;
         g->radial->cy = g->radial->cy * vBox.h;
@@ -215,7 +215,7 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
     }
     }
     if (child->transform) finalTransform = *child->transform * finalTransform;
     if (child->transform) finalTransform = *child->transform * finalTransform;
 
 
-    return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
+    return _appendClipShape(loaderData, child, shape, vBox, svgPath, identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
 }
 }
 
 
 
 
@@ -272,8 +272,7 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg
             if (valid) {
             if (valid) {
                 Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
                 Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
                 comp->transform(finalTransform);
                 comp->transform(finalTransform);
-
-                paint->composite(std::move(comp), CompositeMethod::ClipPath);
+                paint->clip(std::move(comp));
             }
             }
 
 
             node->style->clipPath.applying = false;
             node->style->clipPath.applying = false;
@@ -714,7 +713,6 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee
 
 
 static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
 static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
 {
 {
-    unique_ptr<Scene> finalScene;
     auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite);
     auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite);
 
 
     // mUseTransform = mUseTransform * mTranslate
     // mUseTransform = mUseTransform * mTranslate
@@ -736,10 +734,10 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
         auto vh = (symbol.hasViewBox ? symbol.vh : height);
         auto vh = (symbol.hasViewBox ? symbol.vh : height);
 
 
         Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
         Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
-        if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) {
+        if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) {
             Box box = {symbol.vx, symbol.vy, vw, vh};
             Box box = {symbol.vx, symbol.vy, vw, vh};
             mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
             mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
-        } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) {
+        } else if (!tvg::zero(symbol.vx) || !tvg::zero(symbol.vy)) {
             mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
             mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
         }
         }
 
 
@@ -751,9 +749,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
         mSceneTransform = mUseTransform * mSceneTransform;
         mSceneTransform = mUseTransform * mSceneTransform;
         scene->transform(mSceneTransform);
         scene->transform(mSceneTransform);
 
 
-        if (node->node.use.symbol->node.symbol.overflowVisible) {
-            finalScene = std::move(scene);
-        } else {
+        if (!node->node.use.symbol->node.symbol.overflowVisible) {
             auto viewBoxClip = Shape::gen();
             auto viewBoxClip = Shape::gen();
             viewBoxClip->appendRect(0, 0, width, height, 0, 0);
             viewBoxClip->appendRect(0, 0, width, height, 0, 0);
 
 
@@ -764,21 +760,13 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
             }
             }
             viewBoxClip->transform(mClipTransform);
             viewBoxClip->transform(mClipTransform);
 
 
-            auto compositeLayer = Scene::gen();
-            compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
-            compositeLayer->push(std::move(scene));
-
-            auto root = Scene::gen();
-            root->push(std::move(compositeLayer));
-
-            finalScene = std::move(root);
+            scene->clip(std::move(viewBoxClip));
         }
         }
     } else {
     } else {
         scene->transform(mUseTransform);
         scene->transform(mUseTransform);
-        finalScene = std::move(scene);
     }
     }
 
 
-    return finalScene;
+    return scene;
 }
 }
 
 
 
 
@@ -821,7 +809,7 @@ static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNod
 
 
     Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
     Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
     if (node->transform) textTransform = *node->transform;
     if (node->transform) textTransform = *node->transform;
-    mathTranslateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
+    translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
     text->transform(textTransform);
     text->transform(textTransform);
 
 
     //TODO: handle def values of font and size as used in a system?
     //TODO: handle def values of font and size as used in a system?
@@ -926,10 +914,10 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
 
 
     if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
     if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
 
 
-    if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
+    if (!tvg::equal(w, vBox.w) || !tvg::equal(h, vBox.h)) {
         Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
         Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
         docNode->transform(m);
         docNode->transform(m);
-    } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
+    } else if (!tvg::zero(vBox.x) || !tvg::zero(vBox.y)) {
         docNode->translate(-vBox.x, -vBox.y);
         docNode->translate(-vBox.x, -vBox.y);
     }
     }
 
 
@@ -937,7 +925,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
     viewBoxClip->appendRect(0, 0, w, h);
     viewBoxClip->appendRect(0, 0, w, h);
 
 
     auto compositeLayer = Scene::gen();
     auto compositeLayer = Scene::gen();
-    compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
+    compositeLayer->clip(std::move(viewBoxClip));
     compositeLayer->push(std::move(docNode));
     compositeLayer->push(std::move(docNode));
 
 
     auto root = Scene::gen();
     auto root = Scene::gen();

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

@@ -492,13 +492,13 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
         key[0] = '\0';
         key[0] = '\0';
         val[0] = '\0';
         val[0] = '\0';
 
 
-        if (next == nullptr && sep != nullptr) {
+        if (sep != nullptr && next == nullptr) {
             memcpy(key, buf, sep - buf);
             memcpy(key, buf, sep - buf);
             key[sep - buf] = '\0';
             key[sep - buf] = '\0';
 
 
             memcpy(val, sep + 1, end - sep - 1);
             memcpy(val, sep + 1, end - sep - 1);
             val[end - sep - 1] = '\0';
             val[end - sep - 1] = '\0';
-        } else if (sep < next && sep != nullptr) {
+        } else if (sep != nullptr && sep < next) {
             memcpy(key, buf, sep - buf);
             memcpy(key, buf, sep - buf);
             key[sep - buf] = '\0';
             key[sep - buf] = '\0';
 
 
@@ -522,8 +522,9 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
             }
             }
         }
         }
 
 
+        if (!next) break;
         buf = next + 1;
         buf = next + 1;
-    } while (next != nullptr);
+    } while (true);
 
 
     return true;
     return true;
 }
 }

+ 29 - 20
thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h

@@ -113,7 +113,7 @@ struct SwSpan
     uint8_t coverage;
     uint8_t coverage;
 };
 };
 
 
-struct SwRleData
+struct SwRle
 {
 {
     SwSpan *spans;
     SwSpan *spans;
     uint32_t alloc;
     uint32_t alloc;
@@ -211,8 +211,8 @@ struct SwShape
     SwOutline*   outline = nullptr;
     SwOutline*   outline = nullptr;
     SwStroke*    stroke = nullptr;
     SwStroke*    stroke = nullptr;
     SwFill*      fill = nullptr;
     SwFill*      fill = nullptr;
-    SwRleData*   rle = nullptr;
-    SwRleData*   strokeRle = nullptr;
+    SwRle*   rle = nullptr;
+    SwRle*   strokeRle = nullptr;
     SwBBox       bbox;           //Keep it boundary without stroke region. Using for optimal filling.
     SwBBox       bbox;           //Keep it boundary without stroke region. Using for optimal filling.
 
 
     bool         fastTrack = false;   //Fast Track: axis-aligned rectangle without any clips?
     bool         fastTrack = false;   //Fast Track: axis-aligned rectangle without any clips?
@@ -221,7 +221,7 @@ struct SwShape
 struct SwImage
 struct SwImage
 {
 {
     SwOutline*   outline = nullptr;
     SwOutline*   outline = nullptr;
-    SwRleData*   rle = nullptr;
+    SwRle*   rle = nullptr;
     union {
     union {
         pixel_t*  data;      //system based data pointer
         pixel_t*  data;      //system based data pointer
         uint32_t* buf32;     //for explicit 32bits channels
         uint32_t* buf32;     //for explicit 32bits channels
@@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*);                                        //bl
 
 
 struct SwCompositor;
 struct SwCompositor;
 
 
-struct SwSurface : Surface
+struct SwSurface : RenderSurface
 {
 {
     SwJoin  join;
     SwJoin  join;
     SwAlpha alphas[4];                    //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
     SwAlpha alphas[4];                    //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
     SwBlender blender = nullptr;          //blender (optional)
     SwBlender blender = nullptr;          //blender (optional)
     SwCompositor* compositor = nullptr;   //compositor (optional)
     SwCompositor* compositor = nullptr;   //compositor (optional)
-    BlendMethod blendMethod;              //blending method (uint8_t)
+    BlendMethod blendMethod = BlendMethod::Normal;
 
 
     SwAlpha alpha(CompositeMethod method)
     SwAlpha alpha(CompositeMethod method)
     {
     {
@@ -262,7 +262,7 @@ struct SwSurface : Surface
     {
     {
     }
     }
 
 
-    SwSurface(const SwSurface* rhs) : Surface(rhs)
+    SwSurface(const SwSurface* rhs) : RenderSurface(rhs)
     {
     {
         join = rhs->join;
         join = rhs->join;
         memcpy(alphas, rhs->alphas, sizeof(alphas));
         memcpy(alphas, rhs->alphas, sizeof(alphas));
@@ -272,7 +272,7 @@ struct SwSurface : Surface
      }
      }
 };
 };
 
 
-struct SwCompositor : Compositor
+struct SwCompositor : RenderCompositor
 {
 {
     SwSurface* recoverSfc;                  //Recover surface when composition is started
     SwSurface* recoverSfc;                  //Recover surface when composition is started
     SwCompositor* recoverCmp;               //Recover compositor when composition is done
     SwCompositor* recoverCmp;               //Recover compositor when composition is done
@@ -488,13 +488,14 @@ SwFixed mathAtan(const SwPoint& pt);
 SwFixed mathCos(SwFixed angle);
 SwFixed mathCos(SwFixed angle);
 SwFixed mathSin(SwFixed angle);
 SwFixed mathSin(SwFixed angle);
 void mathSplitCubic(SwPoint* base);
 void mathSplitCubic(SwPoint* base);
+void mathSplitLine(SwPoint* base);
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
 SwFixed mathLength(const SwPoint& pt);
 SwFixed mathLength(const SwPoint& pt);
-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
+int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
 SwFixed mathMean(SwFixed angle1, SwFixed angle2);
 SwFixed mathMean(SwFixed angle1, SwFixed angle2);
 SwPoint mathTransform(const Point* to, const Matrix& transform);
 SwPoint mathTransform(const Point* to, const Matrix& transform);
 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
-bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
 
 
 void shapeReset(SwShape* shape);
 void shapeReset(SwShape* shape);
 bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
 bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
@@ -541,13 +542,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
 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, 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);     //matting 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);
-void rleFree(SwRleData* rle);
-void rleReset(SwRleData* rle);
-void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2);
-void rleClipPath(SwRleData* rle, const SwRleData* clip);
-void rleClipRect(SwRleData* rle, const SwBBox* clip);
+SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
+SwRle* rleRender(const SwBBox* bbox);
+void rleFree(SwRle* rle);
+void rleReset(SwRle* rle);
+void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
+void rleClip(SwRle* rle, const SwRle* clip);
+void rleClip(SwRle* rle, const SwBBox* clip);
 
 
 SwMpool* mpoolInit(uint32_t threads);
 SwMpool* mpoolInit(uint32_t threads);
 bool mpoolTerm(SwMpool* mpool);
 bool mpoolTerm(SwMpool* mpool);
@@ -567,9 +568,17 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
 bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
 bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
 bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
 bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
 void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
 void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
+void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
+void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
-void rasterUnpremultiply(Surface* surface);
-void rasterPremultiply(Surface* surface);
-bool rasterConvertCS(Surface* surface, ColorSpace to);
+void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped);
+void rasterUnpremultiply(RenderSurface* surface);
+void rasterPremultiply(RenderSurface* surface);
+bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
+
+bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
+bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect);
+bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct);
+bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
 
 
 #endif /* _TVG_SW_COMMON_H_ */
 #endif /* _TVG_SW_COMMON_H_ */

+ 20 - 20
thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp

@@ -66,15 +66,15 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
 static uint32_t _estimateAAMargin(const Fill* fdata)
 static uint32_t _estimateAAMargin(const Fill* fdata)
 {
 {
     constexpr float marginScalingFactor = 800.0f;
     constexpr float marginScalingFactor = 800.0f;
-    if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
+    if (fdata->type() == Type::RadialGradient) {
         auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
         auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
-        return mathZero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
+        return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
     }
     }
     auto grad = P(static_cast<const LinearGradient*>(fdata));
     auto grad = P(static_cast<const LinearGradient*>(fdata));
     Point p1 {grad->x1, grad->y1};
     Point p1 {grad->x1, grad->y1};
     Point p2 {grad->x2, grad->y2};
     Point p2 {grad->x2, grad->y2};
-    auto length = mathLength(&p1, &p2);
-    return mathZero(length) ? 0 : static_cast<uint32_t>(marginScalingFactor / length);
+    auto len = length(&p1, &p2);
+    return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / len);
 }
 }
 
 
 
 
@@ -217,7 +217,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
     auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
     auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
 
 
     if (len < FLOAT_EPSILON) {
     if (len < FLOAT_EPSILON) {
-        if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) {
+        if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) {
             fill->solid = true;
             fill->solid = true;
         }
         }
         return true;
         return true;
@@ -228,7 +228,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
     fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
     fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
 
 
     auto gradTransform = linear->transform();
     auto gradTransform = linear->transform();
-    bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+    bool isTransformation = !identity((const Matrix*)(&gradTransform));
 
 
     if (isTransformation) {
     if (isTransformation) {
         gradTransform = transform * gradTransform;
         gradTransform = transform * gradTransform;
@@ -239,7 +239,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
 
 
     if (isTransformation) {
     if (isTransformation) {
         Matrix invTransform;
         Matrix invTransform;
-        if (!mathInverse(&gradTransform, &invTransform)) return false;
+        if (!inverse(&gradTransform, &invTransform)) return false;
 
 
         fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
         fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
 
 
@@ -261,7 +261,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
     auto fy = P(radial)->fy;
     auto fy = P(radial)->fy;
     auto fr = P(radial)->fr;
     auto fr = P(radial)->fr;
 
 
-    if (mathZero(r)) {
+    if (tvg::zero(r)) {
         fill->solid = true;
         fill->solid = true;
         return true;
         return true;
     }
     }
@@ -295,7 +295,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
     if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
     if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
 
 
     auto gradTransform = radial->transform();
     auto gradTransform = radial->transform();
-    bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+    bool isTransformation = !identity((const Matrix*)(&gradTransform));
 
 
     if (isTransformation) gradTransform = transform * gradTransform;
     if (isTransformation) gradTransform = transform * gradTransform;
     else {
     else {
@@ -305,7 +305,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
 
 
     if (isTransformation) {
     if (isTransformation) {
         Matrix invTransform;
         Matrix invTransform;
-        if (!mathInverse(&gradTransform, &invTransform)) return false;
+        if (!inverse(&gradTransform, &invTransform)) return false;
         fill->radial.a11 = invTransform.e11;
         fill->radial.a11 = invTransform.e11;
         fill->radial.a12 = invTransform.e12;
         fill->radial.a12 = invTransform.e12;
         fill->radial.a13 = invTransform.e13;
         fill->radial.a13 = invTransform.e13;
@@ -553,7 +553,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
 
     if (opacity == 255) {
     if (opacity == 255) {
-        if (mathZero(inc)) {
+        if (tvg::zero(inc)) {
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
                 *dst = opBlendNormal(color, *dst, alpha(cmp));
                 *dst = opBlendNormal(color, *dst, alpha(cmp));
@@ -584,7 +584,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
             }
             }
         }
         }
     } else {
     } else {
-        if (mathZero(inc)) {
+        if (tvg::zero(inc)) {
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
                 *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
                 *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
@@ -626,7 +626,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
     float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
     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);
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
 
-    if (mathZero(inc)) {
+    if (tvg::zero(inc)) {
         auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
         auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
         for (uint32_t i = 0; i < len; ++i, ++dst) {
         for (uint32_t i = 0; i < len; ++i, ++dst) {
             *dst = maskOp(src, *dst, ~src);
             *dst = maskOp(src, *dst, ~src);
@@ -643,7 +643,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
         auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
         auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
         auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
         auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
         for (uint32_t j = 0; j < len; ++j, ++dst) {
         for (uint32_t j = 0; j < len; ++j, ++dst) {
-            auto src = MULTIPLY(_fixedPixel(fill, t2), a);
+            auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a);
             *dst = maskOp(src, *dst, ~src);
             *dst = maskOp(src, *dst, ~src);
             t2 += inc2;
             t2 += inc2;
         }
         }
@@ -651,7 +651,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
     } else {
     } else {
         uint32_t counter = 0;
         uint32_t counter = 0;
         while (counter++ < len) {
         while (counter++ < len) {
-            auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
+            auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
             *dst = maskOp(src, *dst, ~src);
             *dst = maskOp(src, *dst, ~src);
             ++dst;
             ++dst;
             t += inc;
             t += inc;
@@ -668,7 +668,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
     float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
     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);
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
 
-    if (mathZero(inc)) {
+    if (tvg::zero(inc)) {
         auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
         auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
         src = MULTIPLY(src, a);
         src = MULTIPLY(src, a);
         for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
         for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
@@ -715,7 +715,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
     float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
     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);
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
 
-    if (mathZero(inc)) {
+    if (tvg::zero(inc)) {
         auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
         auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
         for (uint32_t i = 0; i < len; ++i, ++dst) {
         for (uint32_t i = 0; i < len; ++i, ++dst) {
             *dst = op(color, *dst, a);
             *dst = op(color, *dst, a);
@@ -755,7 +755,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
     float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
     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);
     float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
 
-    if (mathZero(inc)) {
+    if (tvg::zero(inc)) {
         auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
         auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
         if (a == 255) {
         if (a == 255) {
             for (uint32_t i = 0; i < len; ++i, ++dst) {
             for (uint32_t i = 0; i < len; ++i, ++dst) {
@@ -828,9 +828,9 @@ bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform,
 
 
     fill->spread = fdata->spread();
     fill->spread = fdata->spread();
 
 
-    if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
+    if (fdata->type() == Type::LinearGradient) {
         if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
         if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
-    } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
+    } else if (fdata->type() == Type::RadialGradient) {
         if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
         if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
     }
     }
 
 

+ 2 - 2
thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp

@@ -29,7 +29,7 @@
 
 
 static inline bool _onlyShifted(const Matrix& m)
 static inline bool _onlyShifted(const Matrix& m)
 {
 {
-    if (mathEqual(m.e11, 1.0f) && mathEqual(m.e22, 1.0f) && mathZero(m.e12) && mathZero(m.e21)) return true;
+    if (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(m.e21)) return true;
     return false;
     return false;
 }
 }
 
 
@@ -86,7 +86,7 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
         auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
         auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
         image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
         image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
 
 
-        if (mathZero(transform.e12) && mathZero(transform.e21)) image->scaled = true;
+        if (tvg::zero(transform.e12) && tvg::zero(transform.e21)) image->scaled = true;
         else image->scaled = false;
         else image->scaled = false;
     }
     }
 
 

+ 23 - 16
thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp

@@ -44,7 +44,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2)
 }
 }
 
 
 
 
-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
+int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
 {
 {
     auto d1 = base[2] - base[3];
     auto d1 = base[2] - base[3];
     auto d2 = base[1] - base[2];
     auto d2 = base[1] - base[2];
@@ -54,7 +54,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
         if (d2.small()) {
         if (d2.small()) {
             if (d3.small()) {
             if (d3.small()) {
                 angleIn = angleMid = angleOut = 0;
                 angleIn = angleMid = angleOut = 0;
-                return true;
+                return -1;  //ignoreable
             } else {
             } else {
                 angleIn = angleMid = angleOut = mathAtan(d3);
                 angleIn = angleMid = angleOut = mathAtan(d3);
             }
             }
@@ -90,8 +90,8 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
     auto theta1 = abs(mathDiff(angleIn, angleMid));
     auto theta1 = abs(mathDiff(angleIn, angleMid));
     auto theta2 = abs(mathDiff(angleMid, angleOut));
     auto theta2 = abs(mathDiff(angleMid, angleOut));
 
 
-    if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
-    return false;
+    if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size
+    return 1;
 }
 }
 
 
 
 
@@ -179,7 +179,7 @@ SwFixed mathTan(SwFixed angle)
 SwFixed mathAtan(const SwPoint& pt)
 SwFixed mathAtan(const SwPoint& pt)
 {
 {
     if (pt.zero()) return 0;
     if (pt.zero()) return 0;
-    return SwFixed(mathAtan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
+    return SwFixed(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
 }
 }
 
 
 
 
@@ -242,6 +242,15 @@ void mathSplitCubic(SwPoint* base)
 }
 }
 
 
 
 
+void mathSplitLine(SwPoint* base)
+{
+    base[2] = base[1];
+
+    base[1].x = (base[0].x + base[1].x) >> 1;
+    base[1].y = (base[0].y + base[1].y) >> 1;
+}
+
+
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
 {
 {
     auto delta = angle2 - angle1;
     auto delta = angle2 - angle1;
@@ -263,19 +272,19 @@ SwPoint mathTransform(const Point* to, const Matrix& transform)
 }
 }
 
 
 
 
-bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
 {
 {
-    clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
-    clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
-    clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
-    clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
+    clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
+    clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
+    clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
+    clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
 
 
     //Check valid region
     //Check valid region
-    if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
+    if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false;
 
 
     //Check boundary
     //Check boundary
-    if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
-        clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
+    if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
+        clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
 
 
     return true;
     return true;
 }
 }
@@ -303,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
         if (yMin > pt->y) yMin = pt->y;
         if (yMin > pt->y) yMin = pt->y;
         if (yMax < pt->y) yMax = pt->y;
         if (yMax < pt->y) yMax = pt->y;
     }
     }
-    //Since no antialiasing is applied in the Fast Track case,
-    //the rasterization region has to be rearranged.
-    //https://github.com/Samsung/thorvg/issues/916
+
     if (fastTrack) {
     if (fastTrack) {
         renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
         renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
         renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));
         renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));

+ 410 - 0
thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp

@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2024 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 "tvgMath.h"
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Gaussian Blur Implementation                                         */
+/************************************************************************/
+
+struct SwGaussianBlur
+{
+    static constexpr int MAX_LEVEL = 3;
+    int level;
+    int kernel[MAX_LEVEL];
+};
+
+
+static void _gaussianExtendRegion(RenderRegion& region, int extra, int8_t direction)
+{
+    //bbox region expansion for feathering
+    if (direction != 2) {
+        region.x = -extra;
+        region.w = extra * 2;
+    }
+    if (direction != 1) {
+        region.y = -extra;
+        region.h = extra * 2;
+    }
+}
+
+
+static int _gaussianEdgeWrap(int end, int idx)
+{
+    auto r = idx % end;
+    return (r < 0) ? end + r : r;
+}
+
+
+static int _gaussianEdgeExtend(int end, int idx)
+{
+    if (idx < 0) return 0;
+    else if (idx >= end) return end - 1;
+    return idx;
+}
+
+
+static int _gaussianRemap(int end, int idx, int border)
+{
+    if (border == 1) return _gaussianEdgeWrap(end, idx);
+    return _gaussianEdgeExtend(end, idx);
+}
+
+
+//TODO: SIMD OPTIMIZATION?
+static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, int border, bool flipped)
+{
+    if (flipped) {
+        src += (bbox.min.x * stride + bbox.min.y) << 2;
+        dst += (bbox.min.x * stride + bbox.min.y) << 2;
+    } else {
+        src += (bbox.min.y * stride + bbox.min.x) << 2;
+        dst += (bbox.min.y * stride + bbox.min.x) << 2;
+    }
+
+    auto iarr = 1.0f / (dimension + dimension + 1);
+
+    #pragma omp parallel for
+    for (int y = 0; y < h; ++y) {
+        auto p = y * stride;
+        auto i = p * 4;                 //current index
+        auto l = -(dimension + 1);      //left index
+        auto r = dimension;             //right index
+        int acc[4] = {0, 0, 0, 0};      //sliding accumulator
+
+        //initial accumulation
+        for (int x = l; x < r; ++x) {
+            auto id = (_gaussianRemap(w, x, border) + p) * 4;
+            acc[0] += src[id++];
+            acc[1] += src[id++];
+            acc[2] += src[id++];
+            acc[3] += src[id];
+        }
+        //perform filtering
+        for (int x = 0; x < w; ++x, ++r, ++l) {
+            auto rid = (_gaussianRemap(w, r, border) + p) * 4;
+            auto lid = (_gaussianRemap(w, l, border) + p) * 4;
+            acc[0] += src[rid++] - src[lid++];
+            acc[1] += src[rid++] - src[lid++];
+            acc[2] += src[rid++] - src[lid++];
+            acc[3] += src[rid] - src[lid];
+            dst[i++] = static_cast<uint8_t>(acc[0] * iarr + 0.5f);
+            dst[i++] = static_cast<uint8_t>(acc[1] * iarr + 0.5f);
+            dst[i++] = static_cast<uint8_t>(acc[2] * iarr + 0.5f);
+            dst[i++] = static_cast<uint8_t>(acc[3] * iarr + 0.5f);
+        }
+    }
+}
+
+
+static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
+{
+    const auto MAX_LEVEL = SwGaussianBlur::MAX_LEVEL;
+
+    if (tvg::zero(sigma)) return 0;
+
+    data->level = int(SwGaussianBlur::MAX_LEVEL * ((quality - 1) * 0.01f)) + 1;
+
+    //compute box kernel sizes
+    auto wl = (int) sqrt((12 * sigma / MAX_LEVEL) + 1);
+    if (wl % 2 == 0) --wl;
+    auto wu = wl + 2;
+    auto mi = (12 * sigma - MAX_LEVEL * wl * wl - 4 * MAX_LEVEL * wl - 3 * MAX_LEVEL) / (-4 * wl - 4);
+    auto m = int(mi + 0.5f);
+    auto extends = 0;
+
+    for (int i = 0; i < data->level; i++) {
+        data->kernel[i] = ((i < m ? wl : wu) - 1) / 2;
+        extends += data->kernel[i];
+    }
+
+    return extends;
+}
+
+
+bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
+{
+    auto rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
+
+    auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
+
+    //invalid
+    if (extends == 0) {
+        params->invalid = true;
+        free(rd);
+        return false;
+    }
+
+    _gaussianExtendRegion(params->extend, extends, params->direction);
+
+    params->rd = rd;
+
+    return true;
+}
+
+
+bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params)
+{
+    if (cmp->image.channelSize != sizeof(uint32_t)) {
+        TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
+        return false;
+    }
+
+    auto& buffer = surface->compositor->image;
+    auto data = static_cast<SwGaussianBlur*>(params->rd);
+    auto& bbox = cmp->bbox;
+    auto w = (bbox.max.x - bbox.min.x);
+    auto h = (bbox.max.y - bbox.min.y);
+    auto stride = cmp->image.stride;
+    auto front = cmp->image.buf32;
+    auto back = buffer.buf32;
+    auto swapped = false;
+
+    TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
+
+    /* It is best to take advantage of the Gaussian blur’s separable property
+       by dividing the process into two passes. horizontal and vertical.
+       We can expect fewer calculations. */
+
+    //horizontal
+    if (params->direction != 2) {
+        for (int i = 0; i < data->level; ++i) {
+            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], params->border, false);
+            std::swap(front, back);
+            swapped = !swapped;
+        }
+    }
+
+    //vertical. x/y flipping and horionztal access is pretty compatible with the memory architecture.
+    if (params->direction != 1) {
+        rasterXYFlip(front, back, stride, w, h, bbox, false);
+        std::swap(front, back);
+
+        for (int i = 0; i < data->level; ++i) {
+            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, h, w, bbox, data->kernel[i], params->border, true);
+            std::swap(front, back);
+            swapped = !swapped;
+        }
+
+        rasterXYFlip(front, back, stride, h, w, bbox, true);
+        std::swap(front, back);
+    }
+
+    if (swapped) std::swap(cmp->image.buf8, buffer.buf8);
+
+    return true;
+}
+
+/************************************************************************/
+/* Drop Shadow Implementation                                           */
+/************************************************************************/
+
+struct SwDropShadow : SwGaussianBlur
+{
+    SwPoint offset;
+};
+
+
+//TODO: SIMD OPTIMIZATION?
+static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
+{
+    if (flipped) {
+        src += (bbox.min.x * stride + bbox.min.y);
+        dst += (bbox.min.x * stride + bbox.min.y);
+    } else {
+        src += (bbox.min.y * stride + bbox.min.x);
+        dst += (bbox.min.y * stride + bbox.min.x);
+    }
+    auto iarr = 1.0f / (dimension + dimension + 1);
+
+    #pragma omp parallel for
+    for (int y = 0; y < h; ++y) {
+        auto p = y * stride;
+        auto i = p;                     //current index
+        auto l = -(dimension + 1);      //left index
+        auto r = dimension;             //right index
+        int acc = 0;                    //sliding accumulator
+
+        //initial accumulation
+        for (int x = l; x < r; ++x) {
+            auto id = _gaussianEdgeExtend(w, x) + p;
+            acc += A(src[id]);
+        }
+        //perform filtering
+        for (int x = 0; x < w; ++x, ++r, ++l) {
+            auto rid = _gaussianEdgeExtend(w, r) + p;
+            auto lid = _gaussianEdgeExtend(w, l) + p;
+            acc += A(src[rid]) - A(src[lid]);
+            dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr + 0.5f));
+        }
+    }
+}
+
+
+static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
+{
+    src += (region.min.y * stride + region.min.x);
+    dst += (region.min.y * stride + region.min.x);
+
+    auto w = region.max.x - region.min.x;
+    auto h = region.max.y - region.min.y;
+    auto translucent = (direct || opacity < 255);
+
+    //shift offset
+    if (region.min.x + offset.x < 0) src -= offset.x;
+    else dst += offset.x;
+
+    if (region.min.y + offset.y < 0) src -= (offset.y * stride);
+    else dst += (offset.y * stride);
+
+    for (auto y = 0; y < h; ++y) {
+        if (translucent) rasterTranslucentPixel32(dst, src, w, opacity);
+        else rasterPixel32(dst, src, w, opacity);
+        src += stride;
+        dst += stride;
+    }
+}
+
+
+static void _dropShadowExtendRegion(RenderRegion& region, int extra, SwPoint& offset)
+{
+    //bbox region expansion for feathering
+    region.x = -extra;
+    region.w = extra * 2;
+    region.y = -extra;
+    region.h = extra * 2;
+
+    region.x = std::min(region.x + (int32_t)offset.x, region.x);
+    region.y = std::min(region.y + (int32_t)offset.y, region.y);
+    region.w += abs(offset.x);
+    region.h += abs(offset.y);
+}
+
+
+bool effectDropShadowPrepare(RenderEffectDropShadow* params)
+{
+    auto rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
+
+    //compute box kernel sizes
+    auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
+
+    //invalid
+    if (extends == 0 || params->color[3] == 0) {
+        params->invalid = true;
+        free(rd);
+        return false;
+    }
+
+    //offset
+    if (params->distance > 0.0f) {
+        auto radian = tvg::deg2rad(90.0f - params->angle);
+        rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))};
+    } else {
+        rd->offset = {0, 0};
+    }
+
+    //bbox region expansion for feathering
+    _dropShadowExtendRegion(params->extend, extends, rd->offset);
+
+    params->rd = rd;
+
+    return true;
+}
+
+
+//A quite same integration with effectGaussianBlur(). See it for detailed comments.
+//surface[0]: the original image, to overlay it into the filtered image.
+//surface[1]: temporary buffer for generating the filtered image.
+bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct)
+{
+    if (cmp->image.channelSize != sizeof(uint32_t)) {
+        TVGERR("SW_ENGINE", "Not supported grayscale Drop Shadow!");
+        return false;
+    }
+
+    //FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible.
+
+    auto data = static_cast<SwDropShadow*>(params->rd);
+    auto& bbox = cmp->bbox;
+    auto w = (bbox.max.x - bbox.min.x);
+    auto h = (bbox.max.y - bbox.min.y);
+
+    //outside the screen
+    if (abs(data->offset.x) >= w || abs(data->offset.y) >= h) return true;
+
+    SwImage* buffer[] = {&surface[0]->compositor->image, &surface[1]->compositor->image};
+    auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
+    auto stride = cmp->image.stride;
+    auto front = cmp->image.buf32;
+    auto back = buffer[1]->buf32;
+    opacity = MULTIPLY(params->color[3], opacity);
+
+    TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
+
+    //saving the original image in order to overlay it into the filtered image.
+    _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
+    std::swap(front, buffer[0]->buf32);
+    std::swap(front, back);
+
+    //horizontal
+    for (int i = 1; i < data->level; ++i) {
+        _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[i], color, false);
+        std::swap(front, back);
+    }
+
+    //vertical
+    rasterXYFlip(front, back, stride, w, h, bbox, false);
+    std::swap(front, back);
+
+    for (int i = 0; i < data->level; ++i) {
+        _dropShadowFilter(back, front, stride, h, w, bbox, data->kernel[i], color, true);
+        std::swap(front, back);
+    }
+
+    rasterXYFlip(front, back, stride, h, w, bbox, true);
+    std::swap(cmp->image.buf32, back);
+
+    //draw to the main surface directly
+    if (direct) {
+        _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
+        std::swap(cmp->image.buf32, buffer[0]->buf32);
+        return true;
+    }
+
+    //draw to the intermediate surface
+    rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h);
+    _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
+    std::swap(cmp->image.buf32, buffer[1]->buf32);
+
+    //compositing shadow and body
+    auto s = buffer[0]->buf32 + (bbox.min.y * buffer[0]->stride + bbox.min.x);
+    auto d = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
+
+    for (auto y = 0; y < h; ++y) {
+        rasterTranslucentPixel32(d, s, w, 255);
+        s += buffer[0]->stride;
+        d += cmp->image.stride;
+    }
+
+    return true;
+}

+ 127 - 329
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -490,7 +490,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
 /* Rle                                                                  */
 /* Rle                                                                  */
 /************************************************************************/
 /************************************************************************/
 
 
-static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
     auto cbuffer = surface->compositor->image.buf8;
     auto cbuffer = surface->compositor->image.buf8;
@@ -510,7 +510,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask
 }
 }
 
 
 
 
-static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
     auto cbuffer = surface->compositor->image.buf8;
     auto cbuffer = surface->compositor->image.buf8;
@@ -531,7 +531,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask ma
 }
 }
 
 
 
 
-static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
     TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
 
 
@@ -545,7 +545,7 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
 }
 }
 
 
 
 
-static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method);
     TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method);
 
 
@@ -568,10 +568,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
                 *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
                 *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
             }
             }
         }
         }
-        return true;
-    }
     //8bit grayscale
     //8bit grayscale
-    if (surface->channelSize == sizeof(uint8_t)) {
+    } else if (surface->channelSize == sizeof(uint8_t)) {
         uint8_t src;
         uint8_t src;
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             auto dst = &surface->buf8[span->y * surface->stride + span->x];
             auto dst = &surface->buf8[span->y * surface->stride + span->x];
@@ -582,13 +580,12 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
                 *dst = INTERPOLATE8(src, *dst, alpha(cmp));
                 *dst = INTERPOLATE8(src, *dst, alpha(cmp));
             }
             }
         }
         }
-        return true;
     }
     }
-    return false;
+    return true;
 }
 }
 
 
 
 
-static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     if (surface->channelSize != sizeof(uint32_t)) return false;
     if (surface->channelSize != sizeof(uint32_t)) return false;
 
 
@@ -612,7 +609,7 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t
 }
 }
 
 
 
 
-static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
 #if defined(THORVG_AVX_VECTOR_SUPPORT)
 #if defined(THORVG_AVX_VECTOR_SUPPORT)
     return avxRasterTranslucentRle(surface, rle, r, g, b, a);
     return avxRasterTranslucentRle(surface, rle, r, g, b, a);
@@ -624,7 +621,7 @@ static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint
 }
 }
 
 
 
 
-static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b)
+static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -661,7 +658,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r,
 }
 }
 
 
 
 
-static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     if (!rle) return false;
     if (!rle) return false;
 
 
@@ -697,66 +694,9 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
     auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
     auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
     if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \
     if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \
 
 
-
-#if 0 //Enable it when GRAYSCALE image is supported
-static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
-    auto sampleSize = _sampleSize(image->scale);
-    auto span = image->rle->spans;
-    int32_t miny = 0, maxy = 0;
-
-    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        SCALED_IMAGE_RANGE_Y(span->y)
-        auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
-        auto a = MULTIPLY(span->coverage, opacity);
-        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
-            SCALED_IMAGE_RANGE_X
-            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-            if (a < 255) src = MULTIPLY(src, a);
-            *cmp = maskOp(src, *cmp, ~src);
-        }
-    }
-    return true;
-}
-
-
-static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
-    auto sampleSize = _sampleSize(image->scale);
-    auto span = image->rle->spans;
-    int32_t miny = 0, maxy = 0;
-
-    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {        
-        SCALED_IMAGE_RANGE_Y(span->y)
-        auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
-        auto dst = &surface->buf8[span->y * surface->stride + span->x];
-        auto a = MULTIPLY(span->coverage, opacity);
-        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
-            SCALED_IMAGE_RANGE_X
-            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-            if (a < 255) src = MULTIPLY(src, a);
-            src = maskOp(src, *cmp, 0);  //not use alpha
-            *dst = src + MULTIPLY(*dst, ~src);
-        }
-    }
-    return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
-}
-#endif
-
 static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
 static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
 {
 {
-#if 0 //Enable it when GRAYSCALE image is supported
-    TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
-
-    //8bit masking channels composition
-    if (surface->channelSize != sizeof(uint8_t)) return false;
-
-    auto maskOp = _getMaskOp(surface->compositor->method);
-    if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
-    else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
-#endif
+    TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
     return false;
     return false;
 }
 }
 
 
@@ -784,7 +724,6 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
             *dst = src + ALPHA_BLEND(*dst, IA(src));
             *dst = src + ALPHA_BLEND(*dst, IA(src));
         }
         }
     }
     }
-
     return true;
     return true;
 }
 }
 
 
@@ -851,7 +790,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
 
 
     Matrix itransform;
     Matrix itransform;
 
 
-    if (!mathInverse(&transform, &itransform)) return true;
+    if (!inverse(&transform, &itransform)) return true;
 
 
     if (_compositing(surface)) {
     if (_compositing(surface)) {
         if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
         if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
@@ -869,75 +808,6 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
 /* RLE Direct Image                                                     */
 /* RLE Direct Image                                                     */
 /************************************************************************/
 /************************************************************************/
 
 
-#if 0 //Enable it when GRAYSCALE image is supported
-static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
-{
-    auto span = image->rle->spans;
-    auto cbuffer = surface->compositor->image.buf8;
-    auto ctride = surface->compositor->image.stride;
-
-    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
-        auto cmp = &cbuffer[span->y * ctride + span->x];
-        auto alpha = MULTIPLY(span->coverage, opacity);
-        if (alpha == 255) {
-            for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
-                *cmp = maskOp(*src, *cmp, ~*src);
-            }
-        } else {
-            for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
-                auto tmp = MULTIPLY(*src, alpha);
-                *cmp = maskOp(*src, *cmp, ~tmp);
-            }
-        }
-    }
-    return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
-}
-
-
-static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
-{
-    auto span = image->rle->spans;
-    auto cbuffer = surface->compositor->image.buf8;
-    auto ctride = surface->compositor->image.stride;
-
-    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
-        auto cmp = &cbuffer[span->y * ctride + span->x];
-        auto dst = &surface->buf8[span->y * surface->stride + span->x];
-        auto alpha = MULTIPLY(span->coverage, opacity);
-        if (alpha == 255) {
-            for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
-                auto tmp = maskOp(*src, *cmp, 0);  //not use alpha
-                *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
-            }
-        } else {
-            for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
-                auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha
-                *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
-            }
-        }
-    }
-    return true;
-}
-#endif
-
-static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
-{
-#if 0 //Enable it when GRAYSCALE image is supported
-    TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method);
-
-    //8bit masking channels composition
-    if (surface->channelSize != sizeof(uint8_t)) return false;
-
-    auto maskOp = _getMaskOp(surface->compositor->method);
-    if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity);
-    else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity);
-#endif
-    return false;
-}
-
-
 static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
 static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
 {
 {
     TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
     TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
@@ -999,21 +869,19 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
         auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
         auto alpha = MULTIPLY(span->coverage, opacity);
         auto alpha = MULTIPLY(span->coverage, opacity);
-        if (alpha == 255) {
-            for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
-                *dst = *img + ALPHA_BLEND(*dst, IA(*img));
-            }
-        } else {
-            for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
-                auto src = ALPHA_BLEND(*img, alpha);
-                *dst = src + ALPHA_BLEND(*dst, IA(src));
-            }
-        }
+        rasterTranslucentPixel32(dst, img, span->len, alpha);
     }
     }
     return true;
     return true;
 }
 }
 
 
 
 
+static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
+{
+    TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
+    return false;
+}
+
+
 static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
 static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
 {
 {
     if (surface->channelSize == sizeof(uint8_t)) {
     if (surface->channelSize == sizeof(uint8_t)) {
@@ -1037,67 +905,9 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
 /*Scaled Image                                                          */
 /*Scaled Image                                                          */
 /************************************************************************/
 /************************************************************************/
 
 
-#if 0 //Enable it when GRAYSCALE image is supported
-static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
-    auto sampleSize = _sampleSize(image->scale);
-    auto cstride = surface->compositor->image.stride;
-    auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
-    int32_t miny = 0, maxy = 0;
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        SCALED_IMAGE_RANGE_Y(y)
-        auto cmp = cbuffer;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
-            SCALED_IMAGE_RANGE_X
-            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-            if (opacity < 255) src = MULTIPLY(src, opacity);
-            *cmp = maskOp(src, *cmp, ~src);
-        }
-        cbuffer += cstride;
-    }
-    return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
-}
-
-
-static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
-    auto sampleSize = _sampleSize(image->scale);
-    auto cstride = surface->compositor->image.stride;
-    auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
-    auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
-    int32_t miny = 0, maxy = 0;
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        SCALED_IMAGE_RANGE_Y(y)
-        auto cmp = cbuffer;
-        auto dst = dbuffer;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
-            SCALED_IMAGE_RANGE_X
-            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-            if (opacity < 255) src = MULTIPLY(src, opacity);
-            src = maskOp(src, *cmp, 0);  //not use alpha
-            *dst = src + MULTIPLY(*dst, ~src);
-        }
-        cbuffer += cstride;
-        dbuffer += surface->stride;
-    }
-    return true;
-}
-#endif
-
 static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
 static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
 {
 {
-    TVGERR("SW_ENGINE", "Not supported ScaledMaskedImage!");
-#if 0 //Enable it when GRAYSCALE image is supported
-    TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
-
-    auto maskOp = _getMaskOp(surface->compositor->method);
-    if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
-    else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
-#endif
+    TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!");
     return false;
     return false;
 }
 }
 
 
@@ -1202,7 +1012,7 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix&
 {
 {
     Matrix itransform;
     Matrix itransform;
 
 
-    if (!mathInverse(&transform, &itransform)) return true;
+    if (!inverse(&transform, &itransform)) return true;
 
 
     if (_compositing(surface)) {
     if (_compositing(surface)) {
         if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
         if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
@@ -1220,78 +1030,9 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix&
 /* Direct Image                                                         */
 /* Direct Image                                                         */
 /************************************************************************/
 /************************************************************************/
 
 
-#if 0 //Enable it when GRAYSCALE image is supported
-static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto h = static_cast<uint32_t>(region.max.y - region.min.y);
-    auto w = static_cast<uint32_t>(region.max.x - region.min.x);
-    auto cstride = surface->compositor->image.stride;
-
-    auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
-    auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
-
-    for (uint32_t y = 0; y < h; ++y) {
-        auto cmp = cbuffer;
-        auto src = sbuffer;
-        if (opacity == 255) {
-            for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
-                *cmp = maskOp(*src, *cmp, ~*src);
-            }
-        } else {
-            for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
-                auto tmp = MULTIPLY(*src, opacity);
-                *cmp = maskOp(tmp, *cmp, ~tmp);
-            }
-        }
-        cbuffer += cstride;
-        sbuffer += image->stride;
-    }
-    return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
-}
-
-
-static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
-{
-    auto h = static_cast<uint32_t>(region.max.y - region.min.y);
-    auto w = static_cast<uint32_t>(region.max.x - region.min.x);
-    auto cstride = surface->compositor->image.stride;
-
-    auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
-    auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);            //destination buffer
-    auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
-
-    for (uint32_t y = 0; y < h; ++y) {
-        auto cmp = cbuffer;
-        auto dst = dbuffer;
-        auto src = sbuffer;
-        if (opacity == 255) {
-            for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
-                auto tmp = maskOp(*src, *cmp, 0); //not use alpha
-                *dst = tmp + MULTIPLY(*dst, ~tmp);
-            }
-        } else {
-            for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
-                auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha
-                *dst = tmp + MULTIPLY(*dst, ~tmp);
-            }
-        }
-        cbuffer += cstride;
-        dbuffer += surface->stride;
-        sbuffer += image->stride;
-    }
-    return true;
-}
-#endif
-
 static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
 static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
 {
 {
-    TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
-
-#if 0 //Enable it when GRAYSCALE image is supported
-    auto maskOp = _getMaskOp(surface->compositor->method);
-    if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity);
-    else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity);
-#endif
+    TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
     return false;
     return false;
 }
 }
 
 
@@ -1394,37 +1135,24 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
     //32bits channels
     //32bits channels
     if (surface->channelSize == sizeof(uint32_t)) {
     if (surface->channelSize == sizeof(uint32_t)) {
         auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
         auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
-
         for (auto y = region.min.y; y < region.max.y; ++y) {
         for (auto y = region.min.y; y < region.max.y; ++y) {
-            auto dst = dbuffer;
-            auto src = sbuffer;
-            if (opacity == 255) {
-                for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
-                    *dst = *src + ALPHA_BLEND(*dst, IA(*src));
-                }
-            } else {
-                for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
-                    auto tmp = ALPHA_BLEND(*src, opacity);
-                    *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
-                }
-            }
+            rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity);
             dbuffer += surface->stride;
             dbuffer += surface->stride;
             sbuffer += image->stride;
             sbuffer += image->stride;
         }
         }
     //8bits grayscale
     //8bits grayscale
     } else if (surface->channelSize == sizeof(uint8_t)) {
     } else if (surface->channelSize == sizeof(uint8_t)) {
         auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
         auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
-
         for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) {
         for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) {
             auto dst = dbuffer;
             auto dst = dbuffer;
             auto src = sbuffer;
             auto src = sbuffer;
             if (opacity == 255) {
             if (opacity == 255) {
                 for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
                 for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
-                    *dst = *src + MULTIPLY(*dst, ~*src);
+                    *dst = *src + MULTIPLY(*dst, IA(*src));
                 }
                 }
             } else {
             } else {
                 for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
                 for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
-                    *dst = INTERPOLATE8(*src, *dst, opacity);
+                    *dst = INTERPOLATE8(A(*src), *dst, opacity);
                 }
                 }
             }
             }
         }
         }
@@ -1602,13 +1330,23 @@ static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region
 template<typename fillMethod>
 template<typename fillMethod>
 static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 {
 {
-    auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
     auto w = static_cast<uint32_t>(region.max.x - region.min.x);
     auto w = static_cast<uint32_t>(region.max.x - region.min.x);
 
 
-    for (uint32_t y = 0; y < h; ++y) {
-        fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255);
-        buffer += surface->stride;
+    //32 bits
+    if (surface->channelSize == sizeof(uint32_t)) {
+        auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+        for (uint32_t y = 0; y < h; ++y) {
+            fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255);
+            buffer += surface->stride;
+        }
+    //8 bits
+    } else if (surface->channelSize == sizeof(uint8_t)) {
+        auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+        for (uint32_t y = 0; y < h; ++y) {
+            fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskAdd, 255);
+            buffer += surface->stride;
+        }
     }
     }
     return true;
     return true;
 }
 }
@@ -1617,12 +1355,23 @@ static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& reg
 template<typename fillMethod>
 template<typename fillMethod>
 static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 {
 {
-    auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
     auto w = static_cast<uint32_t>(region.max.x - region.min.x);
     auto w = static_cast<uint32_t>(region.max.x - region.min.x);
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
 
 
-    for (uint32_t y = 0; y < h; ++y) {
-        fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, 255);
+    //32 bits
+    if (surface->channelSize == sizeof(uint32_t)) {
+        auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+        for (uint32_t y = 0; y < h; ++y) {
+            fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendSrcOver, 255);
+            buffer += surface->stride;
+        }
+    //8 bits
+    } else if (surface->channelSize == sizeof(uint8_t)) {
+        auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+        for (uint32_t y = 0; y < h; ++y) {
+            fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskNone, 255);
+            buffer += surface->stride;
+        }
     }
     }
     return true;
     return true;
 }
 }
@@ -1663,7 +1412,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
 /************************************************************************/
 /************************************************************************/
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
+static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
     auto cstride = surface->compositor->image.stride;
     auto cstride = surface->compositor->image.stride;
@@ -1678,7 +1427,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleDat
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
+static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
     auto cstride = surface->compositor->image.stride;
     auto cstride = surface->compositor->image.stride;
@@ -1695,7 +1444,7 @@ static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData*
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     auto method = surface->compositor->method;
     auto method = surface->compositor->method;
 
 
@@ -1710,7 +1459,7 @@ static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, c
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterGradientMattedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method);
     TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method);
 
 
@@ -1729,7 +1478,7 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -1742,7 +1491,7 @@ static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle,
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -1757,7 +1506,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
     } else if (surface->channelSize == sizeof(uint8_t)) {
     } else if (surface->channelSize == sizeof(uint8_t)) {
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             auto dst = &surface->buf8[span->y * surface->stride + span->x];
             auto dst = &surface->buf8[span->y * surface->stride + span->x];
-            fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255);
+            fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage);
         }
         }
     }
     }
     return true;
     return true;
@@ -1765,7 +1514,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
 
 
 
 
 template<typename fillMethod>
 template<typename fillMethod>
-static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterSolidGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -1789,7 +1538,7 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co
 }
 }
 
 
 
 
-static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterLinearGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     if (!rle) return false;
     if (!rle) return false;
 
 
@@ -1806,7 +1555,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
 }
 }
 
 
 
 
-static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterRadialGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
 {
 {
     if (!rle) return false;
     if (!rle) return false;
 
 
@@ -1814,9 +1563,9 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
         if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
         if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
         else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
         else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
     } else if (_blending(surface)) {
     } else if (_blending(surface)) {
-        _rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
+        return _rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
     } else {
     } else {
-        if (fill->translucent) _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill);
+        if (fill->translucent) return _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill);
         else return _rasterSolidGradientRle<FillRadial>(surface, rle, fill);
         else return _rasterSolidGradientRle<FillRadial>(surface, rle, fill);
     }
     }
     return false;
     return false;
@@ -1827,6 +1576,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
+void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity)
+{
+    //TODO: Support SIMD accelerations
+    cRasterTranslucentPixels(dst, src, len, opacity);
+}
+
+
+void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity)
+{
+    //TODO: Support SIMD accelerations
+    cRasterPixels(dst, src, len, opacity);
+}
+
 
 
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
 {
 {
@@ -1905,7 +1667,7 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_
 }
 }
 
 
 
 
-void rasterUnpremultiply(Surface* surface)
+void rasterUnpremultiply(RenderSurface* surface)
 {
 {
     if (surface->channelSize != sizeof(uint32_t)) return;
     if (surface->channelSize != sizeof(uint32_t)) return;
 
 
@@ -1935,7 +1697,7 @@ void rasterUnpremultiply(Surface* surface)
 }
 }
 
 
 
 
-void rasterPremultiply(Surface* surface)
+void rasterPremultiply(RenderSurface* surface)
 {
 {
     ScopedLock lock(surface->key);
     ScopedLock lock(surface->key);
     if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return;
     if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return;
@@ -1965,13 +1727,13 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata,
         return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
         return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
     }
     }
 
 
-    auto id = fdata->identifier();
+    auto type = fdata->type();
     if (shape->fastTrack) {
     if (shape->fastTrack) {
-        if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
-        else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
+        if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
+        else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
     } else {
     } else {
-        if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
-        else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
+        if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
+        else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
     }
     }
     return false;
     return false;
 }
 }
@@ -1986,9 +1748,9 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata,
         return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true;
         return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true;
     }
     }
 
 
-    auto id = fdata->identifier();
-    if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
-    else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+    auto type = fdata->type();
+    if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+    else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
 
 
     return false;
     return false;
 }
 }
@@ -2027,12 +1789,12 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, co
 }
 }
 
 
 
 
-bool rasterConvertCS(Surface* surface, ColorSpace to)
+bool rasterConvertCS(RenderSurface* surface, ColorSpace to)
 {
 {
     ScopedLock lock(surface->key);
     ScopedLock lock(surface->key);
     if (surface->cs == to) return true;
     if (surface->cs == to) return true;
 
 
-    //TOOD: Support SIMD accelerations
+    //TODO: Support SIMD accelerations
     auto from = surface->cs;
     auto from = surface->cs;
 
 
     if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) {
     if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) {
@@ -2045,3 +1807,39 @@ bool rasterConvertCS(Surface* surface, ColorSpace to)
     }
     }
     return false;
     return false;
 }
 }
+
+
+//TODO: SIMD OPTIMIZATION?
+void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped)
+{
+    constexpr int BLOCK = 8;  //experimental decision
+
+    if (flipped) {
+        src += ((bbox.min.x * stride) + bbox.min.y);
+        dst += ((bbox.min.y * stride) + bbox.min.x);
+    } else {
+        src += ((bbox.min.y * stride) + bbox.min.x);
+        dst += ((bbox.min.x * stride) + bbox.min.y);
+    }
+
+    #pragma omp parallel for
+    for (int x = 0; x < w; x += BLOCK) {
+        auto bx = std::min(w, x + BLOCK) - x;
+        auto in = &src[x];
+        auto out = &dst[x * stride];
+        for (int y = 0; y < h; y += BLOCK) {
+            auto p = &in[y * stride];
+            auto q = &out[y];
+            auto by = std::min(h, y + BLOCK) - y;
+            for (int xx = 0; xx < bx; ++xx) {
+                for (int yy = 0; yy < by; ++yy) {
+                    *q = *p;
+                    p += stride;
+                    ++q;
+                }
+                p += 1 - by * stride;
+                q += stride - by;
+            }
+        }
+    }
+}

+ 2 - 2
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h

@@ -158,7 +158,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u
 }
 }
 
 
 
 
-static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -185,7 +185,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui
             }
             }
 
 
             //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
             //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
-            //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
+            //In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all
             uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
             uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
             uint32_t avxFilled = 0;
             uint32_t avxFilled = 0;
             if (iterations > 0) {
             if (iterations > 0) {

+ 35 - 3
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h

@@ -20,6 +20,38 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+
+template<typename PIXEL_T>
+static void inline cRasterTranslucentPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
+{
+    //TODO: 64bits faster?
+    if (opacity == 255) {
+        for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
+            *dst = *src + ALPHA_BLEND(*dst, IA(*src));
+        }
+    } else {
+        for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
+            auto tmp = ALPHA_BLEND(*src, opacity);
+            *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+        }
+    }
+}
+
+
+template<typename PIXEL_T>
+static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
+{
+    //TODO: 64bits faster?
+    if (opacity == 255) {
+        for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
+            *dst = *src;
+        }
+    } else {
+        cRasterTranslucentPixels(dst, src, len, opacity);
+    }
+}
+
+
 template<typename PIXEL_T>
 template<typename PIXEL_T>
 static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
 static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
 {
 {
@@ -60,7 +92,7 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
 }
 }
 
 
 
 
-static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 
@@ -125,7 +157,7 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
 }
 }
 
 
 
 
-static bool inline cRasterABGRtoARGB(Surface* surface)
+static bool inline cRasterABGRtoARGB(RenderSurface* surface)
 {
 {
     TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
     TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
 
 
@@ -156,7 +188,7 @@ static bool inline cRasterABGRtoARGB(Surface* surface)
 }
 }
 
 
 
 
-static bool inline cRasterARGBtoABGR(Surface* surface)
+static bool inline cRasterARGBtoABGR(RenderSurface* surface)
 {
 {
     //exactly same with ABGRtoARGB
     //exactly same with ABGRtoARGB
     return cRasterABGRtoARGB(surface);
     return cRasterABGRtoARGB(surface);

+ 1 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h

@@ -89,7 +89,7 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
 }
 }
 
 
 
 
-static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
 {
     auto span = rle->spans;
     auto span = rle->spans;
 
 

+ 17 - 4
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h

@@ -20,6 +20,17 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+struct Vertex
+{
+   Point pt;
+   Point uv;
+};
+
+struct Polygon
+{
+   Vertex vertex[3];
+};
+
 struct AALine
 struct AALine
 {
 {
    int32_t x[2];
    int32_t x[2];
@@ -53,10 +64,12 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
         regionBottom = image->rle->spans[image->rle->size - 1].y;
         regionBottom = image->rle->spans[image->rle->size - 1].y;
     }
     }
 
 
+    if (yStart >= regionBottom) return false;
+
     if (yStart < regionTop) yStart = regionTop;
     if (yStart < regionTop) yStart = regionTop;
     if (yEnd > regionBottom) yEnd = regionBottom;
     if (yEnd > regionBottom) yEnd = regionBottom;
 
 
-    return yEnd > yStart;
+    return true;
 }
 }
 
 
 
 
@@ -673,7 +686,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
     auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
     auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
 
 
     //Skip poly if it's an infinitely thin line
     //Skip poly if it's an infinitely thin line
-    if (mathZero(denom)) return;
+    if (tvg::zero(denom)) return;
 
 
     denom = 1 / denom;   //Reciprocal for speeding up
     denom = 1 / denom;   //Reciprocal for speeding up
     dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
     dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
@@ -689,8 +702,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
     //Determine which side of the polygon the longer edge is on
     //Determine which side of the polygon the longer edge is on
     auto side = (dxdy[1] > dxdy[0]) ? true : false;
     auto side = (dxdy[1] > dxdy[0]) ? true : false;
 
 
-    if (mathEqual(y[0], y[1])) side = x[0] > x[1];
-    if (mathEqual(y[1], y[2])) side = x[2] > x[1];
+    if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
+    if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
 
 
     auto regionTop = region ? region->min.y : image->rle->spans->y;  //Normal Image or Rle Image?
     auto regionTop = region ? region->min.y : image->rle->spans->y;  //Normal Image or Rle Image?
     auto compositing = _compositing(surface);   //Composition required
     auto compositing = _compositing(surface);   //Composition required

+ 124 - 79
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp

@@ -20,6 +20,9 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+#ifdef THORVG_SW_OPENMP_SUPPORT
+    #include <omp.h>
+#endif
 #include <algorithm>
 #include <algorithm>
 #include "tvgMath.h"
 #include "tvgMath.h"
 #include "tvgSwCommon.h"
 #include "tvgSwCommon.h"
@@ -38,7 +41,7 @@ struct SwTask : Task
 {
 {
     SwSurface* surface = nullptr;
     SwSurface* surface = nullptr;
     SwMpool* mpool = nullptr;
     SwMpool* mpool = nullptr;
-    SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
+    SwBBox bbox;                          //Rendering Region
     Matrix transform;
     Matrix transform;
     Array<RenderData> clips;
     Array<RenderData> clips;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
@@ -65,9 +68,7 @@ struct SwTask : Task
     }
     }
 
 
     virtual void dispose() = 0;
     virtual void dispose() = 0;
-    virtual bool clip(SwRleData* target) = 0;
-    virtual SwRleData* rle() = 0;
-
+    virtual bool clip(SwRle* target) = 0;
     virtual ~SwTask() {}
     virtual ~SwTask() {}
 };
 };
 
 
@@ -92,38 +93,34 @@ struct SwShapeTask : SwTask
         if (!rshape->stroke) return 0.0f;
         if (!rshape->stroke) return 0.0f;
 
 
         auto width = rshape->stroke->width;
         auto width = rshape->stroke->width;
-        if (mathZero(width)) return 0.0f;
+        if (tvg::zero(width)) return 0.0f;
 
 
         if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) 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 (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
 
 
         return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
         return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
     }
     }
 
 
-    bool clip(SwRleData* target) override
+    bool clip(SwRle* target) override
     {
     {
-        if (shape.fastTrack) rleClipRect(target, &bbox);
-        else if (shape.rle) rleClipPath(target, shape.rle);
+        if (shape.fastTrack) rleClip(target, &bbox);
+        else if (shape.rle) rleClip(target, shape.rle);
         else return false;
         else return false;
 
 
         return true;
         return true;
     }
     }
 
 
-    SwRleData* rle() override
-    {
-        if (!shape.rle && shape.fastTrack) {
-            shape.rle = rleRender(&shape.bbox);
-        }
-        return shape.rle;
-    }
-
     void run(unsigned tid) override
     void run(unsigned tid) override
     {
     {
-        if (opacity == 0 && !clipper) return;  //Invisible
+        //Invisible
+        if (opacity == 0 && !clipper) {
+            bbox.reset();
+            return;
+        }
 
 
         auto strokeWidth = validStrokeWidth();
         auto strokeWidth = validStrokeWidth();
-        bool visibleFill = false;
-        auto clipRegion = bbox;
+        SwBBox renderRegion{};
+        auto visibleFill = false;
 
 
         //This checks also for the case, if the invisible shape turned to visible by alpha.
         //This checks also for the case, if the invisible shape turned to visible by alpha.
         auto prepareShape = false;
         auto prepareShape = false;
@@ -135,10 +132,11 @@ struct SwShapeTask : SwTask
             rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
             rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
             alpha = MULTIPLY(alpha, opacity);
             alpha = MULTIPLY(alpha, opacity);
             visibleFill = (alpha > 0 || rshape->fill);
             visibleFill = (alpha > 0 || rshape->fill);
+            shapeReset(&shape);
             if (visibleFill || clipper) {
             if (visibleFill || clipper) {
-                shapeReset(&shape);
-                if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
+                if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
                     visibleFill = false;
                     visibleFill = false;
+                    renderRegion.reset();
                 }
                 }
             }
             }
         }
         }
@@ -159,8 +157,8 @@ struct SwShapeTask : SwTask
         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
             if (strokeWidth > 0.0f) {
             if (strokeWidth > 0.0f) {
                 shapeResetStroke(&shape, rshape, transform);
                 shapeResetStroke(&shape, rshape, transform);
-                if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
 
 
+                if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
                 if (auto fill = rshape->strokeFill()) {
                 if (auto fill = rshape->strokeFill()) {
                     auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
                     auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
                     if (ctable) shapeResetStrokeFill(&shape);
                     if (ctable) shapeResetStrokeFill(&shape);
@@ -184,9 +182,13 @@ struct SwShapeTask : SwTask
             //Clip stroke rle
             //Clip stroke rle
             if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
             if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
         }
         }
+
+        bbox = renderRegion; //sync
+
         return;
         return;
 
 
     err:
     err:
+        bbox.reset();
         shapeReset(&shape);
         shapeReset(&shape);
         shapeDelOutline(&shape, mpool, tid);
         shapeDelOutline(&shape, mpool, tid);
     }
     }
@@ -201,20 +203,14 @@ struct SwShapeTask : SwTask
 struct SwImageTask : SwTask
 struct SwImageTask : SwTask
 {
 {
     SwImage image;
     SwImage image;
-    Surface* source;                            //Image source
+    RenderSurface* source;                //Image source
 
 
-    bool clip(SwRleData* target) override
+    bool clip(SwRle* target) override
     {
     {
         TVGERR("SW_ENGINE", "Image is used as ClipPath?");
         TVGERR("SW_ENGINE", "Image is used as ClipPath?");
         return true;
         return true;
     }
     }
 
 
-    SwRleData* rle() override
-    {
-        TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
-        return nullptr;
-    }
-
     void run(unsigned tid) override
     void run(unsigned tid) override
     {
     {
         auto clipRegion = bbox;
         auto clipRegion = bbox;
@@ -452,27 +448,18 @@ bool SwRenderer::blend(BlendMethod method)
     surface->blendMethod = method;
     surface->blendMethod = method;
 
 
     switch (method) {
     switch (method) {
-        case BlendMethod::Add:
-            surface->blender = opBlendAdd;
-            break;
-        case BlendMethod::Screen:
-            surface->blender = opBlendScreen;
+        case BlendMethod::Normal:
+            surface->blender = nullptr;
             break;
             break;
         case BlendMethod::Multiply:
         case BlendMethod::Multiply:
             surface->blender = opBlendMultiply;
             surface->blender = opBlendMultiply;
             break;
             break;
+        case BlendMethod::Screen:
+            surface->blender = opBlendScreen;
+            break;
         case BlendMethod::Overlay:
         case BlendMethod::Overlay:
             surface->blender = opBlendOverlay;
             surface->blender = opBlendOverlay;
             break;
             break;
-        case BlendMethod::Difference:
-            surface->blender = opBlendDifference;
-            break;
-        case BlendMethod::Exclusion:
-            surface->blender = opBlendExclusion;
-            break;
-        case BlendMethod::SrcOver:
-            surface->blender = opBlendSrcOver;
-            break;
         case BlendMethod::Darken:
         case BlendMethod::Darken:
             surface->blender = opBlendDarken;
             surface->blender = opBlendDarken;
             break;
             break;
@@ -491,7 +478,17 @@ bool SwRenderer::blend(BlendMethod method)
         case BlendMethod::SoftLight:
         case BlendMethod::SoftLight:
             surface->blender = opBlendSoftLight;
             surface->blender = opBlendSoftLight;
             break;
             break;
+        case BlendMethod::Difference:
+            surface->blender = opBlendDifference;
+            break;
+        case BlendMethod::Exclusion:
+            surface->blender = opBlendExclusion;
+            break;
+        case BlendMethod::Add:
+            surface->blender = opBlendAdd;
+            break;
         default:
         default:
+            TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method);
             surface->blender = nullptr;
             surface->blender = nullptr;
             break;
             break;
     }
     }
@@ -505,7 +502,7 @@ RenderRegion SwRenderer::region(RenderData data)
 }
 }
 
 
 
 
-bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
+bool SwRenderer::beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity)
 {
 {
     if (!cmp) return false;
     if (!cmp) return false;
     auto p = static_cast<SwCompositor*>(cmp);
     auto p = static_cast<SwCompositor*>(cmp);
@@ -543,31 +540,19 @@ bool SwRenderer::mempool(bool shared)
 }
 }
 
 
 
 
-const Surface* SwRenderer::mainSurface()
+const RenderSurface* SwRenderer::mainSurface()
 {
 {
     return surface;
     return surface;
 }
 }
 
 
 
 
-Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
+SwSurface* SwRenderer::request(int channelSize)
 {
 {
-    auto x = region.x;
-    auto y = region.y;
-    auto w = region.w;
-    auto h = region.h;
-    auto sw = static_cast<int32_t>(surface->w);
-    auto sh = static_cast<int32_t>(surface->h);
-
-    //Out of boundary
-    if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
-
     SwSurface* cmp = nullptr;
     SwSurface* cmp = nullptr;
 
 
-    auto reqChannelSize = CHANNEL_SIZE(cs);
-
     //Use cached data
     //Use cached data
     for (auto p = compositors.begin(); p < compositors.end(); ++p) {
     for (auto p = compositors.begin(); p < compositors.end(); ++p) {
-        if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
+        if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) {
             cmp = *p;
             cmp = *p;
             break;
             break;
         }
         }
@@ -578,18 +563,48 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
         //Inherits attributes from main surface
         //Inherits attributes from main surface
         cmp = new SwSurface(surface);
         cmp = new SwSurface(surface);
         cmp->compositor = new SwCompositor;
         cmp->compositor = new SwCompositor;
-
-        //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h)
-        cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h);
-        cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize;
+        cmp->compositor->image.data = (pixel_t*)malloc(channelSize * surface->stride * surface->h);
+        cmp->compositor->image.w = surface->w;
+        cmp->compositor->image.h = surface->h;
+        cmp->compositor->image.stride = surface->stride;
+        cmp->compositor->image.direct = true;
+        cmp->compositor->valid = true;
+        cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
+        cmp->w = cmp->compositor->image.w;
+        cmp->h = cmp->compositor->image.h;
 
 
         compositors.push(cmp);
         compositors.push(cmp);
     }
     }
 
 
+    //Sync. This may have been modified by post-processing.
+    cmp->data = cmp->compositor->image.data;
+
+    return cmp;
+}
+
+
+RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
+{
+    auto x = region.x;
+    auto y = region.y;
+    auto w = region.w;
+    auto h = region.h;
+    auto sw = static_cast<int32_t>(surface->w);
+    auto sh = static_cast<int32_t>(surface->h);
+
+    //Out of boundary
+    if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
+
+    auto cmp = request(CHANNEL_SIZE(cs));
+
     //Boundary Check
     //Boundary Check
+    if (x < 0) x = 0;
+    if (y < 0) y = 0;
     if (x + w > sw) w = (sw - x);
     if (x + w > sw) w = (sw - x);
     if (y + h > sh) h = (sh - y);
     if (y + h > sh) h = (sh - y);
 
 
+    if (w == 0 || h == 0) return nullptr;
+
     cmp->compositor->recoverSfc = surface;
     cmp->compositor->recoverSfc = surface;
     cmp->compositor->recoverCmp = surface->compositor;
     cmp->compositor->recoverCmp = surface->compositor;
     cmp->compositor->valid = false;
     cmp->compositor->valid = false;
@@ -597,14 +612,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
     cmp->compositor->bbox.min.y = y;
     cmp->compositor->bbox.min.y = y;
     cmp->compositor->bbox.max.x = x + w;
     cmp->compositor->bbox.max.x = x + w;
     cmp->compositor->bbox.max.y = y + h;
     cmp->compositor->bbox.max.y = y + h;
-    cmp->compositor->image.stride = surface->stride;
-    cmp->compositor->image.w = surface->w;
-    cmp->compositor->image.h = surface->h;
-    cmp->compositor->image.direct = true;
-
-    cmp->data = cmp->compositor->image.data;
-    cmp->w = cmp->compositor->image.w;
-    cmp->h = cmp->compositor->image.h;
 
 
     /* TODO: Currently, only blending might work.
     /* TODO: Currently, only blending might work.
        Blending and composition must be handled together. */
        Blending and composition must be handled together. */
@@ -618,7 +625,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
 }
 }
 
 
 
 
-bool SwRenderer::endComposite(Compositor* cmp)
+bool SwRenderer::endComposite(RenderCompositor* cmp)
 {
 {
     if (!cmp) return false;
     if (!cmp) return false;
 
 
@@ -639,6 +646,40 @@ bool SwRenderer::endComposite(Compositor* cmp)
 }
 }
 
 
 
 
+bool SwRenderer::prepare(RenderEffect* effect)
+{
+    switch (effect->type) {
+        case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(effect));
+        case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect));
+        default: return false;
+    }
+}
+
+
+bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct)
+{
+    if (effect->invalid) return false;
+
+    auto p = static_cast<SwCompositor*>(cmp);
+
+    switch (effect->type) {
+        case SceneEffect::GaussianBlur: {
+            return effectGaussianBlur(p, request(surface->channelSize), static_cast<const RenderEffectGaussianBlur*>(effect));
+        }
+        case SceneEffect::DropShadow: {
+            auto cmp1 = request(surface->channelSize);
+            cmp1->compositor->valid = false;
+            auto cmp2 = request(surface->channelSize);
+            SwSurface* surfaces[] = {cmp1, cmp2};
+            auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), opacity, direct);
+            cmp1->compositor->valid = true;
+            return ret;
+        }
+        default: return false;
+    }
+}
+
+
 ColorSpace SwRenderer::colorSpace()
 ColorSpace SwRenderer::colorSpace()
 {
 {
     if (surface) return surface->cs;
     if (surface) return surface->cs;
@@ -681,10 +722,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
     task->surface = surface;
     task->surface = surface;
     task->mpool = mpool;
     task->mpool = mpool;
     task->flags = flags;
     task->flags = flags;
-    task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
-    task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
-    task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
-    task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
+    task->bbox.min.x = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
+    task->bbox.min.y = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
+    task->bbox.max.x = std::min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
+    task->bbox.max.y = std::min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
 
 
     if (!task->pushed) {
     if (!task->pushed) {
         task->pushed = true;
         task->pushed = true;
@@ -697,7 +738,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
 }
 }
 
 
 
 
-RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
 {
     //prepare task
     //prepare task
     auto task = static_cast<SwImageTask*>(data);
     auto task = static_cast<SwImageTask*>(data);
@@ -748,6 +789,10 @@ bool SwRenderer::init(uint32_t threads)
 
 
 int32_t SwRenderer::init()
 int32_t SwRenderer::init()
 {
 {
+#ifdef THORVG_SW_OPENMP_SUPPORT
+    omp_set_num_threads(TaskScheduler::threads());
+#endif
+
     return initEngineCnt;
     return initEngineCnt;
 }
 }
 
 

+ 9 - 5
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h

@@ -37,7 +37,7 @@ class SwRenderer : public RenderMethod
 {
 {
 public:
 public:
     RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
     RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
-    RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
+    RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
     bool preRender() override;
     bool preRender() override;
     bool renderShape(RenderData data) override;
     bool renderShape(RenderData data) override;
     bool renderImage(RenderData data) override;
     bool renderImage(RenderData data) override;
@@ -48,18 +48,21 @@ public:
     bool viewport(const RenderRegion& vp) override;
     bool viewport(const RenderRegion& vp) override;
     bool blend(BlendMethod method) override;
     bool blend(BlendMethod method) override;
     ColorSpace colorSpace() override;
     ColorSpace colorSpace() override;
-    const Surface* mainSurface() override;
+    const RenderSurface* mainSurface() override;
 
 
     bool clear() override;
     bool clear() override;
     bool sync() override;
     bool sync() override;
     bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
     bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
     bool mempool(bool shared);
     bool mempool(bool shared);
 
 
-    Compositor* target(const RenderRegion& region, ColorSpace cs) override;
-    bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override;
-    bool endComposite(Compositor* cmp) override;
+    RenderCompositor* target(const RenderRegion& region, ColorSpace cs) override;
+    bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
+    bool endComposite(RenderCompositor* cmp) override;
     void clearCompositors();
     void clearCompositors();
 
 
+    bool prepare(RenderEffect* effect) override;
+    bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override;
+
     static SwRenderer* gen();
     static SwRenderer* gen();
     static bool init(uint32_t threads);
     static bool init(uint32_t threads);
     static int32_t init();
     static int32_t init();
@@ -76,6 +79,7 @@ private:
     SwRenderer();
     SwRenderer();
     ~SwRenderer();
     ~SwRenderer();
 
 
+    SwSurface* request(int channelSize);
     RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
     RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
 };
 };
 
 

+ 136 - 121
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp

@@ -217,7 +217,7 @@ struct Cell
 
 
 struct RleWorker
 struct RleWorker
 {
 {
-    SwRleData* rle;
+    SwRle* rle;
 
 
     SwPoint cellPos;
     SwPoint cellPos;
     SwPoint cellMin;
     SwPoint cellMin;
@@ -235,6 +235,7 @@ struct RleWorker
     SwPoint pos;
     SwPoint pos;
 
 
     SwPoint bezStack[32 * 3 + 1];
     SwPoint bezStack[32 * 3 + 1];
+    SwPoint lineStack[32 + 1];
     int levStack[32];
     int levStack[32];
 
 
     SwOutline* outline;
     SwOutline* outline;
@@ -297,7 +298,7 @@ static inline SwCoord HYPOT(SwPoint pt)
 }
 }
 
 
 
 
-static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
+static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount)
 {
 {
     x += rw.cellMin.x;
     x += rw.cellMin.x;
     y += rw.cellMin.y;
     y += rw.cellMin.y;
@@ -341,11 +342,11 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
         if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
         if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
             //Clip x range
             //Clip x range
             SwCoord xOver = 0;
             SwCoord xOver = 0;
-            if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+            if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
             if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
             if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
 
 
-            //span->len += (acount + xOver) - 1;
-            span->len += (acount + xOver);
+            //span->len += (aCount + xOver) - 1;
+            span->len += (aCount + xOver);
             return;
             return;
         }
         }
     }
     }
@@ -361,20 +362,20 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
         
         
     //Clip x range
     //Clip x range
     SwCoord xOver = 0;
     SwCoord xOver = 0;
-    if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+    if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
     if (x < rw.cellMin.x) {
     if (x < rw.cellMin.x) {
         xOver -= (rw.cellMin.x - x);
         xOver -= (rw.cellMin.x - x);
         x = rw.cellMin.x;
         x = rw.cellMin.x;
     }
     }
 
 
     //Nothing to draw
     //Nothing to draw
-    if (acount + xOver <= 0) return;
+    if (aCount + xOver <= 0) return;
 
 
     //add a span to the current list
     //add a span to the current list
     auto span = rle->spans + rle->size;
     auto span = rle->spans + rle->size;
     span->x = x;
     span->x = x;
     span->y = y;
     span->y = y;
-    span->len = (acount + xOver);
+    span->len = (aCount + xOver);
     span->coverage = coverage;
     span->coverage = coverage;
     rle->size++;
     rle->size++;
 }
 }
@@ -513,98 +514,116 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
         return;
         return;
     }
     }
 
 
-    auto diff = to - rw.pos;
-    auto f1 = rw.pos - SUBPIXELS(e1);
-    SwPoint f2;
-
-    //inside one cell
-    if (e1 == e2) {
-        ;
-    //any horizontal line
-    } else if (diff.y == 0) {
-        e1.x = e2.x;
-        _setCell(rw, e1);
-    } else if (diff.x == 0) {
-        //vertical line up
-        if (diff.y > 0) {
-            do {
-                f2.y = ONE_PIXEL;
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * f1.x * 2;
-                f1.y = 0;
-                ++e1.y;
-                _setCell(rw, e1);
-            } while(e1.y != e2.y);
-        //vertical line down
+    auto line = rw.lineStack;
+    line[0] = to;
+    line[1] = rw.pos;
+
+    while (true) {
+        auto diff = line[0] - line[1];
+        auto L = HYPOT(diff);
+
+        if (L > SHRT_MAX) {
+            mathSplitLine(line);
+            ++line;
+            continue;
+        }
+        e1 = TRUNC(line[1]);
+        e2 = TRUNC(line[0]);
+
+        auto f1 = line[1] - SUBPIXELS(e1);
+        SwPoint f2;
+
+        //inside one cell
+        if (e1 == e2) {
+            ;
+        //any horizontal line
+        } else if (diff.y == 0) {
+            e1.x = e2.x;
+            _setCell(rw, e1);
+        } else if (diff.x == 0) {
+            //vertical line up
+            if (diff.y > 0) {
+                do {
+                    f2.y = ONE_PIXEL;
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * f1.x * 2;
+                    f1.y = 0;
+                    ++e1.y;
+                    _setCell(rw, e1);
+                } while(e1.y != e2.y);
+            //vertical line down
+            } else {
+                do {
+                    f2.y = 0;
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * f1.x * 2;
+                    f1.y = ONE_PIXEL;
+                    --e1.y;
+                    _setCell(rw, e1);
+                } while(e1.y != e2.y);
+            }
+        //any other line
         } else {
         } else {
+            Area prod = diff.x * f1.y - diff.y * f1.x;
+
+            /* These macros speed up repetitive divisions by replacing them
+               with multiplications and right shifts. */
+            auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
+            auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
+
+            /* The fundamental value `prod' determines which side and the  */
+            /* exact coordinate where the line exits current cell.  It is  */
+            /* also easily updated when moving from one cell to the next.  */
             do {
             do {
-                f2.y = 0;
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * f1.x * 2;
-                f1.y = ONE_PIXEL;
-                --e1.y;
+                auto px = diff.x * ONE_PIXEL;
+                auto py = diff.y * ONE_PIXEL;
+
+                //left
+                if (prod <= 0 && prod - px > 0) {
+                    f2 = {0, SW_UDIV(-prod, -dx_r)};
+                    prod -= py;
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+                    f1 = {ONE_PIXEL, f2.y};
+                    --e1.x;
+                //up
+                } else if (prod - px <= 0 && prod - px + py > 0) {
+                    prod -= px;
+                    f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+                    f1 = {f2.x, 0};
+                    ++e1.y;
+                //right
+                } else if (prod - px + py <= 0 && prod + py >= 0) {
+                    prod += py;
+                    f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+                    f1 = {0, f2.y};
+                    ++e1.x;
+                //down
+                } else {
+                    f2 = {SW_UDIV(prod, -dy_r), 0};
+                    prod += px;
+                    rw.cover += (f2.y - f1.y);
+                    rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+                    f1 = {f2.x, ONE_PIXEL};
+                    --e1.y;
+                }
+
                 _setCell(rw, e1);
                 _setCell(rw, e1);
-            } while(e1.y != e2.y);
+
+            } while(e1 != e2);
         }
         }
-    //any other line
-    } else {
-        Area prod = diff.x * f1.y - diff.y * f1.x;
-
-        /* These macros speed up repetitive divisions by replacing them
-           with multiplications and right shifts. */
-        auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
-        auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
-
-        /* The fundamental value `prod' determines which side and the  */
-        /* exact coordinate where the line exits current cell.  It is  */
-        /* also easily updated when moving from one cell to the next.  */
-        do {
-            auto px = diff.x * ONE_PIXEL;
-            auto py = diff.y * ONE_PIXEL;
-
-            //left
-            if (prod <= 0 && prod - px > 0) {
-                f2 = {0, SW_UDIV(-prod, -dx_r)};
-                prod -= py;
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * (f1.x + f2.x);
-                f1 = {ONE_PIXEL, f2.y};
-                --e1.x;
-            //up
-            } else if (prod - px <= 0 && prod - px + py > 0) {
-                prod -= px;
-                f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * (f1.x + f2.x);
-                f1 = {f2.x, 0};
-                ++e1.y;
-            //right
-            } else if (prod - px + py <= 0 && prod + py >= 0) {
-                prod += py;
-                f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * (f1.x + f2.x);
-                f1 = {0, f2.y};
-                ++e1.x;
-            //down
-            } else {
-                f2 = {SW_UDIV(prod, -dy_r), 0};
-                prod += px;
-                rw.cover += (f2.y - f1.y);
-                rw.area += (f2.y - f1.y) * (f1.x + f2.x);
-                f1 = {f2.x, ONE_PIXEL};
-                --e1.y;
-            }
 
 
-            _setCell(rw, e1);
+        f2 = {line[0].x - SUBPIXELS(e2.x), line[0].y - SUBPIXELS(e2.y)};
+        rw.cover += (f2.y - f1.y);
+        rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+        rw.pos = line[0];
 
 
-        } while(e1 != e2);
+        if (line-- == rw.lineStack) return;
     }
     }
-
-    f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
-    rw.cover += (f2.y - f1.y);
-    rw.area += (f2.y - f1.y) * (f1.x + f2.x);
-    rw.pos = to;
 }
 }
 
 
 
 
@@ -690,31 +709,27 @@ static void _decomposeOutline(RleWorker& rw)
         auto start = UPSCALE(outline->pts[first]);
         auto start = UPSCALE(outline->pts[first]);
         auto pt = outline->pts.data + first;
         auto pt = outline->pts.data + first;
         auto types = outline->types.data + first;
         auto types = outline->types.data + first;
+        ++types;
 
 
         _moveTo(rw, UPSCALE(outline->pts[first]));
         _moveTo(rw, UPSCALE(outline->pts[first]));
 
 
         while (pt < limit) {
         while (pt < limit) {
-            ++pt;
-            ++types;
-
             //emit a single line_to
             //emit a single line_to
             if (types[0] == SW_CURVE_TYPE_POINT) {
             if (types[0] == SW_CURVE_TYPE_POINT) {
+                ++pt;
+                ++types;
                 _lineTo(rw, UPSCALE(*pt));
                 _lineTo(rw, UPSCALE(*pt));
             //types cubic
             //types cubic
             } else {
             } else {
-                pt += 2;
-                types += 2;
-
-                if (pt <= limit) {
-                    _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
-                    continue;
-                }
-                _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
-                goto close;
+                pt += 3;
+                types += 3;
+                if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
+                else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
+                else goto close;
             }
             }
         }
         }
-        _lineTo(rw, start);
     close:
     close:
+        _lineTo(rw, start);
        first = last + 1;
        first = last + 1;
     }
     }
 }
 }
@@ -731,7 +746,7 @@ static int _genRle(RleWorker& rw)
 }
 }
 
 
 
 
-static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt)
+static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt)
 {
 {
     auto out = outSpans;
     auto out = outSpans;
     auto spans = target->spans;
     auto spans = target->spans;
@@ -740,7 +755,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
     auto clipEnd = clip->spans + clip->size;
     auto clipEnd = clip->spans + clip->size;
 
 
     while (spans < end && clipSpans < clipEnd) {
     while (spans < end && clipSpans < clipEnd) {
-        //align y cooridnates.
+        //align y-coordinates.
         if (clipSpans->y > spans->y) {
         if (clipSpans->y > spans->y) {
             ++spans;
             ++spans;
             continue;
             continue;
@@ -750,7 +765,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
             continue;
             continue;
         }
         }
 
 
-        //Try clipping with all clip spans which have a same y coordinate.
+        //Try clipping with all clip spans which have a same y-coordinate.
         auto temp = clipSpans;
         auto temp = clipSpans;
         while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
         while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
             auto sx1 = spans->x;
             auto sx1 = spans->x;
@@ -783,7 +798,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
 }
 }
 
 
 
 
-static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
+static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
 {
 {
     auto out = outSpans;
     auto out = outSpans;
     auto spans = targetRle->spans;
     auto spans = targetRle->spans;
@@ -822,7 +837,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
 }
 }
 
 
 
 
-void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
+void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
 {
 {
     free(rle->spans);
     free(rle->spans);
     rle->spans = clippedSpans;
     rle->spans = clippedSpans;
@@ -834,7 +849,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
+SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
 {
 {
     constexpr auto RENDER_POOL_SIZE = 16384L;
     constexpr auto RENDER_POOL_SIZE = 16384L;
     constexpr auto BAND_SIZE = 40;
     constexpr auto BAND_SIZE = 40;
@@ -862,7 +877,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
     rw.bandShoot = 0;
     rw.bandShoot = 0;
     rw.antiAlias = antiAlias;
     rw.antiAlias = antiAlias;
 
 
-    if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
+    if (!rle) rw.rle = reinterpret_cast<SwRle*>(calloc(1, sizeof(SwRle)));
     else rw.rle = rle;
     else rw.rle = rle;
 
 
     //Generate RLE
     //Generate RLE
@@ -953,12 +968,12 @@ error:
 }
 }
 
 
 
 
-SwRleData* rleRender(const SwBBox* bbox)
+SwRle* rleRender(const SwBBox* bbox)
 {
 {
     auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
     auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
     auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
     auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
 
 
-    auto rle = static_cast<SwRleData*>(malloc(sizeof(SwRleData)));
+    auto rle = static_cast<SwRle*>(malloc(sizeof(SwRle)));
     rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
     rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
     rle->size = height;
     rle->size = height;
     rle->alloc = height;
     rle->alloc = height;
@@ -975,14 +990,14 @@ SwRleData* rleRender(const SwBBox* bbox)
 }
 }
 
 
 
 
-void rleReset(SwRleData* rle)
+void rleReset(SwRle* rle)
 {
 {
     if (!rle) return;
     if (!rle) return;
     rle->size = 0;
     rle->size = 0;
 }
 }
 
 
 
 
-void rleFree(SwRleData* rle)
+void rleFree(SwRle* rle)
 {
 {
     if (!rle) return;
     if (!rle) return;
     if (rle->spans) free(rle->spans);
     if (rle->spans) free(rle->spans);
@@ -990,7 +1005,7 @@ void rleFree(SwRleData* rle)
 }
 }
 
 
 
 
-void rleClipPath(SwRleData *rle, const SwRleData *clip)
+void rleClip(SwRle *rle, const SwRle *clip)
 {
 {
     if (rle->size == 0 || clip->size == 0) return;
     if (rle->size == 0 || clip->size == 0) return;
     auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
     auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
@@ -999,11 +1014,11 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip)
 
 
     _replaceClipSpan(rle, spans, spansEnd - spans);
     _replaceClipSpan(rle, spans, spansEnd - spans);
 
 
-    TVGLOG("SW_ENGINE", "Using ClipPath!");
+    TVGLOG("SW_ENGINE", "Using Path Clipping!");
 }
 }
 
 
 
 
-void rleClipRect(SwRleData *rle, const SwBBox* clip)
+void rleClip(SwRle *rle, const SwBBox* clip)
 {
 {
     if (rle->size == 0) return;
     if (rle->size == 0) return;
     auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
     auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
@@ -1011,5 +1026,5 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip)
 
 
     _replaceClipSpan(rle, spans, spansEnd - spans);
     _replaceClipSpan(rle, spans, spansEnd - spans);
 
 
-    TVGLOG("SW_ENGINE", "Using ClipRect!");
+    TVGLOG("SW_ENGINE", "Using Box Clipping!");
 }
 }

+ 14 - 16
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -22,7 +22,6 @@
 
 
 #include "tvgSwCommon.h"
 #include "tvgSwCommon.h"
 #include "tvgMath.h"
 #include "tvgMath.h"
-#include "tvgLines.h"
 
 
 /************************************************************************/
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /* Internal Class Implementation                                        */
@@ -102,9 +101,9 @@ static bool _outlineClose(SwOutline& outline)
 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
 {
 {
     Line cur = {dash.ptCur, *to};
     Line cur = {dash.ptCur, *to};
-    auto len = lineLength(cur.pt1, cur.pt2);
+    auto len = cur.length();
 
 
-    if (mathZero(len)) {
+    if (tvg::zero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
     //draw the current line fully
     //draw the current line fully
     } else if (len <= dash.curLen) {
     } else if (len <= dash.curLen) {
@@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
             Line left, right;
             Line left, right;
             if (dash.curLen > 0) {
             if (dash.curLen > 0) {
                 len -= dash.curLen;
                 len -= dash.curLen;
-                lineSplitAt(cur, dash.curLen, left, right);
+                cur.split(dash.curLen, left, right);
                 if (!dash.curOpGap) {
                 if (!dash.curOpGap) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                         _outlineMoveTo(*dash.outline, &left.pt1, transform);
                         _outlineMoveTo(*dash.outline, &left.pt1, transform);
@@ -163,10 +162,10 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
 {
 {
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
-    auto len = bezLength(cur);
+    auto len = cur.length();
 
 
     //draw the current line fully
     //draw the current line fully
-    if (mathZero(len)) {
+    if (tvg::zero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
     } else if (len <= dash.curLen) {
     } else if (len <= dash.curLen) {
         dash.curLen -= len;
         dash.curLen -= len;
@@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
             Bezier left, right;
             Bezier left, right;
             if (dash.curLen > 0) {
             if (dash.curLen > 0) {
                 len -= dash.curLen;
                 len -= dash.curLen;
-                bezSplitAt(cur, dash.curLen, left, right);
+                cur.split(dash.curLen, left, right);
                 if (!dash.curOpGap) {
                 if (!dash.curOpGap) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                         _outlineMoveTo(*dash.outline, &left.start, transform);
                         _outlineMoveTo(*dash.outline, &left.start, transform);
@@ -284,7 +283,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
     if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
     if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
 
 
     const Point* close = nullptr;
     const Point* close = nullptr;
-    auto length = 0.0f;
+    auto len = 0.0f;
 
 
     //must begin with moveTo
     //must begin with moveTo
     if (cmds[0] == PathCommand::MoveTo) {
     if (cmds[0] == PathCommand::MoveTo) {
@@ -297,30 +296,30 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
     while (cmdCnt-- > 0) {
     while (cmdCnt-- > 0) {
         switch (*cmds) {
         switch (*cmds) {
             case PathCommand::Close: {
             case PathCommand::Close: {
-                length += mathLength(pts - 1, close);
-                if (subpath) return length;
+                len += length(pts - 1, close);
+                if (subpath) return len;
                 break;
                 break;
             }
             }
             case PathCommand::MoveTo: {
             case PathCommand::MoveTo: {
-                if (subpath) return length;
+                if (subpath) return len;
                 close = pts;
                 close = pts;
                 ++pts;
                 ++pts;
                 break;
                 break;
             }
             }
             case PathCommand::LineTo: {
             case PathCommand::LineTo: {
-                length += mathLength(pts - 1, pts);
+                len += length(pts - 1, pts);
                 ++pts;
                 ++pts;
                 break;
                 break;
             }
             }
             case PathCommand::CubicTo: {
             case PathCommand::CubicTo: {
-                length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
+                len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
                 pts += 3;
                 pts += 3;
                 break;
                 break;
             }
             }
         }
         }
         ++cmds;
         ++cmds;
     }
     }
-    return length;
+    return len;
 }
 }
 
 
 
 
@@ -355,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
     //offset
     //offset
     auto patternLength = 0.0f;
     auto patternLength = 0.0f;
     uint32_t offIdx = 0;
     uint32_t offIdx = 0;
-    if (!mathZero(offset)) {
+    if (!tvg::zero(offset)) {
         for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
         for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
         bool isOdd = dash.cnt % 2;
         bool isOdd = dash.cnt % 2;
         if (isOdd) patternLength *= 2;
         if (isOdd) patternLength *= 2;
@@ -499,7 +498,6 @@ bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& trans
     if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
     if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
     if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
     if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
 
 
-    //Keep it for Rasterization Region
     shape->bbox = renderRegion;
     shape->bbox = renderRegion;
 
 
     //Check valid region
     //Check valid region

+ 22 - 18
thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp

@@ -441,13 +441,23 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
         //initialize with current direction
         //initialize with current direction
         angleIn = angleOut = angleMid = stroke.angleIn;
         angleIn = angleOut = angleMid = stroke.angleIn;
 
 
-        if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
+        auto valid = mathCubicAngle(arc, angleIn, angleMid, angleOut);
+
+        //valid size
+        if (valid > 0 && arc < limit) {
             if (stroke.firstPt) stroke.angleIn = angleIn;
             if (stroke.firstPt) stroke.angleIn = angleIn;
             mathSplitCubic(arc);
             mathSplitCubic(arc);
             arc += 3;
             arc += 3;
             continue;
             continue;
         }
         }
 
 
+        //ignoreable size
+        if (valid < 0 && arc == bezStack) {
+            stroke.center = to;
+            return;
+        }
+
+        //small size
         if (firstArc) {
         if (firstArc) {
             firstArc = false;
             firstArc = false;
             //process corner if necessary
             //process corner if necessary
@@ -662,7 +672,7 @@ static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
     /* Determine if we need to check whether the border radius is greater
     /* Determine if we need to check whether the border radius is greater
        than the radius of curvature of a curve, to handle this case specially.
        than the radius of curvature of a curve, to handle this case specially.
        This is only required if bevel joins or butt caps may be created because
        This is only required if bevel joins or butt caps may be created because
-       round & miter joins and round & square caps cover the nagative sector
+       round & miter joins and round & square caps cover the negative sector
        created with wide strokes. */
        created with wide strokes. */
     if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
     if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
         stroke.handleWideStrokes = true;
         stroke.handleWideStrokes = true;
@@ -715,7 +725,7 @@ static void _endSubPath(SwStroke& stroke)
         _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
         _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
 
 
         /* now end the right subpath accordingly. The left one is rewind
         /* now end the right subpath accordingly. The left one is rewind
-           and deosn't need further processing */
+           and doesn't need further processing */
         _borderClose(right, false);
         _borderClose(right, false);
     }
     }
 }
 }
@@ -845,31 +855,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
 
 
         //A contour cannot start with a cubic control point
         //A contour cannot start with a cubic control point
         if (type == SW_CURVE_TYPE_CUBIC) return false;
         if (type == SW_CURVE_TYPE_CUBIC) return false;
+        ++types;
 
 
         auto closed =  outline.closed.data ? outline.closed.data[i]: false;
         auto closed =  outline.closed.data ? outline.closed.data[i]: false;
 
 
         _beginSubPath(*stroke, start, closed);
         _beginSubPath(*stroke, start, closed);
 
 
         while (pt < limit) {
         while (pt < limit) {
-            ++pt;
-            ++types;
-
-            //emit a signel line_to
+            //emit a single line_to
             if (types[0] == SW_CURVE_TYPE_POINT) {
             if (types[0] == SW_CURVE_TYPE_POINT) {
+                ++pt;
+                ++types;
                 _lineTo(*stroke, *pt);
                 _lineTo(*stroke, *pt);
             //types cubic
             //types cubic
             } else {
             } else {
-                if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
-
-                pt += 2;
-                types += 2;
-
-                if (pt <= limit) {
-                    _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
-                    continue;
-                }
-                _cubicTo(*stroke, pt[-2], pt[-1], start);
-                goto close;
+                pt += 3;
+                types += 3;
+                if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
+                else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start);
+                else goto close;
             }
             }
         }
         }
     close:
     close:

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgBinaryDesc.h

@@ -36,7 +36,7 @@ using TvgBinFlag = TvgBinByte;
 #define TVG_HEADER_SIZE 33                //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
 #define TVG_HEADER_SIZE 33                //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
 #define TVG_HEADER_SIGNATURE "ThorVG"
 #define TVG_HEADER_SIGNATURE "ThorVG"
 #define TVG_HEADER_SIGNATURE_LENGTH 6
 #define TVG_HEADER_SIGNATURE_LENGTH 6
-#define TVG_HEADER_VERSION "001200"       //Major 00, Minor 12, Micro 00
+#define TVG_HEADER_VERSION "001500"       //Major 00, Minor 15, Micro 00
 #define TVG_HEADER_VERSION_LENGTH 6
 #define TVG_HEADER_VERSION_LENGTH 6
 #define TVG_HEADER_RESERVED_LENGTH 1      //Storing flags for extensions
 #define TVG_HEADER_RESERVED_LENGTH 1      //Storing flags for extensions
 #define TVG_HEADER_COMPRESS_SIZE 12       //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
 #define TVG_HEADER_COMPRESS_SIZE 12       //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS

+ 8 - 8
thirdparty/thorvg/src/renderer/tvgCanvas.h

@@ -26,7 +26,7 @@
 #include "tvgPaint.h"
 #include "tvgPaint.h"
 
 
 
 
-enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
+enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged};
 
 
 struct Canvas::Impl
 struct Canvas::Impl
 {
 {
@@ -42,7 +42,7 @@ struct Canvas::Impl
 
 
     ~Impl()
     ~Impl()
     {
     {
-        //make it sure any deffered jobs
+        //make it sure any deferred jobs
         renderer->sync();
         renderer->sync();
         renderer->clear();
         renderer->clear();
 
 
@@ -61,7 +61,7 @@ struct Canvas::Impl
 
 
     Result push(unique_ptr<Paint> paint)
     Result push(unique_ptr<Paint> paint)
     {
     {
-        //You can not push paints during rendering.
+        //You cannot push paints during rendering.
         if (status == Status::Drawing) return Result::InsufficientCondition;
         if (status == Status::Drawing) return Result::InsufficientCondition;
 
 
         auto p = paint.release();
         auto p = paint.release();
@@ -91,7 +91,7 @@ struct Canvas::Impl
 
 
         Array<RenderData> clips;
         Array<RenderData> clips;
         auto flag = RenderUpdateFlag::None;
         auto flag = RenderUpdateFlag::None;
-        if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
+        if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
 
 
         auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
         auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
 
 
@@ -108,7 +108,7 @@ struct Canvas::Impl
 
 
     Result draw()
     Result draw()
     {
     {
-        if (status == Status::Damanged) update(nullptr, false);
+        if (status == Status::Damaged) update(nullptr, false);
         if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
         if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
 
 
         bool rendered = false;
         bool rendered = false;
@@ -124,7 +124,7 @@ struct Canvas::Impl
 
 
     Result sync()
     Result sync()
     {
     {
-        if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition;
+        if (status == Status::Synced || status == Status::Damaged) return Result::InsufficientCondition;
 
 
         if (renderer->sync()) {
         if (renderer->sync()) {
             status = Status::Synced;
             status = Status::Synced;
@@ -136,7 +136,7 @@ struct Canvas::Impl
 
 
     Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
     Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
     {
     {
-        if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition;
+        if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition;
 
 
         RenderRegion val = {x, y, w, h};
         RenderRegion val = {x, y, w, h};
         //intersect if the target buffer is already set.
         //intersect if the target buffer is already set.
@@ -147,7 +147,7 @@ struct Canvas::Impl
         if (vport == val) return Result::Success;
         if (vport == val) return Result::Success;
         renderer->viewport(val);
         renderer->viewport(val);
         vport = val;
         vport = val;
-        status = Status::Damanged;
+        status = Status::Damaged;
         return Result::Success;
         return Result::Success;
     }
     }
 };
 };

+ 0 - 9
thirdparty/thorvg/src/renderer/tvgCommon.h

@@ -54,15 +54,6 @@ using namespace tvg;
     #define strdup _strdup
     #define strdup _strdup
 #endif
 #endif
 
 
-//TVG class identifier values
-#define TVG_CLASS_ID_UNDEFINED 0
-#define TVG_CLASS_ID_SHAPE     1
-#define TVG_CLASS_ID_SCENE     2
-#define TVG_CLASS_ID_PICTURE   3
-#define TVG_CLASS_ID_LINEAR    4
-#define TVG_CLASS_ID_RADIAL    5
-#define TVG_CLASS_ID_TEXT      6
-
 enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
 enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
 
 
 using Size = Point;
 using Size = Point;

+ 17 - 8
thirdparty/thorvg/src/renderer/tvgFill.cpp

@@ -155,15 +155,14 @@ Fill* Fill::duplicate() const noexcept
 }
 }
 
 
 
 
-uint32_t Fill::identifier() const noexcept
+TVG_DEPRECATED uint32_t Fill::identifier() const noexcept
 {
 {
-    return pImpl->id;
+    return (uint32_t) type();
 }
 }
 
 
 
 
 RadialGradient::RadialGradient():pImpl(new Impl())
 RadialGradient::RadialGradient():pImpl(new Impl())
 {
 {
-    Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
     Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
     Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
 }
 }
 
 
@@ -196,15 +195,20 @@ unique_ptr<RadialGradient> RadialGradient::gen() noexcept
 }
 }
 
 
 
 
-uint32_t RadialGradient::identifier() noexcept
+TVG_DEPRECATED uint32_t RadialGradient::identifier() noexcept
 {
 {
-    return TVG_CLASS_ID_RADIAL;
+    return (uint32_t) Type::RadialGradient;
+}
+
+
+Type RadialGradient::type() const noexcept
+{
+    return Type::RadialGradient;
 }
 }
 
 
 
 
 LinearGradient::LinearGradient():pImpl(new Impl())
 LinearGradient::LinearGradient():pImpl(new Impl())
 {
 {
-    Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
     Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
     Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
 }
 }
 
 
@@ -243,8 +247,13 @@ unique_ptr<LinearGradient> LinearGradient::gen() noexcept
 }
 }
 
 
 
 
-uint32_t LinearGradient::identifier() noexcept
+TVG_DEPRECATED uint32_t LinearGradient::identifier() noexcept
 {
 {
-    return TVG_CLASS_ID_LINEAR;
+    return (uint32_t) Type::LinearGradient;
 }
 }
 
 
+
+Type LinearGradient::type() const noexcept
+{
+    return Type::LinearGradient;
+}

+ 0 - 1
thirdparty/thorvg/src/renderer/tvgFill.h

@@ -55,7 +55,6 @@ struct Fill::Impl
     uint32_t cnt = 0;
     uint32_t cnt = 0;
     FillSpread spread;
     FillSpread spread;
     DuplicateMethod<Fill>* dup = nullptr;
     DuplicateMethod<Fill>* dup = nullptr;
-    uint8_t id;
 
 
     ~Impl()
     ~Impl()
     {
     {

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp

@@ -62,7 +62,7 @@ GlCanvas::~GlCanvas()
 Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
 Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
 {
 {
 #ifdef THORVG_GL_RASTER_SUPPORT
 #ifdef THORVG_GL_RASTER_SUPPORT
-    if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
+    if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;
     }
     }
 
 
@@ -75,7 +75,7 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
     renderer->viewport(Canvas::pImpl->vport);
     renderer->viewport(Canvas::pImpl->vport);
 
 
     //Paints must be updated again with this new target.
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
 
     return Result::Success;
     return Result::Success;
 #endif
 #endif

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgLoadModule.h

@@ -80,14 +80,14 @@ struct ImageLoader : LoadModule
     static ColorSpace cs;                           //desired value
     static ColorSpace cs;                           //desired value
 
 
     float w = 0, h = 0;                             //default image size
     float w = 0, h = 0;                             //default image size
-    Surface surface;
+    RenderSurface surface;
 
 
     ImageLoader(FileType type) : LoadModule(type) {}
     ImageLoader(FileType type) : LoadModule(type) {}
 
 
     virtual bool animatable() { return false; }  //true if this loader supports animation.
     virtual bool animatable() { return false; }  //true if this loader supports animation.
     virtual Paint* paint() { return nullptr; }
     virtual Paint* paint() { return nullptr; }
 
 
-    virtual Surface* bitmap()
+    virtual RenderSurface* bitmap()
     {
     {
         if (surface.data) return &surface;
         if (surface.data) return &surface;
         return nullptr;
         return nullptr;

+ 4 - 4
thirdparty/thorvg/src/renderer/tvgLoader.cpp

@@ -294,10 +294,10 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
 {
 {
     *invalid = false;
     *invalid = false;
 
 
-    //TODO: lottie is not sharable.
+    //TODO: svg & lottie is not sharable.
     auto allowCache = true;
     auto allowCache = true;
     auto ext = path.substr(path.find_last_of(".") + 1);
     auto ext = path.substr(path.find_last_of(".") + 1);
-    if (!ext.compare("json")) allowCache = false;
+    if (!ext.compare("svg") || !ext.compare("json")) allowCache = false;
 
 
     if (allowCache) {
     if (allowCache) {
         if (auto loader = _findFromCache(path)) return loader;
         if (auto loader = _findFromCache(path)) return loader;
@@ -317,7 +317,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
         }
         }
         delete(loader);
         delete(loader);
     }
     }
-    //Unkown MimeType. Try with the candidates in the order
+    //Unknown MimeType. Try with the candidates in the order
     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
         if (auto loader = _find(static_cast<FileType>(i))) {
         if (auto loader = _find(static_cast<FileType>(i))) {
             if (loader->open(path)) {
             if (loader->open(path)) {
@@ -392,7 +392,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
             }
             }
         }
         }
     }
     }
-    //Unkown MimeType. Try with the candidates in the order
+    //Unknown MimeType. Try with the candidates in the order
     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
         auto loader = _find(static_cast<FileType>(i));
         auto loader = _find(static_cast<FileType>(i));
         if (loader) {
         if (loader) {

+ 80 - 55
thirdparty/thorvg/src/renderer/tvgPaint.cpp

@@ -32,11 +32,11 @@
 /************************************************************************/
 /************************************************************************/
 
 
 #define PAINT_METHOD(ret, METHOD) \
 #define PAINT_METHOD(ret, METHOD) \
-    switch (id) { \
-        case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
-        case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
-        case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
-        case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \
+    switch (paint->type()) { \
+        case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \
+        case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \
+        case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \
+        case Type::Text: ret = P((Text*)paint)->METHOD; break; \
         default: ret = {}; \
         default: ret = {}; \
     }
     }
 
 
@@ -91,8 +91,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
     //No rotation and no skewing, still can try out clipping the rect region.
     //No rotation and no skewing, still can try out clipping the rect region.
     auto tryClip = false;
     auto tryClip = false;
 
 
-    if ((!mathRightAngle(pm) || mathSkewed(pm))) tryClip = true;
-    if ((!mathRightAngle(rm) || mathSkewed(rm))) tryClip = true;
+    if ((!rightAngle(pm) || skewed(pm))) tryClip = true;
+    if ((!rightAngle(rm) || skewed(rm))) tryClip = true;
 
 
     if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
     if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
 
 
@@ -102,8 +102,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
     auto pt3 = pts + 2;
     auto pt3 = pts + 2;
     auto pt4 = pts + 3;
     auto pt4 = pts + 3;
 
 
-    if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
-        (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
+    if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) ||
+        (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) {
 
 
         RenderRegion after;
         RenderRegion after;
 
 
@@ -164,6 +164,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
     ret->pImpl->opacity = opacity;
     ret->pImpl->opacity = opacity;
 
 
     if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
     if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
+    if (clipper) ret->pImpl->clip(clipper->duplicate());
 
 
     return ret;
     return ret;
 }
 }
@@ -172,7 +173,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
 bool Paint::Impl::rotate(float degree)
 bool Paint::Impl::rotate(float degree)
 {
 {
     if (tr.overriding) return false;
     if (tr.overriding) return false;
-    if (mathEqual(degree, tr.degree)) return true;
+    if (tvg::equal(degree, tr.degree)) return true;
     tr.degree = degree;
     tr.degree = degree;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
 
 
@@ -183,7 +184,7 @@ bool Paint::Impl::rotate(float degree)
 bool Paint::Impl::scale(float factor)
 bool Paint::Impl::scale(float factor)
 {
 {
     if (tr.overriding) return false;
     if (tr.overriding) return false;
-    if (mathEqual(factor, tr.scale)) return true;
+    if (tvg::equal(factor, tr.scale)) return true;
     tr.scale = factor;
     tr.scale = factor;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
 
 
@@ -194,7 +195,7 @@ bool Paint::Impl::scale(float factor)
 bool Paint::Impl::translate(float x, float y)
 bool Paint::Impl::translate(float x, float y)
 {
 {
     if (tr.overriding) return false;
     if (tr.overriding) return false;
-    if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true;
+    if (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true;
     tr.m.e13 = x;
     tr.m.e13 = x;
     tr.m.e23 = y;
     tr.m.e23 = y;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
@@ -207,11 +208,9 @@ bool Paint::Impl::render(RenderMethod* renderer)
 {
 {
     if (opacity == 0) return true;
     if (opacity == 0) return true;
 
 
-    Compositor* cmp = nullptr;
+    RenderCompositor* cmp = nullptr;
 
 
-    /* Note: only ClipPath is processed in update() step.
-        Create a composition image. */
-    if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
+    if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
         RenderRegion region;
         RenderRegion region;
         PAINT_METHOD(region, bounds(renderer));
         PAINT_METHOD(region, bounds(renderer));
 
 
@@ -248,43 +247,49 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
     RenderData trd = nullptr;                 //composite target render data
     RenderData trd = nullptr;                 //composite target render data
     RenderRegion viewport;
     RenderRegion viewport;
     Result compFastTrack = Result::InsufficientCondition;
     Result compFastTrack = Result::InsufficientCondition;
-    bool childClipper = false;
 
 
     if (compData) {
     if (compData) {
         auto target = compData->target;
         auto target = compData->target;
         auto method = compData->method;
         auto method = compData->method;
         P(target)->ctxFlag &= ~ContextFlag::FastTrack;   //reset
         P(target)->ctxFlag &= ~ContextFlag::FastTrack;   //reset
 
 
-        /* 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;
-            else {
-                auto shape = static_cast<Shape*>(target);
-                uint8_t a;
-                shape->fillColor(nullptr, nullptr, nullptr, &a);
-                //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) {
-                viewport = renderer->viewport();
-                if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
-                    P(target)->ctxFlag |= ContextFlag::FastTrack;
+        /* If the transformation has no rotational factors and the Alpha(InvAlpha)Masking involves a simple rectangle,
+           we can optimize by using the viewport instead of the regular AlphaMasking sequence for improved performance. */
+        if (target->type() == Type::Shape) {
+            auto shape = static_cast<Shape*>(target);
+            uint8_t a;
+            shape->fillColor(nullptr, nullptr, nullptr, &a);
+            //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) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) {
+                    viewport = renderer->viewport();
+                    if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
+                        P(target)->ctxFlag |= ContextFlag::FastTrack;
+                    }
                 }
                 }
             }
             }
         }
         }
         if (compFastTrack == Result::InsufficientCondition) {
         if (compFastTrack == Result::InsufficientCondition) {
-            childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
-            trd = P(target)->update(renderer, pm, clips, 255, pFlag, childClipper);
-            if (childClipper) clips.push(trd);
+            trd = P(target)->update(renderer, pm, clips, 255, pFlag, false);
+        }
+    }
+
+    /* 2. Clipping */
+    if (this->clipper) {
+        P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack;   //reset
+        viewport = renderer->viewport();
+        /* TODO: Intersect the clipper's clipper, if both are FastTrack.
+           Update the subsequent clipper first and check its ctxFlag. */
+        if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
+            P(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
+        }
+        if (compFastTrack == Result::InsufficientCondition) {
+            trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true);
+            clips.push(trd);
         }
         }
     }
     }
 
 
-    /* 2. Main Update */
+    /* 3. Main Update */
     auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
     auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
     renderFlag = RenderUpdateFlag::None;
     renderFlag = RenderUpdateFlag::None;
     opacity = MULTIPLY(opacity, this->opacity);
     opacity = MULTIPLY(opacity, this->opacity);
@@ -294,9 +299,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
     tr.cm = pm * tr.m;
     tr.cm = pm * tr.m;
     PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
     PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
 
 
-    /* 3. Composition Post Processing */
+    /* 4. Composition Post Processing */
     if (compFastTrack == Result::Success) renderer->viewport(viewport);
     if (compFastTrack == Result::Success) renderer->viewport(viewport);
-    else if (childClipper) clips.pop();
+    else if (this->clipper) clips.pop();
 
 
     return rd;
     return rd;
 }
 }
@@ -308,7 +313,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
     const auto& m = this->transform(origin);
     const auto& m = this->transform(origin);
 
 
     //Case: No transformed, quick return!
     //Case: No transformed, quick return!
-    if (!transformed || mathIdentity(&m)) {
+    if (!transformed || identity(&m)) {
         PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
         PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
         return ret;
         return ret;
     }
     }
@@ -351,12 +356,18 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
 
 
 void Paint::Impl::reset()
 void Paint::Impl::reset()
 {
 {
+    if (clipper) {
+        delete(clipper);
+        clipper = nullptr;
+    }
+
     if (compData) {
     if (compData) {
         if (P(compData->target)->unref() == 0) delete(compData->target);
         if (P(compData->target)->unref() == 0) delete(compData->target);
         free(compData);
         free(compData);
         compData = nullptr;
         compData = nullptr;
     }
     }
-    mathIdentity(&tr.m);
+
+    tvg::identity(&tr.m);
     tr.degree = 0.0f;
     tr.degree = 0.0f;
     tr.scale = 1.0f;
     tr.scale = 1.0f;
     tr.overriding = false;
     tr.overriding = false;
@@ -437,15 +448,27 @@ Paint* Paint::duplicate() const noexcept
 }
 }
 
 
 
 
-Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
+Result Paint::clip(std::unique_ptr<Paint> clipper) noexcept
 {
 {
-    if (method == CompositeMethod::ClipPath && target && target->identifier() != TVG_CLASS_ID_SHAPE) {
-        TVGERR("RENDERER", "ClipPath only allows the Shape!");
+    auto p = clipper.release();
+
+    if (p && p->type() != Type::Shape) {
+        TVGERR("RENDERER", "Clipping only supports the Shape!");
         return Result::NonSupport;
         return Result::NonSupport;
     }
     }
+    pImpl->clip(p);
+    return Result::Success;
+}
+
+
+Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
+{
+    //TODO: remove. Keep this for the backward compatibility
+    if (target && method == CompositeMethod::ClipPath) return clip(std::move(target));
 
 
     auto p = target.release();
     auto p = target.release();
     if (pImpl->composite(this, p, method)) return Result::Success;
     if (pImpl->composite(this, p, method)) return Result::Success;
+
     delete(p);
     delete(p);
     return Result::InvalidArguments;
     return Result::InvalidArguments;
 }
 }
@@ -457,6 +480,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept
         if (target) *target = pImpl->compData->target;
         if (target) *target = pImpl->compData->target;
         return pImpl->compData->method;
         return pImpl->compData->method;
     } else {
     } else {
+        //TODO: remove. Keep this for the backward compatibility
+        if (pImpl->clipper) {
+            if (target) *target = pImpl->clipper;
+            return CompositeMethod::ClipPath;
+        }
         if (target) *target = nullptr;
         if (target) *target = nullptr;
         return CompositeMethod::None;
         return CompositeMethod::None;
     }
     }
@@ -480,14 +508,17 @@ uint8_t Paint::opacity() const noexcept
 }
 }
 
 
 
 
-uint32_t Paint::identifier() const noexcept
+TVG_DEPRECATED uint32_t Paint::identifier() const noexcept
 {
 {
-    return pImpl->id;
+    return (uint32_t) type();
 }
 }
 
 
 
 
 Result Paint::blend(BlendMethod method) noexcept
 Result Paint::blend(BlendMethod method) noexcept
 {
 {
+    //TODO: Remove later
+    if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport;
+
     if (pImpl->blendMethod != method) {
     if (pImpl->blendMethod != method) {
         pImpl->blendMethod = method;
         pImpl->blendMethod = method;
         pImpl->renderFlag |= RenderUpdateFlag::Blend;
         pImpl->renderFlag |= RenderUpdateFlag::Blend;
@@ -495,9 +526,3 @@ Result Paint::blend(BlendMethod method) noexcept
 
 
     return Result::Success;
     return Result::Success;
 }
 }
-
-
-BlendMethod Paint::blend() const noexcept
-{
-    return pImpl->blendMethod;
-}

+ 19 - 4
thirdparty/thorvg/src/renderer/tvgPaint.h

@@ -49,6 +49,7 @@ namespace tvg
     {
     {
         Paint* paint = nullptr;
         Paint* paint = nullptr;
         Composite* compData = nullptr;
         Composite* compData = nullptr;
+        Paint* clipper = nullptr;
         RenderMethod* renderer = nullptr;
         RenderMethod* renderer = nullptr;
         struct {
         struct {
             Matrix m;                 //input matrix
             Matrix m;                 //input matrix
@@ -67,8 +68,8 @@ namespace tvg
                 m.e31 = 0.0f;
                 m.e31 = 0.0f;
                 m.e32 = 0.0f;
                 m.e32 = 0.0f;
                 m.e33 = 1.0f;
                 m.e33 = 1.0f;
-                mathScale(&m, scale, scale);
-                mathRotate(&m, degree);
+                tvg::scale(&m, scale, scale);
+                tvg::rotate(&m, degree);
             }
             }
         } tr;
         } tr;
         BlendMethod blendMethod;
         BlendMethod blendMethod;
@@ -76,7 +77,6 @@ namespace tvg
         uint8_t ctxFlag;
         uint8_t ctxFlag;
         uint8_t opacity;
         uint8_t opacity;
         uint8_t refCnt = 0;                              //reference count
         uint8_t refCnt = 0;                              //reference count
-        uint8_t id;         //TODO: deprecated, remove it
 
 
         Impl(Paint* pnt) : paint(pnt)
         Impl(Paint* pnt) : paint(pnt)
         {
         {
@@ -89,6 +89,7 @@ namespace tvg
                 if (P(compData->target)->unref() == 0) delete(compData->target);
                 if (P(compData->target)->unref() == 0) delete(compData->target);
                 free(compData);
                 free(compData);
             }
             }
+            if (clipper && P(clipper)->unref() == 0) delete(clipper);
             if (renderer && (renderer->unref() == 0)) delete(renderer);
             if (renderer && (renderer->unref() == 0)) delete(renderer);
         }
         }
 
 
@@ -106,7 +107,7 @@ namespace tvg
 
 
         bool transform(const Matrix& m)
         bool transform(const Matrix& m)
         {
         {
-            tr.m = m;
+            if (&tr.m != &m) tr.m = m;
             tr.overriding = true;
             tr.overriding = true;
             renderFlag |= RenderUpdateFlag::Transform;
             renderFlag |= RenderUpdateFlag::Transform;
 
 
@@ -121,6 +122,20 @@ namespace tvg
             return tr.m;
             return tr.m;
         }
         }
 
 
+        void clip(Paint* clp)
+        {
+            if (this->clipper) {
+                P(this->clipper)->unref();
+                if (this->clipper != clp && P(this->clipper)->refCnt == 0) {
+                    delete(this->clipper);
+                }
+            }
+            this->clipper = clp;
+            if (!clp) return;
+
+            P(clipper)->ref();
+        }
+
         bool composite(Paint* source, Paint* target, CompositeMethod method)
         bool composite(Paint* source, Paint* target, CompositeMethod method)
         {
         {
             //Invalid case
             //Invalid case

+ 11 - 5
thirdparty/thorvg/src/renderer/tvgPicture.cpp

@@ -20,6 +20,7 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+#include "tvgPaint.h"
 #include "tvgPicture.h"
 #include "tvgPicture.h"
 
 
 /************************************************************************/
 /************************************************************************/
@@ -73,11 +74,11 @@ bool Picture::Impl::needComposition(uint8_t opacity)
 bool Picture::Impl::render(RenderMethod* renderer)
 bool Picture::Impl::render(RenderMethod* renderer)
 {
 {
     bool ret = false;
     bool ret = false;
-    renderer->blend(picture->blend());
+    renderer->blend(PP(picture)->blendMethod);
 
 
     if (surface) return renderer->renderImage(rd);
     if (surface) return renderer->renderImage(rd);
     else if (paint) {
     else if (paint) {
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
         if (needComp) {
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             renderer->beginComposite(cmp, CompositeMethod::None, 255);
             renderer->beginComposite(cmp, CompositeMethod::None, 255);
@@ -134,7 +135,6 @@ Result Picture::Impl::load(ImageLoader* loader)
 
 
 Picture::Picture() : pImpl(new Impl(this))
 Picture::Picture() : pImpl(new Impl(this))
 {
 {
-    Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
 }
 }
 
 
 
 
@@ -150,9 +150,15 @@ unique_ptr<Picture> Picture::gen() noexcept
 }
 }
 
 
 
 
-uint32_t Picture::identifier() noexcept
+TVG_DEPRECATED uint32_t Picture::identifier() noexcept
 {
 {
-    return TVG_CLASS_ID_PICTURE;
+    return (uint32_t) Type::Picture;
+}
+
+
+Type Picture::type() const noexcept
+{
+    return Type::Picture;
 }
 }
 
 
 
 

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgPicture.h

@@ -60,7 +60,7 @@ struct Picture::Impl
     ImageLoader* loader = nullptr;
     ImageLoader* loader = nullptr;
 
 
     Paint* paint = nullptr;           //vector picture uses
     Paint* paint = nullptr;           //vector picture uses
-    Surface* surface = nullptr;       //bitmap picture uses
+    RenderSurface* surface = nullptr; //bitmap picture uses
     RenderData rd = nullptr;          //engine data
     RenderData rd = nullptr;          //engine data
     float w = 0, h = 0;
     float w = 0, h = 0;
     Picture* picture = nullptr;
     Picture* picture = nullptr;

+ 72 - 26
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -24,6 +24,7 @@
 #define _TVG_RENDER_H_
 #define _TVG_RENDER_H_
 
 
 #include <math.h>
 #include <math.h>
+#include <cstdarg>
 #include "tvgCommon.h"
 #include "tvgCommon.h"
 #include "tvgArray.h"
 #include "tvgArray.h"
 #include "tvgLock.h"
 #include "tvgLock.h"
@@ -36,9 +37,8 @@ using pixel_t = uint32_t;
 
 
 enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
 enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
 
 
-struct Surface;
-
-enum ColorSpace
+//TODO: Move this in public header unifying with SwCanvas::Colorspace
+enum ColorSpace : uint8_t
 {
 {
     ABGR8888 = 0,      //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
     ABGR8888 = 0,      //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
     ARGB8888,          //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
     ARGB8888,          //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
@@ -48,7 +48,7 @@ enum ColorSpace
     Unsupported        //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
     Unsupported        //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
 };
 };
 
 
-struct Surface
+struct RenderSurface
 {
 {
     union {
     union {
         pixel_t* data = nullptr;    //system based data pointer
         pixel_t* data = nullptr;    //system based data pointer
@@ -62,11 +62,11 @@ struct Surface
     uint8_t channelSize = 0;
     uint8_t channelSize = 0;
     bool premultiplied = false;         //Alpha-premultiplied
     bool premultiplied = false;         //Alpha-premultiplied
 
 
-    Surface()
+    RenderSurface()
     {
     {
     }
     }
 
 
-    Surface(const Surface* rhs)
+    RenderSurface(const RenderSurface* rhs)
     {
     {
         data = rhs->data;
         data = rhs->data;
         stride = rhs->stride;
         stride = rhs->stride;
@@ -80,21 +80,10 @@ struct Surface
 
 
 };
 };
 
 
-struct Compositor
+struct RenderCompositor
 {
 {
     CompositeMethod method;
     CompositeMethod method;
-    uint8_t        opacity;
-};
-
-struct Vertex
-{
-   Point pt;
-   Point uv;
-};
-
-struct Polygon
-{
-   Vertex vertex[3];
+    uint8_t opacity;
 };
 };
 
 
 struct RenderRegion
 struct RenderRegion
@@ -270,11 +259,66 @@ struct RenderShape
     float strokeMiterlimit() const
     float strokeMiterlimit() const
     {
     {
         if (!stroke) return 4.0f;
         if (!stroke) return 4.0f;
-
         return stroke->miterlimit;;
         return stroke->miterlimit;;
     }
     }
 };
 };
 
 
+struct RenderEffect
+{
+    RenderData rd = nullptr;
+    RenderRegion extend = {0, 0, 0, 0};
+    SceneEffect type;
+    bool invalid = false;
+
+    virtual ~RenderEffect()
+    {
+        free(rd);
+    }
+};
+
+struct RenderEffectGaussianBlur : RenderEffect
+{
+    float sigma;
+    uint8_t direction; //0: both, 1: horizontal, 2: vertical
+    uint8_t border;    //0: duplicate, 1: wrap
+    uint8_t quality;   //0 ~ 100  (optional)
+
+    static RenderEffectGaussianBlur* gen(va_list& args)
+    {
+        auto inst = new RenderEffectGaussianBlur;
+        inst->sigma = std::max((float) va_arg(args, double), 0.0f);
+        inst->direction = std::min(va_arg(args, int), 2);
+        inst->border = std::min(va_arg(args, int), 1);
+        inst->quality = std::min(va_arg(args, int), 100);
+        inst->type = SceneEffect::GaussianBlur;
+        return inst;
+    }
+};
+
+struct RenderEffectDropShadow : RenderEffect
+{
+    uint8_t color[4];  //rgba
+    float angle;
+    float distance;
+    float sigma;
+    uint8_t quality;   //0 ~ 100  (optional)
+
+    static RenderEffectDropShadow* gen(va_list& args)
+    {
+        auto inst = new RenderEffectDropShadow;
+        inst->color[0] = va_arg(args, int);
+        inst->color[1] = va_arg(args, int);
+        inst->color[2] = va_arg(args, int);
+        inst->color[3] = std::min(va_arg(args, int), 255);
+        inst->angle = (float) va_arg(args, double);
+        inst->distance = (float) va_arg(args, double);
+        inst->sigma = std::max((float) va_arg(args, double), 0.0f);
+        inst->quality = std::min(va_arg(args, int), 100);
+        inst->type = SceneEffect::DropShadow;
+        return inst;
+    }
+};
+
 class RenderMethod
 class RenderMethod
 {
 {
 private:
 private:
@@ -287,7 +331,7 @@ public:
 
 
     virtual ~RenderMethod() {}
     virtual ~RenderMethod() {}
     virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
     virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
-    virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
+    virtual RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
     virtual bool preRender() = 0;
     virtual bool preRender() = 0;
     virtual bool renderShape(RenderData data) = 0;
     virtual bool renderShape(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
@@ -298,14 +342,17 @@ public:
     virtual bool viewport(const RenderRegion& vp) = 0;
     virtual bool viewport(const RenderRegion& vp) = 0;
     virtual bool blend(BlendMethod method) = 0;
     virtual bool blend(BlendMethod method) = 0;
     virtual ColorSpace colorSpace() = 0;
     virtual ColorSpace colorSpace() = 0;
-    virtual const Surface* mainSurface() = 0;
+    virtual const RenderSurface* mainSurface() = 0;
 
 
     virtual bool clear() = 0;
     virtual bool clear() = 0;
     virtual bool sync() = 0;
     virtual bool sync() = 0;
 
 
-    virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
-    virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
-    virtual bool endComposite(Compositor* cmp) = 0;
+    virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0;
+    virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
+    virtual bool endComposite(RenderCompositor* cmp) = 0;
+
+    virtual bool prepare(RenderEffect* effect) = 0;
+    virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0;
 };
 };
 
 
 static inline bool MASK_REGION_MERGING(CompositeMethod method)
 static inline bool MASK_REGION_MERGING(CompositeMethod method)
@@ -374,7 +421,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
     return (((c) * (a) + 0xff) >> 8);
     return (((c) * (a) + 0xff) >> 8);
 }
 }
 
 
-
 }
 }
 
 
 #endif //_TVG_RENDER_H_
 #endif //_TVG_RENDER_H_

+ 4 - 3
thirdparty/thorvg/src/renderer/tvgSaver.cpp

@@ -20,6 +20,7 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+#include <cstring>
 #include "tvgCommon.h"
 #include "tvgCommon.h"
 #include "tvgSaveModule.h"
 #include "tvgSaveModule.h"
 #include "tvgPaint.h"
 #include "tvgPaint.h"
@@ -122,7 +123,7 @@ Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compre
     auto p = paint.release();
     auto p = paint.release();
     if (!p) return Result::MemoryCorruption;
     if (!p) return Result::MemoryCorruption;
 
 
-    //Already on saving an other resource.
+    //Already on saving another resource.
     if (pImpl->saveModule) {
     if (pImpl->saveModule) {
         if (P(p)->refCnt == 0) delete(p);
         if (P(p)->refCnt == 0) delete(p);
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;
@@ -160,12 +161,12 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
     //animation holds the picture, it must be 1 at the bottom.
     //animation holds the picture, it must be 1 at the bottom.
     auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
     auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
 
 
-    if (mathZero(a->totalFrame())) {
+    if (tvg::zero(a->totalFrame())) {
         if (remove) delete(a);
         if (remove) delete(a);
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;
     }
     }
 
 
-    //Already on saving an other resource.
+    //Already on saving another resource.
     if (pImpl->saveModule) {
     if (pImpl->saveModule) {
         if (remove) delete(a);
         if (remove) delete(a);
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;

+ 62 - 4
thirdparty/thorvg/src/renderer/tvgScene.cpp

@@ -20,15 +20,32 @@
  * SOFTWARE.
  * SOFTWARE.
  */
  */
 
 
+#include <cstdarg>
 #include "tvgScene.h"
 #include "tvgScene.h"
 
 
+/************************************************************************/
+/* Internal Class Implementation                                        */
+/************************************************************************/
+
+Result Scene::Impl::resetEffects()
+{
+    if (effects) {
+        for (auto e = effects->begin(); e < effects->end(); ++e) {
+            delete(*e);
+        }
+        delete(effects);
+        effects = nullptr;
+    }
+    return Result::Success;
+}
+
+
 /************************************************************************/
 /************************************************************************/
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
 Scene::Scene() : pImpl(new Impl(this))
 Scene::Scene() : pImpl(new Impl(this))
 {
 {
-    Paint::pImpl->id = TVG_CLASS_ID_SCENE;
 }
 }
 
 
 
 
@@ -44,9 +61,15 @@ unique_ptr<Scene> Scene::gen() noexcept
 }
 }
 
 
 
 
-uint32_t Scene::identifier() noexcept
+TVG_DEPRECATED uint32_t Scene::identifier() noexcept
+{
+    return (uint32_t) Type::Scene;
+}
+
+
+Type Scene::type() const noexcept
 {
 {
-    return TVG_CLASS_ID_SCENE;
+    return Type::Scene;
 }
 }
 
 
 
 
@@ -54,7 +77,11 @@ Result Scene::push(unique_ptr<Paint> paint) noexcept
 {
 {
     auto p = paint.release();
     auto p = paint.release();
     if (!p) return Result::MemoryCorruption;
     if (!p) return Result::MemoryCorruption;
-    PP(p)->ref();
+    P(p)->ref();
+
+    //Relocated the paint to the current scene space
+    P(p)->renderFlag |= RenderUpdateFlag::Transform;
+
     pImpl->paints.push_back(p);
     pImpl->paints.push_back(p);
 
 
     return Result::Success;
     return Result::Success;
@@ -79,3 +106,34 @@ list<Paint*>& Scene::paints() noexcept
 {
 {
     return pImpl->paints;
     return pImpl->paints;
 }
 }
+
+
+Result Scene::push(SceneEffect effect, ...) noexcept
+{
+    if (effect == SceneEffect::ClearAll) return pImpl->resetEffects();
+
+    if (!pImpl->effects) pImpl->effects = new Array<RenderEffect*>;
+
+    va_list args;
+    va_start(args, effect);
+
+    RenderEffect* re = nullptr;
+
+    switch (effect) {
+        case SceneEffect::GaussianBlur: {
+            re = RenderEffectGaussianBlur::gen(args);
+            break;
+        }
+        case SceneEffect::DropShadow: {
+            re = RenderEffectDropShadow::gen(args);
+            break;
+        }
+        default: break;
+    }
+
+    if (!re) return Result::InvalidArguments;
+
+    pImpl->effects->push(re);
+
+    return Result::Success;
+}

+ 49 - 11
thirdparty/thorvg/src/renderer/tvgScene.h

@@ -23,10 +23,9 @@
 #ifndef _TVG_SCENE_H_
 #ifndef _TVG_SCENE_H_
 #define _TVG_SCENE_H_
 #define _TVG_SCENE_H_
 
 
-#include <float.h>
+#include "tvgMath.h"
 #include "tvgPaint.h"
 #include "tvgPaint.h"
 
 
-
 struct SceneIterator : Iterator
 struct SceneIterator : Iterator
 {
 {
     list<Paint*>* paints;
     list<Paint*>* paints;
@@ -61,8 +60,10 @@ struct Scene::Impl
     list<Paint*> paints;
     list<Paint*> paints;
     RenderData rd = nullptr;
     RenderData rd = nullptr;
     Scene* scene = nullptr;
     Scene* scene = nullptr;
-    uint8_t opacity;                     //for composition
-    bool needComp = false;               //composite or not
+    RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
+    Array<RenderEffect*>* effects = nullptr;
+    uint8_t opacity;         //for composition
+    bool needComp = false;   //composite or not
 
 
     Impl(Scene* s) : scene(s)
     Impl(Scene* s) : scene(s)
     {
     {
@@ -70,6 +71,8 @@ struct Scene::Impl
 
 
     ~Impl()
     ~Impl()
     {
     {
+        resetEffects();
+
         for (auto paint : paints) {
         for (auto paint : paints) {
             if (P(paint)->unref() == 0) delete(paint);
             if (P(paint)->unref() == 0) delete(paint);
         }
         }
@@ -83,12 +86,15 @@ struct Scene::Impl
     {
     {
         if (opacity == 0 || paints.empty()) return false;
         if (opacity == 0 || paints.empty()) return false;
 
 
+        //post effects requires composition
+        if (effects) return true;
+
         //Masking may require composition (even if opacity == 255)
         //Masking may require composition (even if opacity == 255)
         auto compMethod = scene->composite(nullptr);
         auto compMethod = scene->composite(nullptr);
         if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
         if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
 
 
         //Blending may require composition (even if opacity == 255)
         //Blending may require composition (even if opacity == 255)
-        if (scene->blend() != BlendMethod::Normal) return true;
+        if (PP(scene)->blendMethod != BlendMethod::Normal) return true;
 
 
         //Half translucent requires intermediate composition.
         //Half translucent requires intermediate composition.
         if (opacity == 255) return false;
         if (opacity == 255) return false;
@@ -96,31 +102,34 @@ struct Scene::Impl
         //If scene has several children or only scene, it may require composition.
         //If scene has several children or only scene, it may require composition.
         //OPTIMIZE: the bitmap type of the picture would not need the composition.
         //OPTIMIZE: the bitmap type of the picture would not need the composition.
         //OPTIMIZE: a single paint of a scene would not need the composition.
         //OPTIMIZE: a single paint of a scene would not need the composition.
-        if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false;
+        if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false;
 
 
         return true;
         return true;
     }
     }
 
 
     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
     {
     {
+        this->vport = renderer->viewport();
+
         if ((needComp = needComposition(opacity))) {
         if ((needComp = needComposition(opacity))) {
             /* Overriding opacity value. If this scene is half-translucent,
             /* Overriding opacity value. If this scene is half-translucent,
-               It must do intermeidate composition with that opacity value. */
+               It must do intermediate composition with that opacity value. */
             this->opacity = opacity;
             this->opacity = opacity;
             opacity = 255;
             opacity = 255;
         }
         }
         for (auto paint : paints) {
         for (auto paint : paints) {
             paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
             paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
         }
         }
+
         return nullptr;
         return nullptr;
     }
     }
 
 
     bool render(RenderMethod* renderer)
     bool render(RenderMethod* renderer)
     {
     {
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
         auto ret = true;
         auto ret = true;
 
 
-        renderer->blend(scene->blend());
+        renderer->blend(PP(scene)->blendMethod);
 
 
         if (needComp) {
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -131,7 +140,16 @@ struct Scene::Impl
             ret &= paint->pImpl->render(renderer);
             ret &= paint->pImpl->render(renderer);
         }
         }
 
 
-        if (cmp) renderer->endComposite(cmp);
+        if (cmp) {
+            //Apply post effects if any.
+            if (effects) {
+                auto direct = effects->count == 1 ? true : false;
+                for (auto e = effects->begin(); e < effects->end(); ++e) {
+                    renderer->effect(cmp, *e, opacity, direct);
+                }
+            }
+            renderer->endComposite(cmp);
+        }
 
 
         return ret;
         return ret;
     }
     }
@@ -155,7 +173,23 @@ struct Scene::Impl
             if (y2 < region.y + region.h) y2 = (region.y + region.h);
             if (y2 < region.y + region.h) y2 = (region.y + region.h);
         }
         }
 
 
-        return {x1, y1, (x2 - x1), (y2 - y1)};
+        //Extends the render region if post effects require
+        int32_t ex = 0, ey = 0, ew = 0, eh = 0;
+        if (effects) {
+            for (auto e = effects->begin(); e < effects->end(); ++e) {
+                auto effect = *e;
+                if (effect->rd || renderer->prepare(effect)) {
+                    ex = std::min(ex, effect->extend.x);
+                    ey = std::min(ey, effect->extend.y);
+                    ew = std::max(ew, effect->extend.w);
+                    eh = std::max(eh, effect->extend.h);
+                }
+            }
+        }
+
+        auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh};
+        ret.intersect(this->vport);
+        return ret;
     }
     }
 
 
     bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
     bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
@@ -203,6 +237,8 @@ struct Scene::Impl
             dup->paints.push_back(cdup);
             dup->paints.push_back(cdup);
         }
         }
 
 
+        if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
+
         return scene;
         return scene;
     }
     }
 
 
@@ -218,6 +254,8 @@ struct Scene::Impl
     {
     {
         return new SceneIterator(&paints);
         return new SceneIterator(&paints);
     }
     }
+
+    Result resetEffects();
 };
 };
 
 
 #endif //_TVG_SCENE_H_
 #endif //_TVG_SCENE_H_

+ 10 - 11
thirdparty/thorvg/src/renderer/tvgShape.cpp

@@ -34,7 +34,6 @@
 
 
 Shape :: Shape() : pImpl(new Impl(this))
 Shape :: Shape() : pImpl(new Impl(this))
 {
 {
-    Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
 }
 }
 
 
 
 
@@ -52,7 +51,13 @@ unique_ptr<Shape> Shape::gen() noexcept
 
 
 uint32_t Shape::identifier() noexcept
 uint32_t Shape::identifier() noexcept
 {
 {
-    return TVG_CLASS_ID_SHAPE;
+    return (uint32_t) Type::Shape;
+}
+
+
+Type Shape::type() const noexcept
+{
+    return Type::Shape;
 }
 }
 
 
 
 
@@ -151,14 +156,14 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
 }
 }
 
 
 
 
-Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
+TVG_DEPRECATED Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
 {
 {
     //just circle
     //just circle
     if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
     if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
 
 
     const float arcPrecision = 1e-5f;
     const float arcPrecision = 1e-5f;
-    startAngle = mathDeg2Rad(startAngle);
-    sweep = mathDeg2Rad(sweep);
+    startAngle = deg2rad(startAngle);
+    sweep = deg2rad(sweep);
 
 
     auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
     auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
     if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
     if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
@@ -409,12 +414,6 @@ Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
 }
 }
 
 
 
 
-bool Shape::strokeTrim(float* begin, float* end) const noexcept
-{
-    return pImpl->strokeTrim(begin, end);
-}
-
-
 Result Shape::fill(FillRule r) noexcept
 Result Shape::fill(FillRule r) noexcept
 {
 {
     pImpl->rs.rule = r;
     pImpl->rs.rule = r;

+ 5 - 5
thirdparty/thorvg/src/renderer/tvgShape.h

@@ -53,9 +53,9 @@ struct Shape::Impl
     {
     {
         if (!rd) return false;
         if (!rd) return false;
 
 
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
 
 
-        renderer->blend(shape->blend());
+        renderer->blend(PP(shape)->blendMethod);
 
 
         if (needComp) {
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -83,7 +83,7 @@ struct Shape::Impl
         auto method = shape->composite(&target);
         auto method = shape->composite(&target);
         if (!target || method == CompositeMethod::ClipPath) return false;
         if (!target || method == CompositeMethod::ClipPath) return false;
         if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
         if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
-            if (target->identifier() == TVG_CLASS_ID_SHAPE) {
+            if (target->type() == Type::Shape) {
                 auto shape = static_cast<const Shape*>(target);
                 auto shape = static_cast<const Shape*>(target);
                 if (!shape->fill()) {
                 if (!shape->fill()) {
                     uint8_t r, g, b, a;
                     uint8_t r, g, b, a;
@@ -106,7 +106,7 @@ struct Shape::Impl
 
 
         if ((needComp = needComposition(opacity))) {
         if ((needComp = needComposition(opacity))) {
             /* Overriding opacity value. If this scene is half-translucent,
             /* Overriding opacity value. If this scene is half-translucent,
-               It must do intermeidate composition with that opacity value. */ 
+               It must do intermediate composition with that opacity value. */ 
             this->opacity = opacity;
             this->opacity = opacity;
             opacity = 255;
             opacity = 255;
         }
         }
@@ -219,7 +219,7 @@ struct Shape::Impl
             rs.stroke = new RenderStroke();
             rs.stroke = new RenderStroke();
         }
         }
 
 
-        if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
+        if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) &&
             rs.stroke->trim.simultaneous == simultaneous) return;
             rs.stroke->trim.simultaneous == simultaneous) return;
 
 
         rs.stroke->trim.begin = begin;
         rs.stroke->trim.begin = begin;

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp

@@ -82,7 +82,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
 Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
 Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
 {
 {
 #ifdef THORVG_SW_RASTER_SUPPORT
 #ifdef THORVG_SW_RASTER_SUPPORT
-    if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
+    if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;
     }
     }
 
 
@@ -98,7 +98,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
     ImageLoader::cs = static_cast<ColorSpace>(cs);
     ImageLoader::cs = static_cast<ColorSpace>(cs);
 
 
     //Paints must be updated again with this new target.
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
 
     return Result::Success;
     return Result::Success;
 #endif
 #endif

+ 2 - 3
thirdparty/thorvg/src/renderer/tvgText.cpp

@@ -37,7 +37,6 @@
 
 
 Text::Text() : pImpl(new Impl(this))
 Text::Text() : pImpl(new Impl(this))
 {
 {
-    Paint::pImpl->id = TVG_CLASS_ID_TEXT;
 }
 }
 
 
 
 
@@ -111,7 +110,7 @@ unique_ptr<Text> Text::gen() noexcept
 }
 }
 
 
 
 
-uint32_t Text::identifier() noexcept
+Type Text::type() const noexcept
 {
 {
-    return TVG_CLASS_ID_TEXT;
+    return Type::Text;
 }
 }

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgText.h

@@ -90,7 +90,7 @@ struct Text::Impl
     bool render(RenderMethod* renderer)
     bool render(RenderMethod* renderer)
     {
     {
         if (!loader) return true;
         if (!loader) return true;
-        renderer->blend(paint->blend());
+        renderer->blend(PP(paint)->blendMethod);
         return PP(shape)->render(renderer);
         return PP(shape)->render(renderer);
     }
     }
 
 
@@ -115,7 +115,7 @@ struct Text::Impl
         auto fill = P(shape)->rs.fill;
         auto fill = P(shape)->rs.fill;
         if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
         if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
             auto scale = 1.0f / loader->scale;
             auto scale = 1.0f / loader->scale;
-            if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
+            if (fill->type() == Type::LinearGradient) {
                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->y1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->y1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->x2 *= scale;
                 P(static_cast<LinearGradient*>(fill))->x2 *= scale;

+ 9 - 6
thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp

@@ -40,7 +40,7 @@ struct WgCanvas::Impl
 /************************************************************************/
 /************************************************************************/
 
 
 #ifdef THORVG_WG_RASTER_SUPPORT
 #ifdef THORVG_WG_RASTER_SUPPORT
-WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl)
+WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr)
 #else
 #else
 WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
 WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
 #endif
 #endif
@@ -50,14 +50,17 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
 
 
 WgCanvas::~WgCanvas()
 WgCanvas::~WgCanvas()
 {
 {
-    delete pImpl;
+#ifdef THORVG_WG_RASTER_SUPPORT
+    auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
+    renderer->target(nullptr, 0, 0);
+#endif
 }
 }
 
 
 
 
-Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept
+Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, void* device) noexcept
 {
 {
 #ifdef THORVG_WG_RASTER_SUPPORT
 #ifdef THORVG_WG_RASTER_SUPPORT
-    if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
+    if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
         return Result::InsufficientCondition;
         return Result::InsufficientCondition;
     }
     }
 
 
@@ -67,12 +70,12 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) n
     auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
     auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
     if (!renderer) return Result::MemoryCorruption;
     if (!renderer) return Result::MemoryCorruption;
 
 
-    if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h)) return Result::Unknown;
+    if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h, (WGPUDevice)device)) return Result::Unknown;
     Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
     Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
     renderer->viewport(Canvas::pImpl->vport);
     renderer->viewport(Canvas::pImpl->vport);
 
 
     //Paints must be updated again with this new target.
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
 
     return Result::Success;
     return Result::Success;
 #endif
 #endif

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

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 #!/bin/bash -e
 
 
-VERSION=0.14.10
+VERSION=0.15.5
 # Uncomment and set a git hash to use specific commit instead of tag.
 # Uncomment and set a git hash to use specific commit instead of tag.
 #GIT_COMMIT=
 #GIT_COMMIT=