diff --git a/modules/svg/SCsub b/modules/svg/SCsub index af8f6c14f4e..83321cf4493 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -14,7 +14,6 @@ thirdparty_dir = "#thirdparty/thorvg/" thirdparty_sources = [ # common "src/common/tvgCompressor.cpp", - "src/common/tvgLines.cpp", "src/common/tvgMath.cpp", "src/common/tvgStr.cpp", # SVG parser @@ -52,6 +51,7 @@ thirdparty_sources = [ "src/renderer/sw_engine/tvgSwImage.cpp", "src/renderer/sw_engine/tvgSwMath.cpp", "src/renderer/sw_engine/tvgSwMemPool.cpp", + "src/renderer/sw_engine/tvgSwPostEffect.cpp", "src/renderer/sw_engine/tvgSwRaster.cpp", "src/renderer/sw_engine/tvgSwRenderer.cpp", "src/renderer/sw_engine/tvgSwRle.cpp", diff --git a/thirdparty/README.md b/thirdparty/README.md index 1e11c6c9725..117845dd0ad 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -916,7 +916,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.14.10 (366dcd72850c360b49e841e568fc5a154d7cce9e, 2024) +- Version: 0.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS index e00e91a6967..a15f3262a82 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 fc2faca29f9..6df6f52d047 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.10" +#define THORVG_VERSION_STRING "0.15.5" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index 4303092a5ea..1ee898ca6ff 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. Note that ClipPath only supports the Shape type. + ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type. @deprecated Use Paint::clip() instead. AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value. InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 @@ -178,24 +178,46 @@ enum class CompositeMethod * * @see Paint::blend() * - * @note Experimental API + * @since 0.15 */ enum class BlendMethod : uint8_t { Normal = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D - Add, ///< Simply adds pixel values of one layer with the other. (S + D) - Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) Multiply, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) + Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) Overlay, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) - Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) - Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) - SrcOver, ///< Replace the bottom layer with the top layer. Darken, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) Lighten, ///< Only has the opposite action of Darken Only. max(S, D) ColorDodge, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) ColorBurn, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S HardLight, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) - SoftLight ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) + SoftLight, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) + Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) + Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) + Hue, ///< Reserved. Not supported. + Saturation, ///< Reserved. Not supported. + Color, ///< Reserved. Not supported. + Luminosity, ///< Reserved. Not supported. + Add, ///< Simply adds pixel values of one layer with the other. (S + D) + HardMix ///< Reserved. Not supported. +}; + + +/** + * @brief Enumeration that defines methods used for Scene Effects. + * + * This enum provides options to apply various post-processing effects to a scene. + * Scene effects are typically applied to modify the final appearance of a rendered scene, such as blurring. + * + * @see Scene::push(SceneEffect effect, ...) + * + * @note Experimental API + */ +enum class SceneEffect : uint8_t +{ + ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state. + GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} + DropShadow ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]} }; @@ -206,7 +228,29 @@ enum class CanvasEngine { Sw = (1 << 1), ///< CPU rasterizer. Gl = (1 << 2), ///< OpenGL rasterizer. - Wg = (1 << 3), ///< WebGPU rasterizer. (Experimental API) + Wg = (1 << 3), ///< WebGPU rasterizer. @since 0.15 +}; + + +/** + * @brief Enumeration specifying the ThorVG class type value. + * + * ThorVG's drawing objects can return class type values, allowing you to identify the specific class of each object. + * + * @see Paint::type() + * @see Fill::type() + * + * @note Experimental API + */ +enum class Type : uint8_t +{ + Undefined = 0, ///< Unkown class + Shape, ///< Shape class + Scene, ///< Scene class + Picture, ///< Picture class + Text, ///< Text class + LinearGradient = 10, ///< LinearGradient class + RadialGradient ///< RadialGradient class }; @@ -274,7 +318,7 @@ public: /** * @brief Sets the values by which the object is moved in a two-dimensional space. * - * The origin of the coordinate system is in the upper left corner of the canvas. + * The origin of the coordinate system is in the upper-left corner of the canvas. * The horizontal and vertical axes point to the right and down, respectively. * * @param[in] x The value of the horizontal shift. @@ -312,7 +356,6 @@ public: * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. - * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath) */ Result opacity(uint8_t o) noexcept; @@ -324,6 +367,20 @@ public: */ Result composite(std::unique_ptr 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. * @@ -386,22 +443,15 @@ public: CompositeMethod composite(const Paint** target) const noexcept; /** - * @brief Retrieves the current blending method applied to the paint object. + * @brief Returns the ID value of this class. * - * @return The currently set blending method. + * This method can be used to check the current concrete instance type. * - * @note Experimental API + * @return The class type ID of the Paint instance. + * + * @since Experimental API */ - BlendMethod blend() const noexcept; - - /** - * @brief Return the unique id value of the paint instance. - * - * This method can be called for checking the current concrete instance type. - * - * @return The type id of the Paint instance. - */ - uint32_t identifier() const noexcept; + virtual Type type() const noexcept = 0; /** * @brief Unique ID of this instance. @@ -412,6 +462,11 @@ public: */ uint32_t id = 0; + /** + * @see Paint::type() + */ + TVG_DEPRECATED uint32_t identifier() const noexcept; + _TVG_DECLARE_PRIVATE(Paint); }; @@ -503,13 +558,20 @@ public: Fill* duplicate() const noexcept; /** - * @brief Return the unique id value of the Fill instance. + * @brief Returns the ID value of this class. * - * This method can be called for checking the current concrete instance type. + * This method can be used to check the current concrete instance type. * - * @return The type id of the Fill instance. + * @return The class 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); }; @@ -538,7 +600,7 @@ public: * * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. * - * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). + * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). * @see Canvas::sync() * * @note Experimental API @@ -614,7 +676,7 @@ public: * @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync(). * * @note When resetting the target, the viewport will also be reset to the target size. - * @note Experimental API + * @since 0.15 */ virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; @@ -686,13 +748,20 @@ public: static std::unique_ptr gen() noexcept; /** - * @brief Return the unique id value of this class. + * @brief Returns the ID value of this class. * - * This method can be referred for identifying the LinearGradient class type. + * This method can be used to check the current concrete instance type. * - * @return The type id of the LinearGradient class. + * @return The class type ID of the LinearGradient instance. + * + * @since Experimental API */ - static uint32_t identifier() noexcept; + Type type() const noexcept override; + + /** + * @see LinearGradient::type() + */ + TVG_DEPRECATED static uint32_t identifier() noexcept; _TVG_DECLARE_PRIVATE(LinearGradient); }; @@ -744,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 referred for identifying the RadialGradient class type. + * This method can be used to check the current concrete instance type. * - * @return The type id of the RadialGradient class. + * @return The class type ID of the LinearGradient instance. + * + * @since Experimental API */ - static uint32_t identifier() noexcept; + Type type() const noexcept override; + + /** + * @see RadialGradient::type() + */ + TVG_DEPRECATED static uint32_t identifier() noexcept; _TVG_DECLARE_PRIVATE(RadialGradient); }; @@ -774,11 +850,11 @@ public: ~Shape(); /** - * @brief Resets the properties of the shape path. + * @brief Resets the shape path. * - * The transformation matrix, the color, the fill and the stroke properties are retained. + * The transformation matrix, color, fill, and stroke properties are retained. * - * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect. + * @note The memory where the path data is stored is not deallocated at this stage to allow for caching. */ Result reset() noexcept; @@ -836,15 +912,15 @@ public: * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. * - * The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments. + * The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments. * * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. * * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater * than @p w/2 the current point is set to (@p x + @p w/2, @p y) * - * @param[in] x The horizontal coordinate of the upper left corner of the rectangle. - * @param[in] y The vertical coordinate of the upper left corner of the rectangle. + * @param[in] x The horizontal coordinate of the upper-left corner of the rectangle. + * @param[in] y The vertical coordinate of the upper-left corner of the rectangle. * @param[in] w The width of the rectangle. * @param[in] h The height of the rectangle. * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. @@ -886,7 +962,7 @@ public: * * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius). */ - Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept; + TVG_DEPRECATED Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept; /** * @brief Appends a given sub-path to the path. @@ -999,7 +1075,6 @@ public: * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. - * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath) */ Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; @@ -1130,18 +1205,6 @@ public: */ float strokeMiterlimit() const noexcept; - /** - * @brief Gets the trim of the stroke along the defined path segment. - * - * @param[out] begin The starting point of the segment to display along the path. - * @param[out] end Specifies the end of the segment to display along the path. - * - * @return @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise. - * - * @note Experimental API - */ - bool strokeTrim(float* begin, float* end) const noexcept; - /** * @brief Creates a new Shape object. * @@ -1150,13 +1213,20 @@ public: static std::unique_ptr gen() noexcept; /** - * @brief Return the unique id value of this class. + * @brief Returns the ID value of this class. * - * This method can be referred for identifying the Shape class type. + * This method can be used to check the current concrete instance type. * - * @return The type id of the Shape class. + * @return The class type ID of the Shape instance. + * + * @since Experimental API */ - static uint32_t identifier() noexcept; + Type type() const noexcept override; + + /** + * @see Shape::type() + */ + TVG_DEPRECATED static uint32_t identifier() noexcept; _TVG_DECLARE_PRIVATE(Shape); }; @@ -1213,7 +1283,7 @@ public: * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. * @retval Result::NonSupport When trying to load a file with an unknown extension. * - * @warning: It's the user responsibility to release the @p data memory. + * @warning It's the user responsibility to release the @p data memory. * * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. * @since 0.5 @@ -1251,9 +1321,9 @@ public: * @param[in] data A pointer to a memory location where the content of the picture raw data is stored. * @param[in] w The width of the image @p data in pixels. * @param[in] h The height of the image @p data in pixels. + * @param[in] premultiplied If @c true, the given image data is alpha-premultiplied. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. * - * @note It expects premultiplied alpha data. * @since 0.9 */ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; @@ -1281,13 +1351,20 @@ public: static std::unique_ptr 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); @@ -1331,9 +1408,9 @@ public: * * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. * - * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). + * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). * @see Canvas::sync() - * @see Scene::push() + * @see Scene::push(std::unique_ptr paint) * @see Scene::clear() * * @note Experimental API @@ -1352,6 +1429,20 @@ public: */ Result clear(bool free = true) noexcept; + /** + * @brief Apply a post-processing effect to the scene. + * + * This function adds a specified scene effect, such as clearing all effects or applying a Gaussian blur, + * to the scene after it has been rendered. Multiple effects can be applied in sequence. + * + * @param[in] effect The scene effect to apply. Options are defined in the SceneEffect enum. + * For example, use SceneEffect::GaussianBlur to apply a blur with specific parameters. + * @param[in] ... Additional variadic parameters required for certain effects (e.g., sigma and direction for GaussianBlur). + * + * @note Experimental API + */ + Result push(SceneEffect effect, ...) noexcept; + /** * @brief Creates a new Scene object. * @@ -1360,13 +1451,20 @@ public: static std::unique_ptr gen() noexcept; /** - * @brief Return the unique id value of this class. + * @brief Returns the ID value of this class. * - * This method can be referred for identifying the Scene class type. + * This method can be used to check the current concrete instance type. * - * @return The type id of the Scene class. + * @return The class type ID of the Scene instance. + * + * @since Experimental API */ - static uint32_t identifier() noexcept; + Type type() const noexcept override; + + /** + * @see Scene::type() + */ + TVG_DEPRECATED static uint32_t identifier() noexcept; _TVG_DECLARE_PRIVATE(Scene); }; @@ -1377,7 +1475,7 @@ public: * * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. * - * @note Experimental API + * @since 0.15 */ class TVG_API Text final : public Paint { @@ -1422,7 +1520,7 @@ public: * * @see Text::font() * - * @note Experimental API + * @since 0.15 */ Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept; @@ -1434,9 +1532,9 @@ public: * @param[in] f The unique pointer to the gradient fill. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. - * @note Experimental API - * * @see Text::font() + * + * @since 0.15 */ Result fill(std::unique_ptr f) noexcept; @@ -1452,9 +1550,9 @@ public: * @retval Result::InvalidArguments In case the @p path is invalid. * @retval Result::NonSupport When trying to load a file with an unknown extension. * - * @note Experimental API - * * @see Text::unload(const std::string& path) + * + * @since 0.15 */ static Result load(const std::string& path) noexcept; @@ -1475,13 +1573,13 @@ public: * @retval Result::NonSupport When trying to load a file with an unsupported extension. * @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded. * - * @warning: It's the user responsibility to release the @p data memory. + * @warning It's the user responsibility to release the @p data memory. * * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data. * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. - * @note Experimental API - * * @see Text::font(const char* name, float size, const char* style) + * + * @note 0.15 */ static Result load(const char* name, const char* data, uint32_t size, const std::string& mimeType = "ttf", bool copy = false) noexcept; @@ -1495,9 +1593,9 @@ public: * @retval Result::InsufficientCondition Fails if the loader is not initialized. * * @note If the font data is currently in use, it will not be immediately unloaded. - * @note Experimental API - * * @see Text::load(const std::string& path) + * + * @since 0.15 */ static Result unload(const std::string& path) noexcept; @@ -1506,18 +1604,20 @@ public: * * @return A new Text object. * - * @note Experimental API + * @since 0.15 */ static std::unique_ptr 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); }; @@ -1616,8 +1716,6 @@ public: * * @brief A class for the rendering graphic elements with a GL raster engine. * - * @warning Please do not use it. This class is not fully supported yet. - * * @since 0.14 */ class TVG_API GlCanvas final : public Canvas @@ -1666,7 +1764,7 @@ public: * * @warning Please do not use it. This class is not fully supported yet. * - * @note Experimental API + * @since 0.15 */ class TVG_API WgCanvas final : public Canvas { @@ -1680,6 +1778,7 @@ public: * @param[in] surface WGPUSurface, handle to a presentable surface. * @param[in] w The width of the surface. * @param[in] h The height of the surface. + * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally. * * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::NonSupport In case the wg engine is not supported. @@ -1689,14 +1788,14 @@ public: * @see Canvas::viewport() * @see Canvas::sync() */ - Result target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept; + Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept; /** * @brief Creates a new WgCanvas object. * * @return A new WgCanvas object. * - * @note Experimental API + * @since 0.15 */ static std::unique_ptr gen() noexcept; @@ -1752,7 +1851,7 @@ public: * * @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error. * - * @note Experimental API + * @since 0.15 */ static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept; @@ -1857,6 +1956,7 @@ public: * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin. * @note If a marker has been specified, its range will be disregarded. * @see LottieAnimation::segment(const char* marker) + * * @note Experimental API */ Result segment(float begin, float end) noexcept; @@ -1895,7 +1995,7 @@ public: * It's useful when you need to save the composed scene or image from a paint object and recreate it later. * * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment. - * If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result. + * If it doesn't support the file format, the save() method returns the @c Result::NonSupport result. * * Once you export a paint to the file successfully, you can recreate it using the Picture class. * diff --git a/thirdparty/thorvg/src/common/tvgCompressor.cpp b/thirdparty/thorvg/src/common/tvgCompressor.cpp index aebe9a4ef16..714f21e07c9 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; } diff --git a/thirdparty/thorvg/src/common/tvgLines.cpp b/thirdparty/thorvg/src/common/tvgLines.cpp deleted file mode 100644 index 9d704900a50..00000000000 --- 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 d900782b562..00000000000 --- 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/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp index c03b54e5f8b..cb7f24ff403 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) +{ + /* 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,7 +114,7 @@ float mathAtan2(float y, float x) } -bool mathInverse(const Matrix* m, Matrix* out) +bool inverse(const Matrix* m, Matrix* out) { auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) - m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) + @@ -60,7 +137,7 @@ bool mathInverse(const Matrix* m, Matrix* out) } -bool mathIdentity(const Matrix* m) +bool identity(const Matrix* m) { if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f || m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f || @@ -71,7 +148,7 @@ bool mathIdentity(const Matrix* m) } -void mathRotate(Matrix* m, float degree) +void rotate(Matrix* m, float degree) { if (degree == 0.0f) return; @@ -108,9 +185,9 @@ Matrix operator*(const Matrix& lhs, const Matrix& rhs) bool operator==(const Matrix& lhs, const Matrix& rhs) { - if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) || - !mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) || - !mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) { + if (!tvg::equal(lhs.e11, rhs.e11) || !tvg::equal(lhs.e12, rhs.e12) || !tvg::equal(lhs.e13, rhs.e13) || + !tvg::equal(lhs.e21, rhs.e21) || !tvg::equal(lhs.e22, rhs.e22) || !tvg::equal(lhs.e23, rhs.e23) || + !tvg::equal(lhs.e31, rhs.e31) || !tvg::equal(lhs.e32, rhs.e32) || !tvg::equal(lhs.e33, rhs.e33)) { return false; } return true; @@ -133,9 +210,165 @@ Point operator*(const Point& pt, const Matrix& m) return {tx, ty}; } -uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t) + +Point normal(const Point& p1, const Point& p2) +{ + auto dir = p2 - p1; + auto len = length(dir); + if (tvg::zero(len)) return {}; + + auto unitDir = dir / len; + return {-unitDir.y, unitDir.x}; +} + + +float Line::length() const +{ + return _lineLength(pt1, pt2); +} + + +void Line::split(float at, Line& left, Line& right) const +{ + auto len = length(); + auto dx = ((pt2.x - pt1.x) / len) * at; + auto dy = ((pt2.y - pt1.y) / len) * at; + left.pt1 = pt1; + left.pt2.x = left.pt1.x + dx; + left.pt2.y = left.pt1.y + dy; + right.pt1 = left.pt2; + right.pt2 = pt2; +} + + +void Bezier::split(Bezier& left, Bezier& right) const +{ + auto c = (ctrl1.x + ctrl2.x) * 0.5f; + left.ctrl1.x = (start.x + ctrl1.x) * 0.5f; + right.ctrl2.x = (ctrl2.x + end.x) * 0.5f; + left.start.x = start.x; + right.end.x = end.x; + left.ctrl2.x = (left.ctrl1.x + c) * 0.5f; + right.ctrl1.x = (right.ctrl2.x + c) * 0.5f; + left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f; + + c = (ctrl1.y + ctrl2.y) * 0.5f; + left.ctrl1.y = (start.y + ctrl1.y) * 0.5f; + right.ctrl2.y = (ctrl2.y + end.y) * 0.5f; + left.start.y = start.y; + right.end.y = end.y; + left.ctrl2.y = (left.ctrl1.y + c) * 0.5f; + right.ctrl1.y = (right.ctrl2.y + c) * 0.5f; + left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f; +} + + +void Bezier::split(float at, Bezier& left, Bezier& right) const +{ + right = *this; + auto t = right.at(at, right.length()); + right.split(t, left); +} + + +float Bezier::length() const +{ + return _bezLength(*this, _lineLength); +} + + +float Bezier::lengthApprox() const +{ + return _bezLength(*this, _lineLengthApprox); +} + + +void Bezier::split(float t, Bezier& left) +{ + left.start = start; + + left.ctrl1.x = start.x + t * (ctrl1.x - start.x); + left.ctrl1.y = start.y + t * (ctrl1.y - start.y); + + left.ctrl2.x = ctrl1.x + t * (ctrl2.x - ctrl1.x); //temporary holding spot + left.ctrl2.y = ctrl1.y + t * (ctrl2.y - ctrl1.y); //temporary holding spot + + ctrl2.x = ctrl2.x + t * (end.x - ctrl2.x); + ctrl2.y = ctrl2.y + t * (end.y - ctrl2.y); + + ctrl1.x = left.ctrl2.x + t * (ctrl2.x - left.ctrl2.x); + ctrl1.y = left.ctrl2.y + t * (ctrl2.y - left.ctrl2.y); + + left.ctrl2.x = left.ctrl1.x + t * (left.ctrl2.x - left.ctrl1.x); + left.ctrl2.y = left.ctrl1.y + t * (left.ctrl2.y - left.ctrl1.y); + + left.end.x = start.x = left.ctrl2.x + t * (ctrl1.x - left.ctrl2.x); + left.end.y = start.y = left.ctrl2.y + t * (ctrl1.y - left.ctrl2.y); +} + + +float Bezier::at(float at, float length) const +{ + return _bezAt(*this, at, length, _lineLength); +} + + +float Bezier::atApprox(float at, float length) const +{ + return _bezAt(*this, at, length, _lineLengthApprox); +} + + +Point Bezier::at(float t) const +{ + Point cur; + auto it = 1.0f - t; + + auto ax = start.x * it + ctrl1.x * t; + auto bx = ctrl1.x * it + ctrl2.x * t; + auto cx = ctrl2.x * it + end.x * t; + ax = ax * it + bx * t; + bx = bx * it + cx * t; + cur.x = ax * it + bx * t; + + float ay = start.y * it + ctrl1.y * t; + float by = ctrl1.y * it + ctrl2.y * t; + float cy = ctrl2.y * it + end.y * t; + ay = ay * it + by * t; + by = by * it + cy * t; + cur.y = ay * it + by * t; + + return cur; +} + + +float Bezier::angle(float t) const +{ + if (t < 0 || t > 1) return 0; + + //derivate + // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * + // t^2) * p2 + t^2 * p3) + float mt = 1.0f - t; + float d = t * t; + float a = -mt * mt; + float b = 1 - 4 * t + 3 * d; + float c = 2 * t - 3 * d; + + Point pt ={a * start.x + b * ctrl1.x + c * ctrl2.x + d * end.x, a * start.y + b * ctrl1.y + c * ctrl2.y + d * end.y}; + pt.x *= 3; + pt.y *= 3; + + return rad2deg(tvg::atan2(pt.y, pt.x)); +} + + +uint8_t lerp(const uint8_t &start, const uint8_t &end, float t) { auto result = static_cast(start + (end - start) * t); - mathClamp(result, 0, 255); + 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 50786754a15..a917998256f 100644 --- a/thirdparty/thorvg/src/common/tvgMath.h +++ b/thirdparty/thorvg/src/common/tvgMath.h @@ -29,47 +29,47 @@ #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 mathClamp(T& v, const T& min, const T& max) +static inline void clamp(T& v, const T& min, const T& max) { if (v < min) v = min; else if (v > max) v = max; @@ -79,27 +79,27 @@ static inline void mathClamp(T& v, const T& min, const T& max) /* Matrix functions */ /************************************************************************/ -void mathRotate(Matrix* m, float degree); -bool mathInverse(const Matrix* m, Matrix* out); -bool mathIdentity(const Matrix* m); +void rotate(Matrix* m, float degree); +bool inverse(const Matrix* m, Matrix* out); +bool identity(const Matrix* m); Matrix operator*(const Matrix& lhs, const Matrix& rhs); bool operator==(const Matrix& lhs, const Matrix& rhs); -static inline bool mathRightAngle(const Matrix& m) +static inline bool rightAngle(const Matrix& m) { - auto radian = fabsf(mathAtan2(m.e21, m.e11)); - if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true; + auto radian = fabsf(tvg::atan2(m.e21, m.e11)); + if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true; return false; } -static inline bool mathSkewed(const Matrix& m) +static inline bool skewed(const Matrix& m) { - return !mathZero(m.e21 + m.e12); + return !tvg::zero(m.e21 + m.e12); } -static inline void mathIdentity(Matrix* m) +static inline void identity(Matrix* m) { m->e11 = 1.0f; m->e12 = 0.0f; @@ -113,14 +113,14 @@ static inline void mathIdentity(Matrix* m) } -static inline void mathScale(Matrix* m, float sx, float sy) +static inline void scale(Matrix* m, float sx, float sy) { m->e11 *= sx; m->e22 *= sy; } -static inline void mathScaleR(Matrix* m, float x, float y) +static inline void scaleR(Matrix* m, float x, float y) { if (x != 1.0f) { m->e11 *= x; @@ -133,14 +133,14 @@ static inline void mathScaleR(Matrix* m, float x, float y) } -static inline void mathTranslate(Matrix* m, float x, float y) +static inline void translate(Matrix* m, float x, float y) { m->e13 += x; m->e23 += y; } -static inline void mathTranslateR(Matrix* m, float x, float y) +static inline void translateR(Matrix* m, float x, float y) { if (x == 0.0f && y == 0.0f) return; m->e13 += (x * m->e11 + y * m->e12); @@ -160,7 +160,7 @@ static inline void operator*=(Matrix& lhs, const Matrix& rhs) } -static inline void mathLog(const Matrix& m) +static inline void log(const Matrix& m) { TVGLOG("COMMON", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m.e11, m.e12, m.e13, m.e21, m.e22, m.e23, m.e31, m.e32, m.e33); } @@ -172,15 +172,21 @@ static inline void mathLog(const Matrix& m) void operator*=(Point& pt, const Matrix& m); Point operator*(const Point& pt, const Matrix& m); +Point normal(const Point& p1, const Point& p2); - -static inline bool mathZero(const Point& p) +static inline float cross(const Point& lhs, const Point& rhs) { - return mathZero(p.x) && mathZero(p.y); + return lhs.x * rhs.y - rhs.x * lhs.y; } -static inline float mathLength(const Point* a, const Point* b) +static inline bool zero(const Point& p) +{ + return tvg::zero(p.x) && tvg::zero(p.y); +} + + +static inline float length(const Point* a, const Point* b) { auto x = b->x - a->x; auto y = b->y - a->y; @@ -192,7 +198,7 @@ static inline float mathLength(const Point* a, const Point* b) } -static inline float mathLength(const Point& a) +static inline float length(const Point& a) { return sqrtf(a.x * a.x + a.y * a.y); } @@ -200,7 +206,7 @@ static inline float mathLength(const Point& a) static inline bool operator==(const Point& lhs, const Point& rhs) { - return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y); + return tvg::equal(lhs.x, rhs.x) && tvg::equal(lhs.y, rhs.y); } @@ -240,32 +246,61 @@ static inline Point operator/(const Point& lhs, const float rhs) } -static inline Point mathNormal(const Point& p1, const Point& p2) -{ - auto dir = p2 - p1; - auto len = mathLength(dir); - if (mathZero(len)) return {}; - - auto unitDir = dir / len; - return {-unitDir.y, unitDir.x}; -} - - -static inline void mathLog(const Point& pt) +static inline void 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 mathLerp(const uint8_t &start, const uint8_t &end, float t); +uint8_t lerp(const uint8_t &start, const uint8_t &end, float t); + +} #endif //_TVG_MATH_H_ diff --git a/thirdparty/thorvg/src/common/tvgStr.cpp b/thirdparty/thorvg/src/common/tvgStr.cpp index 3f189668096..1ebdd41c5e9 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); diff --git a/thirdparty/thorvg/src/common/tvgStr.h b/thirdparty/thorvg/src/common/tvgStr.h index 56fdf86fb2d..6b16b4c7610 100644 --- a/thirdparty/thorvg/src/common/tvgStr.h +++ b/thirdparty/thorvg/src/common/tvgStr.h @@ -29,7 +29,6 @@ namespace tvg { float strToFloat(const char *nPtr, char **endPtr); //convert to float -int str2int(const char* str, size_t n); //convert to integer char* strDuplicate(const char *str, size_t n); //copy the string char* strAppend(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs char* strDirname(const char* path); //return the full directory name diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp index 6c4c8410e82..49c9f6e8aa2 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 fbaaea87431..0db7d2d233f 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 ad8fb5a6995..3d44edac3a1 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 86a46245d98..cb1306b71a3 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 05cbb54c852..85f6d25dc39 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 6f2bd916d5e..92e23698af2 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 492cebe3a31..1ab3043c24e 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; @@ -593,15 +594,15 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f; brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f; - if (mathZero(saturation)) _red = _green = _blue = brightness; + if (tvg::zero(saturation)) _red = _green = _blue = brightness; else { - if (mathEqual(hue, 360.0)) hue = 0.0f; + if (tvg::equal(hue, 360.0)) hue = 0.0f; hue /= 60.0f; v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation)); p = brightness + brightness - v; - if (!mathZero(v)) sv = (v - p) / v; + if (!tvg::zero(v)) sv = (v - p) / v; else sv = 0; i = static_cast(hue); @@ -858,8 +859,8 @@ static Matrix* _parseTransformationMatrix(const char* value) //Transform to signed. points[0] = fmodf(points[0], 360.0f); if (points[0] < 0) points[0] += 360.0f; - auto c = cosf(mathDeg2Rad(points[0])); - auto s = sinf(mathDeg2Rad(points[0])); + auto c = cosf(deg2rad(points[0])); + auto s = sinf(deg2rad(points[0])); if (ptCount == 1) { Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; *matrix *= tmp; @@ -882,12 +883,12 @@ static Matrix* _parseTransformationMatrix(const char* value) *matrix *= tmp; } else if (state == MatrixState::SkewX) { if (ptCount != 1) goto error; - auto deg = tanf(mathDeg2Rad(points[0])); + auto deg = tanf(deg2rad(points[0])); Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 }; *matrix *= tmp; } else if (state == MatrixState::SkewY) { if (ptCount != 1) goto error; - auto deg = tanf(mathDeg2Rad(points[0])); + auto deg = tanf(deg2rad(points[0])); Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 }; *matrix *= tmp; } @@ -1066,7 +1067,7 @@ static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, co static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); - node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal); } @@ -1614,7 +1615,7 @@ static constexpr struct } circleTags[] = { {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)}, {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)}, - {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)} + {"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)} }; @@ -2554,6 +2555,18 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char } +static SvgColor* _findLatestColor(const SvgLoaderData* loader) +{ + auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc; + + while (parent != nullptr) { + if (parent->style->curColorSet) return &parent->style->color; + parent = parent->parent; + } + return nullptr; +} + + static bool _attrParseStopsStyle(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; @@ -2563,7 +2576,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) stop->a = _toOpacity(value); loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); } else if (!strcmp(key, "stop-color")) { - if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { + if (!strcmp(value, "currentColor")) { + if (auto latestColor = _findLatestColor(loader)) { + stop->r = latestColor->r; + stop->g = latestColor->g; + stop->b = latestColor->b; + } + } else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } } else { @@ -2586,7 +2605,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value) stop->a = _toOpacity(value); } } else if (!strcmp(key, "stop-color")) { - if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { + if (!strcmp(value, "currentColor")) { + if (auto latestColor = _findLatestColor(loader)) { + stop->r = latestColor->r; + stop->g = latestColor->g; + stop->b = latestColor->b; + } + } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); } } else if (!strcmp(key, "style")) { @@ -3341,7 +3366,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, else parent = loader->doc; if (!strcmp(tagName, "style")) { // TODO: For now only the first style node is saved. After the css id selector - // is introduced this if condition shouldin't be necessary any more + // is introduced this if condition shouldn't be necessary any more if (!loader->cssStyle) { node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); loader->cssStyle = node; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index 5661c8ae82c..e64d7afb411 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 115e81aee12..57442139cdf 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 b048695a234..00c7408a7e8 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 09fc8aaac13..da1cdae9e0e 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 ab1fc18b58a..9371ae6c5ad 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; @@ -211,8 +211,8 @@ struct SwShape SwOutline* outline = nullptr; SwStroke* stroke = nullptr; SwFill* fill = nullptr; - SwRleData* rle = nullptr; - SwRleData* strokeRle = nullptr; + SwRle* rle = nullptr; + SwRle* strokeRle = nullptr; SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling. bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips? @@ -221,7 +221,7 @@ struct SwShape struct SwImage { SwOutline* outline = nullptr; - SwRleData* rle = nullptr; + SwRle* rle = nullptr; union { pixel_t* data; //system based data pointer uint32_t* buf32; //for explicit 32bits channels @@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*); //bl struct SwCompositor; -struct SwSurface : Surface +struct SwSurface : RenderSurface { SwJoin join; SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 SwBlender blender = nullptr; //blender (optional) SwCompositor* compositor = nullptr; //compositor (optional) - BlendMethod blendMethod; //blending method (uint8_t) + BlendMethod blendMethod = BlendMethod::Normal; SwAlpha alpha(CompositeMethod method) { @@ -262,7 +262,7 @@ struct SwSurface : Surface { } - SwSurface(const SwSurface* rhs) : Surface(rhs) + SwSurface(const SwSurface* rhs) : RenderSurface(rhs) { join = rhs->join; memcpy(alphas, rhs->alphas, sizeof(alphas)); @@ -272,7 +272,7 @@ struct SwSurface : Surface } }; -struct SwCompositor : Compositor +struct SwCompositor : RenderCompositor { SwSurface* recoverSfc; //Recover surface when composition is started SwCompositor* recoverCmp; //Recover compositor when composition is done @@ -488,13 +488,14 @@ SwFixed mathAtan(const SwPoint& pt); SwFixed mathCos(SwFixed angle); SwFixed mathSin(SwFixed angle); void mathSplitCubic(SwPoint* base); +void mathSplitLine(SwPoint* base); SwFixed mathDiff(SwFixed angle1, SwFixed angle2); SwFixed mathLength(const SwPoint& pt); -bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); +int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwPoint mathTransform(const Point* to, const Matrix& transform); bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); -bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); +bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee); void shapeReset(SwShape* shape); bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); @@ -541,13 +542,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. -SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); -SwRleData* rleRender(const SwBBox* bbox); -void rleFree(SwRleData* rle); -void rleReset(SwRleData* rle); -void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2); -void rleClipPath(SwRleData* rle, const SwRleData* clip); -void rleClipRect(SwRleData* rle, const SwBBox* clip); +SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); +SwRle* rleRender(const SwBBox* bbox); +void rleFree(SwRle* rle); +void rleReset(SwRle* rle); +void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); +void rleClip(SwRle* rle, const SwRle* clip); +void rleClip(SwRle* rle, const SwBBox* clip); SwMpool* mpoolInit(uint32_t threads); bool mpoolTerm(SwMpool* mpool); @@ -567,9 +568,17 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); +void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); +void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); -void rasterUnpremultiply(Surface* surface); -void rasterPremultiply(Surface* surface); -bool rasterConvertCS(Surface* surface, ColorSpace to); +void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped); +void rasterUnpremultiply(RenderSurface* surface); +void rasterPremultiply(RenderSurface* surface); +bool rasterConvertCS(RenderSurface* surface, ColorSpace to); + +bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); +bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); +bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct); +bool effectDropShadowPrepare(RenderEffectDropShadow* effect); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp index 631294ad40b..f70ba7a13d6 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp @@ -66,15 +66,15 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f static uint32_t _estimateAAMargin(const Fill* fdata) { constexpr float marginScalingFactor = 800.0f; - if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { + if (fdata->type() == Type::RadialGradient) { auto radius = P(static_cast(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); } @@ -217,7 +217,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; if (len < FLOAT_EPSILON) { - if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) { + if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) { fill->solid = true; } return true; @@ -228,7 +228,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; auto gradTransform = linear->transform(); - bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); + bool isTransformation = !identity((const Matrix*)(&gradTransform)); if (isTransformation) { gradTransform = transform * gradTransform; @@ -239,7 +239,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr if (isTransformation) { Matrix invTransform; - if (!mathInverse(&gradTransform, &invTransform)) return false; + if (!inverse(&gradTransform, &invTransform)) return false; fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23; @@ -261,7 +261,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr auto fy = P(radial)->fy; auto fr = P(radial)->fr; - if (mathZero(r)) { + if (tvg::zero(r)) { fill->solid = true; return true; } @@ -295,7 +295,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a; auto gradTransform = radial->transform(); - bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); + bool isTransformation = !identity((const Matrix*)(&gradTransform)); if (isTransformation) gradTransform = transform * gradTransform; else { @@ -305,7 +305,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr if (isTransformation) { Matrix invTransform; - if (!mathInverse(&gradTransform, &invTransform)) return false; + if (!inverse(&gradTransform, &invTransform)) return false; fill->radial.a11 = invTransform.e11; fill->radial.a12 = invTransform.e12; fill->radial.a13 = invTransform.e13; @@ -553,7 +553,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); if (opacity == 255) { - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(color, *dst, alpha(cmp)); @@ -584,7 +584,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } } } else { - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); @@ -626,7 +626,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE)))); for (uint32_t i = 0; i < len; ++i, ++dst) { *dst = maskOp(src, *dst, ~src); @@ -643,7 +643,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 auto t2 = static_cast(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; } @@ -651,7 +651,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 } else { uint32_t counter = 0; while (counter++ < len) { - auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a); + auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a); *dst = maskOp(src, *dst, ~src); ++dst; t += inc; @@ -668,7 +668,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto src = A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE))); src = MULTIPLY(src, a); for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) { @@ -715,7 +715,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); for (uint32_t i = 0; i < len; ++i, ++dst) { *dst = op(color, *dst, a); @@ -755,7 +755,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); - if (mathZero(inc)) { + if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); if (a == 255) { for (uint32_t i = 0; i < len; ++i, ++dst) { @@ -828,9 +828,9 @@ bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, fill->spread = fdata->spread(); - if (fdata->identifier() == TVG_CLASS_ID_LINEAR) { + if (fdata->type() == Type::LinearGradient) { if (!_prepareLinear(fill, static_cast(fdata), transform)) return false; - } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { + } else if (fdata->type() == Type::RadialGradient) { if (!_prepareRadial(fill, static_cast(fdata), transform)) return false; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp index 3fc64ce036a..d2c02bb932d 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp @@ -29,7 +29,7 @@ static inline bool _onlyShifted(const Matrix& m) { - if (mathEqual(m.e11, 1.0f) && mathEqual(m.e22, 1.0f) && mathZero(m.e12) && mathZero(m.e21)) return true; + if (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(m.e21)) return true; return false; } @@ -86,7 +86,7 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12)); image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX; - if (mathZero(transform.e12) && mathZero(transform.e21)) image->scaled = true; + if (tvg::zero(transform.e12) && tvg::zero(transform.e21)) image->scaled = true; else image->scaled = false; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp index fb809c4f7e9..1ff99f6aece 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; @@ -263,19 +272,19 @@ SwPoint mathTransform(const Point* to, const Matrix& transform) } -bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee) +bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee) { - clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x; - clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y; - clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x; - clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y; + clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x; + clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y; + clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x; + clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y; //Check valid region - if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false; + if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false; //Check boundary - if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y || - clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false; + if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y || + clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false; return true; } @@ -303,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S if (yMin > pt->y) yMin = pt->y; if (yMax < pt->y) yMax = pt->y; } - //Since no antialiasing is applied in the Fast Track case, - //the rasterization region has to be rearranged. - //https://github.com/Samsung/thorvg/issues/916 + if (fastTrack) { renderRegion.min.x = static_cast(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 00000000000..fd8e532e12a --- /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 b9e95f88cf7..18ffc18e1e0 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -490,7 +490,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin /* Rle */ /************************************************************************/ -static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { auto span = rle->spans; auto cbuffer = surface->compositor->image.buf8; @@ -510,7 +510,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask } -static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { auto span = rle->spans; auto cbuffer = surface->compositor->image.buf8; @@ -531,7 +531,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask ma } -static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); @@ -545,7 +545,7 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint } -static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); @@ -568,10 +568,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } - return true; - } //8bit grayscale - if (surface->channelSize == sizeof(uint8_t)) { + } else if (surface->channelSize == sizeof(uint8_t)) { uint8_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; @@ -582,13 +580,12 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint *dst = INTERPOLATE8(src, *dst, alpha(cmp)); } } - return true; } - return false; + return true; } -static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (surface->channelSize != sizeof(uint32_t)) return false; @@ -612,7 +609,7 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t } -static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { #if defined(THORVG_AVX_VECTOR_SUPPORT) return avxRasterTranslucentRle(surface, rle, r, g, b, a); @@ -624,7 +621,7 @@ static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint } -static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b) +static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b) { auto span = rle->spans; @@ -661,7 +658,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, } -static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (!rle) return false; @@ -697,66 +694,9 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \ if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \ - -#if 0 //Enable it when GRAYSCALE image is supported -static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) -{ - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); - auto span = image->rle->spans; - int32_t miny = 0, maxy = 0; - - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - SCALED_IMAGE_RANGE_Y(span->y) - auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x]; - auto a = MULTIPLY(span->coverage, opacity); - for (uint32_t x = static_cast(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; } @@ -784,7 +724,6 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image *dst = src + ALPHA_BLEND(*dst, IA(src)); } } - return true; } @@ -851,7 +790,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr Matrix itransform; - if (!mathInverse(&transform, &itransform)) return true; + if (!inverse(&transform, &itransform)) return true; if (_compositing(surface)) { if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); @@ -869,75 +808,6 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr /* RLE Direct Image */ /************************************************************************/ -#if 0 //Enable it when GRAYSCALE image is supported -static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) -{ - auto span = image->rle->spans; - auto cbuffer = surface->compositor->image.buf8; - auto ctride = surface->compositor->image.stride; - - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); - auto cmp = &cbuffer[span->y * ctride + span->x]; - auto alpha = MULTIPLY(span->coverage, opacity); - if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { - *cmp = maskOp(*src, *cmp, ~*src); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { - auto tmp = MULTIPLY(*src, alpha); - *cmp = maskOp(*src, *cmp, ~tmp); - } - } - } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); -} - - -static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) -{ - auto span = image->rle->spans; - auto cbuffer = surface->compositor->image.buf8; - auto ctride = surface->compositor->image.stride; - - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); - auto cmp = &cbuffer[span->y * ctride + span->x]; - auto dst = &surface->buf8[span->y * surface->stride + span->x]; - auto alpha = MULTIPLY(span->coverage, opacity); - if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { - auto tmp = maskOp(*src, *cmp, 0); //not use alpha - *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { - auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha - *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); - } - } - } - return true; -} -#endif - -static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) -{ -#if 0 //Enable it when GRAYSCALE image is supported - TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method); - - //8bit masking channels composition - if (surface->channelSize != sizeof(uint8_t)) return false; - - auto maskOp = _getMaskOp(surface->compositor->method); - if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity); - else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity); -#endif - return false; -} - - static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) { TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); @@ -999,21 +869,19 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); auto alpha = MULTIPLY(span->coverage, opacity); - if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - *dst = *img + ALPHA_BLEND(*dst, IA(*img)); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - auto src = ALPHA_BLEND(*img, alpha); - *dst = src + ALPHA_BLEND(*dst, IA(src)); - } - } + rasterTranslucentPixel32(dst, img, span->len, alpha); } return true; } +static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); + return false; +} + + static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { @@ -1037,67 +905,9 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op /*Scaled Image */ /************************************************************************/ -#if 0 //Enable it when GRAYSCALE image is supported -static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) -{ - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); - auto cstride = surface->compositor->image.stride; - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); - int32_t miny = 0, maxy = 0; - - for (auto y = region.min.y; y < region.max.y; ++y) { - SCALED_IMAGE_RANGE_Y(y) - auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { - SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); - if (opacity < 255) src = MULTIPLY(src, opacity); - *cmp = maskOp(src, *cmp, ~src); - } - cbuffer += cstride; - } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); -} - - -static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) -{ - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); - auto cstride = surface->compositor->image.stride; - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); - auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); - int32_t miny = 0, maxy = 0; - - for (auto y = region.min.y; y < region.max.y; ++y) { - SCALED_IMAGE_RANGE_Y(y) - auto cmp = cbuffer; - auto dst = dbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) { - SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); - if (opacity < 255) src = MULTIPLY(src, opacity); - src = maskOp(src, *cmp, 0); //not use alpha - *dst = src + MULTIPLY(*dst, ~src); - } - cbuffer += cstride; - dbuffer += surface->stride; - } - return true; -} -#endif - static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - TVGERR("SW_ENGINE", "Not supported ScaledMaskedImage!"); -#if 0 //Enable it when GRAYSCALE image is supported - TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); - - auto maskOp = _getMaskOp(surface->compositor->method); - if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); - else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); -#endif + TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!"); return false; } @@ -1202,7 +1012,7 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& { Matrix itransform; - if (!mathInverse(&transform, &itransform)) return true; + if (!inverse(&transform, &itransform)) return true; if (_compositing(surface)) { if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); @@ -1220,78 +1030,9 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& /* Direct Image */ /************************************************************************/ -#if 0 //Enable it when GRAYSCALE image is supported -static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) -{ - auto h = static_cast(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; } @@ -1394,37 +1135,24 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; - for (auto y = region.min.y; y < region.max.y; ++y) { - auto dst = dbuffer; - auto src = sbuffer; - if (opacity == 255) { - for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { - *dst = *src + ALPHA_BLEND(*dst, IA(*src)); - } - } else { - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - auto tmp = ALPHA_BLEND(*src, opacity); - *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); - } - } + rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity); dbuffer += surface->stride; sbuffer += image->stride; } //8bits grayscale } else if (surface->channelSize == sizeof(uint8_t)) { auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x]; - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) { auto dst = dbuffer; auto src = sbuffer; if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - *dst = *src + MULTIPLY(*dst, ~*src); + *dst = *src + MULTIPLY(*dst, IA(*src)); } } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - *dst = INTERPOLATE8(*src, *dst, opacity); + *dst = INTERPOLATE8(A(*src), *dst, opacity); } } } @@ -1602,13 +1330,23 @@ static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region template 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; } @@ -1617,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; } @@ -1663,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; @@ -1678,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; @@ -1695,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; @@ -1710,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); @@ -1729,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; @@ -1742,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; @@ -1757,7 +1506,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r } else if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; - fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255); + fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage); } } return true; @@ -1765,7 +1514,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r template -static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterSolidGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { auto span = rle->spans; @@ -1789,7 +1538,7 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co } -static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterLinearGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { if (!rle) return false; @@ -1806,7 +1555,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c } -static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterRadialGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill) { if (!rle) return false; @@ -1814,9 +1563,9 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c if (_matting(surface)) return _rasterGradientMattedRle(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; @@ -1827,6 +1576,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c /* External Class Implementation */ /************************************************************************/ +void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) +{ + //TODO: Support SIMD accelerations + cRasterTranslucentPixels(dst, src, len, opacity); +} + + +void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity) +{ + //TODO: Support SIMD accelerations + cRasterPixels(dst, src, len, opacity); +} + void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) { @@ -1905,7 +1667,7 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ } -void rasterUnpremultiply(Surface* surface) +void rasterUnpremultiply(RenderSurface* surface) { if (surface->channelSize != sizeof(uint32_t)) return; @@ -1935,7 +1697,7 @@ void rasterUnpremultiply(Surface* surface) } -void rasterPremultiply(Surface* surface) +void rasterPremultiply(RenderSurface* surface) { ScopedLock lock(surface->key); if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return; @@ -1965,13 +1727,13 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true; } - auto id = fdata->identifier(); + auto type = fdata->type(); if (shape->fastTrack) { - if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); - else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); + if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); + else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); } else { - if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); - else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); + if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); + else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); } return false; } @@ -1986,9 +1748,9 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true; } - auto id = fdata->identifier(); - if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); - else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); + auto type = fdata->type(); + if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); + else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); return false; } @@ -2027,12 +1789,12 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, co } -bool rasterConvertCS(Surface* surface, ColorSpace to) +bool rasterConvertCS(RenderSurface* surface, ColorSpace to) { ScopedLock lock(surface->key); if (surface->cs == to) return true; - //TOOD: Support SIMD accelerations + //TODO: Support SIMD accelerations auto from = surface->cs; if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) { @@ -2045,3 +1807,39 @@ bool rasterConvertCS(Surface* surface, ColorSpace to) } return false; } + + +//TODO: SIMD OPTIMIZATION? +void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped) +{ + constexpr int BLOCK = 8; //experimental decision + + if (flipped) { + src += ((bbox.min.x * stride) + bbox.min.y); + dst += ((bbox.min.y * stride) + bbox.min.x); + } else { + src += ((bbox.min.y * stride) + bbox.min.x); + dst += ((bbox.min.x * stride) + bbox.min.y); + } + + #pragma omp parallel for + for (int x = 0; x < w; x += BLOCK) { + auto bx = std::min(w, x + BLOCK) - x; + auto in = &src[x]; + auto out = &dst[x * stride]; + for (int y = 0; y < h; y += BLOCK) { + auto p = &in[y * stride]; + auto q = &out[y]; + auto by = std::min(h, y + BLOCK) - y; + for (int xx = 0; xx < bx; ++xx) { + for (int yy = 0; yy < by; ++yy) { + *q = *p; + p += stride; + ++q; + } + p += 1 - by * stride; + q += stride - by; + } + } + } +} diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h index a072a88819a..79cab043f24 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 6d0bd9383da..d79da0e4d8c 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 91cf7743c19..fe693b7f33a 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 88ef2118f27..1162edc8381 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]; @@ -53,10 +64,12 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in regionBottom = image->rle->spans[image->rle->size - 1].y; } + if (yStart >= regionBottom) return false; + if (yStart < regionTop) yStart = regionTop; if (yEnd > regionBottom) yEnd = regionBottom; - return yEnd > yStart; + return true; } @@ -673,7 +686,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0])); //Skip poly if it's an infinitely thin line - if (mathZero(denom)) return; + if (tvg::zero(denom)) return; denom = 1 / denom; //Reciprocal for speeding up dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom; @@ -689,8 +702,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const //Determine which side of the polygon the longer edge is on auto side = (dxdy[1] > dxdy[0]) ? true : false; - if (mathEqual(y[0], y[1])) side = x[0] > x[1]; - if (mathEqual(y[1], y[2])) side = x[2] > x[1]; + if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; + if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image? auto compositing = _compositing(surface); //Composition required diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 116c4e81352..180f3cc37a2 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,7 +41,7 @@ struct SwTask : Task { SwSurface* surface = nullptr; SwMpool* mpool = nullptr; - SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region + SwBBox bbox; //Rendering Region Matrix transform; Array clips; RenderUpdateFlag flags = RenderUpdateFlag::None; @@ -65,9 +68,7 @@ struct SwTask : Task } virtual void dispose() = 0; - virtual bool clip(SwRleData* target) = 0; - virtual SwRleData* rle() = 0; - + virtual bool clip(SwRle* target) = 0; virtual ~SwTask() {} }; @@ -92,38 +93,34 @@ struct SwShapeTask : SwTask if (!rshape->stroke) return 0.0f; auto width = rshape->stroke->width; - if (mathZero(width)) return 0.0f; + if (tvg::zero(width)) return 0.0f; if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f; - if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; + if (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12)); } - bool clip(SwRleData* target) override + bool clip(SwRle* target) override { - if (shape.fastTrack) rleClipRect(target, &bbox); - else if (shape.rle) rleClipPath(target, shape.rle); + if (shape.fastTrack) rleClip(target, &bbox); + else if (shape.rle) rleClip(target, shape.rle); else return false; return true; } - SwRleData* rle() override - { - if (!shape.rle && shape.fastTrack) { - shape.rle = rleRender(&shape.bbox); - } - return shape.rle; - } - void run(unsigned tid) override { - if (opacity == 0 && !clipper) return; //Invisible + //Invisible + if (opacity == 0 && !clipper) { + bbox.reset(); + return; + } auto strokeWidth = validStrokeWidth(); - bool visibleFill = false; - auto clipRegion = bbox; + SwBBox renderRegion{}; + auto visibleFill = false; //This checks also for the case, if the invisible shape turned to visible by alpha. auto prepareShape = false; @@ -135,10 +132,11 @@ struct SwShapeTask : SwTask rshape->fillColor(nullptr, nullptr, nullptr, &alpha); alpha = MULTIPLY(alpha, opacity); visibleFill = (alpha > 0 || rshape->fill); + shapeReset(&shape); if (visibleFill || clipper) { - shapeReset(&shape); - if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) { + if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) { visibleFill = false; + renderRegion.reset(); } } } @@ -159,8 +157,8 @@ struct SwShapeTask : SwTask if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (strokeWidth > 0.0f) { shapeResetStroke(&shape, rshape, transform); - if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err; + if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err; if (auto fill = rshape->strokeFill()) { auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; if (ctable) shapeResetStrokeFill(&shape); @@ -184,9 +182,13 @@ struct SwShapeTask : SwTask //Clip stroke rle if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; } + + bbox = renderRegion; //sync + return; err: + bbox.reset(); shapeReset(&shape); shapeDelOutline(&shape, mpool, tid); } @@ -201,20 +203,14 @@ struct SwShapeTask : SwTask struct SwImageTask : SwTask { SwImage image; - Surface* source; //Image source + RenderSurface* source; //Image source - bool clip(SwRleData* target) override + bool clip(SwRle* target) override { TVGERR("SW_ENGINE", "Image is used as ClipPath?"); return true; } - SwRleData* rle() override - { - TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?"); - return nullptr; - } - void run(unsigned tid) override { auto clipRegion = bbox; @@ -452,27 +448,18 @@ bool SwRenderer::blend(BlendMethod method) surface->blendMethod = method; switch (method) { - case BlendMethod::Add: - surface->blender = opBlendAdd; - break; - case BlendMethod::Screen: - surface->blender = opBlendScreen; + case BlendMethod::Normal: + surface->blender = nullptr; break; case BlendMethod::Multiply: surface->blender = opBlendMultiply; break; + case BlendMethod::Screen: + surface->blender = opBlendScreen; + break; case BlendMethod::Overlay: surface->blender = opBlendOverlay; break; - case BlendMethod::Difference: - surface->blender = opBlendDifference; - break; - case BlendMethod::Exclusion: - surface->blender = opBlendExclusion; - break; - case BlendMethod::SrcOver: - surface->blender = opBlendSrcOver; - break; case BlendMethod::Darken: surface->blender = opBlendDarken; break; @@ -491,7 +478,17 @@ bool SwRenderer::blend(BlendMethod method) case BlendMethod::SoftLight: surface->blender = opBlendSoftLight; break; + case BlendMethod::Difference: + surface->blender = opBlendDifference; + break; + case BlendMethod::Exclusion: + surface->blender = opBlendExclusion; + break; + case BlendMethod::Add: + surface->blender = opBlendAdd; + break; default: + TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method); surface->blender = nullptr; break; } @@ -505,7 +502,7 @@ RenderRegion SwRenderer::region(RenderData data) } -bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) +bool SwRenderer::beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) { if (!cmp) return false; auto p = static_cast(cmp); @@ -543,13 +540,50 @@ 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) +{ + SwSurface* cmp = nullptr; + + //Use cached data + for (auto p = compositors.begin(); p < compositors.end(); ++p) { + if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) { + cmp = *p; + break; + } + } + + //New Composition + if (!cmp) { + //Inherits attributes from main surface + cmp = new SwSurface(surface); + cmp->compositor = new SwCompositor; + 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; @@ -561,35 +595,16 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) //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) { - cmp = *p; - break; - } - } - - //New Composition - if (!cmp) { - //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; - - compositors.push(cmp); - } + auto cmp = request(CHANNEL_SIZE(cs)); //Boundary Check + if (x < 0) x = 0; + if (y < 0) y = 0; if (x + w > sw) w = (sw - x); if (y + h > sh) h = (sh - y); + if (w == 0 || h == 0) return nullptr; + cmp->compositor->recoverSfc = surface; cmp->compositor->recoverCmp = surface->compositor; cmp->compositor->valid = false; @@ -597,14 +612,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) cmp->compositor->bbox.min.y = y; cmp->compositor->bbox.max.x = x + w; cmp->compositor->bbox.max.y = y + h; - cmp->compositor->image.stride = surface->stride; - cmp->compositor->image.w = surface->w; - cmp->compositor->image.h = surface->h; - cmp->compositor->image.direct = true; - - cmp->data = cmp->compositor->image.data; - cmp->w = cmp->compositor->image.w; - cmp->h = cmp->compositor->image.h; /* TODO: Currently, only blending might work. Blending and composition must be handled together. */ @@ -618,7 +625,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) } -bool SwRenderer::endComposite(Compositor* cmp) +bool SwRenderer::endComposite(RenderCompositor* cmp) { if (!cmp) return false; @@ -639,6 +646,40 @@ bool SwRenderer::endComposite(Compositor* cmp) } +bool SwRenderer::prepare(RenderEffect* effect) +{ + switch (effect->type) { + case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast(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; @@ -681,10 +722,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr task->surface = surface; task->mpool = mpool; task->flags = flags; - task->bbox.min.x = mathMax(static_cast(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; @@ -697,7 +738,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr } -RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array& 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); @@ -748,6 +789,10 @@ bool SwRenderer::init(uint32_t threads) int32_t SwRenderer::init() { +#ifdef THORVG_SW_OPENMP_SUPPORT + omp_set_num_threads(TaskScheduler::threads()); +#endif + return initEngineCnt; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index fcd8ad46205..bd6beb8d85c 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -37,7 +37,7 @@ class SwRenderer : public RenderMethod { public: RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; - RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) 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; @@ -48,18 +48,21 @@ public: bool viewport(const RenderRegion& vp) override; bool blend(BlendMethod method) override; ColorSpace colorSpace() override; - const Surface* mainSurface() override; + const RenderSurface* mainSurface() override; bool clear() override; bool sync() override; bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool mempool(bool shared); - Compositor* target(const RenderRegion& region, ColorSpace cs) override; - bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; - bool endComposite(Compositor* cmp) override; + RenderCompositor* target(const RenderRegion& region, ColorSpace cs) override; + bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override; + bool endComposite(RenderCompositor* cmp) override; void clearCompositors(); + bool prepare(RenderEffect* effect) override; + bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override; + static SwRenderer* gen(); static bool init(uint32_t threads); static int32_t init(); @@ -76,6 +79,7 @@ private: SwRenderer(); ~SwRenderer(); + SwSurface* request(int channelSize); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array& 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 42b08de6a58..3e4ad679a8a 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; } } @@ -361,20 +362,20 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor //Clip x range SwCoord xOver = 0; - if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); + if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); if (x < rw.cellMin.x) { xOver -= (rw.cellMin.x - x); x = rw.cellMin.x; } //Nothing to draw - if (acount + xOver <= 0) return; + if (aCount + xOver <= 0) return; //add a span to the current list auto span = rle->spans + rle->size; span->x = x; span->y = y; - span->len = (acount + xOver); + span->len = (aCount + xOver); span->coverage = coverage; rle->size++; } @@ -513,98 +514,116 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) return; } - auto diff = to - rw.pos; - auto f1 = rw.pos - SUBPIXELS(e1); - SwPoint f2; + auto line = rw.lineStack; + line[0] = to; + line[1] = rw.pos; - //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); + while (true) { + auto diff = line[0] - line[1]; + auto L = HYPOT(diff); + + if (L > SHRT_MAX) { + mathSplitLine(line); + ++line; + continue; } - //any other line - } else { - Area prod = diff.x * f1.y - diff.y * f1.x; + e1 = TRUNC(line[1]); + e2 = TRUNC(line[0]); - /* 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; - } + 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; - } while(e1 != e2); + /* 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); + + } while(e1 != e2); + } + + 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]; + + if (line-- == rw.lineStack) return; } - - f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)}; - rw.cover += (f2.y - f1.y); - rw.area += (f2.y - f1.y) * (f1.x + f2.x); - rw.pos = to; } @@ -690,31 +709,27 @@ static void _decomposeOutline(RleWorker& rw) auto start = UPSCALE(outline->pts[first]); auto pt = outline->pts.data + first; auto types = outline->types.data + first; + ++types; _moveTo(rw, UPSCALE(outline->pts[first])); while (pt < limit) { - ++pt; - ++types; - //emit a single line_to if (types[0] == SW_CURVE_TYPE_POINT) { + ++pt; + ++types; _lineTo(rw, UPSCALE(*pt)); //types cubic } else { - pt += 2; - types += 2; - - if (pt <= limit) { - _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); - continue; - } - _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); - goto close; + pt += 3; + types += 3; + if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); + else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); + else goto close; } } - _lineTo(rw, start); close: + _lineTo(rw, start); first = last + 1; } } @@ -731,7 +746,7 @@ static int _genRle(RleWorker& rw) } -static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt) +static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; auto spans = target->spans; @@ -740,7 +755,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar auto clipEnd = clip->spans + clip->size; while (spans < end && clipSpans < clipEnd) { - //align y cooridnates. + //align y-coordinates. if (clipSpans->y > spans->y) { ++spans; continue; @@ -750,7 +765,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar continue; } - //Try clipping with all clip spans which have a same y coordinate. + //Try clipping with all clip spans which have a same y-coordinate. auto temp = clipSpans; while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) { auto sx1 = spans->x; @@ -783,7 +798,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar } -static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) +static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; auto spans = targetRle->spans; @@ -822,7 +837,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl } -void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) +void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size) { free(rle->spans); rle->spans = clippedSpans; @@ -834,7 +849,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) /* External Class Implementation */ /************************************************************************/ -SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) +SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) { constexpr auto RENDER_POOL_SIZE = 16384L; constexpr auto BAND_SIZE = 40; @@ -862,7 +877,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren rw.bandShoot = 0; rw.antiAlias = antiAlias; - if (!rle) rw.rle = reinterpret_cast(calloc(1, sizeof(SwRleData))); + if (!rle) rw.rle = reinterpret_cast(calloc(1, sizeof(SwRle))); else rw.rle = rle; //Generate RLE @@ -953,12 +968,12 @@ error: } -SwRleData* rleRender(const SwBBox* bbox) +SwRle* rleRender(const SwBBox* bbox) { auto width = static_cast(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; @@ -975,14 +990,14 @@ SwRleData* rleRender(const SwBBox* bbox) } -void rleReset(SwRleData* rle) +void rleReset(SwRle* rle) { if (!rle) return; rle->size = 0; } -void rleFree(SwRleData* rle) +void rleFree(SwRle* rle) { if (!rle) return; if (rle->spans) free(rle->spans); @@ -990,7 +1005,7 @@ void rleFree(SwRleData* rle) } -void rleClipPath(SwRleData *rle, const SwRleData *clip) +void rleClip(SwRle *rle, const SwRle *clip) { if (rle->size == 0 || clip->size == 0) return; auto spanCnt = rle->size > clip->size ? rle->size : clip->size; @@ -999,11 +1014,11 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip) _replaceClipSpan(rle, spans, spansEnd - spans); - TVGLOG("SW_ENGINE", "Using ClipPath!"); + TVGLOG("SW_ENGINE", "Using Path Clipping!"); } -void rleClipRect(SwRleData *rle, const SwBBox* clip) +void rleClip(SwRle *rle, const SwBBox* clip) { if (rle->size == 0) return; auto spans = static_cast(malloc(sizeof(SwSpan) * (rle->size))); @@ -1011,5 +1026,5 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip) _replaceClipSpan(rle, spans, spansEnd - spans); - TVGLOG("SW_ENGINE", "Using ClipRect!"); + TVGLOG("SW_ENGINE", "Using Box Clipping!"); } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 24c4a9e3725..4408db0b86f 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 */ @@ -102,9 +101,9 @@ static bool _outlineClose(SwOutline& outline) static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform) { Line cur = {dash.ptCur, *to}; - auto len = lineLength(cur.pt1, cur.pt2); + auto len = cur.length(); - if (mathZero(len)) { + if (tvg::zero(len)) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); //draw the current line fully } else if (len <= dash.curLen) { @@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans Line left, right; if (dash.curLen > 0) { len -= dash.curLen; - lineSplitAt(cur, dash.curLen, left, right); + cur.split(dash.curLen, left, right); if (!dash.curOpGap) { if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { _outlineMoveTo(*dash.outline, &left.pt1, transform); @@ -163,10 +162,10 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform) { Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; - auto len = bezLength(cur); + auto len = cur.length(); //draw the current line fully - if (mathZero(len)) { + if (tvg::zero(len)) { _outlineMoveTo(*dash.outline, &dash.ptCur, transform); } else if (len <= dash.curLen) { dash.curLen -= len; @@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct Bezier left, right; if (dash.curLen > 0) { len -= dash.curLen; - bezSplitAt(cur, dash.curLen, left, right); + cur.split(dash.curLen, left, right); if (!dash.curOpGap) { if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) { _outlineMoveTo(*dash.outline, &left.start, transform); @@ -284,7 +283,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32 if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f; const Point* close = nullptr; - auto length = 0.0f; + auto len = 0.0f; //must begin with moveTo if (cmds[0] == PathCommand::MoveTo) { @@ -297,30 +296,30 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32 while (cmdCnt-- > 0) { switch (*cmds) { case PathCommand::Close: { - length += mathLength(pts - 1, close); - if (subpath) return length; + len += length(pts - 1, close); + if (subpath) return len; break; } case PathCommand::MoveTo: { - if (subpath) return length; + if (subpath) return len; close = pts; ++pts; break; } case PathCommand::LineTo: { - length += mathLength(pts - 1, pts); + len += length(pts - 1, pts); ++pts; break; } case PathCommand::CubicTo: { - length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); + len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length(); pts += 3; break; } } ++cmds; } - return length; + return len; } @@ -355,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans //offset auto patternLength = 0.0f; uint32_t offIdx = 0; - if (!mathZero(offset)) { + if (!tvg::zero(offset)) { for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i]; bool isOdd = dash.cnt % 2; if (isOdd) patternLength *= 2; @@ -499,7 +498,6 @@ bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& trans if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false; if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; - //Keep it for Rasterization Region shape->bbox = renderRegion; //Check valid region diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp index 75ac96be04d..e195f72adf2 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); } } @@ -845,31 +855,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) //A contour cannot start with a cubic control point if (type == SW_CURVE_TYPE_CUBIC) return false; + ++types; auto closed = outline.closed.data ? outline.closed.data[i]: false; _beginSubPath(*stroke, start, closed); while (pt < limit) { - ++pt; - ++types; - - //emit a signel line_to + //emit a single line_to if (types[0] == SW_CURVE_TYPE_POINT) { + ++pt; + ++types; _lineTo(*stroke, *pt); //types cubic } else { - if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false; - - pt += 2; - types += 2; - - if (pt <= limit) { - _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); - continue; - } - _cubicTo(*stroke, pt[-2], pt[-1], start); - goto close; + pt += 3; + types += 3; + if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); + else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start); + else goto close; } } close: diff --git a/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h b/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h index 29f84eb82a6..e40859b6db9 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 199e823034f..c5d2127f9c6 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,7 +91,7 @@ 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}; @@ -108,7 +108,7 @@ struct Canvas::Impl Result draw() { - if (status == Status::Damanged) update(nullptr, false); + if (status == Status::Damaged) update(nullptr, false); if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition; bool rendered = false; @@ -124,7 +124,7 @@ struct Canvas::Impl Result sync() { - if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition; + if (status == Status::Synced || status == Status::Damaged) return Result::InsufficientCondition; if (renderer->sync()) { status = Status::Synced; @@ -136,7 +136,7 @@ struct Canvas::Impl Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) { - if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition; + if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition; RenderRegion val = {x, y, w, h}; //intersect if the target buffer is already set. @@ -147,7 +147,7 @@ struct Canvas::Impl if (vport == val) return Result::Success; renderer->viewport(val); vport = val; - status = Status::Damanged; + status = Status::Damaged; return Result::Success; } }; diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h index 15a2cc4ef04..527221625b6 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 ea1010051ee..19edff5a2cd 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 47f0c051c02..f249356aa2a 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 82666b7ae31..24e2fb8b1be 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/tvgLoadModule.h b/thirdparty/thorvg/src/renderer/tvgLoadModule.h index 1b81d81a4f7..a9c1a685442 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; diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.cpp b/thirdparty/thorvg/src/renderer/tvgLoader.cpp index 6a81ddcdbb3..db51fc215a0 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 37813b19ef7..536e1878521 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -32,11 +32,11 @@ /************************************************************************/ #define PAINT_METHOD(ret, METHOD) \ - switch (id) { \ - case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \ - case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \ - case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \ - case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \ + switch (paint->type()) { \ + case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \ + case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \ + case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \ + case Type::Text: ret = P((Text*)paint)->METHOD; break; \ default: ret = {}; \ } @@ -91,8 +91,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat //No rotation and no skewing, still can try out clipping the rect region. auto tryClip = false; - if ((!mathRightAngle(pm) || mathSkewed(pm))) tryClip = true; - if ((!mathRightAngle(rm) || mathSkewed(rm))) tryClip = true; + if ((!rightAngle(pm) || skewed(pm))) tryClip = true; + if ((!rightAngle(rm) || skewed(rm))) tryClip = true; if (tryClip) return _clipRect(renderer, pts, pm, rm, before); @@ -102,8 +102,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat auto pt3 = pts + 2; auto pt4 = pts + 3; - if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) || - (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) { + if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) || + (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) { RenderRegion after; @@ -164,6 +164,7 @@ Paint* Paint::Impl::duplicate(Paint* ret) ret->pImpl->opacity = opacity; if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method); + if (clipper) ret->pImpl->clip(clipper->duplicate()); return ret; } @@ -172,7 +173,7 @@ Paint* Paint::Impl::duplicate(Paint* ret) bool Paint::Impl::rotate(float degree) { if (tr.overriding) return false; - if (mathEqual(degree, tr.degree)) return true; + if (tvg::equal(degree, tr.degree)) return true; tr.degree = degree; renderFlag |= RenderUpdateFlag::Transform; @@ -183,7 +184,7 @@ bool Paint::Impl::rotate(float degree) bool Paint::Impl::scale(float factor) { if (tr.overriding) return false; - if (mathEqual(factor, tr.scale)) return true; + if (tvg::equal(factor, tr.scale)) return true; tr.scale = factor; renderFlag |= RenderUpdateFlag::Transform; @@ -194,7 +195,7 @@ bool Paint::Impl::scale(float factor) bool Paint::Impl::translate(float x, float y) { if (tr.overriding) return false; - if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true; + if (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true; tr.m.e13 = x; tr.m.e23 = y; renderFlag |= RenderUpdateFlag::Transform; @@ -207,11 +208,9 @@ bool Paint::Impl::render(RenderMethod* renderer) { if (opacity == 0) return true; - Compositor* cmp = nullptr; + RenderCompositor* cmp = nullptr; - /* Note: only ClipPath is processed in update() step. - Create a composition image. */ - if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { + if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { RenderRegion region; PAINT_METHOD(region, bounds(renderer)); @@ -248,43 +247,49 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arraytarget; auto method = compData->method; P(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset - /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle, - we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */ - auto tryFastTrack = false; - if (target->identifier() == TVG_CLASS_ID_SHAPE) { - if (method == CompositeMethod::ClipPath) tryFastTrack = true; - else { - auto shape = static_cast(target); - uint8_t a; - shape->fillColor(nullptr, nullptr, nullptr, &a); - //no gradient fill & no compositions of the composition target. - if (!shape->fill() && !(PP(shape)->compData)) { - if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true; - else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true; - } - } - if (tryFastTrack) { - viewport = renderer->viewport(); - if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { - P(target)->ctxFlag |= ContextFlag::FastTrack; + /* If the transformation has no rotational factors and the Alpha(InvAlpha)Masking involves a simple rectangle, + we can optimize by using the viewport instead of the regular AlphaMasking sequence for improved performance. */ + if (target->type() == Type::Shape) { + auto shape = static_cast(target); + uint8_t a; + shape->fillColor(nullptr, nullptr, nullptr, &a); + //no gradient fill & no compositions of the composition target. + if (!shape->fill() && !(PP(shape)->compData)) { + if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) { + viewport = renderer->viewport(); + if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { + P(target)->ctxFlag |= ContextFlag::FastTrack; + } } } } if (compFastTrack == Result::InsufficientCondition) { - childClipper = compData->method == CompositeMethod::ClipPath ? true : false; - trd = P(target)->update(renderer, pm, clips, 255, pFlag, childClipper); - if (childClipper) clips.push(trd); + trd = P(target)->update(renderer, pm, clips, 255, pFlag, false); } } - /* 2. Main Update */ + /* 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); + } + } + + /* 3. Main Update */ auto newFlag = static_cast(pFlag | renderFlag); renderFlag = RenderUpdateFlag::None; opacity = MULTIPLY(opacity, this->opacity); @@ -294,9 +299,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayviewport(viewport); - else if (childClipper) clips.pop(); + else if (this->clipper) clips.pop(); return rd; } @@ -308,7 +313,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme const auto& m = this->transform(origin); //Case: No transformed, quick return! - if (!transformed || mathIdentity(&m)) { + if (!transformed || identity(&m)) { PAINT_METHOD(ret, bounds(x, y, w, h, stroking)); return ret; } @@ -351,12 +356,18 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme void Paint::Impl::reset() { + if (clipper) { + delete(clipper); + clipper = nullptr; + } + if (compData) { if (P(compData->target)->unref() == 0) delete(compData->target); free(compData); compData = nullptr; } - mathIdentity(&tr.m); + + tvg::identity(&tr.m); tr.degree = 0.0f; tr.scale = 1.0f; tr.overriding = false; @@ -437,15 +448,27 @@ Paint* Paint::duplicate() const noexcept } -Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept +Result Paint::clip(std::unique_ptr clipper) noexcept { - if (method == CompositeMethod::ClipPath && target && target->identifier() != TVG_CLASS_ID_SHAPE) { - TVGERR("RENDERER", "ClipPath only allows the Shape!"); + auto p = clipper.release(); + + if (p && p->type() != Type::Shape) { + TVGERR("RENDERER", "Clipping only supports the Shape!"); return Result::NonSupport; } + pImpl->clip(p); + return Result::Success; +} + + +Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept +{ + //TODO: remove. Keep this for the backward compatibility + if (target && method == CompositeMethod::ClipPath) return clip(std::move(target)); auto p = target.release(); if (pImpl->composite(this, p, method)) return Result::Success; + delete(p); return Result::InvalidArguments; } @@ -457,6 +480,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept if (target) *target = pImpl->compData->target; return pImpl->compData->method; } else { + //TODO: remove. Keep this for the backward compatibility + if (pImpl->clipper) { + if (target) *target = pImpl->clipper; + return CompositeMethod::ClipPath; + } if (target) *target = nullptr; return CompositeMethod::None; } @@ -480,14 +508,17 @@ uint8_t Paint::opacity() const noexcept } -uint32_t Paint::identifier() const noexcept +TVG_DEPRECATED uint32_t Paint::identifier() const noexcept { - return pImpl->id; + return (uint32_t) type(); } Result Paint::blend(BlendMethod method) noexcept { + //TODO: Remove later + if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport; + if (pImpl->blendMethod != method) { pImpl->blendMethod = method; pImpl->renderFlag |= RenderUpdateFlag::Blend; @@ -495,9 +526,3 @@ Result Paint::blend(BlendMethod method) noexcept return Result::Success; } - - -BlendMethod Paint::blend() const noexcept -{ - return pImpl->blendMethod; -} diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h index e43ca239bb7..d78e9bb3d1c 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.h +++ b/thirdparty/thorvg/src/renderer/tvgPaint.h @@ -49,6 +49,7 @@ namespace tvg { Paint* paint = nullptr; Composite* compData = nullptr; + Paint* clipper = nullptr; RenderMethod* renderer = nullptr; struct { Matrix m; //input matrix @@ -67,8 +68,8 @@ namespace tvg m.e31 = 0.0f; m.e32 = 0.0f; m.e33 = 1.0f; - mathScale(&m, scale, scale); - mathRotate(&m, degree); + tvg::scale(&m, scale, scale); + tvg::rotate(&m, degree); } } tr; BlendMethod blendMethod; @@ -76,7 +77,6 @@ namespace tvg uint8_t ctxFlag; uint8_t opacity; uint8_t refCnt = 0; //reference count - uint8_t id; //TODO: deprecated, remove it Impl(Paint* pnt) : paint(pnt) { @@ -89,6 +89,7 @@ namespace tvg if (P(compData->target)->unref() == 0) delete(compData->target); free(compData); } + if (clipper && P(clipper)->unref() == 0) delete(clipper); if (renderer && (renderer->unref() == 0)) delete(renderer); } @@ -106,7 +107,7 @@ namespace tvg bool transform(const Matrix& m) { - tr.m = m; + if (&tr.m != &m) tr.m = m; tr.overriding = true; renderFlag |= RenderUpdateFlag::Transform; @@ -121,6 +122,20 @@ namespace tvg return tr.m; } + void clip(Paint* clp) + { + if (this->clipper) { + P(this->clipper)->unref(); + if (this->clipper != clp && P(this->clipper)->refCnt == 0) { + delete(this->clipper); + } + } + this->clipper = clp; + if (!clp) return; + + P(clipper)->ref(); + } + bool composite(Paint* source, Paint* target, CompositeMethod method) { //Invalid case diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp index 223cc5b026f..d3e31d198a3 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,11 +74,11 @@ bool Picture::Impl::needComposition(uint8_t opacity) bool Picture::Impl::render(RenderMethod* renderer) { bool ret = false; - renderer->blend(picture->blend()); + renderer->blend(PP(picture)->blendMethod); if (surface) return renderer->renderImage(rd); else if (paint) { - Compositor* cmp = nullptr; + RenderCompositor* cmp = nullptr; if (needComp) { cmp = renderer->target(bounds(renderer), renderer->colorSpace()); renderer->beginComposite(cmp, CompositeMethod::None, 255); @@ -134,7 +135,6 @@ Result Picture::Impl::load(ImageLoader* loader) Picture::Picture() : pImpl(new Impl(this)) { - Paint::pImpl->id = TVG_CLASS_ID_PICTURE; } @@ -150,9 +150,15 @@ unique_ptr Picture::gen() noexcept } -uint32_t Picture::identifier() noexcept +TVG_DEPRECATED uint32_t Picture::identifier() noexcept { - return TVG_CLASS_ID_PICTURE; + return (uint32_t) Type::Picture; +} + + +Type Picture::type() const noexcept +{ + return Type::Picture; } diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h index 3a4880cabaa..bbbc4391059 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.h +++ b/thirdparty/thorvg/src/renderer/tvgPicture.h @@ -60,7 +60,7 @@ struct Picture::Impl ImageLoader* loader = nullptr; Paint* paint = nullptr; //vector picture uses - Surface* surface = nullptr; //bitmap picture uses + RenderSurface* surface = nullptr; //bitmap picture uses RenderData rd = nullptr; //engine data float w = 0, h = 0; Picture* picture = nullptr; diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index b0ee42db8d0..eae44a2e8ad 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/thirdparty/thorvg/src/renderer/tvgRender.h @@ -24,6 +24,7 @@ #define _TVG_RENDER_H_ #include +#include #include "tvgCommon.h" #include "tvgArray.h" #include "tvgLock.h" @@ -36,9 +37,8 @@ using pixel_t = uint32_t; enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255}; -struct Surface; - -enum ColorSpace +//TODO: Move this in public header unifying with SwCanvas::Colorspace +enum ColorSpace : uint8_t { ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. @@ -48,7 +48,7 @@ enum ColorSpace Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace. }; -struct Surface +struct RenderSurface { union { pixel_t* data = nullptr; //system based data pointer @@ -62,11 +62,11 @@ struct Surface uint8_t channelSize = 0; bool premultiplied = false; //Alpha-premultiplied - Surface() + RenderSurface() { } - Surface(const Surface* rhs) + RenderSurface(const RenderSurface* rhs) { data = rhs->data; stride = rhs->stride; @@ -80,21 +80,10 @@ struct Surface }; -struct Compositor +struct RenderCompositor { CompositeMethod method; - uint8_t opacity; -}; - -struct Vertex -{ - Point pt; - Point uv; -}; - -struct Polygon -{ - Vertex vertex[3]; + uint8_t opacity; }; struct RenderRegion @@ -270,11 +259,66 @@ struct RenderShape float strokeMiterlimit() const { if (!stroke) return 4.0f; - return stroke->miterlimit;; } }; +struct RenderEffect +{ + RenderData rd = nullptr; + RenderRegion extend = {0, 0, 0, 0}; + SceneEffect type; + bool invalid = false; + + virtual ~RenderEffect() + { + free(rd); + } +}; + +struct RenderEffectGaussianBlur : RenderEffect +{ + float sigma; + uint8_t direction; //0: both, 1: horizontal, 2: vertical + uint8_t border; //0: duplicate, 1: wrap + uint8_t quality; //0 ~ 100 (optional) + + static RenderEffectGaussianBlur* gen(va_list& args) + { + auto inst = new RenderEffectGaussianBlur; + inst->sigma = std::max((float) va_arg(args, double), 0.0f); + inst->direction = std::min(va_arg(args, int), 2); + inst->border = std::min(va_arg(args, int), 1); + inst->quality = std::min(va_arg(args, int), 100); + inst->type = SceneEffect::GaussianBlur; + return inst; + } +}; + +struct RenderEffectDropShadow : RenderEffect +{ + uint8_t color[4]; //rgba + float angle; + float distance; + float sigma; + uint8_t quality; //0 ~ 100 (optional) + + static RenderEffectDropShadow* gen(va_list& args) + { + auto inst = new RenderEffectDropShadow; + inst->color[0] = va_arg(args, int); + inst->color[1] = va_arg(args, int); + inst->color[2] = va_arg(args, int); + inst->color[3] = std::min(va_arg(args, int), 255); + inst->angle = (float) va_arg(args, double); + inst->distance = (float) va_arg(args, double); + inst->sigma = std::max((float) va_arg(args, double), 0.0f); + inst->quality = std::min(va_arg(args, int), 100); + inst->type = SceneEffect::DropShadow; + return inst; + } +}; + class RenderMethod { private: @@ -287,7 +331,7 @@ public: virtual ~RenderMethod() {} virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; - virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 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; @@ -298,14 +342,17 @@ public: virtual bool viewport(const RenderRegion& vp) = 0; virtual bool blend(BlendMethod method) = 0; virtual ColorSpace colorSpace() = 0; - virtual const Surface* mainSurface() = 0; + virtual const RenderSurface* mainSurface() = 0; virtual bool clear() = 0; virtual bool sync() = 0; - virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0; - virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0; - virtual bool endComposite(Compositor* cmp) = 0; + virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0; + virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0; + virtual bool endComposite(RenderCompositor* cmp) = 0; + + virtual bool prepare(RenderEffect* effect) = 0; + virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0; }; static inline bool MASK_REGION_MERGING(CompositeMethod method) @@ -374,7 +421,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) return (((c) * (a) + 0xff) >> 8); } - } #endif //_TVG_RENDER_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgSaver.cpp b/thirdparty/thorvg/src/renderer/tvgSaver.cpp index 79302f69fa5..993fe6d80f8 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 f5809cf93b5..ce169d33ba6 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 TVG_CLASS_ID_SCENE; + return (uint32_t) Type::Scene; +} + + +Type Scene::type() const noexcept +{ + 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 190ecd31b91..7972ae33fb1 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,31 +102,34 @@ struct Scene::Impl //If scene has several children or only scene, it may require composition. //OPTIMIZE: the bitmap type of the picture would not need the composition. //OPTIMIZE: a single paint of a scene would not need the composition. - if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false; + if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false; return true; } RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { + this->vport = renderer->viewport(); + if ((needComp = needComposition(opacity))) { /* Overriding opacity value. If this scene is half-translucent, - It must do intermeidate composition with that opacity value. */ + It must do intermediate composition with that opacity value. */ this->opacity = opacity; opacity = 255; } for (auto paint : paints) { paint->pImpl->update(renderer, transform, clips, opacity, flag, false); } + return nullptr; } bool render(RenderMethod* renderer) { - Compositor* cmp = nullptr; + RenderCompositor* cmp = nullptr; auto ret = true; - renderer->blend(scene->blend()); + renderer->blend(PP(scene)->blendMethod); if (needComp) { cmp = renderer->target(bounds(renderer), renderer->colorSpace()); @@ -131,7 +140,16 @@ struct Scene::Impl ret &= paint->pImpl->render(renderer); } - if (cmp) renderer->endComposite(cmp); + if (cmp) { + //Apply post effects if any. + if (effects) { + auto direct = effects->count == 1 ? true : false; + for (auto e = effects->begin(); e < effects->end(); ++e) { + renderer->effect(cmp, *e, opacity, direct); + } + } + renderer->endComposite(cmp); + } return ret; } @@ -155,7 +173,23 @@ struct Scene::Impl if (y2 < region.y + region.h) y2 = (region.y + region.h); } - return {x1, y1, (x2 - x1), (y2 - y1)}; + //Extends the render region if post effects require + int32_t ex = 0, ey = 0, ew = 0, eh = 0; + if (effects) { + for (auto e = effects->begin(); e < effects->end(); ++e) { + auto effect = *e; + if (effect->rd || renderer->prepare(effect)) { + ex = std::min(ex, effect->extend.x); + ey = std::min(ey, effect->extend.y); + ew = std::max(ew, effect->extend.w); + eh = std::max(eh, effect->extend.h); + } + } + } + + auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh}; + ret.intersect(this->vport); + return ret; } bool bounds(float* px, float* py, float* pw, float* ph, bool stroking) @@ -203,6 +237,8 @@ struct Scene::Impl dup->paints.push_back(cdup); } + if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?"); + return scene; } @@ -218,6 +254,8 @@ struct Scene::Impl { return new SceneIterator(&paints); } + + Result resetEffects(); }; #endif //_TVG_SCENE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp index 3b9293a00e0..269d951f05a 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 440fb312b6c..42f81520606 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/thirdparty/thorvg/src/renderer/tvgShape.h @@ -53,9 +53,9 @@ struct Shape::Impl { if (!rd) return false; - Compositor* cmp = nullptr; + RenderCompositor* cmp = nullptr; - renderer->blend(shape->blend()); + renderer->blend(PP(shape)->blendMethod); if (needComp) { cmp = renderer->target(bounds(renderer), renderer->colorSpace()); @@ -83,7 +83,7 @@ struct Shape::Impl auto method = shape->composite(&target); if (!target || method == CompositeMethod::ClipPath) return false; if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) { - if (target->identifier() == TVG_CLASS_ID_SHAPE) { + if (target->type() == Type::Shape) { auto shape = static_cast(target); if (!shape->fill()) { uint8_t r, g, b, a; @@ -106,7 +106,7 @@ struct Shape::Impl if ((needComp = needComposition(opacity))) { /* Overriding opacity value. If this scene is half-translucent, - It must do intermeidate composition with that opacity value. */ + It must do intermediate composition with that opacity value. */ this->opacity = opacity; opacity = 255; } @@ -219,7 +219,7 @@ struct Shape::Impl rs.stroke = new RenderStroke(); } - if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) && + if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && rs.stroke->trim.simultaneous == simultaneous) return; rs.stroke->trim.begin = begin; diff --git a/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp index d762492f22d..6c4b6da1de4 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/tvgText.cpp b/thirdparty/thorvg/src/renderer/tvgText.cpp index d54c78783c5..b324b95049e 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.cpp +++ b/thirdparty/thorvg/src/renderer/tvgText.cpp @@ -37,7 +37,6 @@ Text::Text() : pImpl(new Impl(this)) { - Paint::pImpl->id = TVG_CLASS_ID_TEXT; } @@ -111,7 +110,7 @@ unique_ptr Text::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 cb9a76c0521..11e01b58ce6 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.h +++ b/thirdparty/thorvg/src/renderer/tvgText.h @@ -90,7 +90,7 @@ struct Text::Impl bool render(RenderMethod* renderer) { if (!loader) return true; - renderer->blend(paint->blend()); + renderer->blend(PP(paint)->blendMethod); return PP(shape)->render(renderer); } @@ -115,7 +115,7 @@ struct Text::Impl auto fill = P(shape)->rs.fill; if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) { auto scale = 1.0f / loader->scale; - if (fill->identifier() == TVG_CLASS_ID_LINEAR) { + if (fill->type() == Type::LinearGradient) { P(static_cast(fill))->x1 *= scale; P(static_cast(fill))->y1 *= scale; P(static_cast(fill))->x2 *= scale; diff --git a/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp index 067e35b1f0d..991f73fc542 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 2c5d84d266c..f9953f2fc9e 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.14.10 +VERSION=0.15.5 # Uncomment and set a git hash to use specific commit instead of tag. #GIT_COMMIT=