From 98193507456e6b3faf174522749bd46b60fc5d07 Mon Sep 17 00:00:00 2001 From: Tristan Grespinet Date: Tue, 14 Jan 2025 16:01:53 +0100 Subject: [PATCH] Fix CI (#749) * Fix CI --- .github/workflows/build_android.yml | 4 + .github/workflows/build_ios.yml | 4 + .github/workflows/build_linux.yml | 4 + .github/workflows/build_macos.yml | 4 + .github/workflows/build_windows.yml | 4 + git.patch | 9073 +++++++++++++++++++++++++++ 6 files changed, 9093 insertions(+) create mode 100644 git.patch diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 517606ef68..bf416af167 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -39,6 +39,10 @@ jobs: with: path: modules/kotlin_jvm + - name: Apply git patch + run: | + git apply --ignore-whitespace modules/kotlin_jvm/git.patch + - uses: actions/setup-java@v4 with: distribution: adopt-hotspot diff --git a/.github/workflows/build_ios.yml b/.github/workflows/build_ios.yml index ad495b1bea..d7ab95b7b1 100644 --- a/.github/workflows/build_ios.yml +++ b/.github/workflows/build_ios.yml @@ -26,6 +26,10 @@ jobs: with: path: modules/kotlin_jvm + - name: Apply git patch + run: | + git apply --ignore-whitespace modules/kotlin_jvm/git.patch + - name: Setup Godot build cache uses: ./modules/kotlin_jvm/.github/actions/scons-cache continue-on-error: true diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 145249be3e..a96025a204 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -63,6 +63,10 @@ jobs: with: path: modules/kotlin_jvm + - name: Apply git patch + run: | + git apply --ignore-whitespace modules/kotlin_jvm/git.patch + - name: Setup Godot build cache uses: ./modules/kotlin_jvm/.github/actions/scons-cache with: diff --git a/.github/workflows/build_macos.yml b/.github/workflows/build_macos.yml index 068b35d5c5..68be47848f 100644 --- a/.github/workflows/build_macos.yml +++ b/.github/workflows/build_macos.yml @@ -79,6 +79,10 @@ jobs: with: path: modules/kotlin_jvm + - name: Apply git patch + run: | + git apply --ignore-whitespace modules/kotlin_jvm/git.patch + - name: Setup Godot build cache uses: ./modules/kotlin_jvm/.github/actions/scons-cache with: diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 73efe3d96c..a8b1f18484 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -63,6 +63,10 @@ jobs: with: path: modules/kotlin_jvm + - name: Apply git patch + run: | + git apply --ignore-whitespace modules/kotlin_jvm/git.patch + - name: Setup Godot build cache uses: ./modules/kotlin_jvm/.github/actions/scons-cache with: diff --git a/git.patch b/git.patch new file mode 100644 index 0000000000..55a37902cb --- /dev/null +++ b/git.patch @@ -0,0 +1,9073 @@ +diff --git a/modules/svg/SCsub b/modules/svg/SCsub +index a32be0e41a..6b200e3da6 100644 +--- a/modules/svg/SCsub ++++ b/modules/svg/SCsub +@@ -13,7 +13,6 @@ thirdparty_dir = "#thirdparty/thorvg/" + thirdparty_sources = [ + # common + "src/common/tvgCompressor.cpp", +- "src/common/tvgLines.cpp", + "src/common/tvgMath.cpp", + "src/common/tvgStr.cpp", + # SVG parser +@@ -51,6 +50,7 @@ thirdparty_sources = [ + "src/renderer/sw_engine/tvgSwImage.cpp", + "src/renderer/sw_engine/tvgSwMath.cpp", + "src/renderer/sw_engine/tvgSwMemPool.cpp", ++ "src/renderer/sw_engine/tvgSwPostEffect.cpp", + "src/renderer/sw_engine/tvgSwRaster.cpp", + "src/renderer/sw_engine/tvgSwRenderer.cpp", + "src/renderer/sw_engine/tvgSwRle.cpp", + +diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS +index e00e91a696..a15f3262a8 100644 +--- a/thirdparty/thorvg/AUTHORS ++++ b/thirdparty/thorvg/AUTHORS +@@ -28,6 +28,10 @@ Nattu Adnan + Gabor Kiss-Vamosi + Lorcán Mc Donagh + Lucas Niu +-Francisco Ramírez ++Francisco Ramírez + Abdelrahman Ashraf + Neo Xu ++Thaddeus Crews ++Josh Soref ++Elliott Sales de Andrade ++Łukasz Pomietło +diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h +index 8c185ccbca..6df6f52d04 100644 +--- a/thirdparty/thorvg/inc/config.h ++++ b/thirdparty/thorvg/inc/config.h +@@ -15,5 +15,5 @@ + // For internal debugging: + //#define THORVG_LOG_ENABLED + +-#define THORVG_VERSION_STRING "0.14.2" ++#define THORVG_VERSION_STRING "0.15.5" + #endif +diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h +index 47414d851a..1ee898ca6f 100644 +--- a/thirdparty/thorvg/inc/thorvg.h ++++ b/thirdparty/thorvg/inc/thorvg.h +@@ -157,7 +157,7 @@ enum class FillRule + enum class CompositeMethod + { + None = 0, ///< No composition is applied. +- ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. ++ ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type. @deprecated Use Paint::clip() instead. + AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value. + InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. + LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 +@@ -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) + 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) +- 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) + }; + + +@@ -176,24 +178,46 @@ enum class CompositeMethod + * + * @see Paint::blend() + * +- * @note Experimental API ++ * @since 0.15 + */ + enum class BlendMethod : uint8_t + { + Normal = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D +- Add, ///< Simply adds pixel values of one layer with the other. (S + D) +- Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) + Multiply, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) ++ Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) + Overlay, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) +- Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) +- Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) +- SrcOver, ///< Replace the bottom layer with the top layer. + Darken, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) + Lighten, ///< Only has the opposite action of Darken Only. max(S, D) + ColorDodge, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) + ColorBurn, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S + HardLight, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) +- SoftLight ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) ++ SoftLight, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) ++ Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) ++ Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) ++ Hue, ///< Reserved. Not supported. ++ Saturation, ///< Reserved. Not supported. ++ Color, ///< Reserved. Not supported. ++ Luminosity, ///< Reserved. Not supported. ++ Add, ///< Simply adds pixel values of one layer with the other. (S + D) ++ HardMix ///< Reserved. Not supported. ++}; ++ ++ ++/** ++ * @brief Enumeration that defines methods used for Scene Effects. ++ * ++ * This enum provides options to apply various post-processing effects to a scene. ++ * Scene effects are typically applied to modify the final appearance of a rendered scene, such as blurring. ++ * ++ * @see Scene::push(SceneEffect effect, ...) ++ * ++ * @note Experimental API ++ */ ++enum class SceneEffect : uint8_t ++{ ++ ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state. ++ GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} ++ DropShadow ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]} + }; + + +@@ -204,7 +228,29 @@ enum class CanvasEngine + { + Sw = (1 << 1), ///< CPU rasterizer. + Gl = (1 << 2), ///< OpenGL rasterizer. +- Wg = (1 << 3), ///< WebGPU rasterizer. (Experimental API) ++ Wg = (1 << 3), ///< WebGPU rasterizer. @since 0.15 ++}; ++ ++ ++/** ++ * @brief Enumeration specifying the ThorVG class type value. ++ * ++ * ThorVG's drawing objects can return class type values, allowing you to identify the specific class of each object. ++ * ++ * @see Paint::type() ++ * @see Fill::type() ++ * ++ * @note Experimental API ++ */ ++enum class Type : uint8_t ++{ ++ Undefined = 0, ///< Unkown class ++ Shape, ///< Shape class ++ Scene, ///< Scene class ++ Picture, ///< Picture class ++ Text, ///< Text class ++ LinearGradient = 10, ///< LinearGradient class ++ RadialGradient ///< RadialGradient class + }; + + +@@ -232,34 +278,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 + * +@@ -300,7 +318,7 @@ public: + /** + * @brief Sets the values by which the object is moved in a two-dimensional space. + * +- * The origin of the coordinate system is in the upper left corner of the canvas. ++ * The origin of the coordinate system is in the upper-left corner of the canvas. + * The horizontal and vertical axes point to the right and down, respectively. + * + * @param[in] x The value of the horizontal shift. +@@ -338,7 +356,6 @@ public: + * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. + * + * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. +- * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath) + */ + Result opacity(uint8_t o) noexcept; + +@@ -350,6 +367,20 @@ public: + */ + Result composite(std::unique_ptr target, CompositeMethod method) noexcept; + ++ /** ++ * @brief Clip the drawing region of the paint object. ++ * ++ * This function restricts the drawing area of the paint object to the specified shape's paths. ++ * ++ * @param[in] clipper The shape object as the clipper. ++ * ++ * @retval Result::NonSupport If the @p clipper type is not Shape. ++ * ++ * @note @p clipper only supports the Shape type. ++ * @note Experimental API ++ */ ++ Result clip(std::unique_ptr clipper) noexcept; ++ + /** + * @brief Sets the blending method for the paint object. + * +@@ -361,7 +392,7 @@ public: + * + * @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 +@@ -371,15 +402,16 @@ public: + /** + * @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] 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 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; + +@@ -411,22 +443,29 @@ public: + CompositeMethod composite(const Paint** target) const noexcept; + + /** +- * @brief Gets the blending method of the object. ++ * @brief Returns the ID value of this class. + * +- * @return The blending method ++ * This method can be used to check the current concrete instance type. + * +- * @note Experimental API ++ * @return The class type ID of the Paint instance. ++ * ++ * @since Experimental API + */ +- BlendMethod blend() const noexcept; ++ virtual Type type() const noexcept = 0; + + /** +- * @brief Return the unique id value of the paint instance. ++ * @brief Unique ID of this instance. + * +- * This method can be called for checking the current concrete instance type. ++ * This is reserved to specify an paint instance in a scene. + * +- * @return The type id of the Paint instance. ++ * @since Experimental API + */ +- uint32_t identifier() const noexcept; ++ uint32_t id = 0; ++ ++ /** ++ * @see Paint::type() ++ */ ++ TVG_DEPRECATED uint32_t identifier() const noexcept; + + _TVG_DECLARE_PRIVATE(Paint); + }; +@@ -519,13 +558,20 @@ public: + Fill* duplicate() const noexcept; + + /** +- * @brief Return the unique id value of the Fill instance. ++ * @brief Returns the ID value of this class. ++ * ++ * This method can be used to check the current concrete instance type. + * +- * This method can be called for checking the current concrete instance type. ++ * @return The class type ID of the Fill instance. + * +- * @return The type id of the Fill instance. ++ * @since Experimental API + */ +- uint32_t identifier() const noexcept; ++ virtual Type type() const noexcept = 0; ++ ++ /** ++ * @see Fill::type() ++ */ ++ TVG_DEPRECATED uint32_t identifier() const noexcept; + + _TVG_DECLARE_PRIVATE(Fill); + }; +@@ -554,7 +600,7 @@ public: + * + * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * +- * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). ++ * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). + * @see Canvas::sync() + * + * @note Experimental API +@@ -630,7 +676,7 @@ public: + * @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync(). + * + * @note When resetting the target, the viewport will also be reset to the target size. +- * @note Experimental API ++ * @since 0.15 + */ + virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; + +@@ -675,7 +721,8 @@ public: + * @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. + * +- * @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; + +@@ -701,13 +748,20 @@ public: + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. ++ * ++ * This method can be used to check the current concrete instance type. + * +- * This method can be referred for identifying the LinearGradient class type. ++ * @return The class type ID of the LinearGradient instance. + * +- * @return The type id of the LinearGradient class. ++ * @since Experimental API + */ +- static uint32_t identifier() noexcept; ++ Type type() const noexcept override; ++ ++ /** ++ * @see LinearGradient::type() ++ */ ++ TVG_DEPRECATED static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(LinearGradient); + }; +@@ -734,6 +788,8 @@ public: + * @param[in] radius The radius of the bounding circle. + * + * @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; + +@@ -757,13 +813,20 @@ public: + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. ++ * ++ * This method can be used to check the current concrete instance type. + * +- * This method can be referred for identifying the RadialGradient class type. ++ * @return The class type ID of the LinearGradient instance. + * +- * @return The type id of the RadialGradient class. ++ * @since Experimental API + */ +- static uint32_t identifier() noexcept; ++ Type type() const noexcept override; ++ ++ /** ++ * @see RadialGradient::type() ++ */ ++ TVG_DEPRECATED static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(RadialGradient); + }; +@@ -787,11 +850,11 @@ public: + ~Shape(); + + /** +- * @brief Resets the properties of the shape path. ++ * @brief Resets the shape path. + * +- * The transformation matrix, the color, the fill and the stroke properties are retained. ++ * The transformation matrix, color, fill, and stroke properties are retained. + * +- * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect. ++ * @note The memory where the path data is stored is not deallocated at this stage to allow for caching. + */ + Result reset() noexcept; + +@@ -849,15 +912,15 @@ public: + * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. + * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. + * +- * The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments. ++ * The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments. + * + * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. + * + * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater + * than @p w/2 the current point is set to (@p x + @p w/2, @p y) + * +- * @param[in] x The horizontal coordinate of the upper left corner of the rectangle. +- * @param[in] y The vertical coordinate of the upper left corner of the rectangle. ++ * @param[in] x The horizontal coordinate of the upper-left corner of the rectangle. ++ * @param[in] y The vertical coordinate of the upper-left corner of the rectangle. + * @param[in] w The width of the rectangle. + * @param[in] h The height of the rectangle. + * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. +@@ -899,7 +962,7 @@ public: + * + * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius). + */ +- Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept; ++ TVG_DEPRECATED Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept; + + /** + * @brief Appends a given sub-path to the path. +@@ -990,7 +1053,7 @@ public: + /** + * @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] end Specifies the end of the segment to display along the path. +@@ -1012,7 +1075,6 @@ public: + * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. + * + * @note Either a solid color or a gradient fill is applied, depending on what was set as last. +- * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath) + */ + Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; + +@@ -1076,7 +1138,6 @@ public: + * @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. + * +- * @return Result::Success when succeed. + */ + Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; + +@@ -1144,18 +1205,6 @@ public: + */ + float strokeMiterlimit() const noexcept; + +- /** +- * @brief Gets the trim of the stroke along the defined path segment. +- * +- * @param[out] begin The starting point of the segment to display along the path. +- * @param[out] end Specifies the end of the segment to display along the path. +- * +- * @return @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise. +- * +- * @note Experimental API +- */ +- bool strokeTrim(float* begin, float* end) const noexcept; +- + /** + * @brief Creates a new Shape object. + * +@@ -1164,13 +1213,20 @@ public: + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. ++ * ++ * This method can be used to check the current concrete instance type. + * +- * This method can be referred for identifying the Shape class type. ++ * @return The class type ID of the Shape instance. + * +- * @return The type id of the Shape class. ++ * @since Experimental API ++ */ ++ Type type() const noexcept override; ++ ++ /** ++ * @see Shape::type() + */ +- static uint32_t identifier() noexcept; ++ TVG_DEPRECATED static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(Shape); + }; +@@ -1219,7 +1275,7 @@ public: + * 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. + * +- * @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] 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. +@@ -1227,7 +1283,7 @@ public: + * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * +- * @warning: It's the user responsibility to release the @p data memory. ++ * @warning It's the user responsibility to release the @p data memory. + * + * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. + * @since 0.5 +@@ -1256,7 +1312,7 @@ public: + 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 + * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations +@@ -1273,39 +1329,19 @@ public: + 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. ++ * @brief Retrieve a paint object from the Picture scene by its Unique ID. + * +- * 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. +- * +- * @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. +- * +- * @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. +- * +- * @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. ++ * This function searches for a paint object within the Picture scene that matches the provided @p id. + * +- * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh. ++ * @param[in] id The Unique ID of the paint object. + * +- * @return The number of polygons in the array. ++ * @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found. + * +- * @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 + */ +- uint32_t mesh(const Polygon** triangles) const noexcept; ++ const Paint* paint(uint32_t id) noexcept; + + /** + * @brief Creates a new Picture object. +@@ -1315,13 +1351,20 @@ public: + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. + * +- * This method can be referred for identifying the Picture class type. ++ * This method can be used to check the current concrete instance type. + * +- * @return The type id of the Picture class. ++ * @return The class type ID of the Picture instance. ++ * ++ * @since Experimental API + */ +- static uint32_t identifier() noexcept; ++ Type type() const noexcept override; ++ ++ /** ++ * @see Picture::type() ++ */ ++ TVG_DEPRECATED static uint32_t identifier() noexcept; + + _TVG_DECLARE_ACCESSOR(Animation); + _TVG_DECLARE_PRIVATE(Picture); +@@ -1365,9 +1408,9 @@ public: + * + * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * +- * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). ++ * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). + * @see Canvas::sync() +- * @see Scene::push() ++ * @see Scene::push(std::unique_ptr paint) + * @see Scene::clear() + * + * @note Experimental API +@@ -1386,6 +1429,20 @@ public: + */ + Result clear(bool free = true) noexcept; + ++ /** ++ * @brief Apply a post-processing effect to the scene. ++ * ++ * This function adds a specified scene effect, such as clearing all effects or applying a Gaussian blur, ++ * to the scene after it has been rendered. Multiple effects can be applied in sequence. ++ * ++ * @param[in] effect The scene effect to apply. Options are defined in the SceneEffect enum. ++ * For example, use SceneEffect::GaussianBlur to apply a blur with specific parameters. ++ * @param[in] ... Additional variadic parameters required for certain effects (e.g., sigma and direction for GaussianBlur). ++ * ++ * @note Experimental API ++ */ ++ Result push(SceneEffect effect, ...) noexcept; ++ + /** + * @brief Creates a new Scene object. + * +@@ -1394,13 +1451,20 @@ public: + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. ++ * ++ * This method can be used to check the current concrete instance type. + * +- * This method can be referred for identifying the Scene class type. ++ * @return The class type ID of the Scene instance. + * +- * @return The type id of the Scene class. ++ * @since Experimental API ++ */ ++ Type type() const noexcept override; ++ ++ /** ++ * @see Scene::type() + */ +- static uint32_t identifier() noexcept; ++ TVG_DEPRECATED static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(Scene); + }; +@@ -1411,7 +1475,7 @@ public: + * + * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. + * +- * @note Experimental API ++ * @since 0.15 + */ + class TVG_API Text final : public Paint + { +@@ -1454,11 +1518,9 @@ public: + * @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. + * +- * @retval Result::InsufficientCondition when the font has not been set up prior to this operation. +- * + * @see Text::font() + * +- * @note Experimental API ++ * @since 0.15 + */ + Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept; + +@@ -1469,12 +1531,10 @@ public: + * + * @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 Experimental API +- * + * @see Text::font() ++ * ++ * @since 0.15 + */ + Result fill(std::unique_ptr f) noexcept; + +@@ -1490,9 +1550,9 @@ public: + * @retval Result::InvalidArguments In case the @p path is invalid. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * +- * @note Experimental API +- * + * @see Text::unload(const std::string& path) ++ * ++ * @since 0.15 + */ + static Result load(const std::string& path) noexcept; + +@@ -1513,13 +1573,13 @@ public: + * @retval Result::NonSupport When trying to load a file with an unsupported extension. + * @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded. + * +- * @warning: It's the user responsibility to release the @p data memory. ++ * @warning It's the user responsibility to release the @p data memory. + * + * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data. + * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. +- * @note Experimental API +- * + * @see Text::font(const char* name, float size, const char* style) ++ * ++ * @note 0.15 + */ + static Result load(const char* name, const char* data, uint32_t size, const std::string& mimeType = "ttf", bool copy = false) noexcept; + +@@ -1533,9 +1593,9 @@ public: + * @retval Result::InsufficientCondition Fails if the loader is not initialized. + * + * @note If the font data is currently in use, it will not be immediately unloaded. +- * @note Experimental API +- * + * @see Text::load(const std::string& path) ++ * ++ * @since 0.15 + */ + static Result unload(const std::string& path) noexcept; + +@@ -1544,18 +1604,20 @@ public: + * + * @return A new Text object. + * +- * @note Experimental API ++ * @since 0.15 + */ + static std::unique_ptr gen() noexcept; + + /** +- * @brief Return the unique id value of this class. ++ * @brief Returns the ID value of this class. + * +- * This method can be referred for identifying the Text class type. ++ * This method can be used to check the current concrete instance type. + * +- * @return The type id of the Text class. ++ * @return The class type ID of the Text instance. ++ * ++ * @since Experimental API + */ +- static uint32_t identifier() noexcept; ++ Type type() const noexcept override; + + _TVG_DECLARE_PRIVATE(Text); + }; +@@ -1654,8 +1716,6 @@ public: + * + * @brief A class for the rendering graphic elements with a GL raster engine. + * +- * @warning Please do not use it. This class is not fully supported yet. +- * + * @since 0.14 + */ + class TVG_API GlCanvas final : public Canvas +@@ -1704,7 +1764,7 @@ public: + * + * @warning Please do not use it. This class is not fully supported yet. + * +- * @note Experimental API ++ * @since 0.15 + */ + class TVG_API WgCanvas final : public Canvas + { +@@ -1718,6 +1778,7 @@ public: + * @param[in] surface WGPUSurface, handle to a presentable surface. + * @param[in] w The width of the surface. + * @param[in] h The height of the surface. ++ * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally. + * + * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. + * @retval Result::NonSupport In case the wg engine is not supported. +@@ -1727,14 +1788,14 @@ public: + * @see Canvas::viewport() + * @see Canvas::sync() + */ +- Result target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept; ++ Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept; + + /** + * @brief Creates a new WgCanvas object. + * + * @return A new WgCanvas object. + * +- * @note Experimental API ++ * @since 0.15 + */ + static std::unique_ptr gen() noexcept; + +@@ -1781,6 +1842,19 @@ public: + */ + 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. ++ * ++ * @since 0.15 ++ */ ++ static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept; ++ + _TVG_DISABLE_CTOR(Initializer); + }; + +@@ -1879,9 +1953,10 @@ public: + * @retval Result::InsufficientCondition In case the animation is not loaded. + * @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. + * @see LottieAnimation::segment(const char* marker) ++ * + * @note Experimental API + */ + Result segment(float begin, float end) noexcept; +@@ -1920,7 +1995,7 @@ public: + * It's useful when you need to save the composed scene or image from a paint object and recreate it later. + * + * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment. +- * If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result. ++ * If it doesn't support the file format, the save() method returns the @c Result::NonSupport result. + * + * Once you export a paint to the file successfully, you can recreate it using the Picture class. + * +@@ -2030,17 +2105,36 @@ class TVG_API Accessor final + public: + ~Accessor(); + ++ TVG_DEPRECATED std::unique_ptr set(std::unique_ptr picture, std::function func) noexcept; ++ + /** + * @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] 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 Experimental API ++ */ ++ Result set(const Picture* picture, std::function 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 set(std::unique_ptr picture, std::function func) noexcept; ++ static uint32_t id(const char* name) noexcept; + + /** + * @brief Creates a new Accessor object. +diff --git a/thirdparty/thorvg/patches/pr2740-renderer-crash-hotfix.patch b/thirdparty/thorvg/patches/pr2740-renderer-crash-hotfix.patch +new file mode 100644 +index 0000000000..50b1e1e4a7 +--- /dev/null ++++ b/thirdparty/thorvg/patches/pr2740-renderer-crash-hotfix.patch +@@ -0,0 +1,45 @@ ++From 8009c75465e5b35da2d5f53532bc65f6df202a3a Mon Sep 17 00:00:00 2001 ++From: Hermet Park ++Date: Tue, 17 Sep 2024 11:35:48 +0900 ++Subject: [PATCH] renderer: hotfix a crash ++ ++prevent a nullptr memory access ++regression by f5337015e971d24379d2ee664895503ab8945e13 ++ ++issue: https://github.com/godotengine/godot/issues/97078 ++--- ++ src/renderer/tvgShape.h | 6 ++++-- ++ 2 files changed, 4 insertions(+), 4 deletions(-) ++ ++diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h ++index 221931dee..e120a85c6 100644 ++--- a/src/renderer/tvgShape.h +++++ b/src/renderer/tvgShape.h ++@@ -51,8 +51,9 @@ struct Shape::Impl ++ ++ bool render(RenderMethod* renderer) ++ { +++ if (!rd) return false; +++ ++ Compositor* cmp = nullptr; ++- bool ret; ++ ++ renderer->blend(shape->blend()); ++ ++@@ -61,7 +62,7 @@ struct Shape::Impl ++ renderer->beginComposite(cmp, CompositeMethod::None, opacity); ++ } ++ ++- ret = renderer->renderShape(rd); +++ auto ret = renderer->renderShape(rd); ++ if (cmp) renderer->endComposite(cmp); ++ return ret; ++ } ++@@ -117,6 +118,7 @@ struct Shape::Impl ++ ++ RenderRegion bounds(RenderMethod* renderer) ++ { +++ if (!rd) return {0, 0, 0, 0}; ++ return renderer->region(rd); ++ } ++ +diff --git a/thirdparty/thorvg/patches/revert-tvgLines-bezier-precision-change.patch b/thirdparty/thorvg/patches/revert-tvgLines-bezier-precision-change.patch +new file mode 100644 +index 0000000000..dd6c8ba5e7 +--- /dev/null ++++ b/thirdparty/thorvg/patches/revert-tvgLines-bezier-precision-change.patch +@@ -0,0 +1,13 @@ ++diff --git a/thirdparty/thorvg/src/common/tvgLines.cpp b/thirdparty/thorvg/src/common/tvgLines.cpp ++index 49d992f127..9d704900a5 100644 ++--- a/thirdparty/thorvg/src/common/tvgLines.cpp +++++ b/thirdparty/thorvg/src/common/tvgLines.cpp ++@@ -79,7 +79,7 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc ++ Bezier left; ++ bezSplitLeft(right, t, left); ++ length = _bezLength(left, lineLengthFunc); ++- if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) { +++ if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { ++ break; ++ } ++ if (length < at) { +diff --git a/thirdparty/thorvg/src/common/tvgArray.h b/thirdparty/thorvg/src/common/tvgArray.h +index 8178bd0e42..19c8f69726 100644 +--- a/thirdparty/thorvg/src/common/tvgArray.h ++++ b/thirdparty/thorvg/src/common/tvgArray.h +@@ -59,7 +59,7 @@ struct Array + data[count++] = element; + } + +- void push(Array& rhs) ++ void push(const Array& rhs) + { + if (rhs.count == 0) return; + grow(rhs.count); +diff --git a/thirdparty/thorvg/src/common/tvgCompressor.cpp b/thirdparty/thorvg/src/common/tvgCompressor.cpp +index b61718f9a7..714f21e07c 100644 +--- a/thirdparty/thorvg/src/common/tvgCompressor.cpp ++++ b/thirdparty/thorvg/src/common/tvgCompressor.cpp +@@ -468,7 +468,7 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded) + encoded += 4; + } + *decoded = output; +- return reserved; ++ return idx; + } + + +@@ -478,6 +478,8 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded) + + unsigned long djb2Encode(const char* str) + { ++ if (!str) return 0; ++ + unsigned long hash = 5381; + int c; + +diff --git a/thirdparty/thorvg/src/common/tvgInlist.h b/thirdparty/thorvg/src/common/tvgInlist.h +index ff28cfd48e..fc99ae3d14 100644 +--- a/thirdparty/thorvg/src/common/tvgInlist.h ++++ b/thirdparty/thorvg/src/common/tvgInlist.h +@@ -100,7 +100,7 @@ struct Inlist + if (element == tail) tail = element->prev; + } + +- bool empty() ++ bool empty() const + { + return head ? false : true; + } +diff --git a/thirdparty/thorvg/src/common/tvgLines.cpp b/thirdparty/thorvg/src/common/tvgLines.cpp +deleted file mode 100644 +index 9d704900a5..0000000000 +--- a/thirdparty/thorvg/src/common/tvgLines.cpp ++++ /dev/null +@@ -1,245 +0,0 @@ +-/* +- * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. +- +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- +- * The above copyright notice and this permission notice shall be included in all +- * copies or substantial portions of the Software. +- +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +- * SOFTWARE. +- */ +- +-#include "tvgMath.h" +-#include "tvgLines.h" +- +-#define BEZIER_EPSILON 1e-2f +- +-/************************************************************************/ +-/* Internal Class Implementation */ +-/************************************************************************/ +- +-static float _lineLengthApprox(const Point& pt1, const Point& pt2) +-{ +- /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm. +- With alpha = 1, beta = 3/8, giving results with the largest error less +- than 7% compared to the exact value. */ +- Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; +- if (diff.x < 0) diff.x = -diff.x; +- if (diff.y < 0) diff.y = -diff.y; +- return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f); +-} +- +- +-static float _lineLength(const Point& pt1, const Point& pt2) +-{ +- Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; +- return sqrtf(diff.x * diff.x + diff.y * diff.y); +-} +- +- +-template +-float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc) +-{ +- Bezier left, right; +- auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end); +- auto chord = lineLengthFunc(cur.start, cur.end); +- +- if (fabsf(len - chord) > BEZIER_EPSILON) { +- tvg::bezSplit(cur, left, right); +- return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc); +- } +- return len; +-} +- +- +-template +-float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc) +-{ +- auto biggest = 1.0f; +- auto smallest = 0.0f; +- auto t = 0.5f; +- +- //just in case to prevent an infinite loop +- if (at <= 0) return 0.0f; +- if (at >= length) return 1.0f; +- +- while (true) { +- auto right = bz; +- Bezier left; +- bezSplitLeft(right, t, left); +- length = _bezLength(left, lineLengthFunc); +- if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { +- break; +- } +- if (length < at) { +- smallest = t; +- t = (t + biggest) * 0.5f; +- } else { +- biggest = t; +- t = (smallest + t) * 0.5f; +- } +- } +- return t; +-} +- +- +-/************************************************************************/ +-/* External Class Implementation */ +-/************************************************************************/ +- +-namespace tvg +-{ +- +-float lineLength(const Point& pt1, const Point& pt2) +-{ +- return _lineLength(pt1, pt2); +-} +- +- +-void lineSplitAt(const Line& cur, float at, Line& left, Line& right) +-{ +- auto len = lineLength(cur.pt1, cur.pt2); +- auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at; +- auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at; +- left.pt1 = cur.pt1; +- left.pt2.x = left.pt1.x + dx; +- left.pt2.y = left.pt1.y + dy; +- right.pt1 = left.pt2; +- right.pt2 = cur.pt2; +-} +- +- +-void bezSplit(const Bezier& cur, Bezier& left, Bezier& right) +-{ +- auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f; +- left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f; +- right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f; +- left.start.x = cur.start.x; +- right.end.x = cur.end.x; +- left.ctrl2.x = (left.ctrl1.x + c) * 0.5f; +- right.ctrl1.x = (right.ctrl2.x + c) * 0.5f; +- left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f; +- +- c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f; +- left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f; +- right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f; +- left.start.y = cur.start.y; +- right.end.y = cur.end.y; +- left.ctrl2.y = (left.ctrl1.y + c) * 0.5f; +- right.ctrl1.y = (right.ctrl2.y + c) * 0.5f; +- left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f; +-} +- +- +-float bezLength(const Bezier& cur) +-{ +- return _bezLength(cur, _lineLength); +-} +- +- +-float bezLengthApprox(const Bezier& cur) +-{ +- return _bezLength(cur, _lineLengthApprox); +-} +- +- +-void bezSplitLeft(Bezier& cur, float at, Bezier& left) +-{ +- left.start = cur.start; +- +- left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x); +- left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y); +- +- left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot +- left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot +- +- cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x); +- cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y); +- +- cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x); +- cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y); +- +- left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x); +- left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y); +- +- left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x); +- left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y); +-} +- +- +-float bezAt(const Bezier& bz, float at, float length) +-{ +- return _bezAt(bz, at, length, _lineLength); +-} +- +- +-float bezAtApprox(const Bezier& bz, float at, float length) +-{ +- return _bezAt(bz, at, length, _lineLengthApprox); +-} +- +- +-void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right) +-{ +- right = cur; +- auto t = bezAt(right, at, bezLength(right)); +- bezSplitLeft(right, t, left); +-} +- +- +-Point bezPointAt(const Bezier& bz, float t) +-{ +- Point cur; +- auto it = 1.0f - t; +- +- auto ax = bz.start.x * it + bz.ctrl1.x * t; +- auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t; +- auto cx = bz.ctrl2.x * it + bz.end.x * t; +- ax = ax * it + bx * t; +- bx = bx * it + cx * t; +- cur.x = ax * it + bx * t; +- +- float ay = bz.start.y * it + bz.ctrl1.y * t; +- float by = bz.ctrl1.y * it + bz.ctrl2.y * t; +- float cy = bz.ctrl2.y * it + bz.end.y * t; +- ay = ay * it + by * t; +- by = by * it + cy * t; +- cur.y = ay * it + by * t; +- +- return cur; +-} +- +- +-float bezAngleAt(const Bezier& bz, float t) +-{ +- if (t < 0 || t > 1) return 0; +- +- //derivate +- // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * +- // t^2) * p2 + t^2 * p3) +- float mt = 1.0f - t; +- float d = t * t; +- float a = -mt * mt; +- float b = 1 - 4 * t + 3 * d; +- float c = 2 * t - 3 * d; +- +- Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y}; +- pt.x *= 3; +- pt.y *= 3; +- +- return mathRad2Deg(mathAtan2(pt.y, pt.x)); +-} +- +- +-} +diff --git a/thirdparty/thorvg/src/common/tvgLines.h b/thirdparty/thorvg/src/common/tvgLines.h +deleted file mode 100644 +index d900782b56..0000000000 +--- a/thirdparty/thorvg/src/common/tvgLines.h ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. +- +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- +- * The above copyright notice and this permission notice shall be included in all +- * copies or substantial portions of the Software. +- +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +- * SOFTWARE. +- */ +- +-#ifndef _TVG_LINES_H_ +-#define _TVG_LINES_H_ +- +-#include "tvgCommon.h" +- +-namespace tvg +-{ +- +-struct Line +-{ +- Point pt1; +- Point pt2; +-}; +- +-float lineLength(const Point& pt1, const Point& pt2); +-void lineSplitAt(const Line& cur, float at, Line& left, Line& right); +- +- +-struct Bezier +-{ +- Point start; +- Point ctrl1; +- Point ctrl2; +- Point end; +-}; +- +-void bezSplit(const Bezier&cur, Bezier& left, Bezier& right); +-float bezLength(const Bezier& cur); +-void bezSplitLeft(Bezier& cur, float at, Bezier& left); +-float bezAt(const Bezier& bz, float at, float length); +-void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right); +-Point bezPointAt(const Bezier& bz, float t); +-float bezAngleAt(const Bezier& bz, float t); +- +-float bezLengthApprox(const Bezier& cur); +-float bezAtApprox(const Bezier& bz, float at, float length); +-} +- +-#endif //_TVG_LINES_H_ +diff --git a/thirdparty/thorvg/src/common/tvgLock.h b/thirdparty/thorvg/src/common/tvgLock.h +index 59f68d0d44..d3a4e41c5c 100644 +--- a/thirdparty/thorvg/src/common/tvgLock.h ++++ b/thirdparty/thorvg/src/common/tvgLock.h +@@ -25,8 +25,6 @@ + + #ifdef THORVG_THREAD_SUPPORT + +-#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR +- + #include + #include "tvgTaskScheduler.h" + +diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp +index c56b32249f..cb7f24ff40 100644 +--- a/thirdparty/thorvg/src/common/tvgMath.cpp ++++ b/thirdparty/thorvg/src/common/tvgMath.cpp +@@ -22,11 +22,88 @@ + + #include "tvgMath.h" + +-//see: https://en.wikipedia.org/wiki/Remez_algorithm +-float mathAtan2(float y, float x) ++#define BEZIER_EPSILON 1e-2f ++ ++ ++/************************************************************************/ ++/* Internal Class Implementation */ ++/************************************************************************/ ++ ++static float _lineLengthApprox(const Point& pt1, const Point& pt2) + { +- if (y == 0.0f && x == 0.0f) return 0.0f; ++ /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm. ++ With alpha = 1, beta = 3/8, giving results with the largest error less ++ than 7% compared to the exact value. */ ++ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; ++ if (diff.x < 0) diff.x = -diff.x; ++ if (diff.y < 0) diff.y = -diff.y; ++ return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f); ++} ++ ++ ++static float _lineLength(const Point& pt1, const Point& pt2) ++{ ++ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; ++ return sqrtf(diff.x * diff.x + diff.y * diff.y); ++} ++ ++ ++template ++float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc) ++{ ++ Bezier left, right; ++ auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end); ++ auto chord = lineLengthFunc(cur.start, cur.end); ++ ++ if (fabsf(len - chord) > BEZIER_EPSILON) { ++ cur.split(left, right); ++ return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc); ++ } ++ return len; ++} ++ ++ ++template ++float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc) ++{ ++ auto biggest = 1.0f; ++ auto smallest = 0.0f; ++ auto t = 0.5f; ++ ++ //just in case to prevent an infinite loop ++ if (at <= 0) return 0.0f; ++ if (at >= length) return 1.0f; ++ ++ while (true) { ++ auto right = bz; ++ Bezier left; ++ right.split(t, left); ++ length = _bezLength(left, lineLengthFunc); ++ if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) { ++ break; ++ } ++ if (length < at) { ++ smallest = t; ++ t = (t + biggest) * 0.5f; ++ } else { ++ biggest = t; ++ t = (smallest + t) * 0.5f; ++ } ++ } ++ return t; ++} ++ ++ ++/************************************************************************/ ++/* External Class Implementation */ ++/************************************************************************/ ++ ++namespace tvg { + ++//https://en.wikipedia.org/wiki/Remez_algorithm ++float atan2(float y, float x) ++{ ++ if (y == 0.0f && x == 0.0f) return 0.0f; + auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y)); + auto s = a * a; + auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; +@@ -37,15 +114,14 @@ float mathAtan2(float y, float x) + } + + +-bool mathInverse(const Matrix* m, Matrix* out) ++bool inverse(const Matrix* m, Matrix* out) + { + auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) - + m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) + + m->e13 * (m->e21 * m->e32 - m->e22 * m->e31); + +- if (mathZero(det)) return false; +- +- auto invDet = 1 / det; ++ auto invDet = 1.0f / det; ++ if (std::isinf(invDet)) return false; + + out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet; + out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet; +@@ -61,7 +137,7 @@ bool mathInverse(const Matrix* m, Matrix* out) + } + + +-bool mathIdentity(const Matrix* m) ++bool identity(const Matrix* m) + { + if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f || + m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f || +@@ -72,7 +148,7 @@ bool mathIdentity(const Matrix* m) + } + + +-void mathRotate(Matrix* m, float degree) ++void rotate(Matrix* m, float degree) + { + if (degree == 0.0f) return; + +@@ -109,9 +185,9 @@ Matrix operator*(const Matrix& lhs, const Matrix& rhs) + + bool operator==(const Matrix& lhs, const Matrix& rhs) + { +- if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) || +- !mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) || +- !mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) { ++ if (!tvg::equal(lhs.e11, rhs.e11) || !tvg::equal(lhs.e12, rhs.e12) || !tvg::equal(lhs.e13, rhs.e13) || ++ !tvg::equal(lhs.e21, rhs.e21) || !tvg::equal(lhs.e22, rhs.e22) || !tvg::equal(lhs.e23, rhs.e23) || ++ !tvg::equal(lhs.e31, rhs.e31) || !tvg::equal(lhs.e32, rhs.e32) || !tvg::equal(lhs.e33, rhs.e33)) { + return false; + } + return true; +@@ -133,3 +209,166 @@ Point operator*(const Point& pt, const Matrix& m) + auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23; + return {tx, ty}; + } ++ ++ ++Point normal(const Point& p1, const Point& p2) ++{ ++ auto dir = p2 - p1; ++ auto len = length(dir); ++ if (tvg::zero(len)) return {}; ++ ++ auto unitDir = dir / len; ++ return {-unitDir.y, unitDir.x}; ++} ++ ++ ++float Line::length() const ++{ ++ return _lineLength(pt1, pt2); ++} ++ ++ ++void Line::split(float at, Line& left, Line& right) const ++{ ++ auto len = length(); ++ auto dx = ((pt2.x - pt1.x) / len) * at; ++ auto dy = ((pt2.y - pt1.y) / len) * at; ++ left.pt1 = pt1; ++ left.pt2.x = left.pt1.x + dx; ++ left.pt2.y = left.pt1.y + dy; ++ right.pt1 = left.pt2; ++ right.pt2 = pt2; ++} ++ ++ ++void Bezier::split(Bezier& left, Bezier& right) const ++{ ++ auto c = (ctrl1.x + ctrl2.x) * 0.5f; ++ left.ctrl1.x = (start.x + ctrl1.x) * 0.5f; ++ right.ctrl2.x = (ctrl2.x + end.x) * 0.5f; ++ left.start.x = start.x; ++ right.end.x = end.x; ++ left.ctrl2.x = (left.ctrl1.x + c) * 0.5f; ++ right.ctrl1.x = (right.ctrl2.x + c) * 0.5f; ++ left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f; ++ ++ c = (ctrl1.y + ctrl2.y) * 0.5f; ++ left.ctrl1.y = (start.y + ctrl1.y) * 0.5f; ++ right.ctrl2.y = (ctrl2.y + end.y) * 0.5f; ++ left.start.y = start.y; ++ right.end.y = end.y; ++ left.ctrl2.y = (left.ctrl1.y + c) * 0.5f; ++ right.ctrl1.y = (right.ctrl2.y + c) * 0.5f; ++ left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f; ++} ++ ++ ++void Bezier::split(float at, Bezier& left, Bezier& right) const ++{ ++ right = *this; ++ auto t = right.at(at, right.length()); ++ right.split(t, left); ++} ++ ++ ++float Bezier::length() const ++{ ++ return _bezLength(*this, _lineLength); ++} ++ ++ ++float Bezier::lengthApprox() const ++{ ++ return _bezLength(*this, _lineLengthApprox); ++} ++ ++ ++void Bezier::split(float t, Bezier& left) ++{ ++ left.start = start; ++ ++ left.ctrl1.x = start.x + t * (ctrl1.x - start.x); ++ left.ctrl1.y = start.y + t * (ctrl1.y - start.y); ++ ++ left.ctrl2.x = ctrl1.x + t * (ctrl2.x - ctrl1.x); //temporary holding spot ++ left.ctrl2.y = ctrl1.y + t * (ctrl2.y - ctrl1.y); //temporary holding spot ++ ++ ctrl2.x = ctrl2.x + t * (end.x - ctrl2.x); ++ ctrl2.y = ctrl2.y + t * (end.y - ctrl2.y); ++ ++ ctrl1.x = left.ctrl2.x + t * (ctrl2.x - left.ctrl2.x); ++ ctrl1.y = left.ctrl2.y + t * (ctrl2.y - left.ctrl2.y); ++ ++ left.ctrl2.x = left.ctrl1.x + t * (left.ctrl2.x - left.ctrl1.x); ++ left.ctrl2.y = left.ctrl1.y + t * (left.ctrl2.y - left.ctrl1.y); ++ ++ left.end.x = start.x = left.ctrl2.x + t * (ctrl1.x - left.ctrl2.x); ++ left.end.y = start.y = left.ctrl2.y + t * (ctrl1.y - left.ctrl2.y); ++} ++ ++ ++float Bezier::at(float at, float length) const ++{ ++ return _bezAt(*this, at, length, _lineLength); ++} ++ ++ ++float Bezier::atApprox(float at, float length) const ++{ ++ return _bezAt(*this, at, length, _lineLengthApprox); ++} ++ ++ ++Point Bezier::at(float t) const ++{ ++ Point cur; ++ auto it = 1.0f - t; ++ ++ auto ax = start.x * it + ctrl1.x * t; ++ auto bx = ctrl1.x * it + ctrl2.x * t; ++ auto cx = ctrl2.x * it + end.x * t; ++ ax = ax * it + bx * t; ++ bx = bx * it + cx * t; ++ cur.x = ax * it + bx * t; ++ ++ float ay = start.y * it + ctrl1.y * t; ++ float by = ctrl1.y * it + ctrl2.y * t; ++ float cy = ctrl2.y * it + end.y * t; ++ ay = ay * it + by * t; ++ by = by * it + cy * t; ++ cur.y = ay * it + by * t; ++ ++ return cur; ++} ++ ++ ++float Bezier::angle(float t) const ++{ ++ if (t < 0 || t > 1) return 0; ++ ++ //derivate ++ // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * ++ // t^2) * p2 + t^2 * p3) ++ float mt = 1.0f - t; ++ float d = t * t; ++ float a = -mt * mt; ++ float b = 1 - 4 * t + 3 * d; ++ float c = 2 * t - 3 * d; ++ ++ Point pt ={a * start.x + b * ctrl1.x + c * ctrl2.x + d * end.x, a * start.y + b * ctrl1.y + c * ctrl2.y + d * end.y}; ++ pt.x *= 3; ++ pt.y *= 3; ++ ++ return rad2deg(tvg::atan2(pt.y, pt.x)); ++} ++ ++ ++uint8_t lerp(const uint8_t &start, const uint8_t &end, float t) ++{ ++ auto result = static_cast(start + (end - start) * t); ++ tvg::clamp(result, 0, 255); ++ return static_cast(result); ++} ++ ++} ++ +diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h +index 668260c689..a917998256 100644 +--- a/thirdparty/thorvg/src/common/tvgMath.h ++++ b/thirdparty/thorvg/src/common/tvgMath.h +@@ -26,73 +26,80 @@ + #define _USE_MATH_DEFINES + + #include +-#include ++#include + #include "tvgCommon.h" + ++namespace tvg ++{ ++ + #define MATH_PI 3.14159265358979323846f + #define MATH_PI2 1.57079632679489661923f + #define FLOAT_EPSILON 1.0e-06f //1.192092896e-07f + #define PATH_KAPPA 0.552284f + +-#define mathMin(x, y) (((x) < (y)) ? (x) : (y)) +-#define mathMax(x, y) (((x) > (y)) ? (x) : (y)) +- +- + /************************************************************************/ + /* General functions */ + /************************************************************************/ + +-float mathAtan2(float y, float x); ++float atan2(float y, float x); + +-static inline float mathDeg2Rad(float degree) ++ ++static inline float deg2rad(float degree) + { + return degree * (MATH_PI / 180.0f); + } + + +-static inline float mathRad2Deg(float radian) ++static inline float rad2deg(float radian) + { + return radian * (180.0f / MATH_PI); + } + + +-static inline bool mathZero(float a) ++static inline bool zero(float a) + { + return (fabsf(a) <= FLOAT_EPSILON) ? true : false; + } + + +-static inline bool mathEqual(float a, float b) ++static inline bool equal(float a, float b) + { +- return mathZero(a - b); ++ return tvg::zero(a - b); + } + + ++template ++static inline void clamp(T& v, const T& min, const T& max) ++{ ++ if (v < min) v = min; ++ else if (v > max) v = max; ++} ++ + /************************************************************************/ + /* Matrix functions */ + /************************************************************************/ + +-void mathRotate(Matrix* m, float degree); +-bool mathInverse(const Matrix* m, Matrix* out); +-bool mathIdentity(const Matrix* m); ++void rotate(Matrix* m, float degree); ++bool inverse(const Matrix* m, Matrix* out); ++bool identity(const Matrix* m); + Matrix operator*(const Matrix& lhs, const Matrix& rhs); + bool operator==(const Matrix& lhs, const Matrix& rhs); + +-static inline bool mathRightAngle(const Matrix* m) ++static inline bool rightAngle(const Matrix& m) + { +- auto radian = fabsf(mathAtan2(m->e21, m->e11)); +- if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true; ++ auto radian = fabsf(tvg::atan2(m.e21, m.e11)); ++ if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true; + return false; + } + + +-static inline bool mathSkewed(const Matrix* m) ++static inline bool skewed(const Matrix& m) + { +- return !mathZero(m->e21 + m->e12); ++ return !tvg::zero(m.e21 + m.e12); + } + + +-static inline void mathIdentity(Matrix* m) ++static inline void identity(Matrix* m) + { + m->e11 = 1.0f; + m->e12 = 0.0f; +@@ -106,14 +113,14 @@ static inline void mathIdentity(Matrix* m) + } + + +-static inline void mathScale(Matrix* m, float sx, float sy) ++static inline void scale(Matrix* m, float sx, float sy) + { + m->e11 *= sx; + m->e22 *= sy; + } + + +-static inline void mathScaleR(Matrix* m, float x, float y) ++static inline void scaleR(Matrix* m, float x, float y) + { + if (x != 1.0f) { + m->e11 *= x; +@@ -126,14 +133,14 @@ static inline void mathScaleR(Matrix* m, float x, float y) + } + + +-static inline void mathTranslate(Matrix* m, float x, float y) ++static inline void translate(Matrix* m, float x, float y) + { + m->e13 += x; + m->e23 += y; + } + + +-static inline void mathTranslateR(Matrix* m, float x, float y) ++static inline void translateR(Matrix* m, float x, float y) + { + if (x == 0.0f && y == 0.0f) return; + m->e13 += (x * m->e11 + y * m->e12); +@@ -153,7 +160,7 @@ static inline void operator*=(Matrix& lhs, const Matrix& rhs) + } + + +-static inline void mathLog(const Matrix& m) ++static inline void log(const Matrix& m) + { + TVGLOG("COMMON", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m.e11, m.e12, m.e13, m.e21, m.e22, m.e23, m.e31, m.e32, m.e33); + } +@@ -165,15 +172,21 @@ static inline void mathLog(const Matrix& m) + + void operator*=(Point& pt, const Matrix& m); + Point operator*(const Point& pt, const Matrix& m); ++Point normal(const Point& p1, const Point& p2); + ++static inline float cross(const Point& lhs, const Point& rhs) ++{ ++ return lhs.x * rhs.y - rhs.x * lhs.y; ++} + +-static inline bool mathZero(const Point& p) ++ ++static inline bool zero(const Point& p) + { +- return mathZero(p.x) && mathZero(p.y); ++ return tvg::zero(p.x) && tvg::zero(p.y); + } + + +-static inline float mathLength(const Point* a, const Point* b) ++static inline float length(const Point* a, const Point* b) + { + auto x = b->x - a->x; + auto y = b->y - a->y; +@@ -185,7 +198,7 @@ static inline float mathLength(const Point* a, const Point* b) + } + + +-static inline float mathLength(const Point& a) ++static inline float length(const Point& a) + { + return sqrtf(a.x * a.x + a.y * a.y); + } +@@ -193,7 +206,7 @@ static inline float mathLength(const Point& a) + + static inline bool operator==(const Point& lhs, const Point& rhs) + { +- return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y); ++ return tvg::equal(lhs.x, rhs.x) && tvg::equal(lhs.y, rhs.y); + } + + +@@ -233,20 +246,61 @@ static inline Point operator/(const Point& lhs, const float rhs) + } + + +-static inline void mathLog(const Point& pt) ++static inline void log(const Point& pt) + { + TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y); + } + ++ ++/************************************************************************/ ++/* Line functions */ ++/************************************************************************/ ++ ++struct Line ++{ ++ Point pt1; ++ Point pt2; ++ ++ void split(float at, Line& left, Line& right) const; ++ float length() const; ++}; ++ ++ ++/************************************************************************/ ++/* Bezier functions */ ++/************************************************************************/ ++ ++struct Bezier ++{ ++ Point start; ++ Point ctrl1; ++ Point ctrl2; ++ Point end; ++ ++ void split(float t, Bezier& left); ++ void split(Bezier& left, Bezier& right) const; ++ void split(float at, Bezier& left, Bezier& right) const; ++ float length() const; ++ float lengthApprox() const; ++ float at(float at, float length) const; ++ float atApprox(float at, float length) const; ++ Point at(float t) const; ++ float angle(float t) const; ++}; ++ ++ + /************************************************************************/ + /* Interpolation functions */ + /************************************************************************/ + + template +-static inline T mathLerp(const T &start, const T &end, float t) ++static inline T lerp(const T &start, const T &end, float t) + { + return static_cast(start + (end - start) * t); + } + ++uint8_t lerp(const uint8_t &start, const uint8_t &end, float t); ++ ++} + + #endif //_TVG_MATH_H_ +diff --git a/thirdparty/thorvg/src/common/tvgStr.cpp b/thirdparty/thorvg/src/common/tvgStr.cpp +index 1336f2447c..1ebdd41c5e 100644 +--- a/thirdparty/thorvg/src/common/tvgStr.cpp ++++ b/thirdparty/thorvg/src/common/tvgStr.cpp +@@ -207,16 +207,6 @@ error: + return 0.0f; + } + +- +-int str2int(const char* str, size_t n) +-{ +- int ret = 0; +- for(size_t i = 0; i < n; ++i) { +- ret = ret * 10 + (str[i] - '0'); +- } +- return ret; +-} +- + char* strDuplicate(const char *str, size_t n) + { + auto len = strlen(str); +@@ -229,6 +219,14 @@ char* strDuplicate(const char *str, size_t n) + return (char *) memcpy(ret, str, n); + } + ++char* strAppend(char* lhs, const char* rhs, size_t n) ++{ ++ if (!rhs) return lhs; ++ if (!lhs) return strDuplicate(rhs, n); ++ lhs = (char*)realloc(lhs, strlen(lhs) + n + 1); ++ return strncat(lhs, rhs, n); ++} ++ + char* strDirname(const char* path) + { + const char *ptr = strrchr(path, '/'); +diff --git a/thirdparty/thorvg/src/common/tvgStr.h b/thirdparty/thorvg/src/common/tvgStr.h +index 9e5f9ba9b5..6b16b4c761 100644 +--- a/thirdparty/thorvg/src/common/tvgStr.h ++++ b/thirdparty/thorvg/src/common/tvgStr.h +@@ -29,8 +29,8 @@ namespace tvg + { + + float strToFloat(const char *nPtr, char **endPtr); //convert to float +-int str2int(const char* str, size_t n); //convert to integer + char* strDuplicate(const char *str, size_t n); //copy the string ++char* strAppend(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs + char* strDirname(const char* path); //return the full directory name + + } +diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +index 6c4c8410e8..49c9f6e8aa 100644 +--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp ++++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +@@ -86,10 +86,10 @@ bool PngLoader::read() + + if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) { + image->format = PNG_FORMAT_BGRA; +- surface.cs = ColorSpace::ARGB8888; ++ surface.cs = ColorSpace::ARGB8888S; + } else { + image->format = PNG_FORMAT_RGBA; +- surface.cs = ColorSpace::ABGR8888; ++ surface.cs = ColorSpace::ABGR8888S; + } + + auto buffer = static_cast(malloc(PNG_IMAGE_SIZE((*image)))); +diff --git a/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp b/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp +index fbaaea8743..0db7d2d233 100644 +--- a/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp ++++ b/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp +@@ -134,7 +134,7 @@ bool WebpLoader::read() + } + + +-Surface* WebpLoader::bitmap() ++RenderSurface* WebpLoader::bitmap() + { + this->done(); + +diff --git a/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h b/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h +index ad8fb5a699..3d44edac3a 100644 +--- a/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h ++++ b/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h +@@ -36,7 +36,7 @@ public: + bool open(const char* data, uint32_t size, bool copy) override; + bool read() override; + +- Surface* bitmap() override; ++ RenderSurface* bitmap() override; + + private: + void run(unsigned tid) override; +diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp +index 86a46245d9..cb1306b71a 100644 +--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp ++++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp +@@ -125,7 +125,7 @@ bool JpgLoader::close() + } + + +-Surface* JpgLoader::bitmap() ++RenderSurface* JpgLoader::bitmap() + { + this->done(); + return ImageLoader::bitmap(); +diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h +index 05cbb54c85..85f6d25dc3 100644 +--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h ++++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h +@@ -46,7 +46,7 @@ public: + bool read() override; + bool close() override; + +- Surface* bitmap() override; ++ RenderSurface* bitmap() override; + }; + + #endif //_TVG_JPG_LOADER_H_ +diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +index 6f2bd916d5..92e23698af 100644 +--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp ++++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +@@ -36,6 +36,8 @@ + #include + #include + #include ++ ++#include "tvgCommon.h" + #include "tvgJpgd.h" + + #ifdef _MSC_VER +@@ -70,7 +72,7 @@ enum jpgd_status + JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE, + JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS, + JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH, +- JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER, ++ JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMETIC_SUPPORT, JPGD_UNEXPECTED_MARKER, + JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS, + JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE, + JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR, +@@ -1382,9 +1384,9 @@ int jpeg_decoder::process_markers() + read_dht_marker(); + break; + } +- // No arithmitic support - dumb patents! ++ // No arithmetic support - dumb patents! + case M_DAC: { +- stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); ++ stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT); + break; + } + case M_DQT: { +@@ -1466,8 +1468,8 @@ void jpeg_decoder::locate_sof_marker() + read_sof_marker(); + break; + } +- case M_SOF9: { /* Arithmitic coding */ +- stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); ++ case M_SOF9: { /* Arithmetic coding */ ++ stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT); + break; + } + default: { +@@ -1738,7 +1740,8 @@ void jpeg_decoder::transform_mcu_expand(int mcu_row) + DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr); + break; + default: +- JPGD_ASSERT(false); ++ TVGERR("JPG", "invalid transform_mcu_expand"); ++ return; + } + DCT_Upsample::Matrix44 a(P + Q); P -= Q; + DCT_Upsample::Matrix44& b = P; +@@ -1831,7 +1834,7 @@ void jpeg_decoder::process_restart() + int i; + int c = 0; + +- // Align to a byte boundry ++ // Align to a byte boundary + // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers! + //get_bits_no_markers(m_bits_left & 7); + +diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +index 8fbf3816ea..1ab3043c24 100644 +--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp ++++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +@@ -177,6 +177,7 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt + else if (strstr(str, "%")) { + if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h; + else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w; ++ else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); + else //if other than it's radius + { + float max = svgParse->global.w; +@@ -588,15 +589,20 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re + float _red = 0, _green = 0, _blue = 0; + uint32_t i = 0; + +- if (mathZero(saturation)) _red = _green = _blue = brightness; ++ while (hue < 0) hue += 360.0f; ++ hue = fmod(hue, 360.0f); ++ saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f; ++ brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f; ++ ++ if (tvg::zero(saturation)) _red = _green = _blue = brightness; + else { +- if (mathEqual(hue, 360.0)) hue = 0.0f; ++ if (tvg::equal(hue, 360.0)) hue = 0.0f; + hue /= 60.0f; + + v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation)); + p = brightness + brightness - v; + +- if (!mathZero(v)) sv = (v - p) / v; ++ if (!tvg::zero(v)) sv = (v - p) / v; + else sv = 0; + + i = static_cast(hue); +@@ -710,15 +716,15 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** + 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] == ')') { + 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) { +- th = float(uint32_t(th) % 360); ++ const char* saturation = nullptr; + hue = _skipSpace(hue, nullptr); + hue = (char*)_skipComma(hue); + hue = _skipSpace(hue, nullptr); + if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') { ++ const char* brightness = nullptr; + ts /= 100.0f; + saturation = _skipSpace(saturation + 1, nullptr); + saturation = (char*)_skipComma(saturation); +@@ -853,8 +859,8 @@ static Matrix* _parseTransformationMatrix(const char* value) + //Transform to signed. + points[0] = fmodf(points[0], 360.0f); + if (points[0] < 0) points[0] += 360.0f; +- auto c = cosf(mathDeg2Rad(points[0])); +- auto s = sinf(mathDeg2Rad(points[0])); ++ auto c = cosf(deg2rad(points[0])); ++ auto s = sinf(deg2rad(points[0])); + if (ptCount == 1) { + Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; + *matrix *= tmp; +@@ -877,12 +883,12 @@ static Matrix* _parseTransformationMatrix(const char* value) + *matrix *= tmp; + } else if (state == MatrixState::SkewX) { + if (ptCount != 1) goto error; +- auto deg = tanf(mathDeg2Rad(points[0])); ++ auto deg = tanf(deg2rad(points[0])); + Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 }; + *matrix *= tmp; + } else if (state == MatrixState::SkewY) { + if (ptCount != 1) goto error; +- auto deg = tanf(mathDeg2Rad(points[0])); ++ auto deg = tanf(deg2rad(points[0])); + Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 }; + *matrix *= tmp; + } +@@ -1061,7 +1067,7 @@ static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, co + static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) + { + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); +- node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); ++ node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal); + } + + +@@ -1609,7 +1615,7 @@ static constexpr struct + } circleTags[] = { + {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)}, + {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)}, +- {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)} ++ {"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)} + }; + + +@@ -2178,6 +2184,7 @@ static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const ch + //TODO: support the def font and size as used in a system? + loader->svgParse->node->node.text.fontSize = 10.0f; + loader->svgParse->node->node.text.fontFamily = nullptr; ++ loader->svgParse->node->node.text.text = nullptr; + + func(buf, bufLength, _attrParseTextNode, loader); + +@@ -2548,6 +2555,18 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char + } + + ++static SvgColor* _findLatestColor(const SvgLoaderData* loader) ++{ ++ auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc; ++ ++ while (parent != nullptr) { ++ if (parent->style->curColorSet) return &parent->style->color; ++ parent = parent->parent; ++ } ++ return nullptr; ++} ++ ++ + static bool _attrParseStopsStyle(void* data, const char* key, const char* value) + { + SvgLoaderData* loader = (SvgLoaderData*)data; +@@ -2557,7 +2576,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) + stop->a = _toOpacity(value); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); + } else if (!strcmp(key, "stop-color")) { +- if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { ++ if (!strcmp(value, "currentColor")) { ++ if (auto latestColor = _findLatestColor(loader)) { ++ stop->r = latestColor->r; ++ stop->g = latestColor->g; ++ stop->b = latestColor->b; ++ } ++ } else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); + } + } else { +@@ -2580,7 +2605,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value) + stop->a = _toOpacity(value); + } + } else if (!strcmp(key, "stop-color")) { +- if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { ++ if (!strcmp(value, "currentColor")) { ++ if (auto latestColor = _findLatestColor(loader)) { ++ stop->r = latestColor->r; ++ stop->g = latestColor->g; ++ stop->b = latestColor->b; ++ } ++ } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { + _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); + } + } else if (!strcmp(key, "style")) { +@@ -3283,6 +3314,7 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content, + for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) { + if (!strncmp(tagName, graphicsTags[i].tag, sz)) { + loader->currentGraphicsNode = nullptr; ++ if (!strncmp(tagName, "text", 4)) loader->openedTag = OpenedTagType::Other; + loader->stack.pop(); + break; + } +@@ -3334,7 +3366,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, + else parent = loader->doc; + if (!strcmp(tagName, "style")) { + // TODO: For now only the first style node is saved. After the css id selector +- // is introduced this if condition shouldin't be necessary any more ++ // is introduced this if condition shouldn't be necessary any more + if (!loader->cssStyle) { + node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); + loader->cssStyle = node; +@@ -3356,11 +3388,9 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, + node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); + if (node && !empty) { + if (!strcmp(tagName, "text")) loader->openedTag = OpenedTagType::Text; +- else { +- auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); +- loader->stack.push(defs); +- loader->currentGraphicsNode = node; +- } ++ auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); ++ loader->stack.push(defs); ++ loader->currentGraphicsNode = node; + } + } else if ((gradientMethod = _findGradientFactory(tagName))) { + SvgStyleGradient* gradient; +@@ -3396,9 +3426,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, + static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length) + { + auto text = &loader->svgParse->node->node.text; +- if (text->text) free(text->text); +- text->text = strDuplicate(content, length); +- loader->openedTag = OpenedTagType::Other; ++ text->text = strAppend(text->text, content, length); + } + + +diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +index 5661c8ae82..e64d7afb41 100644 +--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h ++++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +@@ -215,6 +215,7 @@ enum class SvgParserLengthType + { + Vertical, + Horizontal, ++ Diagonal, + //In case of, for example, radius of radial gradient + Other + }; +diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +index 115e81aee1..57442139cd 100644 +--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp ++++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +@@ -126,7 +126,7 @@ void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, P + rx = fabsf(rx); + ry = fabsf(ry); + +- angle = mathDeg2Rad(angle); ++ angle = deg2rad(angle); + cosPhi = cosf(angle); + sinPhi = sinf(angle); + dx2 = (sx - x) / 2.0f; +@@ -190,14 +190,14 @@ void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, P + cx += (sx + x) / 2.0f; + cy += (sy + y) / 2.0f; + +- //Sstep 4 (F6.5.4) ++ //Step 4 (F6.5.4) + //We dont' use arccos (as per w3c doc), see + //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm + //Note: atan2 (0.0, 1.0) == 0.0 +- at = mathAtan2(((y1p - cyp) / ry), ((x1p - cxp) / rx)); ++ at = tvg::atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx)); + theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at; + +- nat = mathAtan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx)); ++ nat = tvg::atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx)); + deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at; + + if (sweep) { +@@ -469,12 +469,12 @@ static bool _processCommand(Array* cmds, Array* pts, char cm + } + case 'a': + case 'A': { +- if (mathZero(arr[0]) || mathZero(arr[1])) { ++ if (tvg::zero(arr[0]) || tvg::zero(arr[1])) { + Point p = {arr[5], arr[6]}; + cmds->push(PathCommand::LineTo); + pts->push(p); + *cur = {arr[5], arr[6]}; +- } else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) { ++ } else if (!tvg::equal(cur->x, arr[5]) || !tvg::equal(cur->y, arr[6])) { + _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]); + *cur = *curCtl = {arr[5], arr[6]}; + *isQuadratic = false; +diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +index b048695a23..00c7408a7e 100644 +--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp ++++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +@@ -153,7 +153,7 @@ static unique_ptr _applyRadialGradientProperty(SvgStyleGradient* + if (isTransform) finalTransform = *g->transform; + + if (g->userSpace) { +- //The radius scalling is done according to the Units section: ++ //The radius scaling is done according to the Units section: + //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html + g->radial->cx = g->radial->cx * vBox.w; + g->radial->cy = g->radial->cy * vBox.h; +@@ -215,7 +215,7 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape* + } + if (child->transform) finalTransform = *child->transform * finalTransform; + +- return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform); ++ return _appendClipShape(loaderData, child, shape, vBox, svgPath, identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform); + } + + +@@ -272,8 +272,7 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg + if (valid) { + Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath); + comp->transform(finalTransform); +- +- paint->composite(std::move(comp), CompositeMethod::ClipPath); ++ paint->clip(std::move(comp)); + } + + node->style->clipPath.applying = false; +@@ -714,7 +713,6 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee + + static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) + { +- unique_ptr finalScene; + auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite); + + // mUseTransform = mUseTransform * mTranslate +@@ -736,10 +734,10 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod + auto vh = (symbol.hasViewBox ? symbol.vh : height); + + Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; +- if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) { ++ if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) { + Box box = {symbol.vx, symbol.vy, vw, vh}; + mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box); +- } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { ++ } else if (!tvg::zero(symbol.vx) || !tvg::zero(symbol.vy)) { + mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; + } + +@@ -751,9 +749,7 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod + mSceneTransform = mUseTransform * mSceneTransform; + scene->transform(mSceneTransform); + +- if (node->node.use.symbol->node.symbol.overflowVisible) { +- finalScene = std::move(scene); +- } else { ++ if (!node->node.use.symbol->node.symbol.overflowVisible) { + auto viewBoxClip = Shape::gen(); + viewBoxClip->appendRect(0, 0, width, height, 0, 0); + +@@ -764,21 +760,13 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod + } + viewBoxClip->transform(mClipTransform); + +- auto compositeLayer = Scene::gen(); +- compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); +- compositeLayer->push(std::move(scene)); +- +- auto root = Scene::gen(); +- root->push(std::move(compositeLayer)); +- +- finalScene = std::move(root); ++ scene->clip(std::move(viewBoxClip)); + } + } else { + scene->transform(mUseTransform); +- finalScene = std::move(scene); + } + +- return finalScene; ++ return scene; + } + + +@@ -821,7 +809,7 @@ static unique_ptr _textBuildHelper(SvgLoaderData& loaderData, const SvgNod + + Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) textTransform = *node->transform; +- mathTranslateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize); ++ translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize); + text->transform(textTransform); + + //TODO: handle def values of font and size as used in a system? +@@ -926,10 +914,10 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe + + if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); + +- if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) { ++ if (!tvg::equal(w, vBox.w) || !tvg::equal(h, vBox.h)) { + Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); + docNode->transform(m); +- } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) { ++ } else if (!tvg::zero(vBox.x) || !tvg::zero(vBox.y)) { + docNode->translate(-vBox.x, -vBox.y); + } + +@@ -937,7 +925,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe + viewBoxClip->appendRect(0, 0, w, h); + + auto compositeLayer = Scene::gen(); +- compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); ++ compositeLayer->clip(std::move(viewBoxClip)); + compositeLayer->push(std::move(docNode)); + + auto root = Scene::gen(); +diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +index 09fc8aaac1..da1cdae9e0 100644 +--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp ++++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +@@ -492,13 +492,13 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt + key[0] = '\0'; + val[0] = '\0'; + +- if (next == nullptr && sep != nullptr) { ++ if (sep != nullptr && next == nullptr) { + memcpy(key, buf, sep - buf); + key[sep - buf] = '\0'; + + memcpy(val, sep + 1, end - sep - 1); + val[end - sep - 1] = '\0'; +- } else if (sep < next && sep != nullptr) { ++ } else if (sep != nullptr && sep < next) { + memcpy(key, buf, sep - buf); + key[sep - buf] = '\0'; + +@@ -522,8 +522,9 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt + } + } + ++ if (!next) break; + buf = next + 1; +- } while (next != nullptr); ++ } while (true); + + return true; + } +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +index 05cbdc7f3a..9371ae6c5a 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +@@ -113,7 +113,7 @@ struct SwSpan + uint8_t coverage; + }; + +-struct SwRleData ++struct SwRle + { + SwSpan *spans; + uint32_t alloc; +@@ -134,7 +134,6 @@ struct SwFill + { + struct SwLinear { + float dx, dy; +- float len; + float offset; + }; + +@@ -154,6 +153,7 @@ struct SwFill + uint32_t* ctable; + FillSpread spread; + ++ bool solid = false; //solid color fill with the last color from colorStops + bool translucent; + }; + +@@ -211,8 +211,8 @@ struct SwShape + SwOutline* outline = nullptr; + SwStroke* stroke = nullptr; + SwFill* fill = nullptr; +- SwRleData* rle = nullptr; +- SwRleData* strokeRle = nullptr; ++ SwRle* rle = nullptr; ++ SwRle* strokeRle = nullptr; + SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling. + + bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips? +@@ -221,7 +221,7 @@ struct SwShape + struct SwImage + { + SwOutline* outline = nullptr; +- SwRleData* rle = nullptr; ++ SwRle* rle = nullptr; + union { + pixel_t* data; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels +@@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*); //bl + + struct SwCompositor; + +-struct SwSurface : Surface ++struct SwSurface : RenderSurface + { + SwJoin join; + SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 + SwBlender blender = nullptr; //blender (optional) + SwCompositor* compositor = nullptr; //compositor (optional) +- BlendMethod blendMethod; //blending method (uint8_t) ++ BlendMethod blendMethod = BlendMethod::Normal; + + SwAlpha alpha(CompositeMethod method) + { +@@ -262,7 +262,7 @@ struct SwSurface : Surface + { + } + +- SwSurface(const SwSurface* rhs) : Surface(rhs) ++ SwSurface(const SwSurface* rhs) : RenderSurface(rhs) + { + join = rhs->join; + memcpy(alphas, rhs->alphas, sizeof(alphas)); +@@ -272,7 +272,7 @@ struct SwSurface : Surface + } + }; + +-struct SwCompositor : Compositor ++struct SwCompositor : RenderCompositor + { + SwSurface* recoverSfc; //Recover surface when composition is started + SwCompositor* recoverCmp; //Recover compositor when composition is done +@@ -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) + { +- 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) +@@ -404,7 +404,6 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t + return JOIN(255, c1, c2, c3); + } + +- + static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) + { + // s * d +@@ -414,7 +413,6 @@ static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_ + return JOIN(255, c1, c2, c3); + } + +- + static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) + { + // if (2 * d < da) => 2 * s * d, +@@ -490,42 +488,44 @@ SwFixed mathAtan(const SwPoint& pt); + SwFixed mathCos(SwFixed angle); + SwFixed mathSin(SwFixed angle); + void mathSplitCubic(SwPoint* base); ++void mathSplitLine(SwPoint* base); + SwFixed mathDiff(SwFixed angle1, SwFixed angle2); + SwFixed mathLength(const SwPoint& pt); +-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); ++int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); + SwFixed mathMean(SwFixed angle1, SwFixed angle2); +-SwPoint mathTransform(const Point* to, const Matrix* transform); ++SwPoint mathTransform(const Point* to, const Matrix& transform); + bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); +-bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); ++bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee); + + void shapeReset(SwShape* shape); +-bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); ++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 shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); + 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 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 shapeResetStrokeFill(SwShape* shape); + void shapeDelFill(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); + SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); + 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); + void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); + void imageReset(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 fillFree(SwFill* fill); + +@@ -542,13 +542,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 + void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. + void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. + +-SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); +-SwRleData* rleRender(const SwBBox* bbox); +-void rleFree(SwRleData* rle); +-void rleReset(SwRleData* rle); +-void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2); +-void rleClipPath(SwRleData* rle, const SwRleData* clip); +-void rleClipRect(SwRleData* rle, const SwBBox* clip); ++SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); ++SwRle* rleRender(const SwBBox* bbox); ++void rleFree(SwRle* rle); ++void rleReset(SwRle* rle); ++void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); ++void rleClip(SwRle* rle, const SwRle* clip); ++void rleClip(SwRle* rle, const SwBBox* clip); + + SwMpool* mpoolInit(uint32_t threads); + bool mpoolTerm(SwMpool* mpool); +@@ -561,16 +561,24 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx); + void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); + + 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 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 rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); +-bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); ++bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); ++bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); + void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); ++void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); ++void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); + void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); +-void rasterUnpremultiply(Surface* surface); +-void rasterPremultiply(Surface* surface); +-bool rasterConvertCS(Surface* surface, ColorSpace to); ++void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped); ++void rasterUnpremultiply(RenderSurface* surface); ++void rasterPremultiply(RenderSurface* surface); ++bool rasterConvertCS(RenderSurface* surface, ColorSpace to); ++ ++bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); ++bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); ++bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct); ++bool effectDropShadowPrepare(RenderEffectDropShadow* effect); + + #endif /* _TVG_SW_COMMON_H_ */ +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp +index bd0b5ffdcb..f70ba7a13d 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp ++++ b/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; + + 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; + } + +@@ -66,15 +66,15 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f + static uint32_t _estimateAAMargin(const Fill* fdata) + { + constexpr float marginScalingFactor = 800.0f; +- if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { ++ if (fdata->type() == Type::RadialGradient) { + auto radius = P(static_cast(fdata))->r; +- return mathZero(radius) ? 0 : static_cast(marginScalingFactor / radius); ++ return tvg::zero(radius) ? 0 : static_cast(marginScalingFactor / radius); + } + auto grad = P(static_cast(fdata)); + Point p1 {grad->x1, grad->y1}; + Point p2 {grad->x2, grad->y2}; +- auto length = mathLength(&p1, &p2); +- return mathZero(length) ? 0 : static_cast(marginScalingFactor / length); ++ auto len = length(&p1, &p2); ++ return tvg::zero(len) ? 0 : static_cast(marginScalingFactor / len); + } + + +@@ -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) + { ++ if (fill->solid) return true; ++ + if (!fill->ctable) { + fill->ctable = static_cast(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); + if (!fill->ctable) return false; +@@ -205,49 +207,52 @@ 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; + if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; + + fill->linear.dx = x2 - x1; + 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 (tvg::zero(fill->linear.dx) && tvg::zero(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; + + auto gradTransform = linear->transform(); +- bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); ++ bool isTransformation = !identity((const Matrix*)(&gradTransform)); + + if (isTransformation) { +- if (transform) gradTransform = *transform * gradTransform; +- } else if (transform) { +- gradTransform = *transform; ++ gradTransform = transform * gradTransform; ++ } else { ++ gradTransform = transform; + isTransformation = true; + } + + if (isTransformation) { + Matrix invTransform; +- if (!mathInverse(&gradTransform, &invTransform)) return false; ++ if (!inverse(&gradTransform, &invTransform)) return false; + + fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23; + + auto dx = fill->linear.dx; + fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21; + 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; + } + + +-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 cy = P(radial)->cy; +@@ -256,7 +261,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr + auto fy = P(radial)->fy; + auto fr = P(radial)->fr; + +- if (r < FLOAT_EPSILON) return true; ++ if (tvg::zero(r)) { ++ fill->solid = true; ++ return true; ++ } + + fill->radial.dr = r - fr; + fill->radial.dx = cx - fx; +@@ -287,19 +295,17 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr + if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a; + + auto gradTransform = radial->transform(); +- bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); ++ bool isTransformation = !identity((const Matrix*)(&gradTransform)); + +- if (transform) { +- if (isTransformation) gradTransform = *transform * gradTransform; +- else { +- gradTransform = *transform; +- isTransformation = true; +- } ++ if (isTransformation) gradTransform = transform * gradTransform; ++ else { ++ gradTransform = transform; ++ isTransformation = true; + } + + if (isTransformation) { + Matrix invTransform; +- if (!mathInverse(&gradTransform, &invTransform)) return false; ++ if (!inverse(&gradTransform, &invTransform)) return false; + fill->radial.a11 = invTransform.e11; + fill->radial.a12 = invTransform.e12; + fill->radial.a13 = invTransform.e13; +@@ -547,7 +553,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (opacity == 255) { +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, alpha(cmp)); +@@ -578,7 +584,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 + } + } + } else { +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); +@@ -620,7 +626,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE)))); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = maskOp(src, *dst, ~src); +@@ -637,7 +643,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { +- auto src = MULTIPLY(_fixedPixel(fill, t2), a); ++ auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a); + *dst = maskOp(src, *dst, ~src); + t2 += inc2; + } +@@ -645,7 +651,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 + } else { + uint32_t counter = 0; + while (counter++ < len) { +- auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a); ++ auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a); + *dst = maskOp(src, *dst, ~src); + ++dst; + t += inc; +@@ -662,7 +668,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto src = A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE))); + src = MULTIPLY(src, a); + for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) { +@@ -709,7 +715,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = op(color, *dst, a); +@@ -749,7 +755,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + +- if (mathZero(inc)) { ++ if (tvg::zero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + if (a == 255) { + for (uint32_t i = 0; i < len; ++i, ++dst) { +@@ -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; + + fill->spread = fdata->spread(); + +- if (ctable) { +- if (!_updateColorTable(fill, fdata, surface, opacity)) return false; ++ if (fdata->type() == Type::LinearGradient) { ++ if (!_prepareLinear(fill, static_cast(fdata), transform)) return false; ++ } else if (fdata->type() == Type::RadialGradient) { ++ if (!_prepareRadial(fill, static_cast(fdata), transform)) return false; + } + +- if (fdata->identifier() == TVG_CLASS_ID_LINEAR) { +- return _prepareLinear(fill, static_cast(fdata), transform); +- } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { +- return _prepareRadial(fill, static_cast(fdata), transform); +- } ++ if (ctable) return _updateColorTable(fill, fdata, surface, opacity); ++ return true; ++} ++ + +- //LOG: What type of gradient?! ++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->translucent = false; ++ fill->solid = false; + } + + +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp +index e1d41a0d52..d2c02bb932 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp +@@ -27,14 +27,14 @@ + /* 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 (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(m.e21)) return true; + 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); + auto outline = image->outline; +@@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr + outline->closed.reserve(1); + + 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(image->w); +- auto h = static_cast(image->h); +- to[0] = {0, 0}; +- to[1] = {w, 0}; +- to[2] = {w, h}; +- to[3] = {0, h}; +- } ++ auto w = static_cast(image->w); ++ auto h = static_cast(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++) { + 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 */ + /************************************************************************/ + +-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); + + //Fast track: Non-transformed image but just shifted. + if (image->direct) { +- image->ox = -static_cast(nearbyint(transform->e13)); +- image->oy = -static_cast(nearbyint(transform->e23)); ++ image->ox = -static_cast(nearbyint(transform.e13)); ++ image->oy = -static_cast(nearbyint(transform.e23)); + //Figure out the scale factor by transform matrix + } 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; + +- if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true; ++ if (tvg::zero(transform.e12) && tvg::zero(transform.e21)) image->scaled = true; + else image->scaled = false; + } + +- 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); + } + +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +index ae158c836a..1ff99f6aec 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +@@ -44,7 +44,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2) + } + + +-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) ++int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) + { + auto d1 = base[2] - base[3]; + auto d2 = base[1] - base[2]; +@@ -54,7 +54,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw + if (d2.small()) { + if (d3.small()) { + angleIn = angleMid = angleOut = 0; +- return true; ++ return -1; //ignoreable + } else { + angleIn = angleMid = angleOut = mathAtan(d3); + } +@@ -90,8 +90,8 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw + auto theta1 = abs(mathDiff(angleIn, angleMid)); + auto theta2 = abs(mathDiff(angleMid, angleOut)); + +- if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true; +- return false; ++ if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size ++ return 1; + } + + +@@ -179,7 +179,7 @@ SwFixed mathTan(SwFixed angle) + SwFixed mathAtan(const SwPoint& pt) + { + if (pt.zero()) return 0; +- return SwFixed(mathAtan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f); ++ return SwFixed(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f); + } + + +@@ -242,6 +242,15 @@ void mathSplitCubic(SwPoint* base) + } + + ++void mathSplitLine(SwPoint* base) ++{ ++ base[2] = base[1]; ++ ++ base[1].x = (base[0].x + base[1].x) >> 1; ++ base[1].y = (base[0].y + base[1].y) >> 1; ++} ++ ++ + SwFixed mathDiff(SwFixed angle1, SwFixed angle2) + { + auto delta = angle2 - angle1; +@@ -254,30 +263,28 @@ 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)}; + } + + +-bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee) ++bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee) + { +- clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x; +- clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y; +- clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x; +- clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y; ++ clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x; ++ clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y; ++ clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x; ++ clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y; + + //Check valid region +- if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false; ++ if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false; + + //Check boundary +- if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y || +- clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false; ++ if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y || ++ clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false; + + return true; + } +@@ -305,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S + if (yMin > pt->y) yMin = pt->y; + if (yMax < pt->y) yMax = pt->y; + } +- //Since no antialiasing is applied in the Fast Track case, +- //the rasterization region has to be rearranged. +- //https://github.com/Samsung/thorvg/issues/916 ++ + if (fastTrack) { + renderRegion.min.x = static_cast(nearbyint(xMin / 64.0f)); + renderRegion.max.x = static_cast(nearbyint(xMax / 64.0f)); +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp +new file mode 100644 +index 0000000000..fd8e532e12 +--- /dev/null ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp +@@ -0,0 +1,410 @@ ++/* ++ * Copyright (c) 2024 the ThorVG project. All rights reserved. ++ ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ */ ++ ++#include "tvgMath.h" ++#include "tvgSwCommon.h" ++ ++/************************************************************************/ ++/* Gaussian Blur Implementation */ ++/************************************************************************/ ++ ++struct SwGaussianBlur ++{ ++ static constexpr int MAX_LEVEL = 3; ++ int level; ++ int kernel[MAX_LEVEL]; ++}; ++ ++ ++static void _gaussianExtendRegion(RenderRegion& region, int extra, int8_t direction) ++{ ++ //bbox region expansion for feathering ++ if (direction != 2) { ++ region.x = -extra; ++ region.w = extra * 2; ++ } ++ if (direction != 1) { ++ region.y = -extra; ++ region.h = extra * 2; ++ } ++} ++ ++ ++static int _gaussianEdgeWrap(int end, int idx) ++{ ++ auto r = idx % end; ++ return (r < 0) ? end + r : r; ++} ++ ++ ++static int _gaussianEdgeExtend(int end, int idx) ++{ ++ if (idx < 0) return 0; ++ else if (idx >= end) return end - 1; ++ return idx; ++} ++ ++ ++static int _gaussianRemap(int end, int idx, int border) ++{ ++ if (border == 1) return _gaussianEdgeWrap(end, idx); ++ return _gaussianEdgeExtend(end, idx); ++} ++ ++ ++//TODO: SIMD OPTIMIZATION? ++static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, int border, bool flipped) ++{ ++ if (flipped) { ++ src += (bbox.min.x * stride + bbox.min.y) << 2; ++ dst += (bbox.min.x * stride + bbox.min.y) << 2; ++ } else { ++ src += (bbox.min.y * stride + bbox.min.x) << 2; ++ dst += (bbox.min.y * stride + bbox.min.x) << 2; ++ } ++ ++ auto iarr = 1.0f / (dimension + dimension + 1); ++ ++ #pragma omp parallel for ++ for (int y = 0; y < h; ++y) { ++ auto p = y * stride; ++ auto i = p * 4; //current index ++ auto l = -(dimension + 1); //left index ++ auto r = dimension; //right index ++ int acc[4] = {0, 0, 0, 0}; //sliding accumulator ++ ++ //initial accumulation ++ for (int x = l; x < r; ++x) { ++ auto id = (_gaussianRemap(w, x, border) + p) * 4; ++ acc[0] += src[id++]; ++ acc[1] += src[id++]; ++ acc[2] += src[id++]; ++ acc[3] += src[id]; ++ } ++ //perform filtering ++ for (int x = 0; x < w; ++x, ++r, ++l) { ++ auto rid = (_gaussianRemap(w, r, border) + p) * 4; ++ auto lid = (_gaussianRemap(w, l, border) + p) * 4; ++ acc[0] += src[rid++] - src[lid++]; ++ acc[1] += src[rid++] - src[lid++]; ++ acc[2] += src[rid++] - src[lid++]; ++ acc[3] += src[rid] - src[lid]; ++ dst[i++] = static_cast(acc[0] * iarr + 0.5f); ++ dst[i++] = static_cast(acc[1] * iarr + 0.5f); ++ dst[i++] = static_cast(acc[2] * iarr + 0.5f); ++ dst[i++] = static_cast(acc[3] * iarr + 0.5f); ++ } ++ } ++} ++ ++ ++static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality) ++{ ++ const auto MAX_LEVEL = SwGaussianBlur::MAX_LEVEL; ++ ++ if (tvg::zero(sigma)) return 0; ++ ++ data->level = int(SwGaussianBlur::MAX_LEVEL * ((quality - 1) * 0.01f)) + 1; ++ ++ //compute box kernel sizes ++ auto wl = (int) sqrt((12 * sigma / MAX_LEVEL) + 1); ++ if (wl % 2 == 0) --wl; ++ auto wu = wl + 2; ++ auto mi = (12 * sigma - MAX_LEVEL * wl * wl - 4 * MAX_LEVEL * wl - 3 * MAX_LEVEL) / (-4 * wl - 4); ++ auto m = int(mi + 0.5f); ++ auto extends = 0; ++ ++ for (int i = 0; i < data->level; i++) { ++ data->kernel[i] = ((i < m ? wl : wu) - 1) / 2; ++ extends += data->kernel[i]; ++ } ++ ++ return extends; ++} ++ ++ ++bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params) ++{ ++ auto rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur)); ++ ++ auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality); ++ ++ //invalid ++ if (extends == 0) { ++ params->invalid = true; ++ free(rd); ++ return false; ++ } ++ ++ _gaussianExtendRegion(params->extend, extends, params->direction); ++ ++ params->rd = rd; ++ ++ return true; ++} ++ ++ ++bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params) ++{ ++ if (cmp->image.channelSize != sizeof(uint32_t)) { ++ TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!"); ++ return false; ++ } ++ ++ auto& buffer = surface->compositor->image; ++ auto data = static_cast(params->rd); ++ auto& bbox = cmp->bbox; ++ auto w = (bbox.max.x - bbox.min.x); ++ auto h = (bbox.max.y - bbox.min.y); ++ auto stride = cmp->image.stride; ++ auto front = cmp->image.buf32; ++ auto back = buffer.buf32; ++ auto swapped = false; ++ ++ TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level); ++ ++ /* It is best to take advantage of the Gaussian blur’s separable property ++ by dividing the process into two passes. horizontal and vertical. ++ We can expect fewer calculations. */ ++ ++ //horizontal ++ if (params->direction != 2) { ++ for (int i = 0; i < data->level; ++i) { ++ _gaussianFilter(reinterpret_cast(back), reinterpret_cast(front), stride, w, h, bbox, data->kernel[i], params->border, false); ++ std::swap(front, back); ++ swapped = !swapped; ++ } ++ } ++ ++ //vertical. x/y flipping and horionztal access is pretty compatible with the memory architecture. ++ if (params->direction != 1) { ++ rasterXYFlip(front, back, stride, w, h, bbox, false); ++ std::swap(front, back); ++ ++ for (int i = 0; i < data->level; ++i) { ++ _gaussianFilter(reinterpret_cast(back), reinterpret_cast(front), stride, h, w, bbox, data->kernel[i], params->border, true); ++ std::swap(front, back); ++ swapped = !swapped; ++ } ++ ++ rasterXYFlip(front, back, stride, h, w, bbox, true); ++ std::swap(front, back); ++ } ++ ++ if (swapped) std::swap(cmp->image.buf8, buffer.buf8); ++ ++ return true; ++} ++ ++/************************************************************************/ ++/* Drop Shadow Implementation */ ++/************************************************************************/ ++ ++struct SwDropShadow : SwGaussianBlur ++{ ++ SwPoint offset; ++}; ++ ++ ++//TODO: SIMD OPTIMIZATION? ++static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped) ++{ ++ if (flipped) { ++ src += (bbox.min.x * stride + bbox.min.y); ++ dst += (bbox.min.x * stride + bbox.min.y); ++ } else { ++ src += (bbox.min.y * stride + bbox.min.x); ++ dst += (bbox.min.y * stride + bbox.min.x); ++ } ++ auto iarr = 1.0f / (dimension + dimension + 1); ++ ++ #pragma omp parallel for ++ for (int y = 0; y < h; ++y) { ++ auto p = y * stride; ++ auto i = p; //current index ++ auto l = -(dimension + 1); //left index ++ auto r = dimension; //right index ++ int acc = 0; //sliding accumulator ++ ++ //initial accumulation ++ for (int x = l; x < r; ++x) { ++ auto id = _gaussianEdgeExtend(w, x) + p; ++ acc += A(src[id]); ++ } ++ //perform filtering ++ for (int x = 0; x < w; ++x, ++r, ++l) { ++ auto rid = _gaussianEdgeExtend(w, r) + p; ++ auto lid = _gaussianEdgeExtend(w, l) + p; ++ acc += A(src[rid]) - A(src[lid]); ++ dst[i++] = ALPHA_BLEND(color, static_cast(acc * iarr + 0.5f)); ++ } ++ } ++} ++ ++ ++static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct) ++{ ++ src += (region.min.y * stride + region.min.x); ++ dst += (region.min.y * stride + region.min.x); ++ ++ auto w = region.max.x - region.min.x; ++ auto h = region.max.y - region.min.y; ++ auto translucent = (direct || opacity < 255); ++ ++ //shift offset ++ if (region.min.x + offset.x < 0) src -= offset.x; ++ else dst += offset.x; ++ ++ if (region.min.y + offset.y < 0) src -= (offset.y * stride); ++ else dst += (offset.y * stride); ++ ++ for (auto y = 0; y < h; ++y) { ++ if (translucent) rasterTranslucentPixel32(dst, src, w, opacity); ++ else rasterPixel32(dst, src, w, opacity); ++ src += stride; ++ dst += stride; ++ } ++} ++ ++ ++static void _dropShadowExtendRegion(RenderRegion& region, int extra, SwPoint& offset) ++{ ++ //bbox region expansion for feathering ++ region.x = -extra; ++ region.w = extra * 2; ++ region.y = -extra; ++ region.h = extra * 2; ++ ++ region.x = std::min(region.x + (int32_t)offset.x, region.x); ++ region.y = std::min(region.y + (int32_t)offset.y, region.y); ++ region.w += abs(offset.x); ++ region.h += abs(offset.y); ++} ++ ++ ++bool effectDropShadowPrepare(RenderEffectDropShadow* params) ++{ ++ auto rd = (SwDropShadow*)malloc(sizeof(SwDropShadow)); ++ ++ //compute box kernel sizes ++ auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality); ++ ++ //invalid ++ if (extends == 0 || params->color[3] == 0) { ++ params->invalid = true; ++ free(rd); ++ return false; ++ } ++ ++ //offset ++ if (params->distance > 0.0f) { ++ auto radian = tvg::deg2rad(90.0f - params->angle); ++ rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))}; ++ } else { ++ rd->offset = {0, 0}; ++ } ++ ++ //bbox region expansion for feathering ++ _dropShadowExtendRegion(params->extend, extends, rd->offset); ++ ++ params->rd = rd; ++ ++ return true; ++} ++ ++ ++//A quite same integration with effectGaussianBlur(). See it for detailed comments. ++//surface[0]: the original image, to overlay it into the filtered image. ++//surface[1]: temporary buffer for generating the filtered image. ++bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct) ++{ ++ if (cmp->image.channelSize != sizeof(uint32_t)) { ++ TVGERR("SW_ENGINE", "Not supported grayscale Drop Shadow!"); ++ return false; ++ } ++ ++ //FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible. ++ ++ auto data = static_cast(params->rd); ++ auto& bbox = cmp->bbox; ++ auto w = (bbox.max.x - bbox.min.x); ++ auto h = (bbox.max.y - bbox.min.y); ++ ++ //outside the screen ++ if (abs(data->offset.x) >= w || abs(data->offset.y) >= h) return true; ++ ++ SwImage* buffer[] = {&surface[0]->compositor->image, &surface[1]->compositor->image}; ++ auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255); ++ auto stride = cmp->image.stride; ++ auto front = cmp->image.buf32; ++ auto back = buffer[1]->buf32; ++ opacity = MULTIPLY(params->color[3], opacity); ++ ++ TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); ++ ++ //saving the original image in order to overlay it into the filtered image. ++ _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false); ++ std::swap(front, buffer[0]->buf32); ++ std::swap(front, back); ++ ++ //horizontal ++ for (int i = 1; i < data->level; ++i) { ++ _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[i], color, false); ++ std::swap(front, back); ++ } ++ ++ //vertical ++ rasterXYFlip(front, back, stride, w, h, bbox, false); ++ std::swap(front, back); ++ ++ for (int i = 0; i < data->level; ++i) { ++ _dropShadowFilter(back, front, stride, h, w, bbox, data->kernel[i], color, true); ++ std::swap(front, back); ++ } ++ ++ rasterXYFlip(front, back, stride, h, w, bbox, true); ++ std::swap(cmp->image.buf32, back); ++ ++ //draw to the main surface directly ++ if (direct) { ++ _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct); ++ std::swap(cmp->image.buf32, buffer[0]->buf32); ++ return true; ++ } ++ ++ //draw to the intermediate surface ++ rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h); ++ _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct); ++ std::swap(cmp->image.buf32, buffer[1]->buf32); ++ ++ //compositing shadow and body ++ auto s = buffer[0]->buf32 + (bbox.min.y * buffer[0]->stride + bbox.min.x); ++ auto d = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); ++ ++ for (auto y = 0; y < h; ++y) { ++ rasterTranslucentPixel32(d, s, w, 255); ++ s += buffer[0]->stride; ++ d += cmp->image.stride; ++ } ++ ++ return true; ++} +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +index 042d1e2b44..18ffc18e1e 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp ++++ b/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) + { +- //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; + } + +@@ -209,6 +220,8 @@ static inline SwMask _getMaskOp(CompositeMethod method) + case CompositeMethod::SubtractMask: return _opMaskSubtract; + case CompositeMethod::DifferenceMask: return _opMaskDifference; + case CompositeMethod::IntersectMask: return _opMaskIntersect; ++ case CompositeMethod::LightenMask: return _opMaskLighten; ++ case CompositeMethod::DarkenMask: return _opMaskDarken; + default: return nullptr; + } + } +@@ -410,12 +423,11 @@ static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_ + auto h = static_cast(region.max.y - region.min.y); + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; +- auto ialpha = 255 - a; + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { +- *dst = surface->blender(color, *dst, ialpha); ++ *dst = surface->blender(color, *dst, 255); + } + } + return true; +@@ -478,7 +490,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin + /* Rle */ + /************************************************************************/ + +-static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf8; +@@ -498,7 +510,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask + } + + +-static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf8; +@@ -519,7 +531,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask ma + } + + +-static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); + +@@ -533,7 +545,7 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint + } + + +-static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); + +@@ -556,10 +568,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } +- return true; +- } + //8bit grayscale +- if (surface->channelSize == sizeof(uint8_t)) { ++ } else if (surface->channelSize == sizeof(uint8_t)) { + uint8_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; +@@ -570,29 +580,27 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint + *dst = INTERPOLATE8(src, *dst, alpha(cmp)); + } + } +- return true; + } +- return false; ++ return true; + } + + +-static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + if (surface->channelSize != sizeof(uint32_t)) return false; + + auto span = rle->spans; + auto color = surface->join(r, g, b, a); +- auto ialpha = 255 - a; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { +- *dst = surface->blender(color, *dst, ialpha); ++ *dst = surface->blender(color, *dst, 255); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { +- auto tmp = surface->blender(color, *dst, ialpha); ++ auto tmp = surface->blender(color, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, span->coverage); + } + } +@@ -601,7 +609,7 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t + } + + +-static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + #if defined(THORVG_AVX_VECTOR_SUPPORT) + return avxRasterTranslucentRle(surface, rle, r, g, b, a); +@@ -613,7 +621,7 @@ static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint + } + + +-static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b) ++static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b) + { + auto span = rle->spans; + +@@ -650,7 +658,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, + } + + +-static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + if (!rle) return false; + +@@ -686,66 +694,9 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, + auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \ + if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \ + +- +-#if 0 //Enable it when GRAYSCALE image is supported +-static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; +- auto sampleSize = _sampleSize(image->scale); +- auto span = image->rle->spans; +- int32_t miny = 0, maxy = 0; +- +- for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { +- SCALED_IMAGE_RANGE_Y(span->y) +- auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x]; +- auto a = MULTIPLY(span->coverage, opacity); +- for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp) { +- SCALED_IMAGE_RANGE_X +- auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (a < 255) src = MULTIPLY(src, a); +- *cmp = maskOp(src, *cmp, ~src); +- } +- } +- return true; +-} +- +- +-static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; +- auto sampleSize = _sampleSize(image->scale); +- auto span = image->rle->spans; +- int32_t miny = 0, maxy = 0; +- +- for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { +- SCALED_IMAGE_RANGE_Y(span->y) +- auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x]; +- auto dst = &surface->buf8[span->y * surface->stride + span->x]; +- auto a = MULTIPLY(span->coverage, opacity); +- for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp, ++dst) { +- SCALED_IMAGE_RANGE_X +- auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (a < 255) src = MULTIPLY(src, a); +- src = maskOp(src, *cmp, 0); //not use alpha +- *dst = src + MULTIPLY(*dst, ~src); +- } +- } +- return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +-} +-#endif +- + static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) + { +-#if 0 //Enable it when GRAYSCALE image is supported +- TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method); +- +- //8bit masking channels composition +- if (surface->channelSize != sizeof(uint8_t)) return false; +- +- auto maskOp = _getMaskOp(surface->compositor->method); +- if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity); +- else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity); +-#endif ++ TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method); + return false; + } + +@@ -773,7 +724,6 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image + *dst = src + ALPHA_BLEND(*dst, IA(src)); + } + } +- + return true; + } + +@@ -800,9 +750,8 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (opacity < 255) src = ALPHA_BLEND(src, opacity); + auto tmp = surface->blender(src, *dst, 255); +- *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); ++ *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src))); + } + } + } +@@ -832,7 +781,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)) { + TVGERR("SW_ENGINE", "Not supported scaled rle image!"); +@@ -841,9 +790,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr + + Matrix itransform; + +- if (transform) { +- if (!mathInverse(transform, &itransform)) return false; +- } else mathIdentity(&itransform); ++ if (!inverse(&transform, &itransform)) return true; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); +@@ -861,75 +808,6 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr + /* RLE Direct Image */ + /************************************************************************/ + +-#if 0 //Enable it when GRAYSCALE image is supported +-static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) +-{ +- auto span = image->rle->spans; +- auto cbuffer = surface->compositor->image.buf8; +- auto ctride = surface->compositor->image.stride; +- +- for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { +- auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); +- auto cmp = &cbuffer[span->y * ctride + span->x]; +- auto alpha = MULTIPLY(span->coverage, opacity); +- if (alpha == 255) { +- for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { +- *cmp = maskOp(*src, *cmp, ~*src); +- } +- } else { +- for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { +- auto tmp = MULTIPLY(*src, alpha); +- *cmp = maskOp(*src, *cmp, ~tmp); +- } +- } +- } +- return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +-} +- +- +-static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) +-{ +- auto span = image->rle->spans; +- auto cbuffer = surface->compositor->image.buf8; +- auto ctride = surface->compositor->image.stride; +- +- for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { +- auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); +- auto cmp = &cbuffer[span->y * ctride + span->x]; +- auto dst = &surface->buf8[span->y * surface->stride + span->x]; +- auto alpha = MULTIPLY(span->coverage, opacity); +- if (alpha == 255) { +- for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { +- auto tmp = maskOp(*src, *cmp, 0); //not use alpha +- *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); +- } +- } else { +- for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { +- auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha +- *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); +- } +- } +- } +- return true; +-} +-#endif +- +-static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +-{ +-#if 0 //Enable it when GRAYSCALE image is supported +- TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method); +- +- //8bit masking channels composition +- if (surface->channelSize != sizeof(uint8_t)) return false; +- +- auto maskOp = _getMaskOp(surface->compositor->method); +- if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity); +- else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity); +-#endif +- return false; +-} +- +- + static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) + { + TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); +@@ -970,18 +848,12 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { +- *dst = surface->blender(*img, *dst, IA(*img)); +- } +- } else if (opacity == 255) { +- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { +- auto tmp = surface->blender(*img, *dst, 255); +- *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(*img))); ++ *dst = surface->blender(*img, *dst, 255); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { +- auto src = ALPHA_BLEND(*img, opacity); +- auto tmp = surface->blender(src, *dst, IA(src)); +- *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); ++ auto tmp = surface->blender(*img, *dst, 255); ++ *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(*img))); + } + } + } +@@ -997,21 +869,19 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto alpha = MULTIPLY(span->coverage, opacity); +- if (alpha == 255) { +- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { +- *dst = *img + ALPHA_BLEND(*dst, IA(*img)); +- } +- } else { +- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { +- auto src = ALPHA_BLEND(*img, alpha); +- *dst = src + ALPHA_BLEND(*dst, IA(src)); +- } +- } ++ rasterTranslucentPixel32(dst, img, span->len, alpha); + } + return true; + } + + ++static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) ++{ ++ TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); ++ return false; ++} ++ ++ + static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) + { + if (surface->channelSize == sizeof(uint8_t)) { +@@ -1035,67 +905,9 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op + /*Scaled Image */ + /************************************************************************/ + +-#if 0 //Enable it when GRAYSCALE image is supported +-static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; +- auto sampleSize = _sampleSize(image->scale); +- auto cstride = surface->compositor->image.stride; +- auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); +- int32_t miny = 0, maxy = 0; +- +- for (auto y = region.min.y; y < region.max.y; ++y) { +- SCALED_IMAGE_RANGE_Y(y) +- auto cmp = cbuffer; +- for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { +- SCALED_IMAGE_RANGE_X +- auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (opacity < 255) src = MULTIPLY(src, opacity); +- *cmp = maskOp(src, *cmp, ~src); +- } +- cbuffer += cstride; +- } +- return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +-} +- +- +-static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; +- auto sampleSize = _sampleSize(image->scale); +- auto cstride = surface->compositor->image.stride; +- auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); +- auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); +- int32_t miny = 0, maxy = 0; +- +- for (auto y = region.min.y; y < region.max.y; ++y) { +- SCALED_IMAGE_RANGE_Y(y) +- auto cmp = cbuffer; +- auto dst = dbuffer; +- for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) { +- SCALED_IMAGE_RANGE_X +- auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (opacity < 255) src = MULTIPLY(src, opacity); +- src = maskOp(src, *cmp, 0); //not use alpha +- *dst = src + MULTIPLY(*dst, ~src); +- } +- cbuffer += cstride; +- dbuffer += surface->stride; +- } +- return true; +-} +-#endif +- + static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) + { +- TVGERR("SW_ENGINE", "Not supported ScaledMaskedImage!"); +-#if 0 //Enable it when GRAYSCALE image is supported +- TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); +- +- auto maskOp = _getMaskOp(surface->compositor->method); +- if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); +- else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); +-#endif ++ TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!"); + return false; + } + +@@ -1153,9 +965,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, + for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); +- if (opacity < 255) ALPHA_BLEND(src, opacity); + auto tmp = surface->blender(src, *dst, 255); +- *dst = INTERPOLATE(tmp, *dst, A(src)); ++ *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src))); + } + } + return true; +@@ -1197,13 +1008,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; + +- if (transform) { +- if (!mathInverse(transform, &itransform)) return false; +- } else mathIdentity(&itransform); ++ if (!inverse(&transform, &itransform)) return true; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); +@@ -1221,78 +1030,9 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* + /* Direct Image */ + /************************************************************************/ + +-#if 0 //Enable it when GRAYSCALE image is supported +-static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto h = static_cast(region.max.y - region.min.y); +- auto w = static_cast(region.max.x - region.min.x); +- auto cstride = surface->compositor->image.stride; +- +- auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer +- auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); +- +- for (uint32_t y = 0; y < h; ++y) { +- auto cmp = cbuffer; +- auto src = sbuffer; +- if (opacity == 255) { +- for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { +- *cmp = maskOp(*src, *cmp, ~*src); +- } +- } else { +- for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { +- auto tmp = MULTIPLY(*src, opacity); +- *cmp = maskOp(tmp, *cmp, ~tmp); +- } +- } +- cbuffer += cstride; +- sbuffer += image->stride; +- } +- return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +-} +- +- +-static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) +-{ +- auto h = static_cast(region.max.y - region.min.y); +- auto w = static_cast(region.max.x - region.min.x); +- auto cstride = surface->compositor->image.stride; +- +- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer +- auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer +- auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); +- +- for (uint32_t y = 0; y < h; ++y) { +- auto cmp = cbuffer; +- auto dst = dbuffer; +- auto src = sbuffer; +- if (opacity == 255) { +- for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) { +- auto tmp = maskOp(*src, *cmp, 0); //not use alpha +- *dst = tmp + MULTIPLY(*dst, ~tmp); +- } +- } else { +- for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) { +- auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha +- *dst = tmp + MULTIPLY(*dst, ~tmp); +- } +- } +- cbuffer += cstride; +- dbuffer += surface->stride; +- sbuffer += image->stride; +- } +- return true; +-} +-#endif +- + static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) + { +- TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); +- +-#if 0 //Enable it when GRAYSCALE image is supported +- auto maskOp = _getMaskOp(surface->compositor->method); +- if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity); +- else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity); +-#endif ++ TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image"); + return false; + } + +@@ -1339,11 +1079,13 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c + auto src = sbuffer; + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { +- *dst = MULTIPLY(A(*src), alpha(cmp)); ++ auto tmp = MULTIPLY(A(*src), alpha(cmp)); ++ *dst = tmp + MULTIPLY(*dst, 255 - tmp); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { +- *dst = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp))); ++ auto tmp = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp))); ++ *dst = tmp + MULTIPLY(*dst, 255 - tmp); + } + } + buffer += surface->stride; +@@ -1374,10 +1116,9 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, + *dst = INTERPOLATE(tmp, *dst, A(*src)); + } + } else { +- for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { +- auto tmp = ALPHA_BLEND(*src, opacity); +- auto tmp2 = surface->blender(tmp, *dst, 255); +- *dst = INTERPOLATE(tmp2, *dst, A(tmp)); ++ for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { ++ auto tmp = surface->blender(*src, *dst, 255); ++ *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(*src))); + } + } + dbuffer += surface->stride; +@@ -1388,29 +1129,70 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, + + + static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) ++{ ++ auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); ++ ++ //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) { ++ rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity); ++ dbuffer += surface->stride; ++ sbuffer += image->stride; ++ } ++ //8bits grayscale ++ } else if (surface->channelSize == sizeof(uint8_t)) { ++ auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x]; ++ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) { ++ auto dst = dbuffer; ++ auto src = sbuffer; ++ if (opacity == 255) { ++ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { ++ *dst = *src + MULTIPLY(*dst, IA(*src)); ++ } ++ } else { ++ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { ++ *dst = INTERPOLATE8(A(*src), *dst, opacity); ++ } ++ } ++ } ++ } ++ return true; ++} ++ ++ ++static bool _rasterDirectMattedBlendingImage(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 h = static_cast(region.max.y - region.min.y); ++ auto w = static_cast(region.max.x - region.min.x); ++ auto csize = surface->compositor->image.channelSize; ++ auto alpha = surface->alpha(surface->compositor->method); + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); ++ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer ++ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + +- for (auto y = region.min.y; y < region.max.y; ++y) { +- auto dst = dbuffer; ++ for (uint32_t y = 0; y < h; ++y) { ++ auto dst = buffer; ++ auto cmp = cbuffer; + 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)); ++ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { ++ auto tmp = ALPHA_BLEND(*src, alpha(cmp)); ++ *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, A(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)); ++ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { ++ auto tmp = ALPHA_BLEND(*src, alpha(cmp)); ++ *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, MULTIPLY(opacity, A(tmp))); + } + } +- dbuffer += surface->stride; ++ buffer += surface->stride; ++ cbuffer += surface->compositor->image.stride * csize; + sbuffer += image->stride; + } + return true; +@@ -1421,8 +1203,10 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S + static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) + { + if (_compositing(surface)) { +- if (_matting(surface)) return _rasterDirectMattedImage(surface, image, region, opacity); +- else return _rasterDirectMaskedImage(surface, image, region, opacity); ++ if (_matting(surface)) { ++ if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, region, opacity); ++ else return _rasterDirectMattedImage(surface, image, region, opacity); ++ } else return _rasterDirectMaskedImage(surface, image, region, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingImage(surface, image, region, opacity); + } else { +@@ -1433,7 +1217,7 @@ static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& + + + //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 + if (image->rle) { +@@ -1546,13 +1330,23 @@ static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region + template + static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) + { +- auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + +- for (uint32_t y = 0; y < h; ++y) { +- fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255); +- buffer += surface->stride; ++ //32 bits ++ if (surface->channelSize == sizeof(uint32_t)) { ++ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; ++ for (uint32_t y = 0; y < h; ++y) { ++ fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255); ++ buffer += surface->stride; ++ } ++ //8 bits ++ } else if (surface->channelSize == sizeof(uint8_t)) { ++ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; ++ for (uint32_t y = 0; y < h; ++y) { ++ fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskAdd, 255); ++ buffer += surface->stride; ++ } + } + return true; + } +@@ -1561,12 +1355,23 @@ static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& reg + template + static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) + { +- auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + +- for (uint32_t y = 0; y < h; ++y) { +- fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, 255); ++ //32 bits ++ if (surface->channelSize == sizeof(uint32_t)) { ++ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; ++ for (uint32_t y = 0; y < h; ++y) { ++ fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendSrcOver, 255); ++ buffer += surface->stride; ++ } ++ //8 bits ++ } else if (surface->channelSize == sizeof(uint8_t)) { ++ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; ++ for (uint32_t y = 0; y < h; ++y) { ++ fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskNone, 255); ++ buffer += surface->stride; ++ } + } + return true; + } +@@ -1574,8 +1379,6 @@ static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, c + + static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) + { +- if (fill->linear.len < FLOAT_EPSILON) return false; +- + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRect(surface, region, fill); + else return _rasterGradientMaskedRect(surface, region, fill); +@@ -1609,7 +1412,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, + /************************************************************************/ + + template +-static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp) ++static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp) + { + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; +@@ -1624,7 +1427,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleDat + + + template +-static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp) ++static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp) + { + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; +@@ -1641,7 +1444,7 @@ static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* + + + template +-static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + auto method = surface->compositor->method; + +@@ -1656,7 +1459,7 @@ static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, c + + + template +-static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterGradientMattedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method); + +@@ -1675,7 +1478,7 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c + + + template +-static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + auto span = rle->spans; + +@@ -1688,7 +1491,7 @@ static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, + + + template +-static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + auto span = rle->spans; + +@@ -1703,7 +1506,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r + } else if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; +- fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255); ++ fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage); + } + } + return true; +@@ -1711,7 +1514,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r + + + template +-static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterSolidGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + auto span = rle->spans; + +@@ -1735,7 +1538,7 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co + } + + +-static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterLinearGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + if (!rle) return false; + +@@ -1752,7 +1555,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c + } + + +-static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) ++static bool _rasterRadialGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) + { + if (!rle) return false; + +@@ -1760,9 +1563,9 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c + if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); + else return _rasterGradientMaskedRle(surface, rle, fill); + } else if (_blending(surface)) { +- _rasterBlendingGradientRle(surface, rle, fill); ++ return _rasterBlendingGradientRle(surface, rle, fill); + } else { +- if (fill->translucent) _rasterTranslucentGradientRle(surface, rle, fill); ++ if (fill->translucent) return _rasterTranslucentGradientRle(surface, rle, fill); + else return _rasterSolidGradientRle(surface, rle, fill); + } + return false; +@@ -1773,6 +1576,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c + /* External Class Implementation */ + /************************************************************************/ + ++void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) ++{ ++ //TODO: Support SIMD accelerations ++ cRasterTranslucentPixels(dst, src, len, opacity); ++} ++ ++ ++void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) ++{ ++ //TODO: Support SIMD accelerations ++ cRasterPixels(dst, src, len, opacity); ++} ++ + + void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) + { +@@ -1820,7 +1636,7 @@ bool rasterCompositor(SwSurface* surface) + } + + +-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, pixel_t val) + { + if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false; + +@@ -1828,11 +1644,11 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ + if (surface->channelSize == sizeof(uint32_t)) { + //full clear + if (w == surface->stride) { +- rasterPixel32(surface->buf32, 0x00000000, surface->stride * y, w * h); ++ rasterPixel32(surface->buf32, val, surface->stride * y, w * h); + //partial clear + } else { + for (uint32_t i = 0; i < h; i++) { +- rasterPixel32(surface->buf32, 0x00000000, (surface->stride * y + x) + (surface->stride * i), w); ++ rasterPixel32(surface->buf32, val, (surface->stride * y + x) + (surface->stride * i), w); + } + } + //8 bits +@@ -1851,7 +1667,7 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ + } + + +-void rasterUnpremultiply(Surface* surface) ++void rasterUnpremultiply(RenderSurface* surface) + { + if (surface->channelSize != sizeof(uint32_t)) return; + +@@ -1881,7 +1697,7 @@ void rasterUnpremultiply(Surface* surface) + } + + +-void rasterPremultiply(Surface* surface) ++void rasterPremultiply(RenderSurface* surface) + { + ScopedLock lock(surface->key); + if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return; +@@ -1902,27 +1718,39 @@ 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 (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 type = fdata->type(); + if (shape->fastTrack) { +- if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); +- else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); ++ if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); ++ else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); + } else { +- if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); +- else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); ++ if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); ++ else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); + } + return false; + } + + +-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 (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); ++ 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 type = fdata->type(); ++ if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); ++ else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); + + return false; + } +@@ -1952,22 +1780,21 @@ 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 + if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(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); + } + + +-bool rasterConvertCS(Surface* surface, ColorSpace to) ++bool rasterConvertCS(RenderSurface* surface, ColorSpace to) + { + ScopedLock lock(surface->key); + if (surface->cs == to) return true; + +- //TOOD: Support SIMD accelerations ++ //TODO: Support SIMD accelerations + auto from = surface->cs; + + if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) { +@@ -1980,3 +1807,39 @@ bool rasterConvertCS(Surface* surface, ColorSpace to) + } + return false; + } ++ ++ ++//TODO: SIMD OPTIMIZATION? ++void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped) ++{ ++ constexpr int BLOCK = 8; //experimental decision ++ ++ if (flipped) { ++ src += ((bbox.min.x * stride) + bbox.min.y); ++ dst += ((bbox.min.y * stride) + bbox.min.x); ++ } else { ++ src += ((bbox.min.y * stride) + bbox.min.x); ++ dst += ((bbox.min.x * stride) + bbox.min.y); ++ } ++ ++ #pragma omp parallel for ++ for (int x = 0; x < w; x += BLOCK) { ++ auto bx = std::min(w, x + BLOCK) - x; ++ auto in = &src[x]; ++ auto out = &dst[x * stride]; ++ for (int y = 0; y < h; y += BLOCK) { ++ auto p = &in[y * stride]; ++ auto q = &out[y]; ++ auto by = std::min(h, y + BLOCK) - y; ++ for (int xx = 0; xx < bx; ++xx) { ++ for (int yy = 0; yy < by; ++yy) { ++ *q = *p; ++ p += stride; ++ ++q; ++ } ++ p += 1 - by * stride; ++ q += stride - by; ++ } ++ } ++ } ++} +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h +index a072a88819..79cab043f2 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h +@@ -158,7 +158,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u + } + + +-static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + auto span = rle->spans; + +@@ -185,7 +185,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui + } + + //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once +- //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all ++ //In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all + uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG; + uint32_t avxFilled = 0; + if (iterations > 0) { +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h +index 6d0bd9383d..d79da0e4d8 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h +@@ -20,6 +20,38 @@ + * SOFTWARE. + */ + ++ ++template ++static void inline cRasterTranslucentPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity) ++{ ++ //TODO: 64bits faster? ++ if (opacity == 255) { ++ for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { ++ *dst = *src + ALPHA_BLEND(*dst, IA(*src)); ++ } ++ } else { ++ for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { ++ auto tmp = ALPHA_BLEND(*src, opacity); ++ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); ++ } ++ } ++} ++ ++ ++template ++static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity) ++{ ++ //TODO: 64bits faster? ++ if (opacity == 255) { ++ for (uint32_t x = 0; x < len; ++x, ++dst, ++src) { ++ *dst = *src; ++ } ++ } else { ++ cRasterTranslucentPixels(dst, src, len, opacity); ++ } ++} ++ ++ + template + static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len) + { +@@ -60,7 +92,7 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int + } + + +-static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + auto span = rle->spans; + +@@ -125,7 +157,7 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi + } + + +-static bool inline cRasterABGRtoARGB(Surface* surface) ++static bool inline cRasterABGRtoARGB(RenderSurface* surface) + { + TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); + +@@ -156,7 +188,7 @@ static bool inline cRasterABGRtoARGB(Surface* surface) + } + + +-static bool inline cRasterARGBtoABGR(Surface* surface) ++static bool inline cRasterARGBtoABGR(RenderSurface* surface) + { + //exactly same with ABGRtoARGB + return cRasterABGRtoARGB(surface); +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h +index 91cf7743c1..fe693b7f33 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h +@@ -89,7 +89,7 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3 + } + + +-static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) ++static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + auto span = rle->spans; + +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h +index cfce7785c7..1162edc838 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h +@@ -20,6 +20,17 @@ + * SOFTWARE. + */ + ++struct Vertex ++{ ++ Point pt; ++ Point uv; ++}; ++ ++struct Polygon ++{ ++ Vertex vertex[3]; ++}; ++ + struct AALine + { + int32_t x[2]; +@@ -675,7 +686,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const + auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0])); + + //Skip poly if it's an infinitely thin line +- if (mathZero(denom)) return; ++ if (tvg::zero(denom)) return; + + denom = 1 / denom; //Reciprocal for speeding up + dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom; +@@ -691,8 +702,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const + //Determine which side of the polygon the longer edge is on + auto side = (dxdy[1] > dxdy[0]) ? true : false; + +- if (mathEqual(y[0], y[1])) side = x[0] > x[1]; +- if (mathEqual(y[1], y[2])) side = x[2] > x[1]; ++ if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; ++ if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; + + auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image? + auto compositing = _compositing(surface); //Composition required +@@ -868,10 +879,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) + { +- 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 +906,14 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx) + ptx[1] = tx[1]; \ + } while (0) + ++ struct Point ++ { ++ int32_t x, y; ++ }; ++ + 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. */ + int32_t tx[2] = {0, 0}; +@@ -1024,6 +1038,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx) + + static bool _apply(SwSurface* surface, AASpans* aaSpans) + { ++ auto end = surface->buf32 + surface->h * surface->stride; + auto y = aaSpans->yStart; + uint32_t pixel; + uint32_t* dst; +@@ -1044,8 +1059,13 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) + dst = surface->buf32 + (offset + line->x[0]); + if (line->x[0] > 1) pixel = *(dst - 1); + else pixel = *dst; +- + 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]) { + *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos); + ++dst; +@@ -1053,17 +1073,21 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) + } + + //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); + 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; + --pos; + } +- } ++ } + y++; + } + +@@ -1084,7 +1108,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) + | / | + 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)) { + TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); +@@ -1092,7 +1116,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const + } + + //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. + shift XY coordinates to match the sub-pixeling technique. */ +@@ -1104,7 +1128,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const + + float ys = FLT_MAX, ye = -1.0f; + 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 > ye) ye = vertices[i].pt.y; + } +@@ -1135,68 +1159,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const + #endif + 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; +-} +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +index 350f333405..180f3cc37a 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +@@ -20,6 +20,9 @@ + * SOFTWARE. + */ + ++#ifdef THORVG_SW_OPENMP_SUPPORT ++ #include ++#endif + #include + #include "tvgMath.h" + #include "tvgSwCommon.h" +@@ -38,8 +41,8 @@ struct SwTask : Task + { + SwSurface* surface = nullptr; + SwMpool* mpool = nullptr; +- SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region +- Matrix* transform = nullptr; ++ SwBBox bbox; //Rendering Region ++ Matrix transform; + Array clips; + RenderUpdateFlag flags = RenderUpdateFlag::None; + uint8_t opacity; +@@ -65,13 +68,8 @@ struct SwTask : Task + } + + virtual void dispose() = 0; +- virtual bool clip(SwRleData* target) = 0; +- virtual SwRleData* rle() = 0; +- +- virtual ~SwTask() +- { +- free(transform); +- } ++ virtual bool clip(SwRle* target) = 0; ++ virtual ~SwTask() {} + }; + + +@@ -95,40 +93,34 @@ struct SwShapeTask : SwTask + if (!rshape->stroke) return 0.0f; + + auto width = rshape->stroke->width; +- if (mathZero(width)) return 0.0f; ++ if (tvg::zero(width)) return 0.0f; + + if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f; +- if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; ++ if (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; + +- 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)); + } + +- +- bool clip(SwRleData* target) override ++ bool clip(SwRle* target) override + { +- if (shape.fastTrack) rleClipRect(target, &bbox); +- else if (shape.rle) rleClipPath(target, shape.rle); ++ if (shape.fastTrack) rleClip(target, &bbox); ++ else if (shape.rle) rleClip(target, shape.rle); + else return false; + + return true; + } + +- SwRleData* rle() override +- { +- if (!shape.rle && shape.fastTrack) { +- shape.rle = rleRender(&shape.bbox); +- } +- return shape.rle; +- } +- + void run(unsigned tid) override + { +- if (opacity == 0 && !clipper) return; //Invisible ++ //Invisible ++ if (opacity == 0 && !clipper) { ++ bbox.reset(); ++ return; ++ } + + auto strokeWidth = validStrokeWidth(); +- bool visibleFill = false; +- auto clipRegion = bbox; ++ SwBBox renderRegion{}; ++ auto visibleFill = false; + + //This checks also for the case, if the invisible shape turned to visible by alpha. + auto prepareShape = false; +@@ -140,10 +132,11 @@ struct SwShapeTask : SwTask + rshape->fillColor(nullptr, nullptr, nullptr, &alpha); + alpha = MULTIPLY(alpha, opacity); + visibleFill = (alpha > 0 || rshape->fill); ++ shapeReset(&shape); + if (visibleFill || clipper) { +- shapeReset(&shape); +- if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) { ++ if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) { + visibleFill = false; ++ renderRegion.reset(); + } + } + } +@@ -164,8 +157,8 @@ struct SwShapeTask : SwTask + if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { + if (strokeWidth > 0.0f) { + shapeResetStroke(&shape, rshape, transform); +- if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err; + ++ if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err; + if (auto fill = rshape->strokeFill()) { + auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; + if (ctable) shapeResetStrokeFill(&shape); +@@ -189,9 +182,13 @@ struct SwShapeTask : SwTask + //Clip stroke rle + if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; + } ++ ++ bbox = renderRegion; //sync ++ + return; + + err: ++ bbox.reset(); + shapeReset(&shape); + shapeDelOutline(&shape, mpool, tid); + } +@@ -203,77 +200,17 @@ struct SwShapeTask : SwTask + }; + + +-struct SwSceneTask : SwTask +-{ +- Array 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(*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(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(*scene.data); +- auto clipper2 = static_cast(*(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(*rd); +- rleMerge(sceneRle, sceneRle, clipper->rle()); +- } +- } +- } +- +- void dispose() override +- { +- rleFree(sceneRle); +- } +-}; +- +- + struct SwImageTask : SwTask + { + SwImage image; +- Surface* source; //Image source +- const RenderMesh* mesh = nullptr; //Should be valid ptr in action ++ RenderSurface* source; //Image source + +- bool clip(SwRleData* target) override ++ bool clip(SwRle* target) override + { + TVGERR("SW_ENGINE", "Image is used as ClipPath?"); + return true; + } + +- SwRleData* rle() override +- { +- TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?"); +- return nullptr; +- } +- + void run(unsigned tid) override + { + auto clipRegion = bbox; +@@ -293,10 +230,9 @@ struct SwImageTask : SwTask + imageReset(&image); + 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 (image.rle) { + //Clear current task memorypool here if the clippers would use the same memory pool +@@ -336,7 +272,7 @@ static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity) + { + uint8_t r, g, b, a; + if (auto fill = task->rshape->fill) { +- rasterGradientShape(surface, &task->shape, fill->identifier()); ++ rasterGradientShape(surface, &task->shape, fill, opacity); + } else { + task->rshape->fillColor(&r, &g, &b, &a); + a = MULTIPLY(opacity, a); +@@ -348,7 +284,7 @@ static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity + { + uint8_t r, g, b, a; + if (auto strokeFill = task->rshape->strokeFill()) { +- rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); ++ rasterGradientStroke(surface, &task->shape, strokeFill, opacity); + } else { + if (task->rshape->strokeColor(&r, &g, &b, &a)) { + a = MULTIPLY(opacity, a); +@@ -480,7 +416,7 @@ bool SwRenderer::renderImage(RenderData data) + + 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); + } + + +@@ -512,27 +448,18 @@ bool SwRenderer::blend(BlendMethod method) + surface->blendMethod = method; + + switch (method) { +- case BlendMethod::Add: +- surface->blender = opBlendAdd; +- break; +- case BlendMethod::Screen: +- surface->blender = opBlendScreen; ++ case BlendMethod::Normal: ++ surface->blender = nullptr; + break; + case BlendMethod::Multiply: + surface->blender = opBlendMultiply; + break; ++ case BlendMethod::Screen: ++ surface->blender = opBlendScreen; ++ break; + case BlendMethod::Overlay: + surface->blender = opBlendOverlay; + break; +- case BlendMethod::Difference: +- surface->blender = opBlendDifference; +- break; +- case BlendMethod::Exclusion: +- surface->blender = opBlendExclusion; +- break; +- case BlendMethod::SrcOver: +- surface->blender = opBlendSrcOver; +- break; + case BlendMethod::Darken: + surface->blender = opBlendDarken; + break; +@@ -551,7 +478,17 @@ bool SwRenderer::blend(BlendMethod method) + case BlendMethod::SoftLight: + surface->blender = opBlendSoftLight; + break; ++ case BlendMethod::Difference: ++ surface->blender = opBlendDifference; ++ break; ++ case BlendMethod::Exclusion: ++ surface->blender = opBlendExclusion; ++ break; ++ case BlendMethod::Add: ++ surface->blender = opBlendAdd; ++ break; + default: ++ TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method); + surface->blender = nullptr; + break; + } +@@ -565,7 +502,7 @@ RenderRegion SwRenderer::region(RenderData data) + } + + +-bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) ++bool SwRenderer::beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) + { + if (!cmp) return false; + auto p = static_cast(cmp); +@@ -603,31 +540,19 @@ bool SwRenderer::mempool(bool shared) + } + + +-const Surface* SwRenderer::mainSurface() ++const RenderSurface* SwRenderer::mainSurface() + { + return surface; + } + + +-Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) ++SwSurface* SwRenderer::request(int channelSize) + { +- auto x = region.x; +- auto y = region.y; +- auto w = region.w; +- auto h = region.h; +- auto sw = static_cast(surface->w); +- auto sh = static_cast(surface->h); +- +- //Out of boundary +- if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr; +- + SwSurface* cmp = nullptr; + +- auto reqChannelSize = CHANNEL_SIZE(cs); +- + //Use cached data + for (auto p = compositors.begin(); p < compositors.end(); ++p) { +- if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) { ++ if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) { + cmp = *p; + break; + } +@@ -638,18 +563,48 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) + //Inherits attributes from main surface + cmp = new SwSurface(surface); + cmp->compositor = new SwCompositor; +- +- //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h) +- cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h); +- cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize; ++ cmp->compositor->image.data = (pixel_t*)malloc(channelSize * surface->stride * surface->h); ++ cmp->compositor->image.w = surface->w; ++ cmp->compositor->image.h = surface->h; ++ cmp->compositor->image.stride = surface->stride; ++ cmp->compositor->image.direct = true; ++ cmp->compositor->valid = true; ++ cmp->channelSize = cmp->compositor->image.channelSize = channelSize; ++ cmp->w = cmp->compositor->image.w; ++ cmp->h = cmp->compositor->image.h; + + compositors.push(cmp); + } + ++ //Sync. This may have been modified by post-processing. ++ cmp->data = cmp->compositor->image.data; ++ ++ return cmp; ++} ++ ++ ++RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) ++{ ++ auto x = region.x; ++ auto y = region.y; ++ auto w = region.w; ++ auto h = region.h; ++ auto sw = static_cast(surface->w); ++ auto sh = static_cast(surface->h); ++ ++ //Out of boundary ++ if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr; ++ ++ auto cmp = request(CHANNEL_SIZE(cs)); ++ + //Boundary Check ++ if (x < 0) x = 0; ++ if (y < 0) y = 0; + if (x + w > sw) w = (sw - x); + if (y + h > sh) h = (sh - y); + ++ if (w == 0 || h == 0) return nullptr; ++ + cmp->compositor->recoverSfc = surface; + cmp->compositor->recoverCmp = surface->compositor; + cmp->compositor->valid = false; +@@ -657,16 +612,11 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) + cmp->compositor->bbox.min.y = y; + cmp->compositor->bbox.max.x = x + w; + cmp->compositor->bbox.max.y = y + h; +- cmp->compositor->image.stride = surface->stride; +- cmp->compositor->image.w = surface->w; +- cmp->compositor->image.h = surface->h; +- cmp->compositor->image.direct = true; +- +- cmp->data = cmp->compositor->image.data; +- cmp->w = cmp->compositor->image.w; +- cmp->h = cmp->compositor->image.h; + +- rasterClear(cmp, x, y, w, h); ++ /* TODO: Currently, only blending might work. ++ Blending and composition must be handled together. */ ++ auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000; ++ rasterClear(cmp, x, y, w, h, color); + + //Switch render target + surface = cmp; +@@ -675,7 +625,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) + } + + +-bool SwRenderer::endComposite(Compositor* cmp) ++bool SwRenderer::endComposite(RenderCompositor* cmp) + { + if (!cmp) return false; + +@@ -688,13 +638,48 @@ bool SwRenderer::endComposite(Compositor* cmp) + + //Default is alpha blending + 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; + } + + ++bool SwRenderer::prepare(RenderEffect* effect) ++{ ++ switch (effect->type) { ++ case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast(effect)); ++ case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast(effect)); ++ default: return false; ++ } ++} ++ ++ ++bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) ++{ ++ if (effect->invalid) return false; ++ ++ auto p = static_cast(cmp); ++ ++ switch (effect->type) { ++ case SceneEffect::GaussianBlur: { ++ return effectGaussianBlur(p, request(surface->channelSize), static_cast(effect)); ++ } ++ case SceneEffect::DropShadow: { ++ auto cmp1 = request(surface->channelSize); ++ cmp1->compositor->valid = false; ++ auto cmp2 = request(surface->channelSize); ++ SwSurface* surfaces[] = {cmp1, cmp2}; ++ auto ret = effectDropShadow(p, surfaces, static_cast(effect), opacity, direct); ++ cmp1->compositor->valid = true; ++ return ret; ++ } ++ default: return false; ++ } ++} ++ ++ + ColorSpace SwRenderer::colorSpace() + { + if (surface) return surface->cs; +@@ -714,7 +699,7 @@ void SwRenderer::dispose(RenderData data) + } + + +-void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) ++void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) + { + if (!surface) return task; + if (flags == RenderUpdateFlag::None) return task; +@@ -727,29 +712,20 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, + } + + task->clips = clips; +- +- if (transform) { +- if (!task->transform) task->transform = static_cast(malloc(sizeof(Matrix))); +- *task->transform = transform->m; +- } else { +- if (task->transform) free(task->transform); +- task->transform = nullptr; +- } +- ++ task->transform = transform; ++ + //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->surface = surface; + task->mpool = mpool; + task->flags = flags; +- task->bbox.min.x = mathMax(static_cast(0), static_cast(vport.x)); +- task->bbox.min.y = mathMax(static_cast(0), static_cast(vport.y)); +- task->bbox.max.x = mathMin(static_cast(surface->w), static_cast(vport.x + vport.w)); +- task->bbox.max.y = mathMin(static_cast(surface->h), static_cast(vport.y + vport.h)); ++ task->bbox.min.x = std::max(static_cast(0), static_cast(vport.x)); ++ task->bbox.min.y = std::max(static_cast(0), static_cast(vport.y)); ++ task->bbox.max.x = std::min(static_cast(surface->w), static_cast(vport.x + vport.w)); ++ task->bbox.max.y = std::min(static_cast(surface->h), static_cast(vport.y + vport.h)); + + if (!task->pushed) { + task->pushed = true; +@@ -762,7 +738,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, + } + + +-RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) ++RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) + { + //prepare task + auto task = static_cast(data); +@@ -770,33 +746,12 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD + else task->done(); + + task->source = surface; +- task->mesh = mesh; +- +- return prepareCommon(task, transform, clips, opacity, flags); +-} +- +- +-RenderData SwRenderer::prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) +-{ +- //prepare task +- auto task = static_cast(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(*task)->done(); +- } + + return prepareCommon(task, transform, clips, opacity, flags); + } + + +-RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) ++RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) + { + //prepare task + auto task = static_cast(data); +@@ -834,6 +789,10 @@ bool SwRenderer::init(uint32_t threads) + + int32_t SwRenderer::init() + { ++#ifdef THORVG_SW_OPENMP_SUPPORT ++ omp_set_num_threads(TaskScheduler::threads()); ++#endif ++ + return initEngineCnt; + } + +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +index 57be558988..bd6beb8d85 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +@@ -36,9 +36,8 @@ namespace tvg + class SwRenderer : public RenderMethod + { + public: +- RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; +- RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; +- RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; ++ RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; ++ RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; + bool preRender() override; + bool renderShape(RenderData data) override; + bool renderImage(RenderData data) override; +@@ -49,18 +48,21 @@ public: + bool viewport(const RenderRegion& vp) override; + bool blend(BlendMethod method) override; + ColorSpace colorSpace() override; +- const Surface* mainSurface() override; ++ const RenderSurface* mainSurface() override; + + bool clear() override; + bool sync() override; + bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); + bool mempool(bool shared); + +- Compositor* target(const RenderRegion& region, ColorSpace cs) override; +- bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; +- bool endComposite(Compositor* cmp) override; ++ RenderCompositor* target(const RenderRegion& region, ColorSpace cs) override; ++ bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override; ++ bool endComposite(RenderCompositor* cmp) override; + void clearCompositors(); + ++ bool prepare(RenderEffect* effect) override; ++ bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override; ++ + static SwRenderer* gen(); + static bool init(uint32_t threads); + static int32_t init(); +@@ -77,7 +79,8 @@ private: + SwRenderer(); + ~SwRenderer(); + +- RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); ++ SwSurface* request(int channelSize); ++ RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); + }; + + } +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +index 25c6cd90b9..3e4ad679a8 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +@@ -217,7 +217,7 @@ struct Cell + + struct RleWorker + { +- SwRleData* rle; ++ SwRle* rle; + + SwPoint cellPos; + SwPoint cellMin; +@@ -235,6 +235,7 @@ struct RleWorker + SwPoint pos; + + SwPoint bezStack[32 * 3 + 1]; ++ SwPoint lineStack[32 + 1]; + int levStack[32]; + + SwOutline* outline; +@@ -297,7 +298,7 @@ static inline SwCoord HYPOT(SwPoint pt) + } + + +-static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount) ++static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount) + { + x += rw.cellMin.x; + y += rw.cellMin.y; +@@ -341,11 +342,11 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor + if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) { + //Clip x range + SwCoord xOver = 0; +- if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); ++ if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); + if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x); + +- //span->len += (acount + xOver) - 1; +- span->len += (acount + xOver); ++ //span->len += (aCount + xOver) - 1; ++ span->len += (aCount + xOver); + return; + } + } +@@ -358,23 +359,23 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor + rle->spans = static_cast(realloc(rle->spans, rle->alloc * sizeof(SwSpan))); + } + } +- ++ + //Clip x range + SwCoord xOver = 0; +- if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); ++ if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); + if (x < rw.cellMin.x) { + xOver -= (rw.cellMin.x - x); + x = rw.cellMin.x; + } + + //Nothing to draw +- if (acount + xOver <= 0) return; ++ if (aCount + xOver <= 0) return; + + //add a span to the current list + auto span = rle->spans + rle->size; + span->x = x; + span->y = y; +- span->len = (acount + xOver); ++ span->len = (aCount + xOver); + span->coverage = coverage; + rle->size++; + } +@@ -513,98 +514,116 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) + return; + } + +- auto diff = to - rw.pos; +- auto f1 = rw.pos - SUBPIXELS(e1); +- SwPoint f2; +- +- //inside one cell +- if (e1 == e2) { +- ; +- //any horizontal line +- } else if (diff.y == 0) { +- e1.x = e2.x; +- _setCell(rw, e1); +- } else if (diff.x == 0) { +- //vertical line up +- if (diff.y > 0) { +- do { +- f2.y = ONE_PIXEL; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * f1.x * 2; +- f1.y = 0; +- ++e1.y; +- _setCell(rw, e1); +- } while(e1.y != e2.y); +- //vertical line down ++ auto line = rw.lineStack; ++ line[0] = to; ++ line[1] = rw.pos; ++ ++ while (true) { ++ auto diff = line[0] - line[1]; ++ auto L = HYPOT(diff); ++ ++ if (L > SHRT_MAX) { ++ mathSplitLine(line); ++ ++line; ++ continue; ++ } ++ e1 = TRUNC(line[1]); ++ e2 = TRUNC(line[0]); ++ ++ auto f1 = line[1] - SUBPIXELS(e1); ++ SwPoint f2; ++ ++ //inside one cell ++ if (e1 == e2) { ++ ; ++ //any horizontal line ++ } else if (diff.y == 0) { ++ e1.x = e2.x; ++ _setCell(rw, e1); ++ } else if (diff.x == 0) { ++ //vertical line up ++ if (diff.y > 0) { ++ do { ++ f2.y = ONE_PIXEL; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * f1.x * 2; ++ f1.y = 0; ++ ++e1.y; ++ _setCell(rw, e1); ++ } while(e1.y != e2.y); ++ //vertical line down ++ } else { ++ do { ++ f2.y = 0; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * f1.x * 2; ++ f1.y = ONE_PIXEL; ++ --e1.y; ++ _setCell(rw, e1); ++ } while(e1.y != e2.y); ++ } ++ //any other line + } else { ++ Area prod = diff.x * f1.y - diff.y * f1.x; ++ ++ /* These macros speed up repetitive divisions by replacing them ++ with multiplications and right shifts. */ ++ auto dx_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.x); ++ auto dy_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.y); ++ ++ /* The fundamental value `prod' determines which side and the */ ++ /* exact coordinate where the line exits current cell. It is */ ++ /* also easily updated when moving from one cell to the next. */ + do { +- f2.y = 0; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * f1.x * 2; +- f1.y = ONE_PIXEL; +- --e1.y; ++ auto px = diff.x * ONE_PIXEL; ++ auto py = diff.y * ONE_PIXEL; ++ ++ //left ++ if (prod <= 0 && prod - px > 0) { ++ f2 = {0, SW_UDIV(-prod, -dx_r)}; ++ prod -= py; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * (f1.x + f2.x); ++ f1 = {ONE_PIXEL, f2.y}; ++ --e1.x; ++ //up ++ } else if (prod - px <= 0 && prod - px + py > 0) { ++ prod -= px; ++ f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL}; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * (f1.x + f2.x); ++ f1 = {f2.x, 0}; ++ ++e1.y; ++ //right ++ } else if (prod - px + py <= 0 && prod + py >= 0) { ++ prod += py; ++ f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)}; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * (f1.x + f2.x); ++ f1 = {0, f2.y}; ++ ++e1.x; ++ //down ++ } else { ++ f2 = {SW_UDIV(prod, -dy_r), 0}; ++ prod += px; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * (f1.x + f2.x); ++ f1 = {f2.x, ONE_PIXEL}; ++ --e1.y; ++ } ++ + _setCell(rw, e1); +- } while(e1.y != e2.y); ++ ++ } while(e1 != e2); + } +- //any other line +- } else { +- Area prod = diff.x * f1.y - diff.y * f1.x; +- +- /* These macros speed up repetitive divisions by replacing them +- with multiplications and right shifts. */ +- auto dx_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.x); +- auto dy_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.y); +- +- /* The fundamental value `prod' determines which side and the */ +- /* exact coordinate where the line exits current cell. It is */ +- /* also easily updated when moving from one cell to the next. */ +- do { +- auto px = diff.x * ONE_PIXEL; +- auto py = diff.y * ONE_PIXEL; +- +- //left +- if (prod <= 0 && prod - px > 0) { +- f2 = {0, SW_UDIV(-prod, -dx_r)}; +- prod -= py; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * (f1.x + f2.x); +- f1 = {ONE_PIXEL, f2.y}; +- --e1.x; +- //up +- } else if (prod - px <= 0 && prod - px + py > 0) { +- prod -= px; +- f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL}; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * (f1.x + f2.x); +- f1 = {f2.x, 0}; +- ++e1.y; +- //right +- } else if (prod - px + py <= 0 && prod + py >= 0) { +- prod += py; +- f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)}; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * (f1.x + f2.x); +- f1 = {0, f2.y}; +- ++e1.x; +- //down +- } else { +- f2 = {SW_UDIV(prod, -dy_r), 0}; +- prod += px; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * (f1.x + f2.x); +- f1 = {f2.x, ONE_PIXEL}; +- --e1.y; +- } + +- _setCell(rw, e1); ++ f2 = {line[0].x - SUBPIXELS(e2.x), line[0].y - SUBPIXELS(e2.y)}; ++ rw.cover += (f2.y - f1.y); ++ rw.area += (f2.y - f1.y) * (f1.x + f2.x); ++ rw.pos = line[0]; + +- } while(e1 != e2); ++ if (line-- == rw.lineStack) return; + } +- +- f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)}; +- rw.cover += (f2.y - f1.y); +- rw.area += (f2.y - f1.y) * (f1.x + f2.x); +- rw.pos = to; + } + + +@@ -690,31 +709,27 @@ static void _decomposeOutline(RleWorker& rw) + auto start = UPSCALE(outline->pts[first]); + auto pt = outline->pts.data + first; + auto types = outline->types.data + first; ++ ++types; + + _moveTo(rw, UPSCALE(outline->pts[first])); + + while (pt < limit) { +- ++pt; +- ++types; +- + //emit a single line_to + if (types[0] == SW_CURVE_TYPE_POINT) { ++ ++pt; ++ ++types; + _lineTo(rw, UPSCALE(*pt)); + //types cubic + } else { +- pt += 2; +- types += 2; +- +- if (pt <= limit) { +- _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); +- continue; +- } +- _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); +- goto close; ++ pt += 3; ++ types += 3; ++ if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); ++ else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); ++ else goto close; + } + } +- _lineTo(rw, start); + close: ++ _lineTo(rw, start); + first = last + 1; + } + } +@@ -731,7 +746,7 @@ static int _genRle(RleWorker& rw) + } + + +-static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt) ++static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt) + { + auto out = outSpans; + auto spans = target->spans; +@@ -740,7 +755,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar + auto clipEnd = clip->spans + clip->size; + + while (spans < end && clipSpans < clipEnd) { +- //align y cooridnates. ++ //align y-coordinates. + if (clipSpans->y > spans->y) { + ++spans; + continue; +@@ -750,7 +765,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar + continue; + } + +- //Try clipping with all clip spans which have a same y coordinate. ++ //Try clipping with all clip spans which have a same y-coordinate. + auto temp = clipSpans; + while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) { + auto sx1 = spans->x; +@@ -783,7 +798,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar + } + + +-static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) ++static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) + { + auto out = outSpans; + auto spans = targetRle->spans; +@@ -822,47 +837,7 @@ 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(SwRle *rle, SwSpan* clippedSpans, uint32_t size) + { + free(rle->spans); + rle->spans = clippedSpans; +@@ -874,7 +849,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) + /* External Class Implementation */ + /************************************************************************/ + +-SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) ++SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) + { + constexpr auto RENDER_POOL_SIZE = 16384L; + constexpr auto BAND_SIZE = 40; +@@ -902,7 +877,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren + rw.bandShoot = 0; + rw.antiAlias = antiAlias; + +- if (!rle) rw.rle = reinterpret_cast(calloc(1, sizeof(SwRleData))); ++ if (!rle) rw.rle = reinterpret_cast(calloc(1, sizeof(SwRle))); + else rw.rle = rle; + + //Generate RLE +@@ -993,12 +968,12 @@ error: + } + + +-SwRleData* rleRender(const SwBBox* bbox) ++SwRle* rleRender(const SwBBox* bbox) + { + auto width = static_cast(bbox->max.x - bbox->min.x); + auto height = static_cast(bbox->max.y - bbox->min.y); + +- auto rle = static_cast(malloc(sizeof(SwRleData))); ++ auto rle = static_cast(malloc(sizeof(SwRle))); + rle->spans = static_cast(malloc(sizeof(SwSpan) * height)); + rle->size = height; + rle->alloc = height; +@@ -1015,14 +990,14 @@ SwRleData* rleRender(const SwBBox* bbox) + } + + +-void rleReset(SwRleData* rle) ++void rleReset(SwRle* rle) + { + if (!rle) return; + rle->size = 0; + } + + +-void rleFree(SwRleData* rle) ++void rleFree(SwRle* rle) + { + if (!rle) return; + if (rle->spans) free(rle->spans); +@@ -1030,46 +1005,7 @@ 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(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(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(malloc(sizeof(SwSpan) * spanCnt)); +- auto spansEnd = _mergeSpansRegion(clip1, clip2, spans); +- +- _replaceClipSpan(rle, spans, spansEnd - spans); +-} +- +- +-void rleClipPath(SwRleData *rle, const SwRleData *clip) ++void rleClip(SwRle *rle, const SwRle *clip) + { + if (rle->size == 0 || clip->size == 0) return; + auto spanCnt = rle->size > clip->size ? rle->size : clip->size; +@@ -1078,11 +1014,11 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip) + + _replaceClipSpan(rle, spans, spansEnd - spans); + +- TVGLOG("SW_ENGINE", "Using ClipPath!"); ++ TVGLOG("SW_ENGINE", "Using Path Clipping!"); + } + + +-void rleClipRect(SwRleData *rle, const SwBBox* clip) ++void rleClip(SwRle *rle, const SwBBox* clip) + { + if (rle->size == 0) return; + auto spans = static_cast(malloc(sizeof(SwSpan) * (rle->size))); +@@ -1090,5 +1026,5 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip) + + _replaceClipSpan(rle, spans, spansEnd - spans); + +- TVGLOG("SW_ENGINE", "Using ClipRect!"); ++ TVGLOG("SW_ENGINE", "Using Box Clipping!"); + } +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +index 4f069ece97..4408db0b86 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +@@ -22,7 +22,6 @@ + + #include "tvgSwCommon.h" + #include "tvgMath.h" +-#include "tvgLines.h" + + /************************************************************************/ + /* Internal Class Implementation */ +@@ -49,7 +48,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. + if (!closed) _outlineEnd(outline); +@@ -60,14 +59,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.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.types.push(SW_CURVE_TYPE_CUBIC); +@@ -99,12 +98,12 @@ 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}; +- auto len = lineLength(cur.pt1, cur.pt2); ++ auto len = cur.length(); + +- if (mathZero(len)) { ++ if (tvg::zero(len)) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + //draw the current line fully + } else if (len <= dash.curLen) { +@@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans + Line left, right; + if (dash.curLen > 0) { + len -= dash.curLen; +- lineSplitAt(cur, dash.curLen, left, right); ++ cur.split(dash.curLen, left, right); + if (!dash.curOpGap) { + if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { + _outlineMoveTo(*dash.outline, &left.pt1, transform); +@@ -160,13 +159,13 @@ 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}; +- auto len = bezLength(cur); ++ auto len = cur.length(); + + //draw the current line fully +- if (mathZero(len)) { ++ if (tvg::zero(len)) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + } else if (len <= dash.curLen) { + dash.curLen -= len; +@@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct + Bezier left, right; + if (dash.curLen > 0) { + len -= dash.curLen; +- bezSplitAt(cur, dash.curLen, left, right); ++ cur.split(dash.curLen, left, right); + if (!dash.curOpGap) { + if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { + _outlineMoveTo(*dash.outline, &left.start, transform); +@@ -210,7 +209,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct + } + _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform); + } +- if (dash.curLen < 1 && TO_SWCOORD(len) > 1) { ++ if (dash.curLen < 0.1f && TO_SWCOORD(len) > 1) { + //move to next dash + dash.curIdx = (dash.curIdx + 1) % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx]; +@@ -221,7 +220,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); + } +@@ -245,10 +244,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 + if (end > begin) { +@@ -284,7 +283,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32 + if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f; + + const Point* close = nullptr; +- auto length = 0.0f; ++ auto len = 0.0f; + + //must begin with moveTo + if (cmds[0] == PathCommand::MoveTo) { +@@ -297,34 +296,34 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32 + while (cmdCnt-- > 0) { + switch (*cmds) { + case PathCommand::Close: { +- length += mathLength(pts - 1, close); +- if (subpath) return length; ++ len += length(pts - 1, close); ++ if (subpath) return len; + break; + } + case PathCommand::MoveTo: { +- if (subpath) return length; ++ if (subpath) return len; + close = pts; + ++pts; + break; + } + case PathCommand::LineTo: { +- length += mathLength(pts - 1, pts); ++ len += length(pts - 1, pts); + ++pts; + break; + } + case PathCommand::CubicTo: { +- length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); ++ len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length(); + pts += 3; + break; + } + } + ++cmds; + } +- return length; ++ return len; + } + + +-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; + auto cmdCnt = rshape->path.cmds.count; +@@ -341,6 +340,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans + auto offset = 0.0f; + dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset); + 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 (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4); +@@ -353,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans + //offset + auto patternLength = 0.0f; + uint32_t offIdx = 0; +- if (!mathZero(offset)) { ++ if (!tvg::zero(offset)) { + for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i]; + bool isOdd = dash.cnt % 2; + if (isOdd) patternLength *= 2; +@@ -372,7 +373,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans + + //must begin with 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); + cmds++; + pts++; +@@ -387,7 +388,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans + case PathCommand::MoveTo: { + if (trimmed) { + 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); + } else _dashMoveTo(dash, pts); + } else _dashMoveTo(dash, offIdx, offset, pts); +@@ -436,7 +437,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; + auto cmdCnt = rshape->path.cmds.count; +@@ -492,12 +493,11 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* + /* 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 (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; + +- //Keep it for Rasterization Region + shape->bbox = renderRegion; + + //Check valid region +@@ -575,7 +575,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(calloc(1, sizeof(SwStroke))); + auto stroke = shape->stroke; +@@ -586,7 +586,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* strokeOutline = nullptr; +@@ -629,13 +629,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); + } + + +-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); + } +diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp +index 18f5f3eca8..e195f72adf 100644 +--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp ++++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp +@@ -441,13 +441,23 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl + //initialize with current direction + angleIn = angleOut = angleMid = stroke.angleIn; + +- if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) { ++ auto valid = mathCubicAngle(arc, angleIn, angleMid, angleOut); ++ ++ //valid size ++ if (valid > 0 && arc < limit) { + if (stroke.firstPt) stroke.angleIn = angleIn; + mathSplitCubic(arc); + arc += 3; + continue; + } + ++ //ignoreable size ++ if (valid < 0 && arc == bezStack) { ++ stroke.center = to; ++ return; ++ } ++ ++ //small size + if (firstArc) { + firstArc = false; + //process corner if necessary +@@ -662,7 +672,7 @@ static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed) + /* Determine if we need to check whether the border radius is greater + than the radius of curvature of a curve, to handle this case specially. + This is only required if bevel joins or butt caps may be created because +- round & miter joins and round & square caps cover the nagative sector ++ round & miter joins and round & square caps cover the negative sector + created with wide strokes. */ + if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt)) + stroke.handleWideStrokes = true; +@@ -715,7 +725,7 @@ static void _endSubPath(SwStroke& stroke) + _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0); + + /* now end the right subpath accordingly. The left one is rewind +- and deosn't need further processing */ ++ and doesn't need further processing */ + _borderClose(right, false); + } + } +@@ -805,15 +815,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->cap = rshape->strokeCap(); + stroke->miterlimit = static_cast(rshape->strokeMiterlimit() * 65536.0f); +@@ -850,31 +855,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) + + //A contour cannot start with a cubic control point + if (type == SW_CURVE_TYPE_CUBIC) return false; ++ ++types; + + auto closed = outline.closed.data ? outline.closed.data[i]: false; + + _beginSubPath(*stroke, start, closed); + + while (pt < limit) { +- ++pt; +- ++types; +- +- //emit a signel line_to ++ //emit a single line_to + if (types[0] == SW_CURVE_TYPE_POINT) { ++ ++pt; ++ ++types; + _lineTo(*stroke, *pt); + //types cubic + } else { +- if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false; +- +- pt += 2; +- types += 2; +- +- if (pt <= limit) { +- _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); +- continue; +- } +- _cubicTo(*stroke, pt[-2], pt[-1], start); +- goto close; ++ pt += 3; ++ types += 3; ++ if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); ++ else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start); ++ else goto close; + } + } + close: +diff --git a/thirdparty/thorvg/src/renderer/tvgAccessor.cpp b/thirdparty/thorvg/src/renderer/tvgAccessor.cpp +index 903437f29d..a144726804 100644 +--- a/thirdparty/thorvg/src/renderer/tvgAccessor.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgAccessor.cpp +@@ -21,20 +21,21 @@ + */ + + #include "tvgIteratorAccessor.h" ++#include "tvgCompressor.h" + + /************************************************************************/ + /* Internal Class Implementation */ + /************************************************************************/ + +-static bool accessChildren(Iterator* it, function func) ++static bool accessChildren(Iterator* it, function func, void* data) + { + while (auto child = it->next()) { + //Access the child +- if (!func(child)) return false; ++ if (!func(child, data)) return false; + + //Access the children of the child + if (auto it2 = IteratorAccessor::iterator(child)) { +- if (!accessChildren(it2, func)) { ++ if (!accessChildren(it2, func, data)) { + delete(it2); + return false; + } +@@ -44,26 +45,46 @@ static bool accessChildren(Iterator* it, function func + return true; + } + ++ + /************************************************************************/ + /* External Class Implementation */ + /************************************************************************/ + +-unique_ptr Accessor::set(unique_ptr picture, function func) noexcept ++TVG_DEPRECATED unique_ptr Accessor::set(unique_ptr picture, function func) noexcept ++{ ++ auto backward = [](const tvg::Paint* paint, void* data) -> bool ++ { ++ auto func = reinterpret_cast*>(data); ++ if (!(*func)(paint)) return false; ++ return true; ++ }; ++ ++ set(picture.get(), backward, reinterpret_cast(&func)); ++ return picture; ++} ++ ++ ++Result Accessor::set(const Picture* picture, function func, void* data) noexcept + { +- auto p = picture.get(); +- if (!p || !func) return picture; ++ if (!picture || !func) return Result::InvalidArguments; + + //Use the Preorder Tree-Search + + //Root +- if (!func(p)) return picture; ++ if (!func(picture, data)) return Result::Success; + + //Children +- if (auto it = IteratorAccessor::iterator(p)) { +- accessChildren(it, func); ++ if (auto it = IteratorAccessor::iterator(picture)) { ++ accessChildren(it, func, data); + delete(it); + } +- return picture; ++ return Result::Success; ++} ++ ++ ++uint32_t Accessor::id(const char* name) noexcept ++{ ++ return djb2Encode(name); + } + + +diff --git a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp b/thirdparty/thorvg/src/renderer/tvgAnimation.cpp +index be6c2dc7de..6c4711e8c1 100644 +--- a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgAnimation.cpp +@@ -95,7 +95,7 @@ float Animation::duration() const 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; + if (!loader) return Result::InsufficientCondition; +diff --git a/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h b/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h +index 29f84eb82a..e40859b6db 100644 +--- a/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h ++++ b/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h +@@ -36,7 +36,7 @@ using TvgBinFlag = TvgBinByte; + #define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE + #define TVG_HEADER_SIGNATURE "ThorVG" + #define TVG_HEADER_SIGNATURE_LENGTH 6 +-#define TVG_HEADER_VERSION "001200" //Major 00, Minor 12, Micro 00 ++#define TVG_HEADER_VERSION "001500" //Major 00, Minor 15, Micro 00 + #define TVG_HEADER_VERSION_LENGTH 6 + #define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions + #define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS +diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.h b/thirdparty/thorvg/src/renderer/tvgCanvas.h +index 81fd1b7d6f..c5d2127f9c 100644 +--- a/thirdparty/thorvg/src/renderer/tvgCanvas.h ++++ b/thirdparty/thorvg/src/renderer/tvgCanvas.h +@@ -26,7 +26,7 @@ + #include "tvgPaint.h" + + +-enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged}; ++enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged}; + + struct Canvas::Impl + { +@@ -42,7 +42,7 @@ struct Canvas::Impl + + ~Impl() + { +- //make it sure any deffered jobs ++ //make it sure any deferred jobs + renderer->sync(); + renderer->clear(); + +@@ -61,7 +61,7 @@ struct Canvas::Impl + + Result push(unique_ptr paint) + { +- //You can not push paints during rendering. ++ //You cannot push paints during rendering. + if (status == Status::Drawing) return Result::InsufficientCondition; + + auto p = paint.release(); +@@ -91,13 +91,15 @@ struct Canvas::Impl + + Array clips; + auto flag = RenderUpdateFlag::None; +- if (status == Status::Damanged || force) flag = RenderUpdateFlag::All; ++ if (status == Status::Damaged || force) flag = RenderUpdateFlag::All; ++ ++ auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1}; + + if (paint) { +- paint->pImpl->update(renderer, nullptr, clips, 255, flag); ++ paint->pImpl->update(renderer, m, clips, 255, flag); + } else { + for (auto paint : paints) { +- paint->pImpl->update(renderer, nullptr, clips, 255, flag); ++ paint->pImpl->update(renderer, m, clips, 255, flag); + } + } + status = Status::Updating; +@@ -106,7 +108,7 @@ struct Canvas::Impl + + Result draw() + { +- if (status == Status::Damanged) update(nullptr, false); ++ if (status == Status::Damaged) update(nullptr, false); + if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition; + + bool rendered = false; +@@ -122,7 +124,7 @@ struct Canvas::Impl + + Result sync() + { +- if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition; ++ if (status == Status::Synced || status == Status::Damaged) return Result::InsufficientCondition; + + if (renderer->sync()) { + status = Status::Synced; +@@ -134,7 +136,7 @@ struct Canvas::Impl + + Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) + { +- if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition; ++ if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition; + + RenderRegion val = {x, y, w, h}; + //intersect if the target buffer is already set. +@@ -145,7 +147,7 @@ struct Canvas::Impl + if (vport == val) return Result::Success; + renderer->viewport(val); + vport = val; +- status = Status::Damanged; ++ status = Status::Damaged; + return Result::Success; + } + }; +diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h +index 15a2cc4ef0..527221625b 100644 +--- a/thirdparty/thorvg/src/renderer/tvgCommon.h ++++ b/thirdparty/thorvg/src/renderer/tvgCommon.h +@@ -54,15 +54,6 @@ using namespace tvg; + #define strdup _strdup + #endif + +-//TVG class identifier values +-#define TVG_CLASS_ID_UNDEFINED 0 +-#define TVG_CLASS_ID_SHAPE 1 +-#define TVG_CLASS_ID_SCENE 2 +-#define TVG_CLASS_ID_PICTURE 3 +-#define TVG_CLASS_ID_LINEAR 4 +-#define TVG_CLASS_ID_RADIAL 5 +-#define TVG_CLASS_ID_TEXT 6 +- + enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown }; + + using Size = Point; +diff --git a/thirdparty/thorvg/src/renderer/tvgFill.cpp b/thirdparty/thorvg/src/renderer/tvgFill.cpp +index ea1010051e..19edff5a2c 100644 +--- a/thirdparty/thorvg/src/renderer/tvgFill.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgFill.cpp +@@ -155,15 +155,14 @@ Fill* Fill::duplicate() const noexcept + } + + +-uint32_t Fill::identifier() const noexcept ++TVG_DEPRECATED uint32_t Fill::identifier() const noexcept + { +- return pImpl->id; ++ return (uint32_t) type(); + } + + + RadialGradient::RadialGradient():pImpl(new Impl()) + { +- Fill::pImpl->id = TVG_CLASS_ID_RADIAL; + Fill::pImpl->method(new FillDup(pImpl)); + } + +@@ -196,15 +195,20 @@ unique_ptr RadialGradient::gen() noexcept + } + + +-uint32_t RadialGradient::identifier() noexcept ++TVG_DEPRECATED uint32_t RadialGradient::identifier() noexcept + { +- return TVG_CLASS_ID_RADIAL; ++ return (uint32_t) Type::RadialGradient; ++} ++ ++ ++Type RadialGradient::type() const noexcept ++{ ++ return Type::RadialGradient; + } + + + LinearGradient::LinearGradient():pImpl(new Impl()) + { +- Fill::pImpl->id = TVG_CLASS_ID_LINEAR; + Fill::pImpl->method(new FillDup(pImpl)); + } + +@@ -243,8 +247,13 @@ unique_ptr LinearGradient::gen() noexcept + } + + +-uint32_t LinearGradient::identifier() noexcept ++TVG_DEPRECATED uint32_t LinearGradient::identifier() noexcept + { +- return TVG_CLASS_ID_LINEAR; ++ return (uint32_t) Type::LinearGradient; + } + ++ ++Type LinearGradient::type() const noexcept ++{ ++ return Type::LinearGradient; ++} +diff --git a/thirdparty/thorvg/src/renderer/tvgFill.h b/thirdparty/thorvg/src/renderer/tvgFill.h +index 47f0c051c0..f249356aa2 100644 +--- a/thirdparty/thorvg/src/renderer/tvgFill.h ++++ b/thirdparty/thorvg/src/renderer/tvgFill.h +@@ -55,7 +55,6 @@ struct Fill::Impl + uint32_t cnt = 0; + FillSpread spread; + DuplicateMethod* dup = nullptr; +- uint8_t id; + + ~Impl() + { +diff --git a/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp +index 82666b7ae3..24e2fb8b1b 100644 +--- a/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp +@@ -62,7 +62,7 @@ GlCanvas::~GlCanvas() + Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept + { + #ifdef THORVG_GL_RASTER_SUPPORT +- if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) { ++ if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { + return Result::InsufficientCondition; + } + +@@ -75,7 +75,7 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept + renderer->viewport(Canvas::pImpl->vport); + + //Paints must be updated again with this new target. +- Canvas::pImpl->status = Status::Damanged; ++ Canvas::pImpl->status = Status::Damaged; + + return Result::Success; + #endif +diff --git a/thirdparty/thorvg/src/renderer/tvgInitializer.cpp b/thirdparty/thorvg/src/renderer/tvgInitializer.cpp +index 76d89b40ed..c57b20779c 100644 +--- a/thirdparty/thorvg/src/renderer/tvgInitializer.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgInitializer.cpp +@@ -54,36 +54,30 @@ static constexpr bool operator &(CanvasEngine a, CanvasEngine 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; + +- 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; + +- 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; + +- 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]; +- snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro); +- ++ snprintf(sum, sizeof(sum), "%d%02d%02d", majorVal, minorVal, microVal); + _version = atoi(sum); + ++ if (major) *major = majorVal; ++ if (minor) *minor = minorVal; ++ if (micro) *micro = microVal; ++ + return true; + } + +@@ -122,7 +116,7 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept + + 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; + +@@ -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() + { + return _version; + } +- +diff --git a/thirdparty/thorvg/src/renderer/tvgLoadModule.h b/thirdparty/thorvg/src/renderer/tvgLoadModule.h +index c750683771..a9c1a68544 100644 +--- a/thirdparty/thorvg/src/renderer/tvgLoadModule.h ++++ b/thirdparty/thorvg/src/renderer/tvgLoadModule.h +@@ -80,14 +80,14 @@ struct ImageLoader : LoadModule + static ColorSpace cs; //desired value + + float w = 0, h = 0; //default image size +- Surface surface; ++ RenderSurface surface; + + ImageLoader(FileType type) : LoadModule(type) {} + + virtual bool animatable() { return false; } //true if this loader supports animation. + virtual Paint* paint() { return nullptr; } + +- virtual Surface* bitmap() ++ virtual RenderSurface* bitmap() + { + if (surface.data) return &surface; + return nullptr; +@@ -101,7 +101,8 @@ struct FontLoader : LoadModule + + FontLoader(FileType type) : LoadModule(type) {} + +- virtual bool request(Shape* shape, char* text, bool italic = false) = 0; ++ virtual bool request(Shape* shape, char* text) = 0; ++ virtual bool transform(Paint* paint, float fontSize, bool italic) = 0; + }; + + #endif //_TVG_LOAD_MODULE_H_ +diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.cpp b/thirdparty/thorvg/src/renderer/tvgLoader.cpp +index 6a81ddcdbb..db51fc215a 100644 +--- a/thirdparty/thorvg/src/renderer/tvgLoader.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgLoader.cpp +@@ -294,10 +294,10 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid) + { + *invalid = false; + +- //TODO: lottie is not sharable. ++ //TODO: svg & lottie is not sharable. + auto allowCache = true; + auto ext = path.substr(path.find_last_of(".") + 1); +- if (!ext.compare("json")) allowCache = false; ++ if (!ext.compare("svg") || !ext.compare("json")) allowCache = false; + + if (allowCache) { + if (auto loader = _findFromCache(path)) return loader; +@@ -317,7 +317,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid) + } + delete(loader); + } +- //Unkown MimeType. Try with the candidates in the order ++ //Unknown MimeType. Try with the candidates in the order + for (int i = 0; i < static_cast(FileType::Raw); i++) { + if (auto loader = _find(static_cast(i))) { + if (loader->open(path)) { +@@ -392,7 +392,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim + } + } + } +- //Unkown MimeType. Try with the candidates in the order ++ //Unknown MimeType. Try with the candidates in the order + for (int i = 0; i < static_cast(FileType::Raw); i++) { + auto loader = _find(static_cast(i)); + if (loader) { +diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp +index 0ce6540f20..536e187852 100644 +--- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp +@@ -32,16 +32,16 @@ + /************************************************************************/ + + #define PAINT_METHOD(ret, METHOD) \ +- switch (id) { \ +- case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \ +- case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \ +- case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \ +- case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \ ++ switch (paint->type()) { \ ++ case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \ ++ case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \ ++ case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \ ++ case Type::Text: ret = P((Text*)paint)->METHOD; break; \ + default: ret = {}; \ + } + + +-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 + Point tmp[4]; +@@ -50,8 +50,8 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr + + for (int i = 0; i < 4; ++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 > max.x) max.x = tmp[i].x; + 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. */ + auto shape = static_cast(cmpTarget); +@@ -84,18 +84,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren + + //nothing to clip + if (ptsCnt == 0) return Result::InvalidArguments; +- + 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. + auto tryClip = false; + +- if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true; +- if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true; ++ if ((!rightAngle(pm) || skewed(pm))) tryClip = true; ++ if ((!rightAngle(rm) || skewed(rm))) tryClip = true; + +- if (tryClip) return _clipRect(renderer, pts, pTransform, rTransform, before); ++ if (tryClip) return _clipRect(renderer, pts, pm, rm, before); + + //Perpendicular Rectangle? + auto pt1 = pts + 0; +@@ -103,23 +102,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren + auto pt3 = pts + 2; + auto pt4 = pts + 3; + +- if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) || +- (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) { ++ if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) || ++ (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) { + + RenderRegion after; + + auto v1 = *pt1; + 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 + if (v1.x > v2.x) std::swap(v1.x, v2.x); +@@ -158,21 +151,20 @@ 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 +- 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; + + if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method); ++ if (clipper) ret->pImpl->clip(clipper->duplicate()); + + return ret; + } +@@ -180,14 +172,9 @@ Paint* Paint::Impl::duplicate() + + 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 (tvg::equal(degree, tr.degree)) return true; ++ tr.degree = degree; + renderFlag |= RenderUpdateFlag::Transform; + + return true; +@@ -196,14 +183,9 @@ bool Paint::Impl::rotate(float degree) + + 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 (tvg::equal(factor, tr.scale)) return true; ++ tr.scale = factor; + renderFlag |= RenderUpdateFlag::Transform; + + return true; +@@ -212,15 +194,10 @@ bool Paint::Impl::scale(float factor) + + 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 (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true; ++ tr.m.e13 = x; ++ tr.m.e23 = y; + renderFlag |= RenderUpdateFlag::Transform; + + return true; +@@ -229,11 +206,11 @@ bool Paint::Impl::translate(float x, float y) + + bool Paint::Impl::render(RenderMethod* renderer) + { +- Compositor* cmp = nullptr; ++ if (opacity == 0) return true; + +- /* Note: only ClipPath is processed in update() step. +- Create a composition image. */ +- if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { ++ RenderCompositor* cmp = nullptr; ++ ++ if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { + RenderRegion region; + PAINT_METHOD(region, bounds(renderer)); + +@@ -247,8 +224,6 @@ bool Paint::Impl::render(RenderMethod* renderer) + + if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity); + +- renderer->blend(blendMethod); +- + bool ret; + PAINT_METHOD(ret, render(renderer)); + +@@ -258,7 +233,7 @@ bool Paint::Impl::render(RenderMethod* renderer) + } + + +-RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) ++RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + if (this->renderer != renderer) { + if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!"); +@@ -266,72 +241,79 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT + this->renderer = renderer; + } + +- if (renderFlag & RenderUpdateFlag::Transform) rTransform->update(); ++ if (renderFlag & RenderUpdateFlag::Transform) tr.update(); + + /* 1. Composition Pre Processing */ + RenderData trd = nullptr; //composite target render data + RenderRegion viewport; + Result compFastTrack = Result::InsufficientCondition; +- bool childClipper = false; + + if (compData) { + auto target = compData->target; + auto method = compData->method; +- target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset +- +- /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle, +- we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */ +- auto tryFastTrack = false; +- if (target->identifier() == TVG_CLASS_ID_SHAPE) { +- if (method == CompositeMethod::ClipPath) tryFastTrack = true; +- else { +- auto shape = static_cast(target); +- uint8_t a; +- shape->fillColor(nullptr, nullptr, nullptr, &a); +- //no gradient fill & no compositions of the composition target. +- if (!shape->fill() && !(PP(shape)->compData)) { +- if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true; +- else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true; +- } +- } +- if (tryFastTrack) { +- viewport = renderer->viewport(); +- if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) { +- target->pImpl->ctxFlag |= ContextFlag::FastTrack; ++ P(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset ++ ++ /* If the transformation has no rotational factors and the Alpha(InvAlpha)Masking involves a simple rectangle, ++ we can optimize by using the viewport instead of the regular AlphaMasking sequence for improved performance. */ ++ if (target->type() == Type::Shape) { ++ auto shape = static_cast(target); ++ uint8_t a; ++ shape->fillColor(nullptr, nullptr, nullptr, &a); ++ //no gradient fill & no compositions of the composition target. ++ if (!shape->fill() && !(PP(shape)->compData)) { ++ if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) { ++ viewport = renderer->viewport(); ++ if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { ++ P(target)->ctxFlag |= ContextFlag::FastTrack; ++ } + } + } + } + if (compFastTrack == Result::InsufficientCondition) { +- childClipper = compData->method == CompositeMethod::ClipPath ? true : false; +- trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper); +- if (childClipper) clips.push(trd); ++ trd = P(target)->update(renderer, pm, clips, 255, pFlag, false); ++ } ++ } ++ ++ /* 2. Clipping */ ++ if (this->clipper) { ++ P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset ++ viewport = renderer->viewport(); ++ /* TODO: Intersect the clipper's clipper, if both are FastTrack. ++ Update the subsequent clipper first and check its ctxFlag. */ ++ if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) { ++ P(this->clipper)->ctxFlag |= ContextFlag::FastTrack; ++ } ++ if (compFastTrack == Result::InsufficientCondition) { ++ trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true); ++ clips.push(trd); + } + } + +- /* 2. Main Update */ ++ /* 3. Main Update */ + auto newFlag = static_cast(pFlag | renderFlag); + renderFlag = RenderUpdateFlag::None; + opacity = MULTIPLY(opacity, this->opacity); + + RenderData rd = nullptr; +- RenderTransform outTransform(pTransform, rTransform); +- PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper)); + +- /* 3. Composition Post Processing */ ++ tr.cm = pm * tr.m; ++ PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper)); ++ ++ /* 4. Composition Post Processing */ + if (compFastTrack == Result::Success) renderer->viewport(viewport); +- else if (childClipper) clips.pop(); ++ else if (this->clipper) clips.pop(); + + return rd; + } + + +-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; ++ const auto& m = this->transform(origin); + + //Case: No transformed, quick return! +- if (!transformed || !(m = this->transform())) { ++ if (!transformed || identity(&m)) { + PAINT_METHOD(ret, bounds(x, y, w, h, stroking)); + return ret; + } +@@ -355,7 +337,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme + + //Compute the AABB after transformation + 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 > x2) x2 = pt[i].x; +@@ -372,6 +354,32 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme + } + + ++void Paint::Impl::reset() ++{ ++ if (clipper) { ++ delete(clipper); ++ clipper = nullptr; ++ } ++ ++ if (compData) { ++ if (P(compData->target)->unref() == 0) delete(compData->target); ++ free(compData); ++ compData = nullptr; ++ } ++ ++ tvg::identity(&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 */ + /************************************************************************/ +@@ -417,9 +425,7 @@ Result Paint::transform(const Matrix& m) 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 +435,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; + } + +@@ -442,10 +448,27 @@ Paint* Paint::duplicate() const noexcept + } + + ++Result Paint::clip(std::unique_ptr clipper) noexcept ++{ ++ auto p = clipper.release(); ++ ++ if (p && p->type() != Type::Shape) { ++ TVGERR("RENDERER", "Clipping only supports the Shape!"); ++ return Result::NonSupport; ++ } ++ pImpl->clip(p); ++ return Result::Success; ++} ++ ++ + Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept + { ++ //TODO: remove. Keep this for the backward compatibility ++ if (target && method == CompositeMethod::ClipPath) return clip(std::move(target)); ++ + auto p = target.release(); + if (pImpl->composite(this, p, method)) return Result::Success; ++ + delete(p); + return Result::InvalidArguments; + } +@@ -457,6 +480,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept + if (target) *target = pImpl->compData->target; + return pImpl->compData->method; + } else { ++ //TODO: remove. Keep this for the backward compatibility ++ if (pImpl->clipper) { ++ if (target) *target = pImpl->clipper; ++ return CompositeMethod::ClipPath; ++ } + if (target) *target = nullptr; + return CompositeMethod::None; + } +@@ -480,14 +508,17 @@ uint8_t Paint::opacity() const noexcept + } + + +-uint32_t Paint::identifier() const noexcept ++TVG_DEPRECATED uint32_t Paint::identifier() const noexcept + { +- return pImpl->id; ++ return (uint32_t) type(); + } + + +-Result Paint::blend(BlendMethod method) const noexcept ++Result Paint::blend(BlendMethod method) noexcept + { ++ //TODO: Remove later ++ if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport; ++ + if (pImpl->blendMethod != method) { + pImpl->blendMethod = method; + pImpl->renderFlag |= RenderUpdateFlag::Blend; +@@ -495,9 +526,3 @@ Result Paint::blend(BlendMethod method) const noexcept + + return Result::Success; + } +- +- +-BlendMethod Paint::blend() const noexcept +-{ +- return pImpl->blendMethod; +-} +diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h +index bc07ab52ab..d78e9bb3d1 100644 +--- a/thirdparty/thorvg/src/renderer/tvgPaint.h ++++ b/thirdparty/thorvg/src/renderer/tvgPaint.h +@@ -48,17 +48,40 @@ namespace tvg + struct Paint::Impl + { + Paint* paint = nullptr; +- RenderTransform* rTransform = nullptr; + Composite* compData = nullptr; ++ Paint* clipper = 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; ++ tvg::scale(&m, scale, scale); ++ tvg::rotate(&m, degree); ++ } ++ } tr; ++ BlendMethod blendMethod; ++ uint8_t renderFlag; ++ uint8_t ctxFlag; ++ uint8_t opacity; + uint8_t refCnt = 0; //reference count + +- Impl(Paint* pnt) : paint(pnt) {} ++ Impl(Paint* pnt) : paint(pnt) ++ { ++ reset(); ++ } + + ~Impl() + { +@@ -66,7 +89,7 @@ namespace tvg + if (P(compData->target)->unref() == 0) delete(compData->target); + free(compData); + } +- delete(rTransform); ++ if (clipper && P(clipper)->unref() == 0) delete(clipper); + if (renderer && (renderer->unref() == 0)) delete(renderer); + } + +@@ -84,23 +107,33 @@ namespace tvg + + bool transform(const Matrix& m) + { +- if (!rTransform) { +- if (mathIdentity(&m)) return true; +- rTransform = new RenderTransform(); +- } +- rTransform->override(m); ++ if (&tr.m != &m) tr.m = m; ++ tr.overriding = true; + renderFlag |= RenderUpdateFlag::Transform; + + return true; + } + +- Matrix* transform() ++ Matrix& transform(bool origin = false) + { +- if (rTransform) { +- if (renderFlag & RenderUpdateFlag::Transform) rTransform->update(); +- return &rTransform->m; ++ //update transform ++ if (renderFlag & RenderUpdateFlag::Transform) tr.update(); ++ if (origin) return tr.cm; ++ return tr.m; ++ } ++ ++ void clip(Paint* clp) ++ { ++ if (this->clipper) { ++ P(this->clipper)->unref(); ++ if (this->clipper != clp && P(this->clipper)->refCnt == 0) { ++ delete(this->clipper); ++ } + } +- return nullptr; ++ this->clipper = clp; ++ if (!clp) return; ++ ++ P(clipper)->ref(); + } + + bool composite(Paint* source, Paint* target, CompositeMethod method) +@@ -135,10 +168,11 @@ namespace tvg + bool rotate(float degree); + bool scale(float factor); + bool translate(float x, float y); +- bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); +- RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array& 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& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); + bool render(RenderMethod* renderer); +- Paint* duplicate(); ++ Paint* duplicate(Paint* ret = nullptr); ++ void reset(); + }; + } + +diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp +index 5bd55a3f7b..d3e31d198a 100644 +--- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgPicture.cpp +@@ -20,6 +20,7 @@ + * SOFTWARE. + */ + ++#include "tvgPaint.h" + #include "tvgPicture.h" + + /************************************************************************/ +@@ -73,9 +74,11 @@ bool Picture::Impl::needComposition(uint8_t opacity) + bool Picture::Impl::render(RenderMethod* renderer) + { + bool ret = false; ++ renderer->blend(PP(picture)->blendMethod); ++ + if (surface) return renderer->renderImage(rd); + else if (paint) { +- Compositor* cmp = nullptr; ++ RenderCompositor* cmp = nullptr; + if (needComp) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, 255); +@@ -104,21 +107,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) + { + //Same resource has been loaded. +@@ -147,7 +135,6 @@ Result Picture::Impl::load(ImageLoader* loader) + + Picture::Picture() : pImpl(new Impl(this)) + { +- Paint::pImpl->id = TVG_CLASS_ID_PICTURE; + } + + +@@ -163,9 +150,15 @@ unique_ptr Picture::gen() noexcept + } + + +-uint32_t Picture::identifier() noexcept ++TVG_DEPRECATED uint32_t Picture::identifier() noexcept ++{ ++ return (uint32_t) Type::Picture; ++} ++ ++ ++Type Picture::type() const noexcept + { +- return TVG_CLASS_ID_PICTURE; ++ return Type::Picture; + } + + +@@ -215,18 +208,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(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; + } +diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h +index bd7021218a..bbbc439105 100644 +--- a/thirdparty/thorvg/src/renderer/tvgPicture.h ++++ b/thirdparty/thorvg/src/renderer/tvgPicture.h +@@ -60,15 +60,13 @@ struct Picture::Impl + ImageLoader* loader = nullptr; + + Paint* paint = nullptr; //vector picture uses +- Surface* surface = nullptr; //bitmap picture uses ++ RenderSurface* surface = nullptr; //bitmap picture uses + RenderData rd = nullptr; //engine data + float w = 0, h = 0; +- RenderMesh rm; //mesh data + Picture* picture = nullptr; + bool resizing = false; + bool needComp = false; //need composition + +- RenderTransform resizeTransform(const RenderTransform* pTransform); + bool needComposition(uint8_t opacity); + bool render(RenderMethod* renderer); + bool size(float w, float h); +@@ -90,58 +88,37 @@ struct Picture::Impl + delete(paint); + } + +- RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) ++ RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) + { + auto flag = static_cast(pFlag | load()); + + if (surface) { + 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) { + if (resizing) { + loader->resize(paint, w, h); + resizing = 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; + } + + 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; + } + +@@ -176,32 +153,21 @@ struct Picture::Impl + 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(); + +- 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 (loader) { + dup->loader = loader; + ++dup->loader->sharing; +- PP(ret)->renderFlag |= RenderUpdateFlag::Image; ++ PP(picture)->renderFlag |= RenderUpdateFlag::Image; + } + + dup->surface = surface; +@@ -209,13 +175,7 @@ struct Picture::Impl + dup->h = h; + 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() +diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp +index 82145b9aa4..8ee76493c2 100644 +--- a/thirdparty/thorvg/src/renderer/tvgRender.cpp ++++ b/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) + { + auto x1 = x + w; +diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h +index ff55748033..eae44a2e8a 100644 +--- a/thirdparty/thorvg/src/renderer/tvgRender.h ++++ b/thirdparty/thorvg/src/renderer/tvgRender.h +@@ -23,6 +23,8 @@ + #ifndef _TVG_RENDER_H_ + #define _TVG_RENDER_H_ + ++#include ++#include + #include "tvgCommon.h" + #include "tvgArray.h" + #include "tvgLock.h" +@@ -35,9 +37,8 @@ using pixel_t = uint32_t; + + enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255}; + +-struct Surface; +- +-enum ColorSpace ++//TODO: Move this in public header unifying with SwCanvas::Colorspace ++enum ColorSpace : uint8_t + { + ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. + ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. +@@ -47,7 +48,7 @@ enum ColorSpace + Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace. + }; + +-struct Surface ++struct RenderSurface + { + union { + pixel_t* data = nullptr; //system based data pointer +@@ -61,11 +62,11 @@ struct Surface + uint8_t channelSize = 0; + bool premultiplied = false; //Alpha-premultiplied + +- Surface() ++ RenderSurface() + { + } + +- Surface(const Surface* rhs) ++ RenderSurface(const RenderSurface* rhs) + { + data = rhs->data; + stride = rhs->stride; +@@ -79,21 +80,10 @@ struct Surface + + }; + +-struct Compositor ++struct RenderCompositor + { + CompositeMethod method; +- uint8_t opacity; +-}; +- +-struct RenderMesh +-{ +- Polygon* triangles = nullptr; +- uint32_t triangleCnt = 0; +- +- ~RenderMesh() +- { +- free(triangles); +- } ++ uint8_t opacity; + }; + + struct RenderRegion +@@ -110,24 +100,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 + { + float width = 0.0f; +@@ -147,6 +119,57 @@ struct RenderStroke + bool simultaneous = true; + } 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(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() + { + free(dashPattern); +@@ -191,7 +214,7 @@ struct RenderShape + { + if (!stroke) return false; + if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false; +- if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false; ++ if (fabsf(stroke->trim.end - stroke->trim.begin) >= 1.0f) return false; + return true; + } + +@@ -236,11 +259,66 @@ struct RenderShape + float strokeMiterlimit() const + { + if (!stroke) return 4.0f; +- + return stroke->miterlimit;; + } + }; + ++struct RenderEffect ++{ ++ RenderData rd = nullptr; ++ RenderRegion extend = {0, 0, 0, 0}; ++ SceneEffect type; ++ bool invalid = false; ++ ++ virtual ~RenderEffect() ++ { ++ free(rd); ++ } ++}; ++ ++struct RenderEffectGaussianBlur : RenderEffect ++{ ++ float sigma; ++ uint8_t direction; //0: both, 1: horizontal, 2: vertical ++ uint8_t border; //0: duplicate, 1: wrap ++ uint8_t quality; //0 ~ 100 (optional) ++ ++ static RenderEffectGaussianBlur* gen(va_list& args) ++ { ++ auto inst = new RenderEffectGaussianBlur; ++ inst->sigma = std::max((float) va_arg(args, double), 0.0f); ++ inst->direction = std::min(va_arg(args, int), 2); ++ inst->border = std::min(va_arg(args, int), 1); ++ inst->quality = std::min(va_arg(args, int), 100); ++ inst->type = SceneEffect::GaussianBlur; ++ return inst; ++ } ++}; ++ ++struct RenderEffectDropShadow : RenderEffect ++{ ++ uint8_t color[4]; //rgba ++ float angle; ++ float distance; ++ float sigma; ++ uint8_t quality; //0 ~ 100 (optional) ++ ++ static RenderEffectDropShadow* gen(va_list& args) ++ { ++ auto inst = new RenderEffectDropShadow; ++ inst->color[0] = va_arg(args, int); ++ inst->color[1] = va_arg(args, int); ++ inst->color[2] = va_arg(args, int); ++ inst->color[3] = std::min(va_arg(args, int), 255); ++ inst->angle = (float) va_arg(args, double); ++ inst->distance = (float) va_arg(args, double); ++ inst->sigma = std::max((float) va_arg(args, double), 0.0f); ++ inst->quality = std::min(va_arg(args, int), 100); ++ inst->type = SceneEffect::DropShadow; ++ return inst; ++ } ++}; ++ + class RenderMethod + { + private: +@@ -252,9 +330,8 @@ public: + uint32_t unref(); + + virtual ~RenderMethod() {} +- virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; +- virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; +- virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; ++ virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; ++ virtual RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; + virtual bool preRender() = 0; + virtual bool renderShape(RenderData data) = 0; + virtual bool renderImage(RenderData data) = 0; +@@ -265,14 +342,17 @@ public: + virtual bool viewport(const RenderRegion& vp) = 0; + virtual bool blend(BlendMethod method) = 0; + virtual ColorSpace colorSpace() = 0; +- virtual const Surface* mainSurface() = 0; ++ virtual const RenderSurface* mainSurface() = 0; + + virtual bool clear() = 0; + virtual bool sync() = 0; + +- virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0; +- virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0; +- virtual bool endComposite(Compositor* cmp) = 0; ++ virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0; ++ virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0; ++ virtual bool endComposite(RenderCompositor* cmp) = 0; ++ ++ virtual bool prepare(RenderEffect* effect) = 0; ++ virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0; + }; + + static inline bool MASK_REGION_MERGING(CompositeMethod method) +@@ -288,6 +368,8 @@ static inline bool MASK_REGION_MERGING(CompositeMethod method) + //these might expand the rendering region + case CompositeMethod::AddMask: + case CompositeMethod::DifferenceMask: ++ case CompositeMethod::LightenMask: ++ case CompositeMethod::DarkenMask: + return true; + default: + TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method); +@@ -321,6 +403,8 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, Composi + case CompositeMethod::DifferenceMask: + case CompositeMethod::SubtractMask: + case CompositeMethod::IntersectMask: ++ case CompositeMethod::LightenMask: ++ case CompositeMethod::DarkenMask: + return ColorSpace::Grayscale8; + //TODO: Optimize Luma/InvLuma colorspace to Grayscale8 + case CompositeMethod::LumaMask: +@@ -337,7 +421,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) + return (((c) * (a) + 0xff) >> 8); + } + +- + } + + #endif //_TVG_RENDER_H_ +diff --git a/thirdparty/thorvg/src/renderer/tvgSaver.cpp b/thirdparty/thorvg/src/renderer/tvgSaver.cpp +index 79302f69fa..993fe6d80f 100644 +--- a/thirdparty/thorvg/src/renderer/tvgSaver.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgSaver.cpp +@@ -20,6 +20,7 @@ + * SOFTWARE. + */ + ++#include + #include "tvgCommon.h" + #include "tvgSaveModule.h" + #include "tvgPaint.h" +@@ -122,7 +123,7 @@ Result Saver::save(std::unique_ptr paint, const string& path, bool compre + auto p = paint.release(); + if (!p) return Result::MemoryCorruption; + +- //Already on saving an other resource. ++ //Already on saving another resource. + if (pImpl->saveModule) { + if (P(p)->refCnt == 0) delete(p); + return Result::InsufficientCondition; +@@ -160,12 +161,12 @@ Result Saver::save(unique_ptr animation, const string& path, uint32_t + //animation holds the picture, it must be 1 at the bottom. + auto remove = PP(a->picture())->refCnt <= 1 ? true : false; + +- if (mathZero(a->totalFrame())) { ++ if (tvg::zero(a->totalFrame())) { + if (remove) delete(a); + return Result::InsufficientCondition; + } + +- //Already on saving an other resource. ++ //Already on saving another resource. + if (pImpl->saveModule) { + if (remove) delete(a); + return Result::InsufficientCondition; +diff --git a/thirdparty/thorvg/src/renderer/tvgScene.cpp b/thirdparty/thorvg/src/renderer/tvgScene.cpp +index f5809cf93b..ce169d33ba 100644 +--- a/thirdparty/thorvg/src/renderer/tvgScene.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgScene.cpp +@@ -20,15 +20,32 @@ + * SOFTWARE. + */ + ++#include + #include "tvgScene.h" + ++/************************************************************************/ ++/* Internal Class Implementation */ ++/************************************************************************/ ++ ++Result Scene::Impl::resetEffects() ++{ ++ if (effects) { ++ for (auto e = effects->begin(); e < effects->end(); ++e) { ++ delete(*e); ++ } ++ delete(effects); ++ effects = nullptr; ++ } ++ return Result::Success; ++} ++ ++ + /************************************************************************/ + /* External Class Implementation */ + /************************************************************************/ + + Scene::Scene() : pImpl(new Impl(this)) + { +- Paint::pImpl->id = TVG_CLASS_ID_SCENE; + } + + +@@ -44,9 +61,15 @@ unique_ptr Scene::gen() noexcept + } + + +-uint32_t Scene::identifier() noexcept ++TVG_DEPRECATED uint32_t Scene::identifier() noexcept ++{ ++ return (uint32_t) Type::Scene; ++} ++ ++ ++Type Scene::type() const noexcept + { +- return TVG_CLASS_ID_SCENE; ++ return Type::Scene; + } + + +@@ -54,7 +77,11 @@ Result Scene::push(unique_ptr paint) noexcept + { + auto p = paint.release(); + if (!p) return Result::MemoryCorruption; +- PP(p)->ref(); ++ P(p)->ref(); ++ ++ //Relocated the paint to the current scene space ++ P(p)->renderFlag |= RenderUpdateFlag::Transform; ++ + pImpl->paints.push_back(p); + + return Result::Success; +@@ -79,3 +106,34 @@ list& Scene::paints() noexcept + { + return pImpl->paints; + } ++ ++ ++Result Scene::push(SceneEffect effect, ...) noexcept ++{ ++ if (effect == SceneEffect::ClearAll) return pImpl->resetEffects(); ++ ++ if (!pImpl->effects) pImpl->effects = new Array; ++ ++ va_list args; ++ va_start(args, effect); ++ ++ RenderEffect* re = nullptr; ++ ++ switch (effect) { ++ case SceneEffect::GaussianBlur: { ++ re = RenderEffectGaussianBlur::gen(args); ++ break; ++ } ++ case SceneEffect::DropShadow: { ++ re = RenderEffectDropShadow::gen(args); ++ break; ++ } ++ default: break; ++ } ++ ++ if (!re) return Result::InvalidArguments; ++ ++ pImpl->effects->push(re); ++ ++ return Result::Success; ++} +diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h +index 8b1981edfa..7972ae33fb 100644 +--- a/thirdparty/thorvg/src/renderer/tvgScene.h ++++ b/thirdparty/thorvg/src/renderer/tvgScene.h +@@ -23,10 +23,9 @@ + #ifndef _TVG_SCENE_H_ + #define _TVG_SCENE_H_ + +-#include ++#include "tvgMath.h" + #include "tvgPaint.h" + +- + struct SceneIterator : Iterator + { + list* paints; +@@ -61,8 +60,10 @@ struct Scene::Impl + list paints; + RenderData rd = nullptr; + Scene* scene = nullptr; +- uint8_t opacity; //for composition +- bool needComp = false; //composite or not ++ RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; ++ Array* effects = nullptr; ++ uint8_t opacity; //for composition ++ bool needComp = false; //composite or not + + Impl(Scene* s) : scene(s) + { +@@ -70,6 +71,8 @@ struct Scene::Impl + + ~Impl() + { ++ resetEffects(); ++ + for (auto paint : paints) { + if (P(paint)->unref() == 0) delete(paint); + } +@@ -83,12 +86,15 @@ struct Scene::Impl + { + if (opacity == 0 || paints.empty()) return false; + ++ //post effects requires composition ++ if (effects) return true; ++ + //Masking may require composition (even if opacity == 255) + auto compMethod = scene->composite(nullptr); + if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true; + + //Blending may require composition (even if opacity == 255) +- if (scene->blend() != BlendMethod::Normal) return true; ++ if (PP(scene)->blendMethod != BlendMethod::Normal) return true; + + //Half translucent requires intermediate composition. + if (opacity == 255) return false; +@@ -96,40 +102,35 @@ struct Scene::Impl + //If scene has several children or only scene, it may require composition. + //OPTIMIZE: the bitmap type of the picture would not need the composition. + //OPTIMIZE: a single paint of a scene would not need the composition. +- if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false; ++ if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false; + + return true; + } + +- RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) ++ RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) + { ++ this->vport = renderer->viewport(); ++ + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, +- It must do intermeidate composition with that opacity value. */ ++ It must do intermediate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } +- +- if (clipper) { +- Array 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) + { +- Compositor* cmp = nullptr; ++ RenderCompositor* cmp = nullptr; + auto ret = true; + ++ renderer->blend(PP(scene)->blendMethod); ++ + if (needComp) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, opacity); +@@ -139,7 +140,16 @@ struct Scene::Impl + ret &= paint->pImpl->render(renderer); + } + +- if (cmp) renderer->endComposite(cmp); ++ if (cmp) { ++ //Apply post effects if any. ++ if (effects) { ++ auto direct = effects->count == 1 ? true : false; ++ for (auto e = effects->begin(); e < effects->end(); ++e) { ++ renderer->effect(cmp, *e, opacity, direct); ++ } ++ } ++ renderer->endComposite(cmp); ++ } + + return ret; + } +@@ -163,7 +173,23 @@ struct Scene::Impl + if (y2 < region.y + region.h) y2 = (region.y + region.h); + } + +- return {x1, y1, (x2 - x1), (y2 - y1)}; ++ //Extends the render region if post effects require ++ int32_t ex = 0, ey = 0, ew = 0, eh = 0; ++ if (effects) { ++ for (auto e = effects->begin(); e < effects->end(); ++e) { ++ auto effect = *e; ++ if (effect->rd || renderer->prepare(effect)) { ++ ex = std::min(ex, effect->extend.x); ++ ey = std::min(ey, effect->extend.y); ++ ew = std::max(ew, effect->extend.w); ++ eh = std::max(eh, effect->extend.h); ++ } ++ } ++ } ++ ++ auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh}; ++ ret.intersect(this->vport); ++ return ret; + } + + bool bounds(float* px, float* py, float* pw, float* ph, bool stroking) +@@ -198,10 +224,12 @@ struct Scene::Impl + 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) { + auto cdup = paint->duplicate(); +@@ -209,7 +237,9 @@ struct Scene::Impl + dup->paints.push_back(cdup); + } + +- return ret; ++ if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?"); ++ ++ return scene; + } + + void clear(bool free) +@@ -224,6 +254,8 @@ struct Scene::Impl + { + return new SceneIterator(&paints); + } ++ ++ Result resetEffects(); + }; + + #endif //_TVG_SCENE_H_ +diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp +index 3b9293a00e..269d951f05 100644 +--- a/thirdparty/thorvg/src/renderer/tvgShape.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgShape.cpp +@@ -34,7 +34,6 @@ + + Shape :: Shape() : pImpl(new Impl(this)) + { +- Paint::pImpl->id = TVG_CLASS_ID_SHAPE; + } + + +@@ -52,7 +51,13 @@ unique_ptr Shape::gen() noexcept + + uint32_t Shape::identifier() noexcept + { +- return TVG_CLASS_ID_SHAPE; ++ return (uint32_t) Type::Shape; ++} ++ ++ ++Type Shape::type() const noexcept ++{ ++ return Type::Shape; + } + + +@@ -151,14 +156,14 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept + } + + +-Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept ++TVG_DEPRECATED Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept + { + //just circle + if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius); + + const float arcPrecision = 1e-5f; +- startAngle = mathDeg2Rad(startAngle); +- sweep = mathDeg2Rad(sweep); ++ startAngle = deg2rad(startAngle); ++ sweep = deg2rad(sweep); + + auto nCurves = static_cast(fabsf(sweep / MATH_PI2)); + if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves; +@@ -409,12 +414,6 @@ Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept + } + + +-bool Shape::strokeTrim(float* begin, float* end) const noexcept +-{ +- return pImpl->strokeTrim(begin, end); +-} +- +- + Result Shape::fill(FillRule r) noexcept + { + pImpl->rs.rule = r; +diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h +index ebc0b304ab..42f8152060 100644 +--- a/thirdparty/thorvg/src/renderer/tvgShape.h ++++ b/thirdparty/thorvg/src/renderer/tvgShape.h +@@ -34,6 +34,7 @@ struct Shape::Impl + RenderData rd = nullptr; //engine data + Shape* shape; + uint8_t flag = RenderUpdateFlag::None; ++ + uint8_t opacity; //for composition + bool needComp = false; //composite or not + +@@ -50,14 +51,18 @@ struct Shape::Impl + + bool render(RenderMethod* renderer) + { +- Compositor* cmp = nullptr; +- bool ret; ++ if (!rd) return false; ++ ++ RenderCompositor* cmp = nullptr; ++ ++ renderer->blend(PP(shape)->blendMethod); + + if (needComp) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, opacity); + } +- ret = renderer->renderShape(rd); ++ ++ auto ret = renderer->renderShape(rd); + if (cmp) renderer->endComposite(cmp); + return ret; + } +@@ -78,7 +83,7 @@ struct Shape::Impl + auto method = shape->composite(&target); + if (!target || method == CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) { +- if (target->identifier() == TVG_CLASS_ID_SHAPE) { ++ if (target->type() == Type::Shape) { + auto shape = static_cast(target); + if (!shape->fill()) { + uint8_t r, g, b, a; +@@ -95,13 +100,13 @@ struct Shape::Impl + return true; + } + +- RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) ++ RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + if (static_cast(pFlag | flag) == RenderUpdateFlag::None) return rd; + + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, +- It must do intermeidate composition with that opacity value. */ ++ It must do intermediate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } +@@ -113,6 +118,7 @@ struct Shape::Impl + + RenderRegion bounds(RenderMethod* renderer) + { ++ if (!rd) return {0, 0, 0, 0}; + return renderer->region(rd); + } + +@@ -213,26 +219,9 @@ struct Shape::Impl + rs.stroke = new RenderStroke(); + } + +- if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) && ++ if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && + rs.stroke->trim.simultaneous == simultaneous) return; + +- 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.end = end; + rs.stroke->trim.simultaneous = simultaneous; +@@ -359,47 +348,56 @@ struct Shape::Impl + this->flag |= flag; + } + +- Paint* duplicate() ++ Paint* duplicate(Paint* ret) + { +- auto ret = Shape::gen().release(); +- auto dup = ret->pImpl; ++ auto shape = static_cast(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; + + //Color + memcpy(dup->rs.color, rs.color, sizeof(rs.color)); +- dup->flag = RenderUpdateFlag::Color; + + //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 + if (rs.stroke) { +- dup->rs.stroke = new RenderStroke(); ++ if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke; + *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(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 +- 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() +diff --git a/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp +index d762492f22..6c4b6da1de 100644 +--- a/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp +@@ -82,7 +82,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept + Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept + { + #ifdef THORVG_SW_RASTER_SUPPORT +- if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) { ++ if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { + return Result::InsufficientCondition; + } + +@@ -98,7 +98,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t + ImageLoader::cs = static_cast(cs); + + //Paints must be updated again with this new target. +- Canvas::pImpl->status = Status::Damanged; ++ Canvas::pImpl->status = Status::Damaged; + + return Result::Success; + #endif +diff --git a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h +index 93f8481707..58918e88f0 100644 +--- a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h ++++ b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h +@@ -23,8 +23,6 @@ + #ifndef _TVG_TASK_SCHEDULER_H_ + #define _TVG_TASK_SCHEDULER_H_ + +-#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR +- + #include + #include + +diff --git a/thirdparty/thorvg/src/renderer/tvgText.cpp b/thirdparty/thorvg/src/renderer/tvgText.cpp +index 4b5eb35ce5..b324b95049 100644 +--- a/thirdparty/thorvg/src/renderer/tvgText.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgText.cpp +@@ -35,9 +35,8 @@ + /************************************************************************/ + + +-Text::Text() : pImpl(new Impl) ++Text::Text() : pImpl(new Impl(this)) + { +- Paint::pImpl->id = TVG_CLASS_ID_TEXT; + } + + +@@ -95,20 +94,13 @@ Result Text::unload(const std::string& path) 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 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)); + } + + +@@ -118,7 +110,7 @@ unique_ptr Text::gen() noexcept + } + + +-uint32_t Text::identifier() noexcept ++Type Text::type() const noexcept + { +- return TVG_CLASS_ID_TEXT; ++ return Type::Text; + } +diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h +index c56ce8b878..11e01b58ce 100644 +--- a/thirdparty/thorvg/src/renderer/tvgText.h ++++ b/thirdparty/thorvg/src/renderer/tvgText.h +@@ -26,37 +26,27 @@ + #include + #include "tvgShape.h" + #include "tvgFill.h" +- +-#ifdef THORVG_TTF_LOADER_SUPPORT +- #include "tvgTtfLoader.h" +-#else +- #include "tvgLoader.h" +-#endif ++#include "tvgLoader.h" + + struct Text::Impl + { + FontLoader* loader = nullptr; +- Shape* paint = nullptr; ++ Text* paint; ++ Shape* shape; + char* utf8 = nullptr; + float fontSize; + bool italic = false; + bool changed = false; + +- ~Impl() +- { +- free(utf8); +- LoaderMgr::retrieve(loader); +- delete(paint); +- } +- +- Result fill(uint8_t r, uint8_t g, uint8_t b) ++ Impl(Text* p) : paint(p), shape(Shape::gen().release()) + { +- return paint->fill(r, g, b); + } + +- Result fill(Fill* f) ++ ~Impl() + { +- return paint->fill(cast(f)); ++ free(utf8); ++ LoaderMgr::retrieve(loader); ++ delete(shape); + } + + Result text(const char* utf8) +@@ -74,6 +64,11 @@ struct Text::Impl + auto loader = LoaderMgr::loader(name); + if (!loader) return Result::InsufficientCondition; + ++ if (style && strstr(style, "italic")) italic = true; ++ else italic = false; ++ ++ fontSize = size; ++ + //Same resource has been loaded. + if (this->loader == loader) { + this->loader->sharing--; //make it sure the reference counting. +@@ -83,52 +78,44 @@ struct Text::Impl + } + this->loader = static_cast(loader); + +- if (!paint) paint = Shape::gen().release(); +- +- fontSize = size; +- if (style && strstr(style, "italic")) italic = true; + changed = true; + return Result::Success; + } + + 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) + { +- if (paint) return PP(paint)->render(renderer); +- return true; ++ if (!loader) return true; ++ renderer->blend(PP(paint)->blendMethod); ++ return PP(shape)->render(renderer); + } + + bool load() + { + if (!loader) return false; + ++ loader->request(shape, utf8); + //reload + if (changed) { +- loader->request(paint, utf8, italic); + loader->read(); + changed = false; + } +- if (paint) { +- loader->resize(paint, fontSize, fontSize); +- return true; +- } +- return false; ++ return loader->transform(shape, fontSize, italic); + } + +- RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) ++ RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) + { + if (!load()) return nullptr; + + //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; +- if (fill->identifier() == TVG_CLASS_ID_LINEAR) { ++ if (fill->type() == Type::LinearGradient) { + P(static_cast(fill))->x1 *= scale; + P(static_cast(fill))->y1 *= scale; + P(static_cast(fill))->x2 *= scale; +@@ -142,23 +129,25 @@ struct Text::Impl + P(static_cast(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) + { +- 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; + } + +- Paint* duplicate() ++ Paint* duplicate(Paint* ret) + { ++ if (ret) TVGERR("RENDERER", "TODO: duplicate()"); ++ + load(); + +- auto ret = Text::gen().release(); +- auto dup = ret->pImpl; +- if (paint) dup->paint = static_cast(paint->duplicate()); ++ auto text = Text::gen().release(); ++ auto dup = text->pImpl; ++ P(shape)->duplicate(dup->shape); + + if (loader) { + dup->loader = loader; +@@ -169,7 +158,7 @@ struct Text::Impl + dup->italic = italic; + dup->fontSize = fontSize; + +- return ret; ++ return text; + } + + Iterator* iterator() +diff --git a/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp +index 067e35b1f0..991f73fc54 100644 +--- a/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp ++++ b/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp +@@ -40,7 +40,7 @@ struct WgCanvas::Impl + /************************************************************************/ + + #ifdef THORVG_WG_RASTER_SUPPORT +-WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl) ++WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr) + #else + WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr) + #endif +@@ -50,14 +50,17 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr) + + WgCanvas::~WgCanvas() + { +- delete pImpl; ++#ifdef THORVG_WG_RASTER_SUPPORT ++ auto renderer = static_cast(Canvas::pImpl->renderer); ++ renderer->target(nullptr, 0, 0); ++#endif + } + + +-Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept ++Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, void* device) noexcept + { + #ifdef THORVG_WG_RASTER_SUPPORT +- if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) { ++ if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { + return Result::InsufficientCondition; + } + +@@ -67,12 +70,12 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) n + auto renderer = static_cast(Canvas::pImpl->renderer); + if (!renderer) return Result::MemoryCorruption; + +- if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h)) return Result::Unknown; ++ if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h, (WGPUDevice)device)) return Result::Unknown; + Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; + renderer->viewport(Canvas::pImpl->vport); + + //Paints must be updated again with this new target. +- Canvas::pImpl->status = Status::Damanged; ++ Canvas::pImpl->status = Status::Damaged; + + return Result::Success; + #endif +diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh +index 1a68daf3c5..f9953f2fc9 100755 +--- a/thirdparty/thorvg/update-thorvg.sh ++++ b/thirdparty/thorvg/update-thorvg.sh +@@ -1,16 +1,22 @@ + #!/bin/bash -e + +-VERSION=0.14.2 ++VERSION=0.15.5 ++# Uncomment and set a git hash to use specific commit instead of tag. ++#GIT_COMMIT= + +-cd thirdparty/thorvg/ || true ++pushd "$(dirname "$0")" + rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ + + mkdir tmp/ && pushd tmp/ + + # Release +-curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz +-# Current Github main branch tip +-#curl -L -O https://github.com/thorvg/thorvg/archive/refs/heads/main.tar.gz ++if [ ! -z "$GIT_COMMIT" ]; then ++ echo "Updating ThorVG to commit:" $GIT_COMMIT ++ curl -L -O https://github.com/thorvg/thorvg/archive/$GIT_COMMIT.tar.gz ++else ++ echo "Updating ThorVG to tagged release:" $VERSION ++ curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz ++fi + + tar --strip-components=1 -xvf *.tar.gz + rm *.tar.gz +@@ -70,4 +76,4 @@ cp -rv src/loaders/jpg ../src/loaders/ + + popd + rm -rf tmp +- ++popd