Browse Source

Merge pull request #96167 from akien-mga/thorvg-0.14.7

thorvg: Update to 0.14.7
Rémi Verschelde 11 months ago
parent
commit
fe024443e3
37 changed files with 665 additions and 911 deletions
  1. 1 1
      thirdparty/README.md
  2. 1 1
      thirdparty/thorvg/inc/config.h
  3. 72 78
      thirdparty/thorvg/inc/thorvg.h
  4. 1 1
      thirdparty/thorvg/src/common/tvgArray.h
  5. 2 0
      thirdparty/thorvg/src/common/tvgCompressor.cpp
  6. 1 1
      thirdparty/thorvg/src/common/tvgInlist.h
  7. 1 1
      thirdparty/thorvg/src/common/tvgLines.cpp
  8. 0 2
      thirdparty/thorvg/src/common/tvgLock.h
  9. 15 4
      thirdparty/thorvg/src/common/tvgMath.h
  10. 4 3
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
  11. 16 15
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h
  12. 42 28
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
  13. 16 52
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
  14. 3 5
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
  15. 70 36
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
  16. 29 83
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
  17. 17 105
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
  18. 3 4
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
  19. 1 80
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
  20. 20 18
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
  21. 3 8
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp
  22. 31 10
      thirdparty/thorvg/src/renderer/tvgAccessor.cpp
  23. 1 1
      thirdparty/thorvg/src/renderer/tvgAnimation.cpp
  24. 4 2
      thirdparty/thorvg/src/renderer/tvgCanvas.h
  25. 21 21
      thirdparty/thorvg/src/renderer/tvgInitializer.cpp
  26. 72 70
      thirdparty/thorvg/src/renderer/tvgPaint.cpp
  27. 41 22
      thirdparty/thorvg/src/renderer/tvgPaint.h
  28. 17 26
      thirdparty/thorvg/src/renderer/tvgPicture.cpp
  29. 20 60
      thirdparty/thorvg/src/renderer/tvgPicture.h
  30. 0 35
      thirdparty/thorvg/src/renderer/tvgRender.cpp
  31. 66 29
      thirdparty/thorvg/src/renderer/tvgRender.h
  32. 10 18
      thirdparty/thorvg/src/renderer/tvgScene.h
  33. 36 43
      thirdparty/thorvg/src/renderer/tvgShape.h
  34. 0 2
      thirdparty/thorvg/src/renderer/tvgTaskScheduler.h
  35. 3 10
      thirdparty/thorvg/src/renderer/tvgText.cpp
  36. 24 35
      thirdparty/thorvg/src/renderer/tvgText.h
  37. 1 1
      thirdparty/thorvg/update-thorvg.sh

+ 1 - 1
thirdparty/README.md

@@ -909,7 +909,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 ## thorvg
 
 
 - Upstream: https://github.com/thorvg/thorvg
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.14.2 (f6c4d8a94e0b2194fe911d6e19a550683055dd50, 2024)
+- Version: 0.14.7 (e3a6bf5229a9671c385ee78bc33e6e6b611a9729, 2024)
 - License: MIT
 - License: MIT
 
 
 Files extracted from upstream source:
 Files extracted from upstream source:

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

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

+ 72 - 78
thirdparty/thorvg/inc/thorvg.h

@@ -157,7 +157,7 @@ enum class FillRule
 enum class CompositeMethod
 enum class CompositeMethod
 {
 {
     None = 0,           ///< No composition is applied.
     None = 0,           ///< No composition is applied.
-    ClipPath,           ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
+    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.
     AlphaMask,          ///< Alpha Masking using the compositing target's pixels as an alpha value.
     AlphaMask,          ///< Alpha Masking using the compositing target's pixels as an alpha value.
     InvAlphaMask,       ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
     InvAlphaMask,       ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
     LumaMask,           ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
     LumaMask,           ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
@@ -165,7 +165,9 @@ enum class CompositeMethod
     AddMask,            ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) (Experimental API)
     AddMask,            ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) (Experimental API)
     SubtractMask,       ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) (Experimental API)
     SubtractMask,       ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) (Experimental API)
     IntersectMask,      ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) (Experimental API)
     IntersectMask,      ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) (Experimental API)
-    DifferenceMask      ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) (Experimental API)
+    DifferenceMask,     ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) (Experimental API)
+    LightenMask,        ///< Where multiple masks intersect, the highest transparency value is used. (Experimental API)
+    DarkenMask          ///< Where multiple masks intersect, the lowest transparency value is used. (Experimental API)
 };
 };
 
 
 
 
@@ -232,34 +234,6 @@ struct Matrix
 };
 };
 
 
 
 
-/**
- * @brief A data structure representing a texture mesh vertex
- *
- * @param pt The vertex coordinate
- * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0)
- *
- * @note Experimental API
- */
-struct Vertex
-{
-   Point pt;
-   Point uv;
-};
-
-
-/**
- * @brief A data structure representing a triange in a texture mesh
- *
- * @param vertex The three vertices that make up the polygon
- *
- * @note Experimental API
- */
-struct Polygon
-{
-   Vertex vertex[3];
-};
-
-
 /**
 /**
  * @class Paint
  * @class Paint
  *
  *
@@ -361,7 +335,7 @@ public:
      *
      *
      * @note Experimental API
      * @note Experimental API
      */
      */
-    Result blend(BlendMethod method) const noexcept;
+    Result blend(BlendMethod method) noexcept;
 
 
     /**
     /**
      * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead
      * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead
@@ -371,15 +345,16 @@ public:
     /**
     /**
      * @brief Gets the axis-aligned bounding box of the paint object.
      * @brief Gets the axis-aligned bounding box of the paint object.
      *
      *
-     * In case @p transform is @c true, all object's transformations are applied first, and then the bounding box is established. Otherwise, the bounding box is determined before any transformations.
-     *
-     * @param[out] x The x coordinate of the upper left corner of the object.
-     * @param[out] y The y coordinate of the upper left corner of the object.
+     * @param[out] x The x-coordinate of the upper-left corner of the object.
+     * @param[out] y The y-coordinate of the upper-left corner of the object.
      * @param[out] w The width of the object.
      * @param[out] w The width of the object.
      * @param[out] h The height of the object.
      * @param[out] h The height of the object.
-     * @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't.
+     * @param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't.
      *
      *
+     * @note This is useful when you need to figure out the bounding box of the paint in the canvas space.
      * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object.
      * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object.
+     * @note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called.
+     * @see Canvas::update()
      */
      */
     Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
     Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
 
 
@@ -411,9 +386,9 @@ public:
     CompositeMethod composite(const Paint** target) const noexcept;
     CompositeMethod composite(const Paint** target) const noexcept;
 
 
     /**
     /**
-     * @brief Gets the blending method of the object.
+     * @brief Retrieves the current blending method applied to the paint object.
      *
      *
-     * @return The blending method
+     * @return The currently set blending method.
      *
      *
      * @note Experimental API
      * @note Experimental API
      */
      */
@@ -428,6 +403,15 @@ public:
      */
      */
     uint32_t identifier() const noexcept;
     uint32_t identifier() const noexcept;
 
 
+    /**
+     * @brief Unique ID of this instance.
+     *
+     * This is reserved to specify an paint instance in a scene.
+     *
+     * @since Experimental API
+     */
+    uint32_t id = 0;
+
     _TVG_DECLARE_PRIVATE(Paint);
     _TVG_DECLARE_PRIVATE(Paint);
 };
 };
 
 
@@ -675,7 +659,8 @@ public:
      * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
      * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
      * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds.
      * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds.
      *
      *
-     * @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered.
+     * @note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the colorStops().
+     * @see Fill::colorStops()
      */
      */
     Result linear(float x1, float y1, float x2, float y2) noexcept;
     Result linear(float x1, float y1, float x2, float y2) noexcept;
 
 
@@ -734,6 +719,8 @@ public:
      * @param[in] radius The radius of the bounding circle.
      * @param[in] radius The radius of the bounding circle.
      *
      *
      * @retval Result::InvalidArguments in case the @p radius value is zero or less.
      * @retval Result::InvalidArguments in case the @p radius value is zero or less.
+     *
+     * @note In case the @p radius is zero, an object is filled with a single color using the last color specified in the colorStops().
      */
      */
     Result radial(float cx, float cy, float radius) noexcept;
     Result radial(float cx, float cy, float radius) noexcept;
 
 
@@ -990,7 +977,7 @@ public:
     /**
     /**
      * @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
      * @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
      *
      *
-     * The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively.
+     * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular.
      *
      *
      * @param[in] begin Specifies the start of the segment to display along the path.
      * @param[in] begin Specifies the start of the segment to display along the path.
      * @param[in] end Specifies the end of the segment to display along the path.
      * @param[in] end Specifies the end of the segment to display along the path.
@@ -1076,7 +1063,6 @@ public:
      * @param[out] b The blue color channel value in the range [0 ~ 255].
      * @param[out] b The blue color channel value in the range [0 ~ 255].
      * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
      * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
      *
      *
-     * @return Result::Success when succeed.
      */
      */
     Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept;
     Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept;
 
 
@@ -1219,7 +1205,7 @@ public:
      * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
      * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
      * for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
      * for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
      *
      *
-     * @param[in] data A pointer to a memory location where the content of the picture file is stored.
+     * @param[in] data A pointer to a memory location where the content of the picture file is stored. A null-terminated string is expected for non-binary data if @p copy is @c false.
      * @param[in] size The size in bytes of the memory occupied by the @p data.
      * @param[in] size The size in bytes of the memory occupied by the @p data.
      * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
      * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
@@ -1256,7 +1242,7 @@ public:
     Result size(float* w, float* h) const noexcept;
     Result size(float* w, float* h) const noexcept;
 
 
     /**
     /**
-     * @brief Loads a raw data from a memory block with a given size.
+     * @brief Loads raw data in ARGB8888 format from a memory block of the given size.
      *
      *
      * ThorVG efficiently caches the loaded data using the specified @p data address as a key
      * ThorVG efficiently caches the loaded data using the specified @p data address as a key
      * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
      * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
@@ -1265,47 +1251,27 @@ public:
      * @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
      * @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
      * @param[in] w The width of the image @p data in pixels.
      * @param[in] w The width of the image @p data in pixels.
      * @param[in] h The height of the image @p data in pixels.
      * @param[in] h The height of the image @p data in pixels.
-     * @param[in] premultiplied If @c true, the given image data is alpha-premultiplied.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
      *
      *
+     * @note It expects premultiplied alpha data.
      * @since 0.9
      * @since 0.9
      */
      */
     Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
     Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
 
 
     /**
     /**
-     * @brief Sets or removes the triangle mesh to deform the image.
-     *
-     * If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the
-     * image data will be used as the texture.
-     *
-     * If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed.
-     *
-     * Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support.
-     * mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh.
+     * @brief Retrieve a paint object from the Picture scene by its Unique ID.
      *
      *
-     * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh.
-     * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh.
+     * This function searches for a paint object within the Picture scene that matches the provided @p id.
      *
      *
-     * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect.
-     * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+     * @param[in] id The Unique ID of the paint object.
      *
      *
-     * @note Experimental API
-     */
-    Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept;
-
-    /**
-     * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh.
+     * @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found.
      *
      *
-     * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh.
-     *
-     * @return The number of polygons in the array.
-     *
-     * @note Modifying the triangles returned by this method will modify them directly within the mesh.
-     * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+     * @see Accessor::id()
      *
      *
      * @note Experimental API
      * @note Experimental API
      */
      */
-    uint32_t mesh(const Polygon** triangles) const noexcept;
+    const Paint* paint(uint32_t id) noexcept;
 
 
     /**
     /**
      * @brief Creates a new Picture object.
      * @brief Creates a new Picture object.
@@ -1454,8 +1420,6 @@ public:
      * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
      * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
      * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
      * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
      *
      *
-     * @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
-     *
      * @see Text::font()
      * @see Text::font()
      *
      *
      * @note Experimental API
      * @note Experimental API
@@ -1469,8 +1433,6 @@ public:
      *
      *
      * @param[in] f The unique pointer to the gradient fill.
      * @param[in] f The unique pointer to the gradient fill.
      *
      *
-     * @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
-     *
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
      * @note Experimental API
      * @note Experimental API
      *
      *
@@ -1781,6 +1743,19 @@ public:
      */
      */
     static Result term(CanvasEngine engine) noexcept;
     static Result term(CanvasEngine engine) noexcept;
 
 
+    /**
+     * @brief Retrieves the version of the TVG engine.
+     *
+     * @param[out] major A major version number.
+     * @param[out] minor A minor version number.
+     * @param[out] micro A micro version number.
+     *
+     * @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
+     */
+    static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
+
     _TVG_DISABLE_CTOR(Initializer);
     _TVG_DISABLE_CTOR(Initializer);
 };
 };
 
 
@@ -1879,7 +1854,7 @@ public:
      * @retval Result::InsufficientCondition In case the animation is not loaded.
      * @retval Result::InsufficientCondition In case the animation is not loaded.
      * @retval Result::NonSupport When it's not animatable.
      * @retval Result::NonSupport When it's not animatable.
      *
      *
-     * @note Range from 0.0~1.0
+     * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin.
      * @note If a marker has been specified, its range will be disregarded.
      * @note If a marker has been specified, its range will be disregarded.
      * @see LottieAnimation::segment(const char* marker)
      * @see LottieAnimation::segment(const char* marker)
      * @note Experimental API
      * @note Experimental API
@@ -2030,17 +2005,36 @@ class TVG_API Accessor final
 public:
 public:
     ~Accessor();
     ~Accessor();
 
 
+    TVG_DEPRECATED std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept;
+
     /**
     /**
      * @brief Set the access function for traversing the Picture scene tree nodes.
      * @brief Set the access function for traversing the Picture scene tree nodes.
      *
      *
      * @param[in] picture The picture node to traverse the internal scene-tree.
      * @param[in] picture The picture node to traverse the internal scene-tree.
      * @param[in] func The callback function calling for every paint nodes of the Picture.
      * @param[in] func The callback function calling for every paint nodes of the Picture.
-     *
-     * @return Return the given @p picture instance.
+     * @param[in] data Data passed to the @p func as its argument.
      *
      *
      * @note The bitmap based picture might not have the scene-tree.
      * @note The bitmap based picture might not have the scene-tree.
+     *
+     * @note Experimental API
+     */
+    Result set(const Picture* picture, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept;
+
+    /**
+     * @brief Generate a unique ID (hash key) from a given name.
+     *
+     * This function computes a unique identifier value based on the provided string.
+     * You can use this to assign a unique ID to the Paint object.
+     *
+     * @param[in] name The input string to generate the unique identifier from.
+     *
+     * @return The generated unique identifier value.
+     *
+     * @see Paint::id
+     *
+     * @note Experimental API
      */
      */
-    std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept;
+    static uint32_t id(const char* name) noexcept;
 
 
     /**
     /**
      * @brief Creates a new Accessor object.
      * @brief Creates a new Accessor object.

+ 1 - 1
thirdparty/thorvg/src/common/tvgArray.h

@@ -59,7 +59,7 @@ struct Array
         data[count++] = element;
         data[count++] = element;
     }
     }
 
 
-    void push(Array<T>& rhs)
+    void push(const Array<T>& rhs)
     {
     {
         if (rhs.count == 0) return;
         if (rhs.count == 0) return;
         grow(rhs.count);
         grow(rhs.count);

+ 2 - 0
thirdparty/thorvg/src/common/tvgCompressor.cpp

@@ -478,6 +478,8 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
 
 
 unsigned long djb2Encode(const char* str)
 unsigned long djb2Encode(const char* str)
 {
 {
+    if (!str) return 0;
+
     unsigned long hash = 5381;
     unsigned long hash = 5381;
     int c;
     int c;
 
 

+ 1 - 1
thirdparty/thorvg/src/common/tvgInlist.h

@@ -100,7 +100,7 @@ struct Inlist
         if (element == tail) tail = element->prev;
         if (element == tail) tail = element->prev;
     }
     }
 
 
-    bool empty()
+    bool empty() const
     {
     {
         return head ? false : true;
         return head ? false : true;
     }
     }

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

@@ -79,7 +79,7 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
         Bezier left;
         Bezier left;
         bezSplitLeft(right, t, left);
         bezSplitLeft(right, t, left);
         length = _bezLength(left, lineLengthFunc);
         length = _bezLength(left, lineLengthFunc);
-        if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
+        if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) {
             break;
             break;
         }
         }
         if (length < at) {
         if (length < at) {

+ 0 - 2
thirdparty/thorvg/src/common/tvgLock.h

@@ -25,8 +25,6 @@
 
 
 #ifdef THORVG_THREAD_SUPPORT
 #ifdef THORVG_THREAD_SUPPORT
 
 
-#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
-
 #include <mutex>
 #include <mutex>
 #include "tvgTaskScheduler.h"
 #include "tvgTaskScheduler.h"
 
 

+ 15 - 4
thirdparty/thorvg/src/common/tvgMath.h

@@ -78,17 +78,17 @@ bool mathIdentity(const Matrix* m);
 Matrix operator*(const Matrix& lhs, const Matrix& rhs);
 Matrix operator*(const Matrix& lhs, const Matrix& rhs);
 bool operator==(const Matrix& lhs, const Matrix& rhs);
 bool operator==(const Matrix& lhs, const Matrix& rhs);
 
 
-static inline bool mathRightAngle(const Matrix* m)
+static inline bool mathRightAngle(const Matrix& m)
 {
 {
-   auto radian = fabsf(mathAtan2(m->e21, m->e11));
+   auto radian = fabsf(mathAtan2(m.e21, m.e11));
    if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
    if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
    return false;
    return false;
 }
 }
 
 
 
 
-static inline bool mathSkewed(const Matrix* m)
+static inline bool mathSkewed(const Matrix& m)
 {
 {
-    return !mathZero(m->e21 + m->e12);
+    return !mathZero(m.e21 + m.e12);
 }
 }
 
 
 
 
@@ -233,6 +233,17 @@ static inline Point operator/(const Point& lhs, const float rhs)
 }
 }
 
 
 
 
+static inline Point mathNormal(const Point& p1, const Point& p2)
+{
+    auto dir = p2 - p1;
+    auto len = mathLength(dir);
+    if (mathZero(len)) return {};
+
+    auto unitDir = dir / len;
+    return {-unitDir.y, unitDir.x};
+}
+
+
 static inline void mathLog(const Point& pt)
 static inline void mathLog(const Point& pt)
 {
 {
     TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);
     TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);

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

@@ -710,15 +710,16 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
         return true;
         return true;
     } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
     } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
         float th, ts, tb;
         float th, ts, tb;
-        const char *content, *hue, *saturation, *brightness;
-        content = str + 4;
-        content = _skipSpace(content, nullptr);
+        const char* content = _skipSpace(str + 4, nullptr);
+        const char* hue = nullptr;
         if (_parseNumber(&content, &hue, &th) && hue) {
         if (_parseNumber(&content, &hue, &th) && hue) {
+            const char* saturation = nullptr;
             th = float(uint32_t(th) % 360);
             th = float(uint32_t(th) % 360);
             hue = _skipSpace(hue, nullptr);
             hue = _skipSpace(hue, nullptr);
             hue = (char*)_skipComma(hue);
             hue = (char*)_skipComma(hue);
             hue = _skipSpace(hue, nullptr);
             hue = _skipSpace(hue, nullptr);
             if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') {
             if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') {
+                const char* brightness = nullptr;
                 ts /= 100.0f;
                 ts /= 100.0f;
                 saturation = _skipSpace(saturation + 1, nullptr);
                 saturation = _skipSpace(saturation + 1, nullptr);
                 saturation = (char*)_skipComma(saturation);
                 saturation = (char*)_skipComma(saturation);

+ 16 - 15
thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h

@@ -134,7 +134,6 @@ struct SwFill
 {
 {
     struct SwLinear {
     struct SwLinear {
         float dx, dy;
         float dx, dy;
-        float len;
         float offset;
         float offset;
     };
     };
 
 
@@ -154,6 +153,7 @@ struct SwFill
     uint32_t* ctable;
     uint32_t* ctable;
     FillSpread spread;
     FillSpread spread;
 
 
+    bool solid = false; //solid color fill with the last color from colorStops
     bool translucent;
     bool translucent;
 };
 };
 
 
@@ -301,8 +301,8 @@ static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
 
 
 static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
 static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
 {
 {
-    return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
-            ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
+    ++a;
+    return (((((c >> 8) & 0x00ff00ff) * a) & 0xff00ff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff));
 }
 }
 
 
 static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
 static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
@@ -494,38 +494,39 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
 SwFixed mathLength(const SwPoint& pt);
 SwFixed mathLength(const SwPoint& pt);
 bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
 bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
 SwFixed mathMean(SwFixed angle1, SwFixed angle2);
 SwFixed mathMean(SwFixed angle1, SwFixed angle2);
-SwPoint mathTransform(const Point* to, const Matrix* transform);
+SwPoint mathTransform(const Point* to, const Matrix& transform);
 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
 bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
 bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
 
 
 void shapeReset(SwShape* shape);
 void shapeReset(SwShape* shape);
-bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
+bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
 bool shapePrepared(const SwShape* shape);
 bool shapePrepared(const SwShape* shape);
 bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
 bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
 void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
 void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
-void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform);
-bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
+bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
 void shapeFree(SwShape* shape);
 void shapeFree(SwShape* shape);
 void shapeDelStroke(SwShape* shape);
 void shapeDelStroke(SwShape* shape);
-bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
-bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
 void shapeResetFill(SwShape* shape);
 void shapeResetFill(SwShape* shape);
 void shapeResetStrokeFill(SwShape* shape);
 void shapeResetStrokeFill(SwShape* shape);
 void shapeDelFill(SwShape* shape);
 void shapeDelFill(SwShape* shape);
 void shapeDelStrokeFill(SwShape* shape);
 void shapeDelStrokeFill(SwShape* shape);
 
 
-void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform);
+void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix& transform);
 bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
 bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
 SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
 SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
 void strokeFree(SwStroke* stroke);
 void strokeFree(SwStroke* stroke);
 
 
-bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
 bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
 bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
 void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
 void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
 void imageReset(SwImage* image);
 void imageReset(SwImage* image);
 void imageFree(SwImage* image);
 void imageFree(SwImage* image);
 
 
-bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
+const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata);
 void fillReset(SwFill* fill);
 void fillReset(SwFill* fill);
 void fillFree(SwFill* fill);
 void fillFree(SwFill* fill);
 
 
@@ -561,11 +562,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
 void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
 void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
 
 
 bool rasterCompositor(SwSurface* surface);
 bool rasterCompositor(SwSurface* surface);
-bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
 bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity);
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
+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);
 bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
 void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);

+ 42 - 28
thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp

@@ -58,7 +58,7 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
     auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
     auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
 
 
     det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
     det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
-    deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
+    deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr * 0.5f;
     deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
     deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
 }
 }
 
 
@@ -125,6 +125,8 @@ static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
 
 
 static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
 static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
 {
 {
+    if (fill->solid) return true;
+
     if (!fill->ctable) {
     if (!fill->ctable) {
         fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
         fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
         if (!fill->ctable) return false;
         if (!fill->ctable) return false;
@@ -205,28 +207,33 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
 }
 }
 
 
 
 
-bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
+bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& transform)
 {
 {
     float x1, x2, y1, y2;
     float x1, x2, y1, y2;
     if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
     if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
 
 
     fill->linear.dx = x2 - x1;
     fill->linear.dx = x2 - x1;
     fill->linear.dy = y2 - y1;
     fill->linear.dy = y2 - y1;
-    fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
+    auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
 
 
-    if (fill->linear.len < FLOAT_EPSILON) return true;
+    if (len < FLOAT_EPSILON) {
+        if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) {
+            fill->solid = true;
+        }
+        return true;
+    }
 
 
-    fill->linear.dx /= fill->linear.len;
-    fill->linear.dy /= fill->linear.len;
+    fill->linear.dx /= len;
+    fill->linear.dy /= len;
     fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
     fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
 
 
     auto gradTransform = linear->transform();
     auto gradTransform = linear->transform();
     bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
     bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
 
 
     if (isTransformation) {
     if (isTransformation) {
-        if (transform) gradTransform = *transform * gradTransform;
-    } else if (transform) {
-        gradTransform = *transform;
+        gradTransform = transform * gradTransform;
+    } else {
+        gradTransform = transform;
         isTransformation = true;
         isTransformation = true;
     }
     }
 
 
@@ -239,15 +246,13 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
         auto dx = fill->linear.dx;
         auto dx = fill->linear.dx;
         fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
         fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
         fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
         fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
-
-        fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
     }
     }
 
 
     return true;
     return true;
 }
 }
 
 
 
 
-bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
+bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& transform)
 {
 {
     auto cx = P(radial)->cx;
     auto cx = P(radial)->cx;
     auto cy = P(radial)->cy;
     auto cy = P(radial)->cy;
@@ -256,7 +261,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
     auto fy = P(radial)->fy;
     auto fy = P(radial)->fy;
     auto fr = P(radial)->fr;
     auto fr = P(radial)->fr;
 
 
-    if (r < FLOAT_EPSILON) return true;
+    if (mathZero(r)) {
+        fill->solid = true;
+        return true;
+    }
 
 
     fill->radial.dr = r - fr;
     fill->radial.dr = r - fr;
     fill->radial.dx = cx - fx;
     fill->radial.dx = cx - fx;
@@ -289,12 +297,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
     auto gradTransform = radial->transform();
     auto gradTransform = radial->transform();
     bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
     bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
 
 
-    if (transform) {
-        if (isTransformation) gradTransform = *transform * gradTransform;
-        else {
-            gradTransform = *transform;
-            isTransformation = true;
-        }
+    if (isTransformation) gradTransform = transform * gradTransform;
+    else {
+        gradTransform = transform;
+        isTransformation = true;
     }
     }
 
 
     if (isTransformation) {
     if (isTransformation) {
@@ -816,25 +822,32 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
 }
 }
 
 
 
 
-bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
 {
     if (!fill) return false;
     if (!fill) return false;
 
 
     fill->spread = fdata->spread();
     fill->spread = fdata->spread();
 
 
-    if (ctable) {
-        if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
-    }
-
     if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
     if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
-        return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
+        if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
     } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
     } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
-        return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
+        if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
     }
     }
 
 
-    //LOG: What type of gradient?!
+    if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
+    return true;
+}
+
+
+const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
+{
+    if (!fill->solid) return nullptr;
+
+    const Fill::ColorStop* colors;
+    auto cnt = fdata->colorStops(&colors);
+    if (cnt == 0 || !colors) return nullptr;
 
 
-    return false;
+    return colors + cnt - 1;
 }
 }
 
 
 
 
@@ -845,6 +858,7 @@ void fillReset(SwFill* fill)
         fill->ctable = nullptr;
         fill->ctable = nullptr;
     }
     }
     fill->translucent = false;
     fill->translucent = false;
+    fill->solid = false;
 }
 }
 
 
 
 

+ 16 - 52
thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp

@@ -27,14 +27,14 @@
 /* Internal Class Implementation                                        */
 /* Internal Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-static inline bool _onlyShifted(const Matrix* m)
+static inline bool _onlyShifted(const Matrix& m)
 {
 {
-    if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true;
+    if (mathEqual(m.e11, 1.0f) && mathEqual(m.e22, 1.0f) && mathZero(m.e12) && mathZero(m.e21)) return true;
     return false;
     return false;
 }
 }
 
 
 
 
-static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid)
+static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool, unsigned tid)
 {
 {
     image->outline = mpoolReqOutline(mpool, tid);
     image->outline = mpoolReqOutline(mpool, tid);
     auto outline = image->outline;
     auto outline = image->outline;
@@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
     outline->closed.reserve(1);
     outline->closed.reserve(1);
 
 
     Point to[4];
     Point to[4];
-    if (mesh->triangleCnt > 0) {
-        // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple
-        // places. We should be able to re-use one we have already done? Also see:
-        //   tvgPicture.h --> bounds
-        //   tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh
-        //
-        // TODO: Should we calculate the exact path(s) of the triangle mesh instead?
-        // i.e. copy tvgSwShape.capp -> _genOutline?
-        //
-        // TODO: Cntrs?
-        auto triangles = mesh->triangles;
-        auto min = triangles[0].vertex[0].pt;
-        auto max = triangles[0].vertex[0].pt;
-
-        for (uint32_t i = 0; i < mesh->triangleCnt; ++i) {
-            if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
-            else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
-            if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
-            else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
-
-            if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
-            else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
-            if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
-            else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
-
-            if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
-            else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
-            if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
-            else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
-        }
-        to[0] = {min.x, min.y};
-        to[1] = {max.x, min.y};
-        to[2] = {max.x, max.y};
-        to[3] = {min.x, max.y};
-    } else {
-        auto w = static_cast<float>(image->w);
-        auto h = static_cast<float>(image->h);
-        to[0] = {0, 0};
-        to[1] = {w, 0};
-        to[2] = {w, h};
-        to[3] = {0, h};
-    }
+    auto w = static_cast<float>(image->w);
+    auto h = static_cast<float>(image->h);
+    to[0] = {0, 0};
+    to[1] = {w, 0};
+    to[2] = {w, h};
+    to[3] = {0, h};
 
 
     for (int i = 0; i < 4; i++) {
     for (int i = 0; i < 4; i++) {
         outline->pts.push(mathTransform(&to[i], transform));
         outline->pts.push(mathTransform(&to[i], transform));
@@ -108,25 +72,25 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
 {
 {
     image->direct = _onlyShifted(transform);
     image->direct = _onlyShifted(transform);
 
 
     //Fast track: Non-transformed image but just shifted.
     //Fast track: Non-transformed image but just shifted.
     if (image->direct) {
     if (image->direct) {
-        image->ox = -static_cast<int32_t>(nearbyint(transform->e13));
-        image->oy = -static_cast<int32_t>(nearbyint(transform->e23));
+        image->ox = -static_cast<int32_t>(nearbyint(transform.e13));
+        image->oy = -static_cast<int32_t>(nearbyint(transform.e23));
     //Figure out the scale factor by transform matrix
     //Figure out the scale factor by transform matrix
     } else {
     } else {
-        auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
-        auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
+        auto scaleX = sqrtf((transform.e11 * transform.e11) + (transform.e21 * transform.e21));
+        auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
         image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
         image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
 
 
-        if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
+        if (mathZero(transform.e12) && mathZero(transform.e21)) image->scaled = true;
         else image->scaled = false;
         else image->scaled = false;
     }
     }
 
 
-    if (!_genOutline(image, mesh, transform, mpool, tid)) return false;
+    if (!_genOutline(image, transform, mpool, tid)) return false;
     return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
     return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
 }
 }
 
 

+ 3 - 5
thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp

@@ -254,12 +254,10 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
 }
 }
 
 
 
 
-SwPoint mathTransform(const Point* to, const Matrix* transform)
+SwPoint mathTransform(const Point* to, const Matrix& transform)
 {
 {
-    if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
-
-    auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
-    auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
+    auto tx = to->x * transform.e11 + to->y * transform.e12 + transform.e13;
+    auto ty = to->x * transform.e21 + to->y * transform.e22 + transform.e23;
 
 
     return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
     return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
 }
 }

+ 70 - 36
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -194,10 +194,21 @@ static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a)
 }
 }
 
 
 
 
+static inline uint8_t _opMaskLighten(uint8_t s, uint8_t d, uint8_t a)
+{
+    return (s > d) ? s : d;
+}
+
+
+static inline uint8_t _opMaskDarken(uint8_t s, uint8_t d, uint8_t a)
+{
+    return (s < d) ? s : d;
+}
+
+
 static inline bool _direct(CompositeMethod method)
 static inline bool _direct(CompositeMethod method)
 {
 {
-    //subtract & Intersect allows the direct composition
-    if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
+    if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask || method == CompositeMethod::DarkenMask) return true;
     return false;
     return false;
 }
 }
 
 
@@ -209,6 +220,8 @@ static inline SwMask _getMaskOp(CompositeMethod method)
         case CompositeMethod::SubtractMask: return _opMaskSubtract;
         case CompositeMethod::SubtractMask: return _opMaskSubtract;
         case CompositeMethod::DifferenceMask: return _opMaskDifference;
         case CompositeMethod::DifferenceMask: return _opMaskDifference;
         case CompositeMethod::IntersectMask: return _opMaskIntersect;
         case CompositeMethod::IntersectMask: return _opMaskIntersect;
+        case CompositeMethod::LightenMask: return _opMaskLighten;
+        case CompositeMethod::DarkenMask: return _opMaskDarken;
         default: return nullptr;
         default: return nullptr;
     }
     }
 }
 }
@@ -832,7 +845,7 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
 }
 }
 
 
 
 
-static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
+static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
 {
 {
     if (surface->channelSize == sizeof(uint8_t)) {
     if (surface->channelSize == sizeof(uint8_t)) {
         TVGERR("SW_ENGINE", "Not supported scaled rle image!");
         TVGERR("SW_ENGINE", "Not supported scaled rle image!");
@@ -841,9 +854,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
 
 
     Matrix itransform;
     Matrix itransform;
 
 
-    if (transform) {
-        if (!mathInverse(transform, &itransform)) return false;
-    } else mathIdentity(&itransform);
+    if (!mathInverse(&transform, &itransform)) return true;
 
 
     if (_compositing(surface)) {
     if (_compositing(surface)) {
         if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
         if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
@@ -1197,13 +1208,11 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
 }
 }
 
 
 
 
-static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
+static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
 {
 {
     Matrix itransform;
     Matrix itransform;
 
 
-    if (transform) {
-        if (!mathInverse(transform, &itransform)) return false;
-    } else mathIdentity(&itransform);
+    if (!mathInverse(&transform, &itransform)) return true;
 
 
     if (_compositing(surface)) {
     if (_compositing(surface)) {
         if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
         if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
@@ -1389,29 +1398,45 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
 
 
 static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
 static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
 {
 {
-    if (surface->channelSize == sizeof(uint8_t)) {
-        TVGERR("SW_ENGINE", "Not supported grayscale image!");
-        return false;
-    }
-
-    auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
     auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
     auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
 
 
-    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));
+    //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));
+                }
             }
             }
-        } 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));
+            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);
+                }
+            } else {
+                for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
+                    *dst = INTERPOLATE8(*src, *dst, opacity);
+                }
             }
             }
         }
         }
-        dbuffer += surface->stride;
-        sbuffer += image->stride;
     }
     }
     return true;
     return true;
 }
 }
@@ -1433,7 +1458,7 @@ static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox&
 
 
 
 
 //Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
 //Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
-static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
+static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
 {
 {
     //RLE Image
     //RLE Image
     if (image->rle) {
     if (image->rle) {
@@ -1574,8 +1599,6 @@ static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, c
 
 
 static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
 {
 {
-    if (fill->linear.len < FLOAT_EPSILON) return false;
-
     if (_compositing(surface)) {
     if (_compositing(surface)) {
         if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill);
         if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill);
         else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill);
         else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill);
@@ -1902,10 +1925,16 @@ void rasterPremultiply(Surface* surface)
 }
 }
 
 
 
 
-bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
 {
 {
     if (!shape->fill) return false;
     if (!shape->fill) return false;
 
 
+    if (auto color = fillFetchSolid(shape->fill, fdata)) {
+        auto a = MULTIPLY(color->a, opacity);
+        return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
+    }
+
+    auto id = fdata->identifier();
     if (shape->fastTrack) {
     if (shape->fastTrack) {
         if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
         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);
         else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
@@ -1917,10 +1946,16 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
 }
 }
 
 
 
 
-bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
 {
 {
     if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
     if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
 
 
+    if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) {
+        auto a = MULTIPLY(color->a, opacity);
+        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);
     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);
     else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
 
 
@@ -1952,13 +1987,12 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
 }
 }
 
 
 
 
-bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity)
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
 {
 {
     //Outside of the viewport, skip the rendering
     //Outside of the viewport, skip the rendering
     if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
     if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
 
 
-    if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity);
-    else return _rasterImage(surface, image, transform, bbox, opacity);
+    return _rasterImage(surface, image, transform, bbox, opacity);
 }
 }
 
 
 
 

+ 29 - 83
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h

@@ -53,12 +53,10 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
         regionBottom = image->rle->spans[image->rle->size - 1].y;
         regionBottom = image->rle->spans[image->rle->size - 1].y;
     }
     }
 
 
-    if (yStart >= regionBottom) return false;
-
     if (yStart < regionTop) yStart = regionTop;
     if (yStart < regionTop) yStart = regionTop;
     if (yEnd > regionBottom) yEnd = regionBottom;
     if (yEnd > regionBottom) yEnd = regionBottom;
 
 
-    return true;
+    return yEnd > yStart;
 }
 }
 
 
 
 
@@ -868,10 +866,8 @@ static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t re
 
 
 static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
 static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
 {
 {
-    if (lines[y].length[eidx] < abs(x - x2)) {
-        lines[y].length[eidx] = abs(x - x2);
-        lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
-    }
+    lines[y].length[eidx] = abs(x - x2);
+    lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
 }
 }
 
 
 
 
@@ -897,9 +893,14 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
         ptx[1] = tx[1]; \
         ptx[1] = tx[1]; \
     } while (0)
     } while (0)
 
 
+    struct Point
+    {
+        int32_t x, y;
+    };
+
     int32_t y = 0;
     int32_t y = 0;
-    SwPoint pEdge = {-1, -1};       //previous edge point
-    SwPoint edgeDiff = {0, 0};      //temporary used for point distance
+    Point pEdge = {-1, -1};       //previous edge point
+    Point edgeDiff = {0, 0};      //temporary used for point distance
 
 
     /* store bigger to tx[0] between prev and current edge's x positions. */
     /* store bigger to tx[0] between prev and current edge's x positions. */
     int32_t tx[2] = {0, 0};
     int32_t tx[2] = {0, 0};
@@ -1024,6 +1025,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
 
 
 static bool _apply(SwSurface* surface, AASpans* aaSpans)
 static bool _apply(SwSurface* surface, AASpans* aaSpans)
 {
 {
+    auto end = surface->buf32 + surface->h * surface->stride;
     auto y = aaSpans->yStart;
     auto y = aaSpans->yStart;
     uint32_t pixel;
     uint32_t pixel;
     uint32_t* dst;
     uint32_t* dst;
@@ -1044,8 +1046,13 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
             dst = surface->buf32 + (offset + line->x[0]);
             dst = surface->buf32 + (offset + line->x[0]);
             if (line->x[0] > 1) pixel = *(dst - 1);
             if (line->x[0] > 1) pixel = *(dst - 1);
             else pixel = *dst;
             else pixel = *dst;
-
             pos = 1;
             pos = 1;
+
+            //exceptional handling. out of memory bound.
+            if (dst + line->length[0] >= end) {
+                pos += (dst + line->length[0] - end);
+            }
+
             while (pos <= line->length[0]) {
             while (pos <= line->length[0]) {
                 *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
                 *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
                 ++dst;
                 ++dst;
@@ -1053,17 +1060,21 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
             }
             }
 
 
             //Right edge
             //Right edge
-            dst = surface->buf32 + (offset + line->x[1] - 1);
+            dst = surface->buf32 + offset + line->x[1] - 1;
+
             if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
             if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
             else pixel = *dst;
             else pixel = *dst;
+            pos = line->length[1];
 
 
-            pos = width;
-            while ((int32_t)(width - line->length[1]) < pos) {
-                *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
+            //exceptional handling. out of memory bound.
+            if (dst - pos < surface->buf32) --pos;
+
+            while (pos > 0) {
+                *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
                 --dst;
                 --dst;
                 --pos;
                 --pos;
             }
             }
-          }
+        }
         y++;
         y++;
     }
     }
 
 
@@ -1084,7 +1095,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
     | /  |
     | /  |
     3 -- 2
     3 -- 2
 */
 */
-static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
 {
 {
     if (surface->channelSize == sizeof(uint8_t)) {
     if (surface->channelSize == sizeof(uint8_t)) {
         TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
         TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
@@ -1092,7 +1103,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
     }
     }
 
 
     //Exceptions: No dedicated drawing area?
     //Exceptions: No dedicated drawing area?
-    if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
+    if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
 
 
    /* Prepare vertices.
    /* Prepare vertices.
       shift XY coordinates to match the sub-pixeling technique. */
       shift XY coordinates to match the sub-pixeling technique. */
@@ -1104,7 +1115,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
 
 
     float ys = FLT_MAX, ye = -1.0f;
     float ys = FLT_MAX, ye = -1.0f;
     for (int i = 0; i < 4; i++) {
     for (int i = 0; i < 4; i++) {
-        if (transform) vertices[i].pt *= *transform;
+        vertices[i].pt *= transform;
         if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
         if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
         if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
         if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
     }
     }
@@ -1135,68 +1146,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
 #endif
 #endif
     return _apply(surface, aaSpans);
     return _apply(surface, aaSpans);
 }
 }
-
-
-/*
-    Provide any number of triangles to draw a mesh using the supplied image.
-    Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
-    Example:
-
-      0 -- 1       0 -- 1   0
-      |  / |  -->  |  /   / |
-      | /  |       | /   /  |
-      2 -- 3       2   1 -- 2
-
-      Should provide two Polygons, one for each triangle.
-      // TODO: region?
-*/
-static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
-{
-    if (surface->channelSize == sizeof(uint8_t)) {
-        TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
-        return false;
-    }
-
-    //Exceptions: No dedicated drawing area?
-    if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
-
-    // Step polygons once to transform
-    auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
-    float ys = FLT_MAX, ye = -1.0f;
-    for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
-        transformedTris[i] = mesh->triangles[i];
-        transformedTris[i].vertex[0].pt *= *transform;
-        transformedTris[i].vertex[1].pt *= *transform;
-        transformedTris[i].vertex[2].pt *= *transform;
-
-        if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
-        else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
-        if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
-        else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
-        if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
-        else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
-
-        // Convert normalized UV coordinates to image coordinates
-        transformedTris[i].vertex[0].uv.x *= (float)image->w;
-        transformedTris[i].vertex[0].uv.y *= (float)image->h;
-        transformedTris[i].vertex[1].uv.x *= (float)image->w;
-        transformedTris[i].vertex[1].uv.y *= (float)image->h;
-        transformedTris[i].vertex[2].uv.x *= (float)image->w;
-        transformedTris[i].vertex[2].uv.y *= (float)image->h;
-    }
-
-    // Get AA spans and step polygons again to draw
-    if (auto aaSpans = _AASpans(ys, ye, image, region)) {
-        for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
-            _rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
-        }
-#if 0
-        if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
-            _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
-        }
-#endif
-        _apply(surface, aaSpans);
-    }
-    free(transformedTris);
-    return true;
-}

+ 17 - 105
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp

@@ -39,7 +39,7 @@ struct SwTask : Task
     SwSurface* surface = nullptr;
     SwSurface* surface = nullptr;
     SwMpool* mpool = nullptr;
     SwMpool* mpool = nullptr;
     SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
     SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
-    Matrix* transform = nullptr;
+    Matrix transform;
     Array<RenderData> clips;
     Array<RenderData> clips;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
     uint8_t opacity;
     uint8_t opacity;
@@ -68,10 +68,7 @@ struct SwTask : Task
     virtual bool clip(SwRleData* target) = 0;
     virtual bool clip(SwRleData* target) = 0;
     virtual SwRleData* rle() = 0;
     virtual SwRleData* rle() = 0;
 
 
-    virtual ~SwTask()
-    {
-        free(transform);
-    }
+    virtual ~SwTask() {}
 };
 };
 
 
 
 
@@ -100,8 +97,7 @@ struct SwShapeTask : SwTask
         if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
         if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
         if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
         if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
 
 
-        if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
-        else return width;
+        return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
     }
     }
 
 
 
 
@@ -203,64 +199,10 @@ struct SwShapeTask : SwTask
 };
 };
 
 
 
 
-struct SwSceneTask : SwTask
-{
-    Array<RenderData> scene;    //list of paints render data (SwTask)
-    SwRleData* sceneRle = nullptr;
-
-    bool clip(SwRleData* target) override
-    {
-        //Only one shape
-        if (scene.count == 1) {
-            return static_cast<SwTask*>(*scene.data)->clip(target);
-        }
-
-        //More than one shapes
-        if (sceneRle) rleClipPath(target, sceneRle);
-        else TVGLOG("SW_ENGINE", "No clippers in a scene?");
-
-        return true;
-    }
-
-    SwRleData* rle() override
-    {
-        return sceneRle;
-    }
-
-    void run(unsigned tid) override
-    {
-        //TODO: Skip the run if the scene hans't changed.
-        if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
-        else rleReset(sceneRle);
-
-        //Merge shapes if it has more than one shapes
-        if (scene.count > 1) {
-            //Merge first two clippers
-            auto clipper1 = static_cast<SwTask*>(*scene.data);
-            auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
-
-            rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
-
-            //Unify the remained clippers
-            for (auto rd = scene.begin() + 2; rd < scene.end(); ++rd) {
-                auto clipper = static_cast<SwTask*>(*rd);
-                rleMerge(sceneRle, sceneRle, clipper->rle());
-            }
-        }
-    }
-
-    void dispose() override
-    {
-        rleFree(sceneRle);
-    }
-};
-
-
 struct SwImageTask : SwTask
 struct SwImageTask : SwTask
 {
 {
     SwImage image;
     SwImage image;
     Surface* source;                            //Image source
     Surface* source;                            //Image source
-    const RenderMesh* mesh = nullptr;           //Should be valid ptr in action
 
 
     bool clip(SwRleData* target) override
     bool clip(SwRleData* target) override
     {
     {
@@ -293,10 +235,9 @@ struct SwImageTask : SwTask
             imageReset(&image);
             imageReset(&image);
             if (!image.data || image.w == 0 || image.h == 0) goto end;
             if (!image.data || image.w == 0 || image.h == 0) goto end;
 
 
-            if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end;
+            if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
 
 
-            // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
-            if (mesh->triangleCnt == 0 && clips.count > 0) {
+            if (clips.count > 0) {
                 if (!imageGenRle(&image, bbox, false)) goto end;
                 if (!imageGenRle(&image, bbox, false)) goto end;
                 if (image.rle) {
                 if (image.rle) {
                     //Clear current task memorypool here if the clippers would use the same memory pool
                     //Clear current task memorypool here if the clippers would use the same memory pool
@@ -336,7 +277,7 @@ static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
 {
 {
     uint8_t r, g, b, a;
     uint8_t r, g, b, a;
     if (auto fill = task->rshape->fill) {
     if (auto fill = task->rshape->fill) {
-        rasterGradientShape(surface, &task->shape, fill->identifier());
+        rasterGradientShape(surface, &task->shape, fill, opacity);
     } else {
     } else {
         task->rshape->fillColor(&r, &g, &b, &a);
         task->rshape->fillColor(&r, &g, &b, &a);
         a = MULTIPLY(opacity, a);
         a = MULTIPLY(opacity, a);
@@ -348,7 +289,7 @@ static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity
 {
 {
     uint8_t r, g, b, a;
     uint8_t r, g, b, a;
     if (auto strokeFill = task->rshape->strokeFill()) {
     if (auto strokeFill = task->rshape->strokeFill()) {
-        rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
+        rasterGradientStroke(surface, &task->shape, strokeFill, opacity);
     } else {
     } else {
         if (task->rshape->strokeColor(&r, &g, &b, &a)) {
         if (task->rshape->strokeColor(&r, &g, &b, &a)) {
             a = MULTIPLY(opacity, a);
             a = MULTIPLY(opacity, a);
@@ -480,7 +421,7 @@ bool SwRenderer::renderImage(RenderData data)
 
 
     if (task->opacity == 0) return true;
     if (task->opacity == 0) return true;
 
 
-    return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
+    return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
 }
 }
 
 
 
 
@@ -688,7 +629,8 @@ bool SwRenderer::endComposite(Compositor* cmp)
 
 
     //Default is alpha blending
     //Default is alpha blending
     if (p->method == CompositeMethod::None) {
     if (p->method == CompositeMethod::None) {
-        return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity);
+        Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+        return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
     }
     }
 
 
     return true;
     return true;
@@ -714,7 +656,7 @@ void SwRenderer::dispose(RenderData data)
 }
 }
 
 
 
 
-void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
+void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
 {
     if (!surface) return task;
     if (!surface) return task;
     if (flags == RenderUpdateFlag::None) return task;
     if (flags == RenderUpdateFlag::None) return task;
@@ -727,20 +669,11 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
     }
     }
 
 
     task->clips = clips;
     task->clips = clips;
-
-    if (transform) {
-        if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
-        *task->transform = transform->m;
-    } else {
-        if (task->transform) free(task->transform);
-        task->transform = nullptr;
-    }
-
+    task->transform = transform;
+    
     //zero size?
     //zero size?
-    if (task->transform) {
-        if (task->transform->e11 == 0.0f && task->transform->e12 == 0.0f) return task; //zero width
-        if (task->transform->e21 == 0.0f && task->transform->e22 == 0.0f) return task; //zero height
-    }
+    if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
+    if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
 
 
     task->opacity = opacity;
     task->opacity = opacity;
     task->surface = surface;
     task->surface = surface;
@@ -762,7 +695,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
 }
 }
 
 
 
 
-RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
 {
     //prepare task
     //prepare task
     auto task = static_cast<SwImageTask*>(data);
     auto task = static_cast<SwImageTask*>(data);
@@ -770,33 +703,12 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
     else task->done();
     else task->done();
 
 
     task->source = surface;
     task->source = surface;
-    task->mesh = mesh;
-
-    return prepareCommon(task, transform, clips, opacity, flags);
-}
-
-
-RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
-{
-    //prepare task
-    auto task = static_cast<SwSceneTask*>(data);
-    if (!task) task = new SwSceneTask;
-    else task->done();
-
-    task->scene = scene;
-
-    //TODO: Failed threading them. It would be better if it's possible.
-    //See: https://github.com/thorvg/thorvg/issues/1409
-    //Guarantee composition targets get ready.
-    for (auto task = scene.begin(); task < scene.end(); ++task) {
-        static_cast<SwTask*>(*task)->done();
-    }
 
 
     return prepareCommon(task, transform, clips, opacity, flags);
     return prepareCommon(task, transform, clips, opacity, flags);
 }
 }
 
 
 
 
-RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
+RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
 {
 {
     //prepare task
     //prepare task
     auto task = static_cast<SwShapeTask*>(data);
     auto task = static_cast<SwShapeTask*>(data);

+ 3 - 4
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h

@@ -36,9 +36,8 @@ namespace tvg
 class SwRenderer : public RenderMethod
 class SwRenderer : public RenderMethod
 {
 {
 public:
 public:
-    RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
-    RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
-    RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
+    RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
+    RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
     bool preRender() override;
     bool preRender() override;
     bool renderShape(RenderData data) override;
     bool renderShape(RenderData data) override;
     bool renderImage(RenderData data) override;
     bool renderImage(RenderData data) override;
@@ -77,7 +76,7 @@ private:
     SwRenderer();
     SwRenderer();
     ~SwRenderer();
     ~SwRenderer();
 
 
-    RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
+    RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
 };
 };
 
 
 }
 }

+ 1 - 80
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp

@@ -358,7 +358,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
             rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
             rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
         }
         }
     }
     }
-
+        
     //Clip x range
     //Clip x range
     SwCoord xOver = 0;
     SwCoord xOver = 0;
     if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
     if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
@@ -822,46 +822,6 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
 }
 }
 
 
 
 
-static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
-{
-    auto out = outSpans;
-    auto spans1 = clip1->spans;
-    auto end1 = clip1->spans + clip1->size;
-    auto spans2 = clip2->spans;
-    auto end2 = clip2->spans + clip2->size;
-
-    //list two spans up in y order
-    //TODO: Remove duplicated regions?
-    while (spans1 < end1 && spans2 < end2) {
-        while (spans1 < end1 && spans1->y <= spans2->y) {
-            *out = *spans1;
-            ++spans1;
-            ++out;
-        }
-        if (spans1 >= end1) break;
-        while (spans2 < end2 && spans2->y <= spans1->y) {
-            *out = *spans2;
-            ++spans2;
-            ++out;
-        }
-    }
-
-    //Leftovers
-    while (spans1 < end1) {
-        *out = *spans1;
-        ++spans1;
-        ++out;
-    }
-    while (spans2 < end2) {
-        *out = *spans2;
-        ++spans2;
-        ++out;
-    }
-
-    return out;
-}
-
-
 void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
 void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
 {
 {
     free(rle->spans);
     free(rle->spans);
@@ -1030,45 +990,6 @@ void rleFree(SwRleData* rle)
 }
 }
 
 
 
 
-void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
-{
-    if (!rle || (!clip1 && !clip2)) return;
-    if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return;
-
-    TVGLOG("SW_ENGINE", "Unifying Rle!");
-
-    //clip1 is empty, just copy clip2
-    if (!clip1 || clip1->size == 0) {
-        if (clip2) {
-            auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip2->size)));
-            memcpy(spans, clip2->spans, clip2->size);
-            _replaceClipSpan(rle, spans, clip2->size);
-        } else {
-            _replaceClipSpan(rle, nullptr, 0);
-        }
-        return;
-    }
-
-    //clip2 is empty, just copy clip1
-    if (!clip2 || clip2->size == 0) {
-        if (clip1) {
-            auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip1->size)));
-            memcpy(spans, clip1->spans, clip1->size);
-            _replaceClipSpan(rle, spans, clip1->size);
-        } else {
-            _replaceClipSpan(rle, nullptr, 0);
-        }
-        return;
-    }
-
-    auto spanCnt = clip1->size + clip2->size;
-    auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * spanCnt));
-    auto spansEnd = _mergeSpansRegion(clip1, clip2, spans);
-
-    _replaceClipSpan(rle, spans, spansEnd - spans);
-}
-
-
 void rleClipPath(SwRleData *rle, const SwRleData *clip)
 void rleClipPath(SwRleData *rle, const SwRleData *clip)
 {
 {
     if (rle->size == 0 || clip->size == 0) return;
     if (rle->size == 0 || clip->size == 0) return;

+ 20 - 18
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -49,7 +49,7 @@ static bool _outlineEnd(SwOutline& outline)
 }
 }
 
 
 
 
-static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform, bool closed = false)
+static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix& transform, bool closed = false)
 {
 {
     //make it a contour, if the last contour is not closed yet.
     //make it a contour, if the last contour is not closed yet.
     if (!closed) _outlineEnd(outline);
     if (!closed) _outlineEnd(outline);
@@ -60,14 +60,14 @@ static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* tr
 }
 }
 
 
 
 
-static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
+static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix& transform)
 {
 {
     outline.pts.push(mathTransform(to, transform));
     outline.pts.push(mathTransform(to, transform));
     outline.types.push(SW_CURVE_TYPE_POINT);
     outline.types.push(SW_CURVE_TYPE_POINT);
 }
 }
 
 
 
 
-static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
 {
 {
     outline.pts.push(mathTransform(ctrl1, transform));
     outline.pts.push(mathTransform(ctrl1, transform));
     outline.types.push(SW_CURVE_TYPE_CUBIC);
     outline.types.push(SW_CURVE_TYPE_CUBIC);
@@ -99,7 +99,7 @@ static bool _outlineClose(SwOutline& outline)
 }
 }
 
 
 
 
-static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
+static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
 {
 {
     Line cur = {dash.ptCur, *to};
     Line cur = {dash.ptCur, *to};
     auto len = lineLength(cur.pt1, cur.pt2);
     auto len = lineLength(cur.pt1, cur.pt2);
@@ -160,7 +160,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
 }
 }
 
 
 
 
-static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
 {
 {
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
     auto len = bezLength(cur);
     auto len = bezLength(cur);
@@ -221,7 +221,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
 }
 }
 
 
 
 
-static void _dashClose(SwDashStroke& dash, const Matrix* transform)
+static void _dashClose(SwDashStroke& dash, const Matrix& transform)
 {
 {
     _dashLineTo(dash, &dash.ptStart, transform);
     _dashLineTo(dash, &dash.ptStart, transform);
 }
 }
@@ -245,10 +245,10 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
 }
 }
 
 
 
 
-static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
+static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length, float trimBegin, float trimEnd)
 {
 {
-    auto begin = length * rshape->stroke->trim.begin;
-    auto end = length * rshape->stroke->trim.end;
+    auto begin = length * trimBegin;
+    auto end = length * trimEnd;
 
 
     //default
     //default
     if (end > begin) {
     if (end > begin) {
@@ -324,7 +324,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
 }
 }
 
 
 
 
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& transform, bool trimmed, SwMpool* mpool, unsigned tid)
 {
 {
     const PathCommand* cmds = rshape->path.cmds.data;
     const PathCommand* cmds = rshape->path.cmds.data;
     auto cmdCnt = rshape->path.cmds.count;
     auto cmdCnt = rshape->path.cmds.count;
@@ -341,6 +341,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
     auto offset = 0.0f;
     auto offset = 0.0f;
     dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
     dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
     auto simultaneous = rshape->stroke->trim.simultaneous;
     auto simultaneous = rshape->stroke->trim.simultaneous;
+    float trimBegin = 0.0f, trimEnd = 1.0f;
+    if (trimmed) rshape->stroke->strokeTrim(trimBegin, trimEnd);
 
 
     if (dash.cnt == 0) {
     if (dash.cnt == 0) {
         if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
         if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
@@ -372,7 +374,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
 
 
     //must begin with moveTo
     //must begin with moveTo
     if (cmds[0] == PathCommand::MoveTo) {
     if (cmds[0] == PathCommand::MoveTo) {
-        if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
+        if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous), trimBegin, trimEnd);
         _dashMoveTo(dash, offIdx, offset, pts);
         _dashMoveTo(dash, offIdx, offset, pts);
         cmds++;
         cmds++;
         pts++;
         pts++;
@@ -387,7 +389,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
             case PathCommand::MoveTo: {
             case PathCommand::MoveTo: {
                 if (trimmed) {
                 if (trimmed) {
                     if (simultaneous) {
                     if (simultaneous) {
-                        _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
+                        _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true), trimBegin, trimEnd);
                         _dashMoveTo(dash, offIdx, offset, pts);
                         _dashMoveTo(dash, offIdx, offset, pts);
                     } else _dashMoveTo(dash, pts);
                     } else _dashMoveTo(dash, pts);
                 } else _dashMoveTo(dash, offIdx, offset, pts);
                 } else _dashMoveTo(dash, offIdx, offset, pts);
@@ -436,7 +438,7 @@ static bool _axisAlignedRect(const SwOutline* outline)
 }
 }
 
 
 
 
-static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
+static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool hasComposite)
 {
 {
     const PathCommand* cmds = rshape->path.cmds.data;
     const PathCommand* cmds = rshape->path.cmds.data;
     auto cmdCnt = rshape->path.cmds.count;
     auto cmdCnt = rshape->path.cmds.count;
@@ -492,7 +494,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform,  const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
+bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform,  const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
 {
 {
     if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
     if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
     if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
     if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
@@ -575,7 +577,7 @@ void shapeDelStroke(SwShape* shape)
 }
 }
 
 
 
 
-void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform)
+void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform)
 {
 {
     if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
     if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
     auto stroke = shape->stroke;
     auto stroke = shape->stroke;
@@ -586,7 +588,7 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* t
 }
 }
 
 
 
 
-bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
 {
 {
     SwOutline* shapeOutline = nullptr;
     SwOutline* shapeOutline = nullptr;
     SwOutline* strokeOutline = nullptr;
     SwOutline* strokeOutline = nullptr;
@@ -629,13 +631,13 @@ clear:
 }
 }
 
 
 
 
-bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
 {
     return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
     return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
 }
 }
 
 
 
 
-bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
 {
     return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
     return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
 }
 }

+ 3 - 8
thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp

@@ -805,15 +805,10 @@ void strokeFree(SwStroke* stroke)
 }
 }
 
 
 
 
-void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform)
+void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix& transform)
 {
 {
-    if (transform) {
-        stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
-        stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
-    } else {
-        stroke->sx = stroke->sy = 1.0f;
-    }
-
+    stroke->sx = sqrtf(powf(transform.e11, 2.0f) + powf(transform.e21, 2.0f));
+    stroke->sy = sqrtf(powf(transform.e12, 2.0f) + powf(transform.e22, 2.0f));
     stroke->width = HALF_STROKE(rshape->strokeWidth());
     stroke->width = HALF_STROKE(rshape->strokeWidth());
     stroke->cap = rshape->strokeCap();
     stroke->cap = rshape->strokeCap();
     stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);
     stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);

+ 31 - 10
thirdparty/thorvg/src/renderer/tvgAccessor.cpp

@@ -21,20 +21,21 @@
  */
  */
 
 
 #include "tvgIteratorAccessor.h"
 #include "tvgIteratorAccessor.h"
+#include "tvgCompressor.h"
 
 
 /************************************************************************/
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /* Internal Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func)
+static bool accessChildren(Iterator* it, function<bool(const Paint* paint, void* data)> func, void* data)
 {
 {
     while (auto child = it->next()) {
     while (auto child = it->next()) {
         //Access the child
         //Access the child
-        if (!func(child)) return false;
+        if (!func(child, data)) return false;
 
 
         //Access the children of the child
         //Access the children of the child
         if (auto it2 = IteratorAccessor::iterator(child)) {
         if (auto it2 = IteratorAccessor::iterator(child)) {
-            if (!accessChildren(it2, func)) {
+            if (!accessChildren(it2, func, data)) {
                 delete(it2);
                 delete(it2);
                 return false;
                 return false;
             }
             }
@@ -44,26 +45,46 @@ static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func
     return true;
     return true;
 }
 }
 
 
+
 /************************************************************************/
 /************************************************************************/
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
 
 
-unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept
+TVG_DEPRECATED unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept
+{
+    auto backward = [](const tvg::Paint* paint, void* data) -> bool
+    {
+        auto func = reinterpret_cast<function<bool(const Paint* paint)>*>(data);
+        if (!(*func)(paint)) return false;
+        return true;
+    };
+
+    set(picture.get(), backward, reinterpret_cast<void*>(&func));
+    return picture;
+}
+
+
+Result Accessor::set(const Picture* picture, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
 {
 {
-    auto p = picture.get();
-    if (!p || !func) return picture;
+    if (!picture || !func) return Result::InvalidArguments;
 
 
     //Use the Preorder Tree-Search
     //Use the Preorder Tree-Search
 
 
     //Root
     //Root
-    if (!func(p)) return picture;
+    if (!func(picture, data)) return Result::Success;
 
 
     //Children
     //Children
-    if (auto it = IteratorAccessor::iterator(p)) {
-        accessChildren(it, func);
+    if (auto it = IteratorAccessor::iterator(picture)) {
+        accessChildren(it, func, data);
         delete(it);
         delete(it);
     }
     }
-    return picture;
+    return Result::Success;
+}
+
+
+uint32_t Accessor::id(const char* name) noexcept
+{
+    return djb2Encode(name);
 }
 }
 
 
 
 

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgAnimation.cpp

@@ -95,7 +95,7 @@ float Animation::duration() const noexcept
 
 
 Result Animation::segment(float begin, float end) noexcept
 Result Animation::segment(float begin, float end) noexcept
 {
 {
-    if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments;
+    if (begin < 0.0 || end > 1.0 || begin > end) return Result::InvalidArguments;
 
 
     auto loader = pImpl->picture->pImpl->loader;
     auto loader = pImpl->picture->pImpl->loader;
     if (!loader) return Result::InsufficientCondition;
     if (!loader) return Result::InsufficientCondition;

+ 4 - 2
thirdparty/thorvg/src/renderer/tvgCanvas.h

@@ -93,11 +93,13 @@ struct Canvas::Impl
         auto flag = RenderUpdateFlag::None;
         auto flag = RenderUpdateFlag::None;
         if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
         if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
 
 
+        auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
+
         if (paint) {
         if (paint) {
-            paint->pImpl->update(renderer, nullptr, clips, 255, flag);
+            paint->pImpl->update(renderer, m, clips, 255, flag);
         } else {
         } else {
             for (auto paint : paints) {
             for (auto paint : paints) {
-                paint->pImpl->update(renderer, nullptr, clips, 255, flag);
+                paint->pImpl->update(renderer, m, clips, 255, flag);
             }
             }
         }
         }
         status = Status::Updating;
         status = Status::Updating;

+ 21 - 21
thirdparty/thorvg/src/renderer/tvgInitializer.cpp

@@ -54,36 +54,30 @@ static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
     return int(a) & int(b);
     return int(a) & int(b);
 }
 }
 
 
-static bool _buildVersionInfo()
+static bool _buildVersionInfo(uint32_t* major, uint32_t* minor, uint32_t* micro)
 {
 {
-    auto SRC = THORVG_VERSION_STRING;   //ex) 0.3.99
-    auto p = SRC;
+    auto VER = THORVG_VERSION_STRING;
+    auto p = VER;
     const char* x;
     const char* x;
 
 
-    char major[3];
-    x = strchr(p, '.');
-    if (!x) return false;
-    memcpy(major, p, x - p);
-    major[x - p] = '\0';
+    if (!(x = strchr(p, '.'))) return false;
+    uint32_t majorVal = atoi(p);
     p = x + 1;
     p = x + 1;
 
 
-    char minor[3];
-    x = strchr(p, '.');
-    if (!x) return false;
-    memcpy(minor, p, x - p);
-    minor[x - p] = '\0';
+    if (!(x = strchr(p, '.'))) return false;
+    uint32_t minorVal = atoi(p);
     p = x + 1;
     p = x + 1;
 
 
-    char micro[3];
-    x = SRC + strlen(THORVG_VERSION_STRING);
-    memcpy(micro, p, x - p);
-    micro[x - p] = '\0';
+    uint32_t microVal = atoi(p);
 
 
     char sum[7];
     char sum[7];
-    snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro);
-
+    snprintf(sum, sizeof(sum), "%d%02d%02d", majorVal, minorVal, microVal);
     _version = atoi(sum);
     _version = atoi(sum);
 
 
+    if (major) *major = majorVal;
+    if (minor) *minor = minorVal;
+    if (micro) *micro = microVal;
+
     return true;
     return true;
 }
 }
 
 
@@ -122,7 +116,7 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
 
 
     if (_initCnt++ > 0) return Result::Success;
     if (_initCnt++ > 0) return Result::Success;
 
 
-    if (!_buildVersionInfo()) return Result::Unknown;
+    if (!_buildVersionInfo(nullptr, nullptr, nullptr)) return Result::Unknown;
 
 
     if (!LoaderMgr::init()) return Result::Unknown;
     if (!LoaderMgr::init()) return Result::Unknown;
 
 
@@ -172,8 +166,14 @@ Result Initializer::term(CanvasEngine engine) noexcept
 }
 }
 
 
 
 
+const char* Initializer::version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept
+{
+    if ((!major && ! minor && !micro) || _buildVersionInfo(major, minor, micro)) return THORVG_VERSION_STRING;
+    return nullptr;
+}
+
+
 uint16_t THORVG_VERSION_NUMBER()
 uint16_t THORVG_VERSION_NUMBER()
 {
 {
     return _version;
     return _version;
 }
 }
-

+ 72 - 70
thirdparty/thorvg/src/renderer/tvgPaint.cpp

@@ -41,7 +41,7 @@
     }
     }
 
 
 
 
-static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before)
+static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& pm, const Matrix& rm, RenderRegion& before)
 {
 {
     //sorting
     //sorting
     Point tmp[4];
     Point tmp[4];
@@ -50,8 +50,8 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr
 
 
     for (int i = 0; i < 4; ++i) {
     for (int i = 0; i < 4; ++i) {
         tmp[i] = pts[i];
         tmp[i] = pts[i];
-        if (rTransform) tmp[i] *= rTransform->m;
-        if (pTransform) tmp[i] *= pTransform->m;
+        tmp[i] *= rm;
+        tmp[i] *= pm;
         if (tmp[i].x < min.x) min.x = tmp[i].x;
         if (tmp[i].x < min.x) min.x = tmp[i].x;
         if (tmp[i].x > max.x) max.x = tmp[i].x;
         if (tmp[i].x > max.x) max.x = tmp[i].x;
         if (tmp[i].y < min.y) min.y = tmp[i].y;
         if (tmp[i].y < min.y) min.y = tmp[i].y;
@@ -73,7 +73,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr
 }
 }
 
 
 
 
-static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before)
+static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before)
 {
 {
     /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
     /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
     auto shape = static_cast<Shape*>(cmpTarget);
     auto shape = static_cast<Shape*>(cmpTarget);
@@ -84,18 +84,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
 
 
     //nothing to clip
     //nothing to clip
     if (ptsCnt == 0) return Result::InvalidArguments;
     if (ptsCnt == 0) return Result::InvalidArguments;
-
     if (ptsCnt != 4) return Result::InsufficientCondition;
     if (ptsCnt != 4) return Result::InsufficientCondition;
 
 
-    if (rTransform && (cmpTarget->pImpl->renderFlag & RenderUpdateFlag::Transform)) rTransform->update();
+    auto& rm = P(cmpTarget)->transform();
 
 
     //No rotation and no skewing, still can try out clipping the rect region.
     //No rotation and no skewing, still can try out clipping the rect region.
     auto tryClip = false;
     auto tryClip = false;
 
 
-    if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true;
-    if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true;
+    if ((!mathRightAngle(pm) || mathSkewed(pm))) tryClip = true;
+    if ((!mathRightAngle(rm) || mathSkewed(rm))) tryClip = true;
 
 
-    if (tryClip) return _clipRect(renderer, pts, pTransform, rTransform, before);
+    if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
 
 
     //Perpendicular Rectangle?
     //Perpendicular Rectangle?
     auto pt1 = pts + 0;
     auto pt1 = pts + 0;
@@ -110,16 +109,10 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
 
 
         auto v1 = *pt1;
         auto v1 = *pt1;
         auto v2 = *pt3;
         auto v2 = *pt3;
-
-        if (rTransform) {
-            v1 *= rTransform->m;
-            v2 *= rTransform->m;
-        }
-
-        if (pTransform) {
-            v1 *= pTransform->m;
-            v2 *= pTransform->m;
-        }
+        v1 *= rm;
+        v2 *= rm;
+        v1 *= pm;
+        v2 *= pm;
 
 
         //sorting
         //sorting
         if (v1.x > v2.x) std::swap(v1.x, v2.x);
         if (v1.x > v2.x) std::swap(v1.x, v2.x);
@@ -158,17 +151,15 @@ Iterator* Paint::Impl::iterator()
 }
 }
 
 
 
 
-Paint* Paint::Impl::duplicate()
+Paint* Paint::Impl::duplicate(Paint* ret)
 {
 {
-    Paint* ret;
-    PAINT_METHOD(ret, duplicate());
+    if (ret) ret->composite(nullptr, CompositeMethod::None);
+
+    PAINT_METHOD(ret, duplicate(ret));
 
 
     //duplicate Transform
     //duplicate Transform
-    if (rTransform) {
-        ret->pImpl->rTransform = new RenderTransform();
-        *ret->pImpl->rTransform = *rTransform;
-        ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
-    }
+    ret->pImpl->tr = tr;
+    ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
 
 
     ret->pImpl->opacity = opacity;
     ret->pImpl->opacity = opacity;
 
 
@@ -180,14 +171,9 @@ Paint* Paint::Impl::duplicate()
 
 
 bool Paint::Impl::rotate(float degree)
 bool Paint::Impl::rotate(float degree)
 {
 {
-    if (rTransform) {
-        if (rTransform->overriding) return false;
-        if (mathEqual(degree, rTransform->degree)) return true;
-    } else {
-        if (mathZero(degree)) return true;
-        rTransform = new RenderTransform();
-    }
-    rTransform->degree = degree;
+    if (tr.overriding) return false;
+    if (mathEqual(degree, tr.degree)) return true;
+    tr.degree = degree;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
 
 
     return true;
     return true;
@@ -196,14 +182,9 @@ bool Paint::Impl::rotate(float degree)
 
 
 bool Paint::Impl::scale(float factor)
 bool Paint::Impl::scale(float factor)
 {
 {
-    if (rTransform) {
-        if (rTransform->overriding) return false;
-        if (mathEqual(factor, rTransform->scale)) return true;
-    } else {
-        if (mathEqual(factor, 1.0f)) return true;
-        rTransform = new RenderTransform();
-    }
-    rTransform->scale = factor;
+    if (tr.overriding) return false;
+    if (mathEqual(factor, tr.scale)) return true;
+    tr.scale = factor;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
 
 
     return true;
     return true;
@@ -212,15 +193,10 @@ bool Paint::Impl::scale(float factor)
 
 
 bool Paint::Impl::translate(float x, float y)
 bool Paint::Impl::translate(float x, float y)
 {
 {
-    if (rTransform) {
-        if (rTransform->overriding) return false;
-        if (mathEqual(x, rTransform->m.e13) && mathEqual(y, rTransform->m.e23)) return true;
-    } else {
-        if (mathZero(x) && mathZero(y)) return true;
-        rTransform = new RenderTransform();
-    }
-    rTransform->m.e13 = x;
-    rTransform->m.e23 = y;
+    if (tr.overriding) return false;
+    if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true;
+    tr.m.e13 = x;
+    tr.m.e23 = y;
     renderFlag |= RenderUpdateFlag::Transform;
     renderFlag |= RenderUpdateFlag::Transform;
 
 
     return true;
     return true;
@@ -229,6 +205,8 @@ bool Paint::Impl::translate(float x, float y)
 
 
 bool Paint::Impl::render(RenderMethod* renderer)
 bool Paint::Impl::render(RenderMethod* renderer)
 {
 {
+    if (opacity == 0) return true;
+
     Compositor* cmp = nullptr;
     Compositor* cmp = nullptr;
 
 
     /* Note: only ClipPath is processed in update() step.
     /* Note: only ClipPath is processed in update() step.
@@ -258,7 +236,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
 }
 }
 
 
 
 
-RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
 {
 {
     if (this->renderer != renderer) {
     if (this->renderer != renderer) {
         if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!");
         if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!");
@@ -266,7 +244,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
         this->renderer = renderer;
         this->renderer = renderer;
     }
     }
 
 
-    if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
+    if (renderFlag & RenderUpdateFlag::Transform) tr.update();
 
 
     /* 1. Composition Pre Processing */
     /* 1. Composition Pre Processing */
     RenderData trd = nullptr;                 //composite target render data
     RenderData trd = nullptr;                 //composite target render data
@@ -277,7 +255,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
     if (compData) {
     if (compData) {
         auto target = compData->target;
         auto target = compData->target;
         auto method = compData->method;
         auto method = compData->method;
-        target->pImpl->ctxFlag &= ~ContextFlag::FastTrack;   //reset
+        P(target)->ctxFlag &= ~ContextFlag::FastTrack;   //reset
 
 
         /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
         /* 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. */
            we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
@@ -296,14 +274,14 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
             }
             }
             if (tryFastTrack) {
             if (tryFastTrack) {
                 viewport = renderer->viewport();
                 viewport = renderer->viewport();
-                if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) {
-                    target->pImpl->ctxFlag |= ContextFlag::FastTrack;
+                if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
+                    P(target)->ctxFlag |= ContextFlag::FastTrack;
                 }
                 }
             }
             }
         }
         }
         if (compFastTrack == Result::InsufficientCondition) {
         if (compFastTrack == Result::InsufficientCondition) {
             childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
             childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
-            trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper);
+            trd = P(target)->update(renderer, pm, clips, 255, pFlag, childClipper);
             if (childClipper) clips.push(trd);
             if (childClipper) clips.push(trd);
         }
         }
     }
     }
@@ -314,8 +292,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
     opacity = MULTIPLY(opacity, this->opacity);
     opacity = MULTIPLY(opacity, this->opacity);
 
 
     RenderData rd = nullptr;
     RenderData rd = nullptr;
-    RenderTransform outTransform(pTransform, rTransform);
-    PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
+
+    tr.cm = pm * tr.m;
+    PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
 
 
     /* 3. Composition Post Processing */
     /* 3. Composition Post Processing */
     if (compFastTrack == Result::Success) renderer->viewport(viewport);
     if (compFastTrack == Result::Success) renderer->viewport(viewport);
@@ -325,13 +304,13 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
 }
 }
 
 
 
 
-bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
+bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin)
 {
 {
-    Matrix* m = nullptr;
     bool ret;
     bool ret;
+    const auto& m = this->transform(origin);
 
 
     //Case: No transformed, quick return!
     //Case: No transformed, quick return!
-    if (!transformed || !(m = this->transform())) {
+    if (!transformed || mathIdentity(&m)) {
         PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
         PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
         return ret;
         return ret;
     }
     }
@@ -355,7 +334,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
 
 
     //Compute the AABB after transformation
     //Compute the AABB after transformation
     for (int i = 0; i < 4; i++) {
     for (int i = 0; i < 4; i++) {
-        pt[i] *= *m;
+        pt[i] *= m;
 
 
         if (pt[i].x < x1) x1 = pt[i].x;
         if (pt[i].x < x1) x1 = pt[i].x;
         if (pt[i].x > x2) x2 = pt[i].x;
         if (pt[i].x > x2) x2 = pt[i].x;
@@ -372,6 +351,26 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
 }
 }
 
 
 
 
+void Paint::Impl::reset()
+{
+    if (compData) {
+        if (P(compData->target)->unref() == 0) delete(compData->target);
+        free(compData);
+        compData = nullptr;
+    }
+    mathIdentity(&tr.m);
+    tr.degree = 0.0f;
+    tr.scale = 1.0f;
+    tr.overriding = false;
+
+    blendMethod = BlendMethod::Normal;
+    renderFlag = RenderUpdateFlag::None;
+    ctxFlag = ContextFlag::Invalid;
+    opacity = 255;
+    paint->id = 0;
+}
+
+
 /************************************************************************/
 /************************************************************************/
 /* External Class Implementation                                        */
 /* External Class Implementation                                        */
 /************************************************************************/
 /************************************************************************/
@@ -417,9 +416,7 @@ Result Paint::transform(const Matrix& m) noexcept
 
 
 Matrix Paint::transform() noexcept
 Matrix Paint::transform() noexcept
 {
 {
-    auto pTransform = pImpl->transform();
-    if (pTransform) return *pTransform;
-    return {1, 0, 0, 0, 1, 0, 0, 0, 1};
+    return pImpl->transform();
 }
 }
 
 
 
 
@@ -429,9 +426,9 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
 }
 }
 
 
 
 
-Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
+Result Paint::bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept
 {
 {
-    if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
+    if (pImpl->bounds(x, y, w, h, transformed, true, transformed)) return Result::Success;
     return Result::InsufficientCondition;
     return Result::InsufficientCondition;
 }
 }
 
 
@@ -444,6 +441,11 @@ Paint* Paint::duplicate() const noexcept
 
 
 Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
 Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
 {
 {
+    if (method == CompositeMethod::ClipPath && target && target->identifier() != TVG_CLASS_ID_SHAPE) {
+        TVGERR("RENDERER", "ClipPath only allows the Shape!");
+        return Result::NonSupport;
+    }
+
     auto p = target.release();
     auto p = target.release();
     if (pImpl->composite(this, p, method)) return Result::Success;
     if (pImpl->composite(this, p, method)) return Result::Success;
     delete(p);
     delete(p);
@@ -486,7 +488,7 @@ uint32_t Paint::identifier() const noexcept
 }
 }
 
 
 
 
-Result Paint::blend(BlendMethod method) const noexcept
+Result Paint::blend(BlendMethod method) noexcept
 {
 {
     if (pImpl->blendMethod != method) {
     if (pImpl->blendMethod != method) {
         pImpl->blendMethod = method;
         pImpl->blendMethod = method;

+ 41 - 22
thirdparty/thorvg/src/renderer/tvgPaint.h

@@ -48,17 +48,40 @@ namespace tvg
     struct Paint::Impl
     struct Paint::Impl
     {
     {
         Paint* paint = nullptr;
         Paint* paint = nullptr;
-        RenderTransform* rTransform = nullptr;
         Composite* compData = nullptr;
         Composite* compData = nullptr;
         RenderMethod* renderer = nullptr;
         RenderMethod* renderer = nullptr;
-        BlendMethod blendMethod = BlendMethod::Normal;   //uint8_t
-        uint8_t renderFlag = RenderUpdateFlag::None;
-        uint8_t ctxFlag = ContextFlag::Invalid;
-        uint8_t id;
-        uint8_t opacity = 255;
+        struct {
+            Matrix m;                 //input matrix
+            Matrix cm;                //multipled parents matrix
+            float degree;             //rotation degree
+            float scale;              //scale factor
+            bool overriding;          //user transform?
+
+            void update()
+            {
+                if (overriding) return;
+                m.e11 = 1.0f;
+                m.e12 = 0.0f;
+                m.e21 = 0.0f;
+                m.e22 = 1.0f;
+                m.e31 = 0.0f;
+                m.e32 = 0.0f;
+                m.e33 = 1.0f;
+                mathScale(&m, scale, scale);
+                mathRotate(&m, degree);
+            }
+        } tr;
+        BlendMethod blendMethod;
+        uint8_t renderFlag;
+        uint8_t ctxFlag;
+        uint8_t opacity;
         uint8_t refCnt = 0;                              //reference count
         uint8_t refCnt = 0;                              //reference count
+        uint8_t id;         //TODO: deprecated, remove it
 
 
-        Impl(Paint* pnt) : paint(pnt) {}
+        Impl(Paint* pnt) : paint(pnt)
+        {
+            reset();
+        }
 
 
         ~Impl()
         ~Impl()
         {
         {
@@ -66,7 +89,6 @@ namespace tvg
                 if (P(compData->target)->unref() == 0) delete(compData->target);
                 if (P(compData->target)->unref() == 0) delete(compData->target);
                 free(compData);
                 free(compData);
             }
             }
-            delete(rTransform);
             if (renderer && (renderer->unref() == 0)) delete(renderer);
             if (renderer && (renderer->unref() == 0)) delete(renderer);
         }
         }
 
 
@@ -84,23 +106,19 @@ namespace tvg
 
 
         bool transform(const Matrix& m)
         bool transform(const Matrix& m)
         {
         {
-            if (!rTransform) {
-                if (mathIdentity(&m)) return true;
-                rTransform = new RenderTransform();
-            }
-            rTransform->override(m);
+            tr.m = m;
+            tr.overriding = true;
             renderFlag |= RenderUpdateFlag::Transform;
             renderFlag |= RenderUpdateFlag::Transform;
 
 
             return true;
             return true;
         }
         }
 
 
-        Matrix* transform()
+        Matrix& transform(bool origin = false)
         {
         {
-            if (rTransform) {
-                if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
-                return &rTransform->m;
-            }
-            return nullptr;
+            //update transform
+            if (renderFlag & RenderUpdateFlag::Transform) tr.update();
+            if (origin) return tr.cm;
+            return tr.m;
         }
         }
 
 
         bool composite(Paint* source, Paint* target, CompositeMethod method)
         bool composite(Paint* source, Paint* target, CompositeMethod method)
@@ -135,10 +153,11 @@ namespace tvg
         bool rotate(float degree);
         bool rotate(float degree);
         bool scale(float factor);
         bool scale(float factor);
         bool translate(float x, float y);
         bool translate(float x, float y);
-        bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
-        RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
+        bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false);
+        RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
         bool render(RenderMethod* renderer);
         bool render(RenderMethod* renderer);
-        Paint* duplicate();
+        Paint* duplicate(Paint* ret = nullptr);
+        void reset();
     };
     };
 }
 }
 
 

+ 17 - 26
thirdparty/thorvg/src/renderer/tvgPicture.cpp

@@ -104,21 +104,6 @@ RenderRegion Picture::Impl::bounds(RenderMethod* renderer)
 }
 }
 
 
 
 
-RenderTransform Picture::Impl::resizeTransform(const RenderTransform* pTransform)
-{
-    //Overriding Transformation by the desired image size
-    auto sx = w / loader->w;
-    auto sy = h / loader->h;
-    auto scale = sx < sy ? sx : sy;
-
-    RenderTransform tmp;
-    tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
-
-    if (!pTransform) return tmp;
-    else return RenderTransform(pTransform, &tmp);
-}
-
-
 Result Picture::Impl::load(ImageLoader* loader)
 Result Picture::Impl::load(ImageLoader* loader)
 {
 {
     //Same resource has been loaded.
     //Same resource has been loaded.
@@ -215,18 +200,24 @@ Result Picture::size(float* w, float* h) const noexcept
 }
 }
 
 
 
 
-Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept
+const Paint* Picture::paint(uint32_t id) noexcept
 {
 {
-    if (!triangles && triangleCnt > 0) return Result::InvalidArguments;
-    if (triangles && triangleCnt == 0) return Result::InvalidArguments;
-
-    pImpl->mesh(triangles, triangleCnt);
-    return Result::Success;
-}
+    struct Value
+    {
+        uint32_t id;
+        const Paint* ret;
+    } value = {id, nullptr};
 
 
+    auto cb = [](const tvg::Paint* paint, void* data) -> bool
+    {
+        auto p = static_cast<Value*>(data);
+        if (p->id == paint->id) {
+            p->ret = paint;
+            return false;
+        }
+        return true;
+    };
 
 
-uint32_t Picture::mesh(const Polygon** triangles) const noexcept
-{
-    if (triangles) *triangles = pImpl->rm.triangles;
-    return pImpl->rm.triangleCnt;
+    tvg::Accessor::gen()->set(this, cb, &value);
+    return value.ret;
 }
 }

+ 20 - 60
thirdparty/thorvg/src/renderer/tvgPicture.h

@@ -63,12 +63,10 @@ struct Picture::Impl
     Surface* surface = nullptr;       //bitmap picture uses
     Surface* surface = nullptr;       //bitmap picture uses
     RenderData rd = nullptr;          //engine data
     RenderData rd = nullptr;          //engine data
     float w = 0, h = 0;
     float w = 0, h = 0;
-    RenderMesh rm;                    //mesh data
     Picture* picture = nullptr;
     Picture* picture = nullptr;
     bool resizing = false;
     bool resizing = false;
     bool needComp = false;            //need composition
     bool needComp = false;            //need composition
 
 
-    RenderTransform resizeTransform(const RenderTransform* pTransform);
     bool needComposition(uint8_t opacity);
     bool needComposition(uint8_t opacity);
     bool render(RenderMethod* renderer);
     bool render(RenderMethod* renderer);
     bool size(float w, float h);
     bool size(float w, float h);
@@ -90,58 +88,37 @@ struct Picture::Impl
         delete(paint);
         delete(paint);
     }
     }
 
 
-    RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+    RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
     {
     {
         auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
         auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
 
 
         if (surface) {
         if (surface) {
             if (flag == RenderUpdateFlag::None) return rd;
             if (flag == RenderUpdateFlag::None) return rd;
-            auto transform = resizeTransform(pTransform);
-            rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, flag);
+
+            //Overriding Transformation by the desired image size
+            auto sx = w / loader->w;
+            auto sy = h / loader->h;
+            auto scale = sx < sy ? sx : sy;
+            auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1};
+
+            rd = renderer->prepare(surface, rd, m, clips, opacity, flag);
         } else if (paint) {
         } else if (paint) {
             if (resizing) {
             if (resizing) {
                 loader->resize(paint, w, h);
                 loader->resize(paint, w, h);
                 resizing = false;
                 resizing = false;
             }
             }
             needComp = needComposition(opacity) ? true : false;
             needComp = needComposition(opacity) ? true : false;
-            rd = paint->pImpl->update(renderer, pTransform, clips, opacity, flag, clipper);
+            rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
         }
         }
         return rd;
         return rd;
     }
     }
 
 
     bool bounds(float* x, float* y, float* w, float* h, bool stroking)
     bool bounds(float* x, float* y, float* w, float* h, bool stroking)
     {
     {
-        if (rm.triangleCnt > 0) {
-            auto triangles = rm.triangles;
-            auto min = triangles[0].vertex[0].pt;
-            auto max = triangles[0].vertex[0].pt;
-
-            for (uint32_t i = 0; i < rm.triangleCnt; ++i) {
-                if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
-                else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
-                if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
-                else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
-
-                if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
-                else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
-                if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
-                else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
-
-                if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
-                else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
-                if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
-                else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
-            }
-            if (x) *x = min.x;
-            if (y) *y = min.y;
-            if (w) *w = max.x - min.x;
-            if (h) *h = max.y - min.y;
-        } else {
-            if (x) *x = 0;
-            if (y) *y = 0;
-            if (w) *w = this->w;
-            if (h) *h = this->h;
-        }
+        if (x) *x = 0;
+        if (y) *y = 0;
+        if (w) *w = this->w;
+        if (h) *h = this->h;
         return true;
         return true;
     }
     }
 
 
@@ -176,32 +153,21 @@ struct Picture::Impl
         return load(loader);
         return load(loader);
     }
     }
 
 
-    void mesh(const Polygon* triangles, const uint32_t triangleCnt)
+    Paint* duplicate(Paint* ret)
     {
     {
-        if (triangles && triangleCnt > 0) {
-            this->rm.triangleCnt = triangleCnt;
-            this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
-            memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
-        } else {
-            free(this->rm.triangles);
-            this->rm.triangles = nullptr;
-            this->rm.triangleCnt = 0;
-        }
-    }
+        if (ret) TVGERR("RENDERER", "TODO: duplicate()");
 
 
-    Paint* duplicate()
-    {
         load();
         load();
 
 
-        auto ret = Picture::gen().release();
-        auto dup = ret->pImpl;
+        auto picture = Picture::gen().release();
+        auto dup = picture->pImpl;
 
 
         if (paint) dup->paint = paint->duplicate();
         if (paint) dup->paint = paint->duplicate();
 
 
         if (loader) {
         if (loader) {
             dup->loader = loader;
             dup->loader = loader;
             ++dup->loader->sharing;
             ++dup->loader->sharing;
-            PP(ret)->renderFlag |= RenderUpdateFlag::Image;
+            PP(picture)->renderFlag |= RenderUpdateFlag::Image;
         }
         }
 
 
         dup->surface = surface;
         dup->surface = surface;
@@ -209,13 +175,7 @@ struct Picture::Impl
         dup->h = h;
         dup->h = h;
         dup->resizing = resizing;
         dup->resizing = resizing;
 
 
-        if (rm.triangleCnt > 0) {
-            dup->rm.triangleCnt = rm.triangleCnt;
-            dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
-            memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
-        }
-
-        return ret;
+        return picture;
     }
     }
 
 
     Iterator* iterator()
     Iterator* iterator()

+ 0 - 35
thirdparty/thorvg/src/renderer/tvgRender.cpp

@@ -46,41 +46,6 @@ uint32_t RenderMethod::unref()
 }
 }
 
 
 
 
-void RenderTransform::override(const Matrix& m)
-{
-    this->m = m;
-    overriding = true;
-}
-
-
-void RenderTransform::update()
-{
-    if (overriding) return;
-
-    m.e11 = 1.0f;
-    m.e12 = 0.0f;
-
-    m.e21 = 0.0f;
-    m.e22 = 1.0f;
-
-    m.e31 = 0.0f;
-    m.e32 = 0.0f;
-    m.e33 = 1.0f;
-
-    mathScale(&m, scale, scale);
-    mathRotate(&m, degree);
-}
-
-
-RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
-{
-    if (lhs && rhs) m = lhs->m * rhs->m;
-    else if (lhs) m = lhs->m;
-    else if (rhs) m = rhs->m;
-    else mathIdentity(&m);
-}
-
-
 void RenderRegion::intersect(const RenderRegion& rhs)
 void RenderRegion::intersect(const RenderRegion& rhs)
 {
 {
     auto x1 = x + w;
     auto x1 = x + w;

+ 66 - 29
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -23,6 +23,7 @@
 #ifndef _TVG_RENDER_H_
 #ifndef _TVG_RENDER_H_
 #define _TVG_RENDER_H_
 #define _TVG_RENDER_H_
 
 
+#include <math.h>
 #include "tvgCommon.h"
 #include "tvgCommon.h"
 #include "tvgArray.h"
 #include "tvgArray.h"
 #include "tvgLock.h"
 #include "tvgLock.h"
@@ -85,15 +86,15 @@ struct Compositor
     uint8_t        opacity;
     uint8_t        opacity;
 };
 };
 
 
-struct RenderMesh
+struct Vertex
 {
 {
-    Polygon* triangles = nullptr;
-    uint32_t triangleCnt = 0;
+   Point pt;
+   Point uv;
+};
 
 
-    ~RenderMesh()
-    {
-        free(triangles);
-    }
+struct Polygon
+{
+   Vertex vertex[3];
 };
 };
 
 
 struct RenderRegion
 struct RenderRegion
@@ -110,24 +111,6 @@ struct RenderRegion
     }
     }
 };
 };
 
 
-struct RenderTransform
-{
-    Matrix m;
-    float degree = 0.0f;  //rotation degree
-    float scale = 1.0f;   //scale factor
-    bool overriding = false;  //user transform?
-
-    void update();
-    void override(const Matrix& m);
-
-    RenderTransform()
-    {
-        m.e13 = m.e23 = 0.0f;
-    }
-
-    RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
-};
-
 struct RenderStroke
 struct RenderStroke
 {
 {
     float width = 0.0f;
     float width = 0.0f;
@@ -147,6 +130,57 @@ struct RenderStroke
         bool simultaneous = true;
         bool simultaneous = true;
     } trim;
     } trim;
 
 
+    void operator=(const RenderStroke& rhs)
+    {
+        width = rhs.width;
+
+        memcpy(color, rhs.color, sizeof(color));
+
+        delete(fill);
+        if (rhs.fill) fill = rhs.fill->duplicate();
+        else fill = nullptr;
+
+        free(dashPattern);
+        if (rhs.dashCnt > 0) {
+            dashPattern = static_cast<float*>(malloc(sizeof(float) * rhs.dashCnt));
+            memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt);
+        } else {
+            dashPattern = nullptr;
+        }
+        dashCnt = rhs.dashCnt;
+        miterlimit = rhs.miterlimit;
+        cap = rhs.cap;
+        join = rhs.join;
+        strokeFirst = rhs.strokeFirst;
+        trim = rhs.trim;
+    }
+
+    bool strokeTrim(float& begin, float& end) const
+    {
+        begin = trim.begin;
+        end = trim.end;
+
+        if (fabsf(end - begin) >= 1.0f) {
+            begin = 0.0f;
+            end = 1.0f;
+            return false;
+        }
+
+        auto loop = true;
+
+        if (begin > 1.0f && end > 1.0f) loop = false;
+        if (begin < 0.0f && end < 0.0f) loop = false;
+        if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f  && end <= 1.0f) loop = false;
+
+        if (begin > 1.0f) begin -= 1.0f;
+        if (begin < 0.0f) begin += 1.0f;
+        if (end > 1.0f) end -= 1.0f;
+        if (end < 0.0f) end += 1.0f;
+
+        if ((loop && begin < end) || (!loop && begin > end)) std::swap(begin, end);
+        return true;
+    }
+
     ~RenderStroke()
     ~RenderStroke()
     {
     {
         free(dashPattern);
         free(dashPattern);
@@ -191,7 +225,7 @@ struct RenderShape
     {
     {
         if (!stroke) return false;
         if (!stroke) return false;
         if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
         if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
-        if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
+        if (fabsf(stroke->trim.end - stroke->trim.begin) >= 1.0f) return false;
         return true;
         return true;
     }
     }
 
 
@@ -252,9 +286,8 @@ public:
     uint32_t unref();
     uint32_t unref();
 
 
     virtual ~RenderMethod() {}
     virtual ~RenderMethod() {}
-    virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
-    virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
-    virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
+    virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
+    virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
     virtual bool preRender() = 0;
     virtual bool preRender() = 0;
     virtual bool renderShape(RenderData data) = 0;
     virtual bool renderShape(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
@@ -288,6 +321,8 @@ static inline bool MASK_REGION_MERGING(CompositeMethod method)
         //these might expand the rendering region
         //these might expand the rendering region
         case CompositeMethod::AddMask:
         case CompositeMethod::AddMask:
         case CompositeMethod::DifferenceMask:
         case CompositeMethod::DifferenceMask:
+        case CompositeMethod::LightenMask:
+        case CompositeMethod::DarkenMask:
             return true;
             return true;
         default:
         default:
             TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
             TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
@@ -321,6 +356,8 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, Composi
         case CompositeMethod::DifferenceMask:
         case CompositeMethod::DifferenceMask:
         case CompositeMethod::SubtractMask:
         case CompositeMethod::SubtractMask:
         case CompositeMethod::IntersectMask:
         case CompositeMethod::IntersectMask:
+        case CompositeMethod::LightenMask:
+        case CompositeMethod::DarkenMask:
             return ColorSpace::Grayscale8;
             return ColorSpace::Grayscale8;
         //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
         //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
         case CompositeMethod::LumaMask:
         case CompositeMethod::LumaMask:

+ 10 - 18
thirdparty/thorvg/src/renderer/tvgScene.h

@@ -101,7 +101,7 @@ struct Scene::Impl
         return true;
         return true;
     }
     }
 
 
-    RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
+    RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
     {
     {
         if ((needComp = needComposition(opacity))) {
         if ((needComp = needComposition(opacity))) {
             /* Overriding opacity value. If this scene is half-translucent,
             /* Overriding opacity value. If this scene is half-translucent,
@@ -109,20 +109,10 @@ struct Scene::Impl
             this->opacity = opacity;
             this->opacity = opacity;
             opacity = 255;
             opacity = 255;
         }
         }
-
-        if (clipper) {
-            Array<RenderData> rds(paints.size());
-            for (auto paint : paints) {
-                rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
-            }
-            rd = renderer->prepare(rds, rd, transform, clips, opacity, flag);
-            return rd;
-        } else {
-            for (auto paint : paints) {
-                paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
-            }
-            return nullptr;
+        for (auto paint : paints) {
+            paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
         }
         }
+        return nullptr;
     }
     }
 
 
     bool render(RenderMethod* renderer)
     bool render(RenderMethod* renderer)
@@ -198,10 +188,12 @@ struct Scene::Impl
         return true;
         return true;
     }
     }
 
 
-    Paint* duplicate()
+    Paint* duplicate(Paint* ret)
     {
     {
-        auto ret = Scene::gen().release();
-        auto dup = ret->pImpl;
+        if (ret) TVGERR("RENDERER", "TODO: duplicate()");
+
+        auto scene = Scene::gen().release();
+        auto dup = scene->pImpl;
 
 
         for (auto paint : paints) {
         for (auto paint : paints) {
             auto cdup = paint->duplicate();
             auto cdup = paint->duplicate();
@@ -209,7 +201,7 @@ struct Scene::Impl
             dup->paints.push_back(cdup);
             dup->paints.push_back(cdup);
         }
         }
 
 
-        return ret;
+        return scene;
     }
     }
 
 
     void clear(bool free)
     void clear(bool free)

+ 36 - 43
thirdparty/thorvg/src/renderer/tvgShape.h

@@ -34,6 +34,7 @@ struct Shape::Impl
     RenderData rd = nullptr;            //engine data
     RenderData rd = nullptr;            //engine data
     Shape* shape;
     Shape* shape;
     uint8_t flag = RenderUpdateFlag::None;
     uint8_t flag = RenderUpdateFlag::None;
+
     uint8_t opacity;                    //for composition
     uint8_t opacity;                    //for composition
     bool needComp = false;              //composite or not
     bool needComp = false;              //composite or not
 
 
@@ -95,7 +96,7 @@ struct Shape::Impl
         return true;
         return true;
     }
     }
 
 
-    RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+    RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
     {
     {
         if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
         if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
 
 
@@ -216,23 +217,6 @@ struct Shape::Impl
         if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
         if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
             rs.stroke->trim.simultaneous == simultaneous) return;
             rs.stroke->trim.simultaneous == simultaneous) return;
 
 
-        auto loop = true;
-
-        if (begin > 1.0f && end > 1.0f) loop = false;
-        if (begin < 0.0f && end < 0.0f) loop = false;
-        if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f  && end <= 1.0f) loop = false;
-
-        if (begin > 1.0f) begin -= 1.0f;
-        if (begin < 0.0f) begin += 1.0f;
-        if (end > 1.0f) end -= 1.0f;
-        if (end < 0.0f) end += 1.0f;
-
-        if ((loop && begin < end) || (!loop && begin > end)) {
-            auto tmp = begin;
-            begin = end;
-            end = tmp;
-        }
-
         rs.stroke->trim.begin = begin;
         rs.stroke->trim.begin = begin;
         rs.stroke->trim.end = end;
         rs.stroke->trim.end = end;
         rs.stroke->trim.simultaneous = simultaneous;
         rs.stroke->trim.simultaneous = simultaneous;
@@ -359,47 +343,56 @@ struct Shape::Impl
         this->flag |= flag;
         this->flag |= flag;
     }
     }
 
 
-    Paint* duplicate()
+    Paint* duplicate(Paint* ret)
     {
     {
-        auto ret = Shape::gen().release();
-        auto dup = ret->pImpl;
+        auto shape = static_cast<Shape*>(ret);
+        if (shape) shape->reset();
+        else shape = Shape::gen().release();
+
+        auto dup = shape->pImpl;
+        delete(dup->rs.fill);
 
 
+        //Default Properties
+        dup->flag = RenderUpdateFlag::All;
         dup->rs.rule = rs.rule;
         dup->rs.rule = rs.rule;
 
 
         //Color
         //Color
         memcpy(dup->rs.color, rs.color, sizeof(rs.color));
         memcpy(dup->rs.color, rs.color, sizeof(rs.color));
-        dup->flag = RenderUpdateFlag::Color;
 
 
         //Path
         //Path
-        if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
-            dup->rs.path.cmds = rs.path.cmds;
-            dup->rs.path.pts = rs.path.pts;
-            dup->flag |= RenderUpdateFlag::Path;
-        }
+        dup->rs.path.cmds.push(rs.path.cmds);
+        dup->rs.path.pts.push(rs.path.pts);
 
 
         //Stroke
         //Stroke
         if (rs.stroke) {
         if (rs.stroke) {
-            dup->rs.stroke = new RenderStroke();
+            if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
             *dup->rs.stroke = *rs.stroke;
             *dup->rs.stroke = *rs.stroke;
-            memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
-            if (rs.stroke->dashCnt > 0) {
-                dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
-                memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
-            }
-            if (rs.stroke->fill) {
-                dup->rs.stroke->fill = rs.stroke->fill->duplicate();
-                dup->flag |= RenderUpdateFlag::GradientStroke;
-            }
-            dup->flag |= RenderUpdateFlag::Stroke;
+        } else {
+            delete(dup->rs.stroke);
+            dup->rs.stroke = nullptr;
         }
         }
 
 
         //Fill
         //Fill
-        if (rs.fill) {
-            dup->rs.fill = rs.fill->duplicate();
-            dup->flag |= RenderUpdateFlag::Gradient;
-        }
+        if (rs.fill) dup->rs.fill = rs.fill->duplicate();
+        else dup->rs.fill = nullptr;
 
 
-        return ret;
+        return shape;
+    }
+
+    void reset()
+    {
+        PP(shape)->reset();
+        rs.path.cmds.clear();
+        rs.path.pts.clear();
+
+        rs.color[3] = 0;
+        rs.rule = FillRule::Winding;
+
+        delete(rs.stroke);
+        rs.stroke = nullptr;
+
+        delete(rs.fill);
+        rs.fill = nullptr;
     }
     }
 
 
     Iterator* iterator()
     Iterator* iterator()

+ 0 - 2
thirdparty/thorvg/src/renderer/tvgTaskScheduler.h

@@ -23,8 +23,6 @@
 #ifndef _TVG_TASK_SCHEDULER_H_
 #ifndef _TVG_TASK_SCHEDULER_H_
 #define _TVG_TASK_SCHEDULER_H_
 #define _TVG_TASK_SCHEDULER_H_
 
 
-#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
-
 #include <mutex>
 #include <mutex>
 #include <condition_variable>
 #include <condition_variable>
 
 

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

@@ -35,7 +35,7 @@
 /************************************************************************/
 /************************************************************************/
 
 
 
 
-Text::Text() : pImpl(new Impl)
+Text::Text() : pImpl(new Impl(this))
 {
 {
     Paint::pImpl->id = TVG_CLASS_ID_TEXT;
     Paint::pImpl->id = TVG_CLASS_ID_TEXT;
 }
 }
@@ -95,20 +95,13 @@ Result Text::unload(const std::string& path) noexcept
 
 
 Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
 Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
 {
 {
-    if (!pImpl->paint) return Result::InsufficientCondition;
-
-    return pImpl->fill(r, g, b);
+    return pImpl->shape->fill(r, g, b);
 }
 }
 
 
 
 
 Result Text::fill(unique_ptr<Fill> f) noexcept
 Result Text::fill(unique_ptr<Fill> f) noexcept
 {
 {
-    if (!pImpl->paint) return Result::InsufficientCondition;
-
-    auto p = f.release();
-    if (!p) return Result::MemoryCorruption;
-
-    return pImpl->fill(p);
+    return pImpl->shape->fill(std::move(f));
 }
 }
 
 
 
 

+ 24 - 35
thirdparty/thorvg/src/renderer/tvgText.h

@@ -36,27 +36,22 @@
 struct Text::Impl
 struct Text::Impl
 {
 {
     FontLoader* loader = nullptr;
     FontLoader* loader = nullptr;
-    Shape* paint = nullptr;
+    Text* paint;
+    Shape* shape;
     char* utf8 = nullptr;
     char* utf8 = nullptr;
     float fontSize;
     float fontSize;
     bool italic = false;
     bool italic = false;
     bool changed = false;
     bool changed = false;
 
 
-    ~Impl()
+    Impl(Text* p) : paint(p), shape(Shape::gen().release())
     {
     {
-        free(utf8);
-        LoaderMgr::retrieve(loader);
-        delete(paint);
     }
     }
 
 
-    Result fill(uint8_t r, uint8_t g, uint8_t b)
-    {
-        return paint->fill(r, g, b);
-    }
-
-    Result fill(Fill* f)
+    ~Impl()
     {
     {
-        return paint->fill(cast<Fill>(f));
+        free(utf8);
+        LoaderMgr::retrieve(loader);
+        delete(shape);
     }
     }
 
 
     Result text(const char* utf8)
     Result text(const char* utf8)
@@ -83,8 +78,6 @@ struct Text::Impl
         }
         }
         this->loader = static_cast<FontLoader*>(loader);
         this->loader = static_cast<FontLoader*>(loader);
 
 
-        if (!paint) paint = Shape::gen().release();
-
         fontSize = size;
         fontSize = size;
         if (style && strstr(style, "italic")) italic = true;
         if (style && strstr(style, "italic")) italic = true;
         changed = true;
         changed = true;
@@ -93,14 +86,12 @@ struct Text::Impl
 
 
     RenderRegion bounds(RenderMethod* renderer)
     RenderRegion bounds(RenderMethod* renderer)
     {
     {
-        if (paint) return P(paint)->bounds(renderer);
-        else return {0, 0, 0, 0};
+        return P(shape)->bounds(renderer);
     }
     }
 
 
     bool render(RenderMethod* renderer)
     bool render(RenderMethod* renderer)
     {
     {
-        if (paint) return PP(paint)->render(renderer);
-        return true;
+        return PP(shape)->render(renderer);
     }
     }
 
 
     bool load()
     bool load()
@@ -109,24 +100,20 @@ struct Text::Impl
 
 
         //reload
         //reload
         if (changed) {
         if (changed) {
-            loader->request(paint, utf8, italic);
+            loader->request(shape, utf8, italic);
             loader->read();
             loader->read();
             changed = false;
             changed = false;
         }
         }
-        if (paint) {
-            loader->resize(paint, fontSize, fontSize);
-            return true;
-        }
-        return false;
+        return loader->resize(shape, fontSize, fontSize);
     }
     }
 
 
-    RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+    RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
     {
     {
         if (!load()) return nullptr;
         if (!load()) return nullptr;
 
 
         //transform the gradient coordinates based on the final scaled font.
         //transform the gradient coordinates based on the final scaled font.
-        if (P(paint)->flag & RenderUpdateFlag::Gradient) {
-            auto fill = P(paint)->rs.fill;
+        auto fill = P(shape)->rs.fill;
+        if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
             auto scale = 1.0f / loader->scale;
             auto scale = 1.0f / loader->scale;
             if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
             if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
@@ -142,23 +129,25 @@ struct Text::Impl
                 P(static_cast<RadialGradient*>(fill))->fr *= scale;
                 P(static_cast<RadialGradient*>(fill))->fr *= scale;
             }
             }
         }
         }
-        return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper);
+        return PP(shape)->update(renderer, transform, clips, opacity, pFlag, false);
     }
     }
 
 
     bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
     bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
     {
     {
-        if (!load() || !paint) return false;
-        paint->bounds(x, y, w, h, true);
+        if (!load()) return false;
+        PP(shape)->bounds(x, y, w, h, true, true, false);
         return true;
         return true;
     }
     }
 
 
-    Paint* duplicate()
+    Paint* duplicate(Paint* ret)
     {
     {
+        if (ret) TVGERR("RENDERER", "TODO: duplicate()");
+
         load();
         load();
 
 
-        auto ret = Text::gen().release();
-        auto dup = ret->pImpl;
-        if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
+        auto text = Text::gen().release();
+        auto dup = text->pImpl;
+        P(shape)->duplicate(dup->shape);
 
 
         if (loader) {
         if (loader) {
             dup->loader = loader;
             dup->loader = loader;
@@ -169,7 +158,7 @@ struct Text::Impl
         dup->italic = italic;
         dup->italic = italic;
         dup->fontSize = fontSize;
         dup->fontSize = fontSize;
 
 
-        return ret;
+        return text;
     }
     }
 
 
     Iterator* iterator()
     Iterator* iterator()

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

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 #!/bin/bash -e
 
 
-VERSION=0.14.2
+VERSION=0.14.7
 
 
 cd thirdparty/thorvg/ || true
 cd thirdparty/thorvg/ || true
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/