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 = [
     # common
     "src/common/tvgCompressor.cpp",
-    "src/common/tvgLines.cpp",
     "src/common/tvgMath.cpp",
     "src/common/tvgStr.cpp",
     # SVG parser
@@ -52,6 +51,7 @@ thirdparty_sources = [
     "src/renderer/sw_engine/tvgSwImage.cpp",
     "src/renderer/sw_engine/tvgSwMath.cpp",
     "src/renderer/sw_engine/tvgSwMemPool.cpp",
+    "src/renderer/sw_engine/tvgSwPostEffect.cpp",
     "src/renderer/sw_engine/tvgSwRaster.cpp",
     "src/renderer/sw_engine/tvgSwRenderer.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
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.14.10 (366dcd72850c360b49e841e568fc5a154d7cce9e, 2024)
+- Version: 0.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024)
 - License: MIT
 
 Files extracted from upstream source:

+ 5 - 1
thirdparty/thorvg/AUTHORS

@@ -28,6 +28,10 @@ Nattu Adnan <[email protected]>
 Gabor Kiss-Vamosi <[email protected]>
 Lorcán Mc Donagh <[email protected]>
 Lucas Niu <[email protected]>
-Francisco Ramírez <[email protected]>
+Francisco Ramírez <[email protected]> 
 Abdelrahman Ashraf <[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:
 //#define THORVG_LOG_ENABLED
 
-#define THORVG_VERSION_STRING "0.14.10"
+#define THORVG_VERSION_STRING "0.15.5"
 #endif

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

@@ -157,7 +157,7 @@ enum class FillRule
 enum class CompositeMethod
 {
     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.
     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
@@ -178,24 +178,46 @@ enum class CompositeMethod
  *
  * @see Paint::blend()
  *
- * @note Experimental API
+ * @since 0.15
  */
 enum class BlendMethod : uint8_t
 {
     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)
+    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)
-    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)
     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)
     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)
-    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.
     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.
      *
-     * 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.
      *
      * @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.
      *
      * @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;
 
@@ -324,6 +367,20 @@ public:
      */
     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.
      *
@@ -386,22 +443,15 @@ public:
     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.
@@ -412,6 +462,11 @@ public:
      */
     uint32_t id = 0;
 
+    /**
+     * @see Paint::type()
+     */
+    TVG_DEPRECATED uint32_t identifier() const noexcept;
+
     _TVG_DECLARE_PRIVATE(Paint);
 };
 
@@ -503,13 +558,20 @@ public:
     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);
 };
@@ -538,7 +600,7 @@ public:
      *
      * 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()
      *
      * @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().
      *
      * @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;
 
@@ -686,13 +748,20 @@ public:
     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);
 };
@@ -744,13 +813,20 @@ public:
     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);
 };
@@ -774,11 +850,11 @@ public:
     ~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;
 
@@ -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 @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 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)
      *
-     * @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] h The height 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).
      */
-    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.
@@ -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.
      *
      * @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;
 
@@ -1130,18 +1205,6 @@ public:
      */
     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.
      *
@@ -1150,13 +1213,20 @@ public:
     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);
 };
@@ -1213,7 +1283,7 @@ public:
      * @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.
      *
-     * @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.
      * @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] w The width 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.
      *
-     * @note It expects premultiplied alpha data.
      * @since 0.9
      */
     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;
 
     /**
-     * @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_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.
      *
-     * @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 Scene::push()
+     * @see Scene::push(std::unique_ptr<Paint> paint)
      * @see Scene::clear()
      *
      * @note Experimental API
@@ -1352,6 +1429,20 @@ public:
      */
     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.
      *
@@ -1360,13 +1451,20 @@ public:
     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);
 };
@@ -1377,7 +1475,7 @@ public:
  *
  * @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
 {
@@ -1422,7 +1520,7 @@ public:
      *
      * @see Text::font()
      *
-     * @note Experimental API
+     * @since 0.15
      */
     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.
      *
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
-     * @note Experimental API
-     *
      * @see Text::font()
+     *
+     * @since 0.15
      */
     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::NonSupport When trying to load a file with an unknown extension.
      *
-     * @note Experimental API
-     *
      * @see Text::unload(const std::string& path)
+     *
+     * @since 0.15
      */
     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::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 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)
+     *
+     * @note 0.15
      */
     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.
      *
      * @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)
+     * 
+     * @since 0.15
      */
     static Result unload(const std::string& path) noexcept;
 
@@ -1506,18 +1604,20 @@ public:
      *
      * @return A new Text object.
      *
-     * @note Experimental API
+     * @since 0.15
      */
     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);
 };
@@ -1616,8 +1716,6 @@ public:
  *
  * @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
  */
 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.
  *
- * @note Experimental API
+ * @since 0.15
  */
 class TVG_API WgCanvas final : public Canvas
 {
@@ -1680,6 +1778,7 @@ public:
      * @param[in] surface WGPUSurface, handle to a presentable surface.
      * @param[in] w The width 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::NonSupport In case the wg engine is not supported.
@@ -1689,14 +1788,14 @@ public:
      * @see Canvas::viewport()
      * @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.
      *
      * @return A new WgCanvas object.
      *
-     * @note Experimental API
+     * @since 0.15
      */
     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.
      *
-     * @note Experimental API
+     * @since 0.15
      */
     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 If a marker has been specified, its range will be disregarded.
      * @see LottieAnimation::segment(const char* marker)
+     *
      * @note Experimental API
      */
     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.
  *
  * 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.
  *

+ 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;
     }
     *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"
 
-//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 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) -
                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 ||
         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;
 
@@ -108,9 +185,9 @@ Matrix 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 true;
@@ -133,9 +210,165 @@ Point operator*(const Point& pt, const Matrix& m)
     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);
-    mathClamp(result, 0, 255);
+    tvg::clamp(result, 0, 255);
     return static_cast<uint8_t>(result);
 }
+
+}
+

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

@@ -29,47 +29,47 @@
 #include <cmath>
 #include "tvgCommon.h"
 
+namespace tvg
+{
+
 #define MATH_PI  3.14159265358979323846f
 #define MATH_PI2 1.57079632679489661923f
 #define FLOAT_EPSILON 1.0e-06f  //1.192092896e-07f
 #define PATH_KAPPA 0.552284f
 
-#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
-#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
-
-
 /************************************************************************/
 /* 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);
 }
 
 
-static inline float mathRad2Deg(float radian)
+static inline float rad2deg(float radian)
 {
     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;
 }
 
 
-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>
-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;
     else if (v > max) v = max;
@@ -79,27 +79,27 @@ static inline void mathClamp(T& v, const T& min, const T& max)
 /* 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);
 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;
 }
 
 
-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->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->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) {
         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->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;
     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);
 }
@@ -172,15 +172,21 @@ static inline void mathLog(const Matrix& m)
 
 void operator*=(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 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);
 }
@@ -200,7 +206,7 @@ static inline float mathLength(const Point& a)
 
 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                                              */
 /************************************************************************/
 
 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);
 }
 
-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_

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

@@ -207,16 +207,6 @@ error:
     return 0.0f;
 }
 
-
-int str2int(const char* str, size_t n)
-{
-    int ret = 0;
-    for(size_t i = 0; i < n; ++i) {
-        ret = ret * 10 + (str[i] - '0');
-    }
-    return ret;
-}
-
 char* strDuplicate(const char *str, size_t n)
 {
     auto len = strlen(str);

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

@@ -29,7 +29,6 @@ namespace tvg
 {
 
 float strToFloat(const char *nPtr, char **endPtr);  //convert to float
-int str2int(const char* str, size_t n);             //convert to integer
 char* strDuplicate(const char *str, size_t n);      //copy the string
 char* 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

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

@@ -86,10 +86,10 @@ bool PngLoader::read()
 
     if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
         image->format = PNG_FORMAT_BGRA;
-        surface.cs = ColorSpace::ARGB8888;
+        surface.cs = ColorSpace::ARGB8888S;
     } else {
         image->format = PNG_FORMAT_RGBA;
-        surface.cs = ColorSpace::ABGR8888;
+        surface.cs = ColorSpace::ABGR8888S;
     }
 
     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();
 

+ 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 read() override;
 
-    Surface* bitmap() override;
+    RenderSurface* bitmap() override;
 
 private:
     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();
     return ImageLoader::bitmap();

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

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

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

@@ -36,6 +36,8 @@
 #include <stdio.h>
 #include <setjmp.h>
 #include <stdint.h>
+
+#include "tvgCommon.h"
 #include "tvgJpgd.h"
 
 #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_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_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_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,
@@ -1382,9 +1384,9 @@ int jpeg_decoder::process_markers()
                 read_dht_marker();
                 break;
             }
-            // No arithmitic support - dumb patents!
+            // No arithmetic support - dumb patents!
             case M_DAC: {
-                stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+                stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
                 break;
             }
             case M_DQT: {
@@ -1466,8 +1468,8 @@ void jpeg_decoder::locate_sof_marker()
           read_sof_marker();
           break;
         }
-        case M_SOF9: {  /* Arithmitic coding */
-          stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+        case M_SOF9: {  /* Arithmetic coding */
+          stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
           break;
         }
         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);
                 break;
             default:
-                JPGD_ASSERT(false);
+                TVGERR("JPG", "invalid transform_mcu_expand");
+                return;
         }
         DCT_Upsample::Matrix44 a(P + Q); P -= Q;
         DCT_Upsample::Matrix44& b = P;
@@ -1831,7 +1834,7 @@ void jpeg_decoder::process_restart()
     int i;
     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!
     //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, "%")) {
         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::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
         {
             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;
     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 {
-        if (mathEqual(hue, 360.0)) hue = 0.0f;
+        if (tvg::equal(hue, 360.0)) hue = 0.0f;
         hue /= 60.0f;
 
         v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
         p = brightness + brightness - v;
 
-        if (!mathZero(v)) sv = (v - p) / v;
+        if (!tvg::zero(v)) sv = (v - p) / v;
         else sv = 0;
 
         i = static_cast<uint8_t>(hue);
@@ -858,8 +859,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
             //Transform to signed.
             points[0] = fmodf(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) {
                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
                 *matrix *= tmp;
@@ -882,12 +883,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
             *matrix *= tmp;
         } else if (state == MatrixState::SkewX) {
             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;
         } else if (state == MatrixState::SkewY) {
             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;
         }
@@ -1066,7 +1067,7 @@ static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, co
 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
 {
     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[] = {
     {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
     {"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)
 {
     SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2563,7 +2576,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
         stop->a = _toOpacity(value);
         loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
     } 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);
         }
     } else {
@@ -2586,7 +2605,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
             stop->a = _toOpacity(value);
         }
     } 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);
         }
     } else if (!strcmp(key, "style")) {
@@ -3341,7 +3366,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
             else parent = loader->doc;
             if (!strcmp(tagName, "style")) {
                 // 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) {
                     node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
                     loader->cssStyle = node;

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

@@ -215,6 +215,7 @@ enum class SvgParserLengthType
 {
     Vertical,
     Horizontal,
+    Diagonal,
     //In case of, for example, radius of radial gradient
     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);
     ry = fabsf(ry);
 
-    angle = mathDeg2Rad(angle);
+    angle = deg2rad(angle);
     cosPhi = cosf(angle);
     sinPhi = sinf(angle);
     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;
     cy += (sy + y) / 2.0f;
 
-    //Sstep 4 (F6.5.4)
+    //Step 4 (F6.5.4)
     //We dont' use arccos (as per w3c doc), see
     //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
     //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;
 
-    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;
 
     if (sweep) {
@@ -469,12 +469,12 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         }
         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]};
                 cmds->push(PathCommand::LineTo);
                 pts->push(p);
                 *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]);
                 *cur = *curCtl = {arr[5], arr[6]};
                 *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 (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
         g->radial->cx = g->radial->cx * vBox.w;
         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;
 
-    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) {
                 Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
                 comp->transform(finalTransform);
-
-                paint->composite(std::move(comp), CompositeMethod::ClipPath);
+                paint->clip(std::move(comp));
             }
 
             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)
 {
-    unique_ptr<Scene> finalScene;
     auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite);
 
     // mUseTransform = mUseTransform * mTranslate
@@ -736,10 +734,10 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
         auto vh = (symbol.hasViewBox ? symbol.vh : height);
 
         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};
             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};
         }
 
@@ -751,9 +749,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
         mSceneTransform = mUseTransform * 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();
             viewBoxClip->appendRect(0, 0, width, height, 0, 0);
 
@@ -764,21 +760,13 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
             }
             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 {
         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};
     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);
 
     //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 (!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);
         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);
     }
 
@@ -937,7 +925,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
     viewBoxClip->appendRect(0, 0, w, h);
 
     auto compositeLayer = Scene::gen();
-    compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
+    compositeLayer->clip(std::move(viewBoxClip));
     compositeLayer->push(std::move(docNode));
 
     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';
         val[0] = '\0';
 
-        if (next == nullptr && sep != nullptr) {
+        if (sep != nullptr && next == nullptr) {
             memcpy(key, buf, sep - buf);
             key[sep - buf] = '\0';
 
             memcpy(val, sep + 1, end - sep - 1);
             val[end - sep - 1] = '\0';
-        } else if (sep < next && sep != nullptr) {
+        } else if (sep != nullptr && sep < next) {
             memcpy(key, buf, sep - buf);
             key[sep - buf] = '\0';
 
@@ -522,8 +522,9 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
             }
         }
 
+        if (!next) break;
         buf = next + 1;
-    } while (next != nullptr);
+    } while (true);
 
     return true;
 }

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

@@ -113,7 +113,7 @@ struct SwSpan
     uint8_t coverage;
 };
 
-struct SwRleData
+struct SwRle
 {
     SwSpan *spans;
     uint32_t alloc;
@@ -211,8 +211,8 @@ struct SwShape
     SwOutline*   outline = nullptr;
     SwStroke*    stroke = 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.
 
     bool         fastTrack = false;   //Fast Track: axis-aligned rectangle without any clips?
@@ -221,7 +221,7 @@ struct SwShape
 struct SwImage
 {
     SwOutline*   outline = nullptr;
-    SwRleData*   rle = nullptr;
+    SwRle*   rle = nullptr;
     union {
         pixel_t*  data;      //system based data pointer
         uint32_t* buf32;     //for explicit 32bits channels
@@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*);                                        //bl
 
 struct SwCompositor;
 
-struct SwSurface : Surface
+struct SwSurface : RenderSurface
 {
     SwJoin  join;
     SwAlpha alphas[4];                    //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
     SwBlender blender = nullptr;          //blender (optional)
     SwCompositor* compositor = nullptr;   //compositor (optional)
-    BlendMethod blendMethod;              //blending method (uint8_t)
+    BlendMethod blendMethod = BlendMethod::Normal;
 
     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;
         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
     SwCompositor* recoverCmp;               //Recover compositor when composition is done
@@ -488,13 +488,14 @@ SwFixed mathAtan(const SwPoint& pt);
 SwFixed mathCos(SwFixed angle);
 SwFixed mathSin(SwFixed angle);
 void mathSplitCubic(SwPoint* base);
+void mathSplitLine(SwPoint* base);
 SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
 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);
 SwPoint mathTransform(const Point* to, const Matrix& transform);
 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);
 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, 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);
 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 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 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 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_ */

+ 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)
 {
     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;
-        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));
     Point p1 {grad->x1, grad->y1};
     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;
 
     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;
         }
         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;
 
     auto gradTransform = linear->transform();
-    bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+    bool isTransformation = !identity((const Matrix*)(&gradTransform));
 
     if (isTransformation) {
         gradTransform = transform * gradTransform;
@@ -239,7 +239,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
 
     if (isTransformation) {
         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;
 
@@ -261,7 +261,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
     auto fy = P(radial)->fy;
     auto fr = P(radial)->fr;
 
-    if (mathZero(r)) {
+    if (tvg::zero(r)) {
         fill->solid = 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;
 
     auto gradTransform = radial->transform();
-    bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+    bool isTransformation = !identity((const Matrix*)(&gradTransform));
 
     if (isTransformation) gradTransform = transform * gradTransform;
     else {
@@ -305,7 +305,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
 
     if (isTransformation) {
         Matrix invTransform;
-        if (!mathInverse(&gradTransform, &invTransform)) return false;
+        if (!inverse(&gradTransform, &invTransform)) return false;
         fill->radial.a11 = invTransform.e11;
         fill->radial.a12 = invTransform.e12;
         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);
 
     if (opacity == 255) {
-        if (mathZero(inc)) {
+        if (tvg::zero(inc)) {
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
                 *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 {
-        if (mathZero(inc)) {
+        if (tvg::zero(inc)) {
             auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
             for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
                 *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 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))));
         for (uint32_t i = 0; i < len; ++i, ++dst) {
             *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 inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
         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);
             t2 += inc2;
         }
@@ -651,7 +651,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
     } else {
         uint32_t counter = 0;
         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;
             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 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)));
         src = MULTIPLY(src, a);
         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 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));
         for (uint32_t i = 0; i < len; ++i, ++dst) {
             *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 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));
         if (a == 255) {
             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();
 
-    if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
+    if (fdata->type() == Type::LinearGradient) {
         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;
     }
 

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

@@ -29,7 +29,7 @@
 
 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;
 }
 
@@ -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));
         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;
     }
 

+ 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 d2 = base[1] - base[2];
@@ -54,7 +54,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
         if (d2.small()) {
             if (d3.small()) {
                 angleIn = angleMid = angleOut = 0;
-                return true;
+                return -1;  //ignoreable
             } else {
                 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 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)
 {
     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)
 {
     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
-    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
-    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;
 }
@@ -303,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
         if (yMin > pt->y) yMin = 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) {
         renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 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                                                                  */
 /************************************************************************/
 
-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 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 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);
 
@@ -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);
 
@@ -568,10 +568,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
                 *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
             }
         }
-        return true;
-    }
     //8bit grayscale
-    if (surface->channelSize == sizeof(uint8_t)) {
+    } else if (surface->channelSize == sizeof(uint8_t)) {
         uint8_t src;
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             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));
             }
         }
-        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;
 
@@ -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)
     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;
 
@@ -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;
 
@@ -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; \
     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)
 {
-#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;
 }
 
@@ -784,7 +724,6 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
             *dst = src + ALPHA_BLEND(*dst, IA(src));
         }
     }
-
     return true;
 }
 
@@ -851,7 +790,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
 
     Matrix itransform;
 
-    if (!mathInverse(&transform, &itransform)) return true;
+    if (!inverse(&transform, &itransform)) return true;
 
     if (_compositing(surface)) {
         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                                                     */
 /************************************************************************/
 
-#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)
 {
     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 img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
         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;
 }
 
 
+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)
 {
     if (surface->channelSize == sizeof(uint8_t)) {
@@ -1037,67 +905,9 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
 /*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)
 {
-    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;
 }
 
@@ -1202,7 +1012,7 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix&
 {
     Matrix itransform;
 
-    if (!mathInverse(&transform, &itransform)) return true;
+    if (!inverse(&transform, &itransform)) return true;
 
     if (_compositing(surface)) {
         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                                                         */
 /************************************************************************/
 
-#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)
 {
-    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;
 }
 
@@ -1394,37 +1135,24 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
     //32bits channels
     if (surface->channelSize == sizeof(uint32_t)) {
         auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
-
         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;
             sbuffer += image->stride;
         }
     //8bits grayscale
     } else if (surface->channelSize == sizeof(uint8_t)) {
         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) {
             auto dst = dbuffer;
             auto src = sbuffer;
             if (opacity == 255) {
                 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 {
                 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>
 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 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;
 }
@@ -1617,12 +1355,23 @@ static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& reg
 template<typename fillMethod>
 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 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;
 }
@@ -1663,7 +1412,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
 /************************************************************************/
 
 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 cstride = surface->compositor->image.stride;
@@ -1678,7 +1427,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleDat
 
 
 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 cstride = surface->compositor->image.stride;
@@ -1695,7 +1444,7 @@ static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData*
 
 
 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;
 
@@ -1710,7 +1459,7 @@ static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, c
 
 
 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);
 
@@ -1729,7 +1478,7 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c
 
 
 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;
 
@@ -1742,7 +1491,7 @@ static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle,
 
 
 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;
 
@@ -1757,7 +1506,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
     } else if (surface->channelSize == sizeof(uint8_t)) {
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             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;
@@ -1765,7 +1514,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
 
 
 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;
 
@@ -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;
 
@@ -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;
 
@@ -1814,9 +1563,9 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
         if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
         else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
     } else if (_blending(surface)) {
-        _rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
+        return _rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
     } 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);
     }
     return false;
@@ -1827,6 +1576,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
 /* 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)
 {
@@ -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;
 
@@ -1935,7 +1697,7 @@ void rasterUnpremultiply(Surface* surface)
 }
 
 
-void rasterPremultiply(Surface* surface)
+void rasterPremultiply(RenderSurface* surface)
 {
     ScopedLock lock(surface->key);
     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;
     }
 
-    auto id = fdata->identifier();
+    auto type = fdata->type();
     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 {
-        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;
 }
@@ -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;
     }
 
-    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;
 }
@@ -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);
     if (surface->cs == to) return true;
 
-    //TOOD: Support SIMD accelerations
+    //TODO: Support SIMD accelerations
     auto from = surface->cs;
 
     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;
 }
+
+
+//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;
 
@@ -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
-            //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 avxFilled = 0;
             if (iterations > 0) {

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

@@ -20,6 +20,38 @@
  * 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>
 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;
 
@@ -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);
 
@@ -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
     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;
 

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

@@ -20,6 +20,17 @@
  * SOFTWARE.
  */
 
+struct Vertex
+{
+   Point pt;
+   Point uv;
+};
+
+struct Polygon
+{
+   Vertex vertex[3];
+};
+
 struct AALine
 {
    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;
     }
 
+    if (yStart >= regionBottom) return false;
+
     if (yStart < regionTop) yStart = regionTop;
     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]));
 
     //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
     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
     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 compositing = _compositing(surface);   //Composition required

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

@@ -20,6 +20,9 @@
  * SOFTWARE.
  */
 
+#ifdef THORVG_SW_OPENMP_SUPPORT
+    #include <omp.h>
+#endif
 #include <algorithm>
 #include "tvgMath.h"
 #include "tvgSwCommon.h"
@@ -38,7 +41,7 @@ struct SwTask : Task
 {
     SwSurface* surface = nullptr;
     SwMpool* mpool = nullptr;
-    SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
+    SwBBox bbox;                          //Rendering Region
     Matrix transform;
     Array<RenderData> clips;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
@@ -65,9 +68,7 @@ struct SwTask : Task
     }
 
     virtual void dispose() = 0;
-    virtual bool clip(SwRleData* target) = 0;
-    virtual SwRleData* rle() = 0;
-
+    virtual bool clip(SwRle* target) = 0;
     virtual ~SwTask() {}
 };
 
@@ -92,38 +93,34 @@ struct SwShapeTask : SwTask
         if (!rshape->stroke) return 0.0f;
 
         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 (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));
     }
 
-    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;
 
         return true;
     }
 
-    SwRleData* rle() override
-    {
-        if (!shape.rle && shape.fastTrack) {
-            shape.rle = rleRender(&shape.bbox);
-        }
-        return shape.rle;
-    }
-
     void run(unsigned tid) override
     {
-        if (opacity == 0 && !clipper) return;  //Invisible
+        //Invisible
+        if (opacity == 0 && !clipper) {
+            bbox.reset();
+            return;
+        }
 
         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.
         auto prepareShape = false;
@@ -135,10 +132,11 @@ struct SwShapeTask : SwTask
             rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
             alpha = MULTIPLY(alpha, opacity);
             visibleFill = (alpha > 0 || rshape->fill);
+            shapeReset(&shape);
             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;
+                    renderRegion.reset();
                 }
             }
         }
@@ -159,8 +157,8 @@ struct SwShapeTask : SwTask
         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
             if (strokeWidth > 0.0f) {
                 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()) {
                     auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
                     if (ctable) shapeResetStrokeFill(&shape);
@@ -184,9 +182,13 @@ struct SwShapeTask : SwTask
             //Clip stroke rle
             if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
         }
+
+        bbox = renderRegion; //sync
+
         return;
 
     err:
+        bbox.reset();
         shapeReset(&shape);
         shapeDelOutline(&shape, mpool, tid);
     }
@@ -201,20 +203,14 @@ struct SwShapeTask : SwTask
 struct SwImageTask : SwTask
 {
     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?");
         return true;
     }
 
-    SwRleData* rle() override
-    {
-        TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
-        return nullptr;
-    }
-
     void run(unsigned tid) override
     {
         auto clipRegion = bbox;
@@ -452,27 +448,18 @@ bool SwRenderer::blend(BlendMethod method)
     surface->blendMethod = method;
 
     switch (method) {
-        case BlendMethod::Add:
-            surface->blender = opBlendAdd;
-            break;
-        case BlendMethod::Screen:
-            surface->blender = opBlendScreen;
+        case BlendMethod::Normal:
+            surface->blender = nullptr;
             break;
         case BlendMethod::Multiply:
             surface->blender = opBlendMultiply;
             break;
+        case BlendMethod::Screen:
+            surface->blender = opBlendScreen;
+            break;
         case BlendMethod::Overlay:
             surface->blender = opBlendOverlay;
             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:
             surface->blender = opBlendDarken;
             break;
@@ -491,7 +478,17 @@ bool SwRenderer::blend(BlendMethod method)
         case BlendMethod::SoftLight:
             surface->blender = opBlendSoftLight;
             break;
+        case BlendMethod::Difference:
+            surface->blender = opBlendDifference;
+            break;
+        case BlendMethod::Exclusion:
+            surface->blender = opBlendExclusion;
+            break;
+        case BlendMethod::Add:
+            surface->blender = opBlendAdd;
+            break;
         default:
+            TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method);
             surface->blender = nullptr;
             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;
     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;
 }
 
 
-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;
 
-    auto reqChannelSize = CHANNEL_SIZE(cs);
-
     //Use cached data
     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;
             break;
         }
@@ -578,18 +563,48 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
         //Inherits attributes from main surface
         cmp = new SwSurface(surface);
         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);
     }
 
+    //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
+    if (x < 0) x = 0;
+    if (y < 0) y = 0;
     if (x + w > sw) w = (sw - x);
     if (y + h > sh) h = (sh - y);
 
+    if (w == 0 || h == 0) return nullptr;
+
     cmp->compositor->recoverSfc = surface;
     cmp->compositor->recoverCmp = surface->compositor;
     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.max.x = x + w;
     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.
        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;
 
@@ -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()
 {
     if (surface) return surface->cs;
@@ -681,10 +722,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
     task->surface = surface;
     task->mpool = mpool;
     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) {
         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
     auto task = static_cast<SwImageTask*>(data);
@@ -748,6 +789,10 @@ bool SwRenderer::init(uint32_t threads)
 
 int32_t SwRenderer::init()
 {
+#ifdef THORVG_SW_OPENMP_SUPPORT
+    omp_set_num_threads(TaskScheduler::threads());
+#endif
+
     return initEngineCnt;
 }
 

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

@@ -37,7 +37,7 @@ class SwRenderer : public RenderMethod
 {
 public:
     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 renderShape(RenderData data) override;
     bool renderImage(RenderData data) override;
@@ -48,18 +48,21 @@ public:
     bool viewport(const RenderRegion& vp) override;
     bool blend(BlendMethod method) override;
     ColorSpace colorSpace() override;
-    const Surface* mainSurface() override;
+    const RenderSurface* mainSurface() override;
 
     bool clear() override;
     bool sync() override;
     bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
     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();
 
+    bool prepare(RenderEffect* effect) override;
+    bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override;
+
     static SwRenderer* gen();
     static bool init(uint32_t threads);
     static int32_t init();
@@ -76,6 +79,7 @@ private:
     SwRenderer();
     ~SwRenderer();
 
+    SwSurface* request(int channelSize);
     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
 {
-    SwRleData* rle;
+    SwRle* rle;
 
     SwPoint cellPos;
     SwPoint cellMin;
@@ -235,6 +235,7 @@ struct RleWorker
     SwPoint pos;
 
     SwPoint bezStack[32 * 3 + 1];
+    SwPoint lineStack[32 + 1];
     int levStack[32];
 
     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;
     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)) {
             //Clip x range
             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);
 
-            //span->len += (acount + xOver) - 1;
-            span->len += (acount + xOver);
+            //span->len += (aCount + xOver) - 1;
+            span->len += (aCount + xOver);
             return;
         }
     }
@@ -361,20 +362,20 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
         
     //Clip x range
     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);
         x = rw.cellMin.x;
     }
 
     //Nothing to draw
-    if (acount + xOver <= 0) return;
+    if (aCount + xOver <= 0) return;
 
     //add a span to the current list
     auto span = rle->spans + rle->size;
     span->x = x;
     span->y = y;
-    span->len = (acount + xOver);
+    span->len = (aCount + xOver);
     span->coverage = coverage;
     rle->size++;
 }
@@ -513,98 +514,116 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
         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 {
+            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 {
-                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);
-            } 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 pt = outline->pts.data + first;
         auto types = outline->types.data + first;
+        ++types;
 
         _moveTo(rw, UPSCALE(outline->pts[first]));
 
         while (pt < limit) {
-            ++pt;
-            ++types;
-
             //emit a single line_to
             if (types[0] == SW_CURVE_TYPE_POINT) {
+                ++pt;
+                ++types;
                 _lineTo(rw, UPSCALE(*pt));
             //types cubic
             } 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:
+        _lineTo(rw, start);
        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 spans = target->spans;
@@ -740,7 +755,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
     auto clipEnd = clip->spans + clip->size;
 
     while (spans < end && clipSpans < clipEnd) {
-        //align y cooridnates.
+        //align y-coordinates.
         if (clipSpans->y > spans->y) {
             ++spans;
             continue;
@@ -750,7 +765,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
             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;
         while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
             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 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);
     rle->spans = clippedSpans;
@@ -834,7 +849,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
 /* 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 BAND_SIZE = 40;
@@ -862,7 +877,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
     rw.bandShoot = 0;
     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;
 
     //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 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->size = height;
     rle->alloc = height;
@@ -975,14 +990,14 @@ SwRleData* rleRender(const SwBBox* bbox)
 }
 
 
-void rleReset(SwRleData* rle)
+void rleReset(SwRle* rle)
 {
     if (!rle) return;
     rle->size = 0;
 }
 
 
-void rleFree(SwRleData* rle)
+void rleFree(SwRle* rle)
 {
     if (!rle) return;
     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;
     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);
 
-    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;
     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);
 
-    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 "tvgMath.h"
-#include "tvgLines.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -102,9 +101,9 @@ static bool _outlineClose(SwOutline& outline)
 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
 {
     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);
     //draw the current line fully
     } else if (len <= dash.curLen) {
@@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
             Line left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
-                lineSplitAt(cur, dash.curLen, left, right);
+                cur.split(dash.curLen, left, right);
                 if (!dash.curOpGap) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                         _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)
 {
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
-    auto len = bezLength(cur);
+    auto len = cur.length();
 
     //draw the current line fully
-    if (mathZero(len)) {
+    if (tvg::zero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
     } else if (len <= dash.curLen) {
         dash.curLen -= len;
@@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
             Bezier left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
-                bezSplitAt(cur, dash.curLen, left, right);
+                cur.split(dash.curLen, left, right);
                 if (!dash.curOpGap) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
                         _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;
 
     const Point* close = nullptr;
-    auto length = 0.0f;
+    auto len = 0.0f;
 
     //must begin with moveTo
     if (cmds[0] == PathCommand::MoveTo) {
@@ -297,30 +296,30 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
     while (cmdCnt-- > 0) {
         switch (*cmds) {
             case PathCommand::Close: {
-                length += mathLength(pts - 1, close);
-                if (subpath) return length;
+                len += length(pts - 1, close);
+                if (subpath) return len;
                 break;
             }
             case PathCommand::MoveTo: {
-                if (subpath) return length;
+                if (subpath) return len;
                 close = pts;
                 ++pts;
                 break;
             }
             case PathCommand::LineTo: {
-                length += mathLength(pts - 1, pts);
+                len += length(pts - 1, pts);
                 ++pts;
                 break;
             }
             case PathCommand::CubicTo: {
-                length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
+                len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
                 pts += 3;
                 break;
             }
         }
         ++cmds;
     }
-    return length;
+    return len;
 }
 
 
@@ -355,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
     //offset
     auto patternLength = 0.0f;
     uint32_t offIdx = 0;
-    if (!mathZero(offset)) {
+    if (!tvg::zero(offset)) {
         for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
         bool isOdd = dash.cnt % 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 (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
 
-    //Keep it for Rasterization Region
     shape->bbox = renderRegion;
 
     //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
         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;
             mathSplitCubic(arc);
             arc += 3;
             continue;
         }
 
+        //ignoreable size
+        if (valid < 0 && arc == bezStack) {
+            stroke.center = to;
+            return;
+        }
+
+        //small size
         if (firstArc) {
             firstArc = false;
             //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
        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
-       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. */
     if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
         stroke.handleWideStrokes = true;
@@ -715,7 +725,7 @@ static void _endSubPath(SwStroke& stroke)
         _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
 
         /* 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);
     }
 }
@@ -845,31 +855,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
 
         //A contour cannot start with a cubic control point
         if (type == SW_CURVE_TYPE_CUBIC) return false;
+        ++types;
 
         auto closed =  outline.closed.data ? outline.closed.data[i]: false;
 
         _beginSubPath(*stroke, start, closed);
 
         while (pt < limit) {
-            ++pt;
-            ++types;
-
-            //emit a signel line_to
+            //emit a single line_to
             if (types[0] == SW_CURVE_TYPE_POINT) {
+                ++pt;
+                ++types;
                 _lineTo(*stroke, *pt);
             //types cubic
             } 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:

+ 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_SIGNATURE "ThorVG"
 #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_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

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

@@ -26,7 +26,7 @@
 #include "tvgPaint.h"
 
 
-enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
+enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged};
 
 struct Canvas::Impl
 {
@@ -42,7 +42,7 @@ struct Canvas::Impl
 
     ~Impl()
     {
-        //make it sure any deffered jobs
+        //make it sure any deferred jobs
         renderer->sync();
         renderer->clear();
 
@@ -61,7 +61,7 @@ struct Canvas::Impl
 
     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;
 
         auto p = paint.release();
@@ -91,7 +91,7 @@ struct Canvas::Impl
 
         Array<RenderData> clips;
         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};
 
@@ -108,7 +108,7 @@ struct Canvas::Impl
 
     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;
 
         bool rendered = false;
@@ -124,7 +124,7 @@ struct Canvas::Impl
 
     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()) {
             status = Status::Synced;
@@ -136,7 +136,7 @@ struct Canvas::Impl
 
     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};
         //intersect if the target buffer is already set.
@@ -147,7 +147,7 @@ struct Canvas::Impl
         if (vport == val) return Result::Success;
         renderer->viewport(val);
         vport = val;
-        status = Status::Damanged;
+        status = Status::Damaged;
         return Result::Success;
     }
 };

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

@@ -54,15 +54,6 @@ using namespace tvg;
     #define strdup _strdup
 #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 };
 
 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())
 {
-    Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
     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())
 {
-    Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
     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;
     FillSpread spread;
     DuplicateMethod<Fill>* dup = nullptr;
-    uint8_t id;
 
     ~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
 {
 #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;
     }
 
@@ -75,7 +75,7 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
     renderer->viewport(Canvas::pImpl->vport);
 
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
     return Result::Success;
 #endif

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

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

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

@@ -294,10 +294,10 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
 {
     *invalid = false;
 
-    //TODO: lottie is not sharable.
+    //TODO: svg & lottie is not sharable.
     auto allowCache = true;
     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 (auto loader = _findFromCache(path)) return loader;
@@ -317,7 +317,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
         }
         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++) {
         if (auto loader = _find(static_cast<FileType>(i))) {
             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++) {
         auto loader = _find(static_cast<FileType>(i));
         if (loader) {

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

@@ -32,11 +32,11 @@
 /************************************************************************/
 
 #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 = {}; \
     }
 
@@ -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.
     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);
 
@@ -102,8 +102,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
     auto pt3 = pts + 2;
     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;
 
@@ -164,6 +164,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
     ret->pImpl->opacity = opacity;
 
     if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
+    if (clipper) ret->pImpl->clip(clipper->duplicate());
 
     return ret;
 }
@@ -172,7 +173,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
 bool Paint::Impl::rotate(float degree)
 {
     if (tr.overriding) return false;
-    if (mathEqual(degree, tr.degree)) return true;
+    if (tvg::equal(degree, tr.degree)) return true;
     tr.degree = degree;
     renderFlag |= RenderUpdateFlag::Transform;
 
@@ -183,7 +184,7 @@ bool Paint::Impl::rotate(float degree)
 bool Paint::Impl::scale(float factor)
 {
     if (tr.overriding) return false;
-    if (mathEqual(factor, tr.scale)) return true;
+    if (tvg::equal(factor, tr.scale)) return true;
     tr.scale = factor;
     renderFlag |= RenderUpdateFlag::Transform;
 
@@ -194,7 +195,7 @@ bool Paint::Impl::scale(float factor)
 bool Paint::Impl::translate(float x, float y)
 {
     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.e23 = y;
     renderFlag |= RenderUpdateFlag::Transform;
@@ -207,11 +208,9 @@ bool Paint::Impl::render(RenderMethod* renderer)
 {
     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;
         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
     RenderRegion viewport;
     Result compFastTrack = Result::InsufficientCondition;
-    bool childClipper = false;
 
     if (compData) {
         auto target = compData->target;
         auto method = compData->method;
         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) {
-            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);
     renderFlag = RenderUpdateFlag::None;
     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;
     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);
-    else if (childClipper) clips.pop();
+    else if (this->clipper) clips.pop();
 
     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);
 
     //Case: No transformed, quick return!
-    if (!transformed || mathIdentity(&m)) {
+    if (!transformed || identity(&m)) {
         PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
         return ret;
     }
@@ -351,12 +356,18 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
 
 void Paint::Impl::reset()
 {
+    if (clipper) {
+        delete(clipper);
+        clipper = nullptr;
+    }
+
     if (compData) {
         if (P(compData->target)->unref() == 0) delete(compData->target);
         free(compData);
         compData = nullptr;
     }
-    mathIdentity(&tr.m);
+
+    tvg::identity(&tr.m);
     tr.degree = 0.0f;
     tr.scale = 1.0f;
     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;
     }
+    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();
     if (pImpl->composite(this, p, method)) return Result::Success;
+
     delete(p);
     return Result::InvalidArguments;
 }
@@ -457,6 +480,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept
         if (target) *target = pImpl->compData->target;
         return pImpl->compData->method;
     } else {
+        //TODO: remove. Keep this for the backward compatibility
+        if (pImpl->clipper) {
+            if (target) *target = pImpl->clipper;
+            return CompositeMethod::ClipPath;
+        }
         if (target) *target = nullptr;
         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
 {
+    //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) {
         pImpl->blendMethod = method;
         pImpl->renderFlag |= RenderUpdateFlag::Blend;
@@ -495,9 +526,3 @@ Result Paint::blend(BlendMethod method) noexcept
 
     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;
         Composite* compData = nullptr;
+        Paint* clipper = nullptr;
         RenderMethod* renderer = nullptr;
         struct {
             Matrix m;                 //input matrix
@@ -67,8 +68,8 @@ namespace tvg
                 m.e31 = 0.0f;
                 m.e32 = 0.0f;
                 m.e33 = 1.0f;
-                mathScale(&m, scale, scale);
-                mathRotate(&m, degree);
+                tvg::scale(&m, scale, scale);
+                tvg::rotate(&m, degree);
             }
         } tr;
         BlendMethod blendMethod;
@@ -76,7 +77,6 @@ namespace tvg
         uint8_t ctxFlag;
         uint8_t opacity;
         uint8_t refCnt = 0;                              //reference count
-        uint8_t id;         //TODO: deprecated, remove it
 
         Impl(Paint* pnt) : paint(pnt)
         {
@@ -89,6 +89,7 @@ namespace tvg
                 if (P(compData->target)->unref() == 0) delete(compData->target);
                 free(compData);
             }
+            if (clipper && P(clipper)->unref() == 0) delete(clipper);
             if (renderer && (renderer->unref() == 0)) delete(renderer);
         }
 
@@ -106,7 +107,7 @@ namespace tvg
 
         bool transform(const Matrix& m)
         {
-            tr.m = m;
+            if (&tr.m != &m) tr.m = m;
             tr.overriding = true;
             renderFlag |= RenderUpdateFlag::Transform;
 
@@ -121,6 +122,20 @@ namespace tvg
             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)
         {
             //Invalid case

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

@@ -20,6 +20,7 @@
  * SOFTWARE.
  */
 
+#include "tvgPaint.h"
 #include "tvgPicture.h"
 
 /************************************************************************/
@@ -73,11 +74,11 @@ bool Picture::Impl::needComposition(uint8_t opacity)
 bool Picture::Impl::render(RenderMethod* renderer)
 {
     bool ret = false;
-    renderer->blend(picture->blend());
+    renderer->blend(PP(picture)->blendMethod);
 
     if (surface) return renderer->renderImage(rd);
     else if (paint) {
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             renderer->beginComposite(cmp, CompositeMethod::None, 255);
@@ -134,7 +135,6 @@ Result Picture::Impl::load(ImageLoader* loader)
 
 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;
 
     Paint* paint = nullptr;           //vector picture uses
-    Surface* surface = nullptr;       //bitmap picture uses
+    RenderSurface* surface = nullptr; //bitmap picture uses
     RenderData rd = nullptr;          //engine data
     float w = 0, h = 0;
     Picture* picture = nullptr;

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

@@ -24,6 +24,7 @@
 #define _TVG_RENDER_H_
 
 #include <math.h>
+#include <cstdarg>
 #include "tvgCommon.h"
 #include "tvgArray.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};
 
-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.
     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.
 };
 
-struct Surface
+struct RenderSurface
 {
     union {
         pixel_t* data = nullptr;    //system based data pointer
@@ -62,11 +62,11 @@ struct Surface
     uint8_t channelSize = 0;
     bool premultiplied = false;         //Alpha-premultiplied
 
-    Surface()
+    RenderSurface()
     {
     }
 
-    Surface(const Surface* rhs)
+    RenderSurface(const RenderSurface* rhs)
     {
         data = rhs->data;
         stride = rhs->stride;
@@ -80,21 +80,10 @@ struct Surface
 
 };
 
-struct Compositor
+struct RenderCompositor
 {
     CompositeMethod method;
-    uint8_t        opacity;
-};
-
-struct Vertex
-{
-   Point pt;
-   Point uv;
-};
-
-struct Polygon
-{
-   Vertex vertex[3];
+    uint8_t opacity;
 };
 
 struct RenderRegion
@@ -270,11 +259,66 @@ struct RenderShape
     float strokeMiterlimit() const
     {
         if (!stroke) return 4.0f;
-
         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
 {
 private:
@@ -287,7 +331,7 @@ public:
 
     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(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 renderShape(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
@@ -298,14 +342,17 @@ public:
     virtual bool viewport(const RenderRegion& vp) = 0;
     virtual bool blend(BlendMethod method) = 0;
     virtual ColorSpace colorSpace() = 0;
-    virtual const Surface* mainSurface() = 0;
+    virtual const RenderSurface* mainSurface() = 0;
 
     virtual bool clear() = 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)
@@ -374,7 +421,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
     return (((c) * (a) + 0xff) >> 8);
 }
 
-
 }
 
 #endif //_TVG_RENDER_H_

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

@@ -20,6 +20,7 @@
  * SOFTWARE.
  */
 
+#include <cstring>
 #include "tvgCommon.h"
 #include "tvgSaveModule.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();
     if (!p) return Result::MemoryCorruption;
 
-    //Already on saving an other resource.
+    //Already on saving another resource.
     if (pImpl->saveModule) {
         if (P(p)->refCnt == 0) delete(p);
         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.
     auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
 
-    if (mathZero(a->totalFrame())) {
+    if (tvg::zero(a->totalFrame())) {
         if (remove) delete(a);
         return Result::InsufficientCondition;
     }
 
-    //Already on saving an other resource.
+    //Already on saving another resource.
     if (pImpl->saveModule) {
         if (remove) delete(a);
         return Result::InsufficientCondition;

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

@@ -20,15 +20,32 @@
  * SOFTWARE.
  */
 
+#include <cstdarg>
 #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                                        */
 /************************************************************************/
 
 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();
     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);
 
     return Result::Success;
@@ -79,3 +106,34 @@ list<Paint*>& Scene::paints() noexcept
 {
     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_
 #define _TVG_SCENE_H_
 
-#include <float.h>
+#include "tvgMath.h"
 #include "tvgPaint.h"
 
-
 struct SceneIterator : Iterator
 {
     list<Paint*>* paints;
@@ -61,8 +60,10 @@ struct Scene::Impl
     list<Paint*> paints;
     RenderData rd = 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)
     {
@@ -70,6 +71,8 @@ struct Scene::Impl
 
     ~Impl()
     {
+        resetEffects();
+
         for (auto paint : paints) {
             if (P(paint)->unref() == 0) delete(paint);
         }
@@ -83,12 +86,15 @@ struct Scene::Impl
     {
         if (opacity == 0 || paints.empty()) return false;
 
+        //post effects requires composition
+        if (effects) return true;
+
         //Masking may require composition (even if opacity == 255)
         auto compMethod = scene->composite(nullptr);
         if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
 
         //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.
         if (opacity == 255) return false;
@@ -96,31 +102,34 @@ struct Scene::Impl
         //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: 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;
     }
 
     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))) {
             /* 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;
             opacity = 255;
         }
         for (auto paint : paints) {
             paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
         }
+
         return nullptr;
     }
 
     bool render(RenderMethod* renderer)
     {
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
         auto ret = true;
 
-        renderer->blend(scene->blend());
+        renderer->blend(PP(scene)->blendMethod);
 
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -131,7 +140,16 @@ struct Scene::Impl
             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;
     }
@@ -155,7 +173,23 @@ struct Scene::Impl
             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)
@@ -203,6 +237,8 @@ struct Scene::Impl
             dup->paints.push_back(cdup);
         }
 
+        if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
+
         return scene;
     }
 
@@ -218,6 +254,8 @@ struct Scene::Impl
     {
         return new SceneIterator(&paints);
     }
+
+    Result resetEffects();
 };
 
 #endif //_TVG_SCENE_H_

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

@@ -34,7 +34,6 @@
 
 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
 {
-    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
     if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
 
     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));
     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
 {
     pImpl->rs.rule = r;

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

@@ -53,9 +53,9 @@ struct Shape::Impl
     {
         if (!rd) return false;
 
-        Compositor* cmp = nullptr;
+        RenderCompositor* cmp = nullptr;
 
-        renderer->blend(shape->blend());
+        renderer->blend(PP(shape)->blendMethod);
 
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -83,7 +83,7 @@ struct Shape::Impl
         auto method = shape->composite(&target);
         if (!target || method == CompositeMethod::ClipPath) return false;
         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);
                 if (!shape->fill()) {
                     uint8_t r, g, b, a;
@@ -106,7 +106,7 @@ struct Shape::Impl
 
         if ((needComp = needComposition(opacity))) {
             /* 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;
             opacity = 255;
         }
@@ -219,7 +219,7 @@ struct Shape::Impl
             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.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
 {
 #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;
     }
 
@@ -98,7 +98,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
     ImageLoader::cs = static_cast<ColorSpace>(cs);
 
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
     return Result::Success;
 #endif

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

@@ -37,7 +37,6 @@
 
 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)
     {
         if (!loader) return true;
-        renderer->blend(paint->blend());
+        renderer->blend(PP(paint)->blendMethod);
         return PP(shape)->render(renderer);
     }
 
@@ -115,7 +115,7 @@ struct Text::Impl
         auto fill = P(shape)->rs.fill;
         if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
             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))->y1 *= 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
-WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl)
+WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr)
 #else
 WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
 #endif
@@ -50,14 +50,17 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
 
 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
-    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;
     }
 
@@ -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);
     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};
     renderer->viewport(Canvas::pImpl->vport);
 
     //Paints must be updated again with this new target.
-    Canvas::pImpl->status = Status::Damanged;
+    Canvas::pImpl->status = Status::Damaged;
 
     return Result::Success;
 #endif

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

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