From 9e11b78d1cb6718fa8357be3b3f4b8bdadd6b660 Mon Sep 17 00:00:00 2001 From: Martin Capitanio Date: Thu, 11 May 2023 17:39:50 +0200 Subject: Update ThorVG to v0.9.0 https://github.com/thorvg/thorvg/releases/tag/v0.9.0 Fixes #72478 (cherry picked from commit 5db751832d54092c9d153c0fe07f9cc4616a2d01) --- thirdparty/thorvg/AUTHORS | 7 +- thirdparty/thorvg/LICENSE | 2 +- thirdparty/thorvg/inc/config.h | 2 +- thirdparty/thorvg/inc/thorvg.h | 185 ++- thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h | 40 +- thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp | 5 +- thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp | 55 +- thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp | 5 +- .../thorvg/src/lib/sw_engine/tvgSwMemPool.cpp | 11 +- .../thorvg/src/lib/sw_engine/tvgSwRaster.cpp | 688 +++++---- .../thorvg/src/lib/sw_engine/tvgSwRasterAvx.h | 22 +- thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h | 90 +- .../thorvg/src/lib/sw_engine/tvgSwRasterNeon.h | 24 +- .../thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h | 141 +- .../src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 21 +- .../thorvg/src/lib/sw_engine/tvgSwRenderer.cpp | 341 +++-- .../thorvg/src/lib/sw_engine/tvgSwRenderer.h | 15 +- thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp | 180 ++- thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp | 45 +- .../thorvg/src/lib/sw_engine/tvgSwStroke.cpp | 24 +- thirdparty/thorvg/src/lib/tvgAccessor.cpp | 19 +- thirdparty/thorvg/src/lib/tvgArray.h | 3 +- thirdparty/thorvg/src/lib/tvgBezier.cpp | 5 +- thirdparty/thorvg/src/lib/tvgBezier.h | 3 +- thirdparty/thorvg/src/lib/tvgBinaryDesc.h | 6 +- thirdparty/thorvg/src/lib/tvgCanvas.cpp | 15 +- thirdparty/thorvg/src/lib/tvgCanvasImpl.h | 3 +- thirdparty/thorvg/src/lib/tvgCommon.h | 13 +- thirdparty/thorvg/src/lib/tvgFill.cpp | 3 +- thirdparty/thorvg/src/lib/tvgFill.h | 3 +- thirdparty/thorvg/src/lib/tvgGlCanvas.cpp | 3 +- thirdparty/thorvg/src/lib/tvgInitializer.cpp | 16 +- thirdparty/thorvg/src/lib/tvgIteratorAccessor.h | 5 +- thirdparty/thorvg/src/lib/tvgLinearGradient.cpp | 3 +- thirdparty/thorvg/src/lib/tvgLoadModule.h | 9 +- thirdparty/thorvg/src/lib/tvgLoader.cpp | 3 +- thirdparty/thorvg/src/lib/tvgLoader.h | 3 +- thirdparty/thorvg/src/lib/tvgLzw.cpp | 6 +- thirdparty/thorvg/src/lib/tvgLzw.h | 3 +- thirdparty/thorvg/src/lib/tvgMath.h | 5 +- thirdparty/thorvg/src/lib/tvgPaint.cpp | 75 +- thirdparty/thorvg/src/lib/tvgPaint.h | 13 +- thirdparty/thorvg/src/lib/tvgPicture.cpp | 23 +- thirdparty/thorvg/src/lib/tvgPictureImpl.h | 116 +- thirdparty/thorvg/src/lib/tvgRadialGradient.cpp | 3 +- thirdparty/thorvg/src/lib/tvgRender.cpp | 3 +- thirdparty/thorvg/src/lib/tvgRender.h | 185 ++- thirdparty/thorvg/src/lib/tvgSaveModule.h | 5 +- thirdparty/thorvg/src/lib/tvgSaver.cpp | 3 +- thirdparty/thorvg/src/lib/tvgScene.cpp | 3 +- thirdparty/thorvg/src/lib/tvgSceneImpl.h | 34 +- thirdparty/thorvg/src/lib/tvgShape.cpp | 168 +-- thirdparty/thorvg/src/lib/tvgShapeImpl.h | 364 +++-- thirdparty/thorvg/src/lib/tvgSwCanvas.cpp | 5 +- thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp | 12 +- thirdparty/thorvg/src/lib/tvgTaskScheduler.h | 11 +- .../src/loaders/external_png/tvgPngLoader.cpp | 62 +- .../thorvg/src/loaders/external_png/tvgPngLoader.h | 5 +- thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp | 38 +- thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h | 5 +- thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp | 34 +- thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h | 2 +- thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp | 37 +- thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h | 5 +- .../thorvg/src/loaders/svg/tvgSvgCssStyle.cpp | 75 +- thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h | 2 +- thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp | 1462 ++++++++++++-------- thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h | 5 +- .../thorvg/src/loaders/svg/tvgSvgLoaderCommon.h | 110 +- thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp | 11 +- thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h | 2 +- .../thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp | 61 +- .../thorvg/src/loaders/svg/tvgSvgSceneBuilder.h | 4 +- thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp | 6 +- thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h | 2 +- thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp | 2 +- thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h | 2 +- .../src/loaders/tvg/tvgTvgBinInterpreter.cpp | 55 +- thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h | 2 +- thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp | 3 +- thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h | 2 +- thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp | 25 +- thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h | 3 +- thirdparty/thorvg/update-thorvg.sh | 4 +- 84 files changed, 3183 insertions(+), 1898 deletions(-) (limited to 'thirdparty/thorvg') diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS index 11f3f170a5..c5f8529da9 100644 --- a/thirdparty/thorvg/AUTHORS +++ b/thirdparty/thorvg/AUTHORS @@ -1,4 +1,4 @@ -Hermet Park +Hermet Park Prudhvi Raj Vasireddi Junsu Choi Pranay Samanta @@ -15,3 +15,8 @@ Michal Maciola Peter Vullings K. S. Ernest (iFire) Lee Rémi Verschelde +Martin Liska +Vincenzo Pupillo +EunSik Jeong +Samsung Electronics Co., Ltd +Rafał Mikrut diff --git a/thirdparty/thorvg/LICENSE b/thirdparty/thorvg/LICENSE index 2f0361a864..d056ff6cbf 100644 --- a/thirdparty/thorvg/LICENSE +++ b/thirdparty/thorvg/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020 - 2022 notice for the ThorVG Project (see AUTHORS) +Copyright (c) 2020 - 2023 notice for the ThorVG Project (see AUTHORS) 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: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 78522d6d2d..89caf0161d 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -13,5 +13,5 @@ #define THORVG_JPG_LOADER_SUPPORT 1 -#define THORVG_VERSION_STRING "0.8.4" +#define THORVG_VERSION_STRING "0.9.0" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index b1f2e9e286..bde045f76c 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/thirdparty/thorvg/inc/thorvg.h @@ -2,11 +2,12 @@ * @file thorvg.h * * The main APIs enabling the TVG initialization, preparation of the canvas and provisioning of its content: - * - drawing shapes such as line, curve, arc, rectangle, circle or user-defined - * - drawing pictures - SVG, PNG, JPG, RAW - * - solid or gradient filling - * - continuous and dashed stroking - * - clipping and masking + * - drawing shapes: line, arc, curve, path, polygon... + * - drawing pictures: tvg, svg, png, jpg, bitmap... + * - drawing fillings: solid, linear and radial gradient... + * - drawing stroking: continuous stroking with arbitrary width, join, cap, dash styles. + * - drawing composition: blending, masking, path clipping... + * - drawing scene graph & affine transformation (translation, rotation, scale, ...) * and finally drawing the canvas and TVG termination. */ @@ -14,20 +15,36 @@ #ifndef _THORVG_H_ #define _THORVG_H_ +#include #include #include -#ifdef TVG_BUILD - #if defined(_WIN32) && !defined(__clang__) - #define TVG_EXPORT __declspec(dllexport) - #define TVG_DEPRECATED __declspec(deprecated) +#ifdef TVG_API + #undef TVG_API +#endif + +#if defined(_WIN32) && !defined(__clang__) + #if TVG_BUILD + #if TVG_EXPORT + #define TVG_API __declspec(dllexport) + #else + #define TVG_API + #endif #else - #define TVG_EXPORT __attribute__ ((visibility ("default"))) - #define TVG_DEPRECATED __attribute__ ((__deprecated__)) + #define TVG_API #endif + #define TVG_DEPRECATED __declspec(deprecated) #else - #define TVG_EXPORT - #define TVG_DEPRECATED + #if TVG_BUILD + #if TVG_EXPORT + #define TVG_API __attribute__ ((visibility ("default"))) + #else + #define TVG_API + #endif + #else + #define TVG_API + #endif + #define TVG_DEPRECATED __attribute__ ((__deprecated__)) #endif #ifdef __cplusplus @@ -147,7 +164,7 @@ enum class CompositeMethod ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible. InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible. - LumaMask ///< @BETA_API The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. + LumaMask ///< The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. @since 0.9 }; /** @@ -183,6 +200,33 @@ struct Matrix float e31, e32, e33; }; +/** + * @brief A data structure representing a texture mesh vertex + * + * @param pt The vertex coordinate + * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0) + * + * @BETA_API + */ +struct Vertex +{ + Point pt; + Point uv; +}; + + +/** + * @brief A data structure representing a triange in a texture mesh + * + * @param vertex The three vertices that make up the polygon + * + * @BETA_API + */ +struct Polygon +{ + Vertex vertex[3]; +}; + /** * @class Paint @@ -193,7 +237,7 @@ struct Matrix * Paint represents such a graphical object and its behaviors such as duplication, transformation and composition. * TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them. */ -class TVG_EXPORT Paint +class TVG_API Paint { public: virtual ~Paint(); @@ -263,6 +307,7 @@ public: * @return Result::Success when succeed. * * @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; @@ -335,28 +380,12 @@ public: */ CompositeMethod composite(const Paint** target) const noexcept; - /** - * @brief Gets the composition source object and the composition method. - * - * @param[out] source The paint of the composition source object. - * @param[out] method The method used to composite the source object with the target. - * - * @return Result::Success when the paint object used as a composition target, Result::InsufficientCondition otherwise. - * - * @warning Please do not use it, this API is not official one. It could be modified in the next version. - * - * @BETA_API - */ - Result composite(const Paint** source, CompositeMethod* method) 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. - * - * @BETA_API */ uint32_t identifier() const noexcept; @@ -376,7 +405,7 @@ public: * It specifies the gradient behavior in case the area defined by the gradient bounds * is smaller than the area to be filled. */ -class TVG_EXPORT Fill +class TVG_API Fill { public: /** @@ -463,8 +492,6 @@ public: * This method can be called for checking the current concrete instance type. * * @return The type id of the Fill instance. - * - * @BETA_API */ uint32_t identifier() const noexcept; @@ -482,7 +509,7 @@ public: * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. */ -class TVG_EXPORT Canvas +class TVG_API Canvas { public: Canvas(RenderMethod*); @@ -578,7 +605,7 @@ public: * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds. * The behavior outside the gradient bounds depends on the value specified in the spread API. */ -class TVG_EXPORT LinearGradient final : public Fill +class TVG_API LinearGradient final : public Fill { public: ~LinearGradient(); @@ -630,8 +657,6 @@ public: * This method can be referred for identifying the LinearGradient class type. * * @return The type id of the LinearGradient class. - * - * @BETA_API */ static uint32_t identifier() noexcept; @@ -645,7 +670,7 @@ public: * @brief A class representing the radial gradient fill of the Shape object. * */ -class TVG_EXPORT RadialGradient final : public Fill +class TVG_API RadialGradient final : public Fill { public: ~RadialGradient(); @@ -689,8 +714,6 @@ public: * This method can be referred for identifying the RadialGradient class type. * * @return The type id of the RadialGradient class. - * - * @BETA_API */ static uint32_t identifier() noexcept; @@ -710,7 +733,7 @@ public: * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders. * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. */ -class TVG_EXPORT Shape final : public Paint +class TVG_API Shape final : public Paint { public: ~Shape(); @@ -943,6 +966,7 @@ public: * @return Result::Success when succeed. * * @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) noexcept; @@ -968,6 +992,18 @@ public: */ Result fill(FillRule r) noexcept; + + /** + * @brief Sets the rendering order of the stroke and the fill. + * + * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @BETA_API + */ + Result order(bool strokeFirst) noexcept; + + /** * @brief Gets the commands data of the path. * @@ -1074,8 +1110,6 @@ public: * This method can be referred for identifying the Shape class type. * * @return The type id of the Shape class. - * - * @BETA_API */ static uint32_t identifier() noexcept; @@ -1091,7 +1125,7 @@ public: * * @note Supported formats are depended on the available TVG loaders. */ -class TVG_EXPORT Picture final : public Paint +class TVG_API Picture final : public Paint { public: ~Picture(); @@ -1185,11 +1219,50 @@ public: /** * @brief Loads a raw data from a memory block with a given size. * + * @retval Result::Success When succeed, Result::InsufficientCondition otherwise. + * @retval Result::FailedAllocation An internal error possibly with memory allocation. + * + * @since 0.9 + */ + Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; + + /** + * @brief Sets or removes the triangle mesh to deform the image. + * + * If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the + * image data will be used as the texture. + * + * If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed. + * + * Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support. + * mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh. + * + * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh. + * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh. + * + * @return Result::Success When succeed. + * @return Result::Unknown If fails + * + * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. * @warning Please do not use it, this API is not official one. It could be modified in the next version. * * @BETA_API */ - Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; + Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept; + + /** + * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh. + * + * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh. + * + * @return uint32_t The number of polygons in the array. + * + * @note Modifying the triangles returned by this method will modify them directly within the mesh. + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @BETA_API + */ + uint32_t mesh(const Polygon** triangles) const noexcept; /** * @brief Gets the position and the size of the loaded SVG picture. @@ -1213,8 +1286,6 @@ public: * This method can be referred for identifying the Picture class type. * * @return The type id of the Picture class. - * - * @BETA_API */ static uint32_t identifier() noexcept; @@ -1233,7 +1304,7 @@ public: * As a group, the scene can be transformed, made translucent and composited with other target paints, * its children will be affected by the scene world. */ -class TVG_EXPORT Scene final : public Paint +class TVG_API Scene final : public Paint { public: ~Scene(); @@ -1293,8 +1364,6 @@ public: * This method can be referred for identifying the Scene class type. * * @return The type id of the Scene class. - * - * @BETA_API */ static uint32_t identifier() noexcept; @@ -1307,7 +1376,7 @@ public: * * @brief A class for the rendering graphical elements with a software raster engine. */ -class TVG_EXPORT SwCanvas final : public Canvas +class TVG_API SwCanvas final : public Canvas { public: ~SwCanvas(); @@ -1398,7 +1467,7 @@ public: * * @BETA_API */ -class TVG_EXPORT GlCanvas final : public Canvas +class TVG_API GlCanvas final : public Canvas { public: ~GlCanvas(); @@ -1430,7 +1499,7 @@ public: * * @brief A class that enables initialization and termination of the TVG engines. */ -class TVG_EXPORT Initializer final +class TVG_API Initializer final { public: /** @@ -1492,7 +1561,7 @@ public: * * @since 0.5 */ -class TVG_EXPORT Saver final +class TVG_API Saver final { public: ~Saver(); @@ -1562,13 +1631,13 @@ public: * * @BETA_API */ -class TVG_EXPORT Accessor final +class TVG_API Accessor final { public: ~Accessor(); /** - * @brief Access the Picture scene stree nodes. + * @brief Set the access function for traversing the Picture scene tree nodes. * * @param[in] picture The picture node to traverse the internal scene-tree. * @param[in] func The callback function calling for every paint nodes of the Picture. @@ -1579,7 +1648,7 @@ public: * * @BETA_API */ - std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint)) noexcept; + std::unique_ptr set(std::unique_ptr picture, std::function func) noexcept; /** * @brief Creates a new Accessor object. diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h index 157fdb8f82..0e9029bf73 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. +/* + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SW_COMMON_H_ #define _TVG_SW_COMMON_H_ @@ -222,11 +223,16 @@ struct SwImage { SwOutline* outline = nullptr; SwRleData* rle = nullptr; - uint32_t* data = nullptr; + union { + pixel_t* data; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels + uint8_t* buf8; //for explicit 8bits grayscale + }; uint32_t w, h, stride; int32_t ox = 0; //offset x int32_t oy = 0; //offset y float scale; + uint8_t channelSize; bool direct = false; //draw image directly (with offset) bool scaled = false; //draw scaled image @@ -235,7 +241,7 @@ struct SwImage struct SwBlender { uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - uint32_t (*lumaValue)(uint32_t c); + uint8_t (*luma)(uint8_t* c); }; struct SwCompositor; @@ -301,12 +307,12 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); void shapeReset(SwShape* shape); -bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); bool shapePrepared(const SwShape* shape); -bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias); +bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); -void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform); -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform); +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); void shapeFree(SwShape* shape); void shapeDelStroke(SwShape* shape); bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); @@ -316,12 +322,12 @@ void shapeResetStrokeFill(SwShape* shape); void shapeDelFill(SwShape* shape); void shapeDelStrokeFill(SwShape* shape); -void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform); +void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform); bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); @@ -334,10 +340,12 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len); 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 rleClipPath(SwRleData *rle, const SwRleData *clip); -void rleClipRect(SwRleData *rle, const SwBBox* clip); +void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2); +void rleClipPath(SwRleData* rle, const SwRleData* clip); +void rleClipRect(SwRleData* rle, const SwBBox* clip); SwMpool* mpoolInit(uint32_t threads); bool mpoolTerm(SwMpool* mpool); @@ -350,11 +358,13 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); -bool rasterClear(SwSurface* surface); +bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); -void rasterUnpremultiply(SwSurface* surface); +void rasterUnpremultiply(Surface* surface); +void rasterPremultiply(Surface* surface); +bool rasterConvertCS(Surface* surface, ColorSpace to); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp index 04014a9ec3..694bc35231 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. +/* + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgSwCommon.h" diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp index c02e28b432..9e215dbefd 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgSwCommon.h" @@ -33,7 +34,7 @@ static inline bool _onlyShifted(const Matrix* m) } -static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid) +static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid) { image->outline = mpoolReqOutline(mpool, tid); auto outline = image->outline; @@ -51,10 +52,50 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, outline->closed[0] = true; } - auto w = static_cast(image->w); - auto h = static_cast(image->h); + Point to[4]; + if (mesh->triangleCnt > 0) { + // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple + // places. We should be able to re-use one we have already done? Also see: + // tvgPictureImpl.h --> bounds + // tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh + // + // TODO: Should we calculate the exact path(s) of the triangle mesh instead? + // i.e. copy tvgSwShape.capp -> _genOutline? + // + // TODO: Cntrs? + auto triangles = mesh->triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; + + for (uint32_t i = 0; i < mesh->triangleCnt; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + to[0] = {min.x, min.y}; + to[1] = {max.x, min.y}; + to[2] = {max.x, max.y}; + to[3] = {min.x, max.y}; + } else { + auto w = static_cast(image->w); + auto h = static_cast(image->h); + to[0] = {0, 0}; + to[1] = {w, 0}; + to[2] = {w, h}; + to[3] = {0, h}; + } - Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}}; for (int i = 0; i < 4; i++) { outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform); outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT; @@ -78,7 +119,7 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, /* External Class Implementation */ /************************************************************************/ -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { image->direct = _onlyShifted(transform); @@ -96,7 +137,7 @@ bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipReg else image->scaled = false; } - if (!_genOutline(image, transform, mpool, tid)) return false; + if (!_genOutline(image, mesh, transform, mpool, tid)) return false; return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct); } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp index 1027bb1f79..5a4f58d9a6 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. +/* + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include "tvgSwCommon.h" diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp index d2962e6d8d..05ff9ddf0b 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgSwCommon.h" @@ -59,16 +60,16 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx) SwMpool* mpoolInit(unsigned threads) { - if (threads == 0) threads = 1; + auto allocSize = threads + 1; auto mpool = static_cast(calloc(sizeof(SwMpool), 1)); - mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * threads)); + mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); if (!mpool->outline) goto err; - mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * threads)); + mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); if (!mpool->strokeOutline) goto err; - mpool->allocSize = threads; + mpool->allocSize = allocSize; return mpool; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp index ffd74bdd47..1f10afd9b3 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. +/* + * Copyright (c) 2020 - 2023 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 @@ -37,8 +37,8 @@ /************************************************************************/ constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; - -static inline uint32_t _multiplyAlpha(uint32_t c, uint32_t a) +template +static inline T _multiply(T c, T a) { return ((c * a + 0xff) >> 8); } @@ -56,15 +56,29 @@ static inline uint32_t _ialpha(uint32_t c) } -static inline uint32_t _abgrLumaValue(uint32_t c) +static inline uint8_t _alpha(uint8_t* a) { - return ((((c&0xff)*54) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B + return *a; } -static inline uint32_t _argbLumaValue(uint32_t c) +static inline uint8_t _ialpha(uint8_t* a) { - return ((((c&0xff)*19) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R + return ~(*a); +} + + +static inline uint8_t _abgrLuma(uint8_t* c) +{ + auto v = *(uint32_t*)c; + return ((((v&0xff)*54) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B +} + + +static inline uint8_t _argbLuma(uint8_t* c) +{ + auto v = *(uint32_t*)c; + return ((((v&0xff)*19) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R } @@ -148,65 +162,95 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t } +void _rasterGrayscale8(uint8_t *dst, uint32_t val, uint32_t offset, int32_t len) +{ + cRasterPixels(dst, val, offset, len); +} + /************************************************************************/ /* Rect */ /************************************************************************/ -static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*)) { - TVGLOG("SW_ENGINE", "Masked Rect"); - - auto buffer = surface->buffer + (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); - - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer - - for (uint32_t y = 0; y < h; ++y) { - auto dst = &buffer[y * surface->stride]; - auto cmp = &cbuffer[y * surface->stride]; - for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp) { - auto tmp = ALPHA_BLEND(color, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + ((region.min.y * surface->compositor->image.stride + region.min.x) * csize); //compositor buffer + + TVGLOG("SW_ENGINE", "Masked Rect [Region: %lu %lu %u %u]", region.min.x, region.min.y, w, h); + + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + auto cmp = &cbuffer[y * surface->stride * csize]; + for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { + auto tmp = ALPHA_BLEND(color, blender(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + } + } + //8bits grayscale + } 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) { + auto dst = &buffer[y * surface->stride]; + auto cmp = &cbuffer[y * surface->stride * csize]; + for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { + auto tmp = _multiply(a, blender(cmp)); + *dst = tmp + _multiply(*dst, _ialpha(tmp)); + } } } return true; } -static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t color) +static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b) { - auto buffer = surface->buffer + (region.min.y * surface->stride); 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) { - rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w); + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, 255); + auto buffer = surface->buf32 + (region.min.y * surface->stride); + for (uint32_t y = 0; y < h; ++y) { + rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w); + } + //8bits grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + auto buffer = surface->buf8 + (region.min.y * surface->stride); + for (uint32_t y = 0; y < h; ++y) { + _rasterGrayscale8(buffer + y * surface->stride, 255, region.min.x, w); + } } return true; } -static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint8_t opacity) +static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (_compositing(surface)) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterMaskedRect(surface, region, color, _alpha); + return _rasterMaskedRect(surface, region, r, g, b, a, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterMaskedRect(surface, region, color, _ialpha); + return _rasterMaskedRect(surface, region, r, g, b, a, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterMaskedRect(surface, region, color, surface->blender.lumaValue); + return _rasterMaskedRect(surface, region, r, g, b, a, surface->blender.luma); } } else { - if (opacity == 255) { - return _rasterSolidRect(surface, region, color); + if (a == 255) { + return _rasterSolidRect(surface, region, r, g, b); } else { #if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRect(surface, region, color); + return avxRasterTranslucentRect(surface, region, r, g, b, a); #elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRect(surface, region, color); + return neonRasterTranslucentRect(surface, region, r, g, b, a); #else - return cRasterTranslucentRect(surface, region, color); + return cRasterTranslucentRect(surface, region, r, g, b, a); #endif } } @@ -218,41 +262,74 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color /* Rle */ /************************************************************************/ -static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Masked Rle"); auto span = rle->spans; uint32_t src; - auto cbuffer = surface->compositor->image.data; - - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x]; - if (span->coverage == 255) src = color; - else src = ALPHA_BLEND(color, span->coverage); - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto cbuffer = surface->compositor->image.buf8; + auto csize = surface->compositor->image.channelSize; + + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, a); + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + if (span->coverage == 255) src = color; + else src = ALPHA_BLEND(color, span->coverage); + for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { + auto tmp = ALPHA_BLEND(src, blender(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + } + } + //8bit grayscale + } 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]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + if (span->coverage == 255) src = a; + else src = _multiply(a, span->coverage); + for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { + auto tmp = _multiply(src, blender(cmp)); + *dst = tmp + _multiply(*dst, _ialpha(tmp)); + } } } return true; } -static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t color) +static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b) { auto span = rle->spans; - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - if (span->coverage == 255) { - rasterRGBA32(surface->buffer + span->y * surface->stride, color, span->x, span->len); - } else { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto src = ALPHA_BLEND(color, span->coverage); - auto ialpha = 255 - span->coverage; - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = src + ALPHA_BLEND(*dst, ialpha); + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, 255); + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + if (span->coverage == 255) { + rasterRGBA32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); + } else { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto src = ALPHA_BLEND(color, span->coverage); + auto ialpha = 255 - span->coverage; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + } + } + } + //8bit grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + if (span->coverage == 255) { + _rasterGrayscale8(surface->buf8 + span->y * surface->stride, 255, span->x, span->len); + } else { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = span->coverage; + } } } } @@ -260,28 +337,28 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t c } -static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8_t opacity) +static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (!rle) return false; if (_compositing(surface)) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterMaskedRle(surface, rle, color, _alpha); + return _rasterMaskedRle(surface, rle, r, g, b, a, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterMaskedRle(surface, rle, color, _ialpha); + return _rasterMaskedRle(surface, rle, r, g, b, a, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterMaskedRle(surface, rle, color, surface->blender.lumaValue); + return _rasterMaskedRle(surface, rle, r, g, b, a, surface->blender.luma); } } else { - if (opacity == 255) { - return _rasterSolidRle(surface, rle, color); + if (a == 255) { + return _rasterSolidRle(surface, rle, r, g, b); } else { #if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRle(surface, rle, color); + return avxRasterTranslucentRle(surface, rle, r, g, b, a); #elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRle(surface, rle, color); + return neonRasterTranslucentRle(surface, rle, r, g, b, a); #else - return cRasterTranslucentRle(surface, rle, color); + return cRasterTranslucentRle(surface, rle, r, g, b, a); #endif } } @@ -301,7 +378,7 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.lumaValue); + return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.luma); } } else { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr); @@ -313,25 +390,26 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c /* RLE Scaled RGBA Image */ /************************************************************************/ -static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Scaled Masked Translucent Rle Image"); auto span = image->rle->spans; + auto csize = surface->compositor->image.channelSize; //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; - auto alpha = _multiplyAlpha(span->coverage, opacity); - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto alpha = _multiply(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); + auto tmp = ALPHA_BLEND(src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -340,14 +418,14 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = span->y * itransform->e22 + itransform->e23; if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; - auto alpha = _multiplyAlpha(span->coverage, opacity); - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto alpha = _multiply(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); + auto tmp = ALPHA_BLEND(src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -356,32 +434,33 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const } -static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Scaled Masked Rle Image"); auto span = image->rle->spans; + auto csize = surface->compositor->image.channelSize; //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); + auto tmp = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); + auto tmp = ALPHA_BLEND(src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -391,21 +470,21 @@ static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* i for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = span->y * itransform->e22 + itransform->e23; if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto tmp = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp)); + auto tmp = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage); + auto tmp = ALPHA_BLEND(src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -424,12 +503,12 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto alpha = _multiplyAlpha(span->coverage, opacity); + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = _multiply(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -438,12 +517,12 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = span->y * itransform->e22 + itransform->e23; if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto alpha = _multiplyAlpha(span->coverage, opacity); + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = _multiply(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -461,19 +540,19 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale); + auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -483,19 +562,19 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto sy = span->y * itransform->e22 + itransform->e23; if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy); + auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -522,7 +601,7 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); + return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -530,7 +609,7 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); + return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma); } } } else { @@ -545,26 +624,27 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const /* RLE Direct RGBA Image */ /************************************************************************/ -static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Direct Masked Rle Image"); auto span = image->rle->spans; - auto cbuffer = surface->compositor->image.data; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x]; - auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox); - auto alpha = _multiplyAlpha(span->coverage, opacity); + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + 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, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(alpha, blendMethod(*cmp))); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, _multiply(alpha, blender(cmp))); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -573,25 +653,26 @@ static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const } -static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Direct Masked Rle Image"); auto span = image->rle->spans; - auto cbuffer = surface->compositor->image.data; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x]; - auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(span->coverage, blendMethod(*cmp))); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, _multiply(span->coverage, blender(cmp))); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -605,9 +686,9 @@ static bool _rasterDirectTranslucentRleRGBAImage(SwSurface* surface, const SwIma auto span = image->rle->spans; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox); - auto alpha = _multiplyAlpha(span->coverage, opacity); + 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); for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { auto src = ALPHA_BLEND(*img, alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); @@ -622,8 +703,8 @@ static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image) auto span = image->rle->spans; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); if (span->coverage == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { *dst = *img + ALPHA_BLEND(*dst, _ialpha(*img)); @@ -648,7 +729,7 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.lumaValue); + return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.luma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -656,7 +737,7 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.lumaValue); + return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.luma); } } } else { @@ -679,7 +760,7 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.lumaValue); + return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.luma); } } else { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, nullptr); @@ -687,18 +768,34 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return false; } +static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity) +{ + if (_compositing(surface)) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.luma); + } + } else { + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, nullptr); + } + return false; +} + /************************************************************************/ /*Scaled RGBA Image */ /************************************************************************/ - -static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Scaled Masked Image"); - auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x); + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { @@ -707,15 +804,15 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw if (sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp)); - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); + auto alpha = _multiply(opacity, blender(cmp)); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; } // Up-Scaled } else { @@ -724,27 +821,28 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw if ((uint32_t)sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp)); - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); + auto alpha = _multiply(opacity, blender(cmp)); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; } } return true; } -static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t (*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Scaled Masked Image"); - auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x); + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { @@ -753,14 +851,14 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag if (sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp)); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; } // Up-Scaled } else { @@ -769,14 +867,14 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag if ((uint32_t)sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp)); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp)); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; } } return true; @@ -785,7 +883,7 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) { - auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x); + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { @@ -796,7 +894,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), opacity); + auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), opacity); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -809,7 +907,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), opacity); + auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), opacity); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -820,7 +918,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale) { - auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x); + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { @@ -831,7 +929,7 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale); + auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -844,7 +942,7 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy); + auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -870,7 +968,7 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); + return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -878,7 +976,7 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); + return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma); } } } else { @@ -893,54 +991,56 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat /* Direct RGBA Image */ /************************************************************************/ -static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t (*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Direct Masked Image"); - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h2 = static_cast(region.max.y - region.min.y); auto w2 = static_cast(region.max.x - region.min.x); + auto csize = surface->compositor->image.channelSize; - auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } buffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; sbuffer += image->stride; } return true; } -static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint8_t (*blender)(uint8_t*)) { TVGLOG("SW_ENGINE", "Direct Masked Translucent Image"); - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h2 = static_cast(region.max.y - region.min.y); auto w2 = static_cast(region.max.x - region.min.x); + auto csize = surface->compositor->image.channelSize; - auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) { - auto tmp = ALPHA_BLEND(*src, _multiplyAlpha(opacity, blendMethod(*cmp))); + for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, _multiply(opacity, blender(cmp))); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } buffer += surface->stride; - cbuffer += surface->compositor->image.stride; + cbuffer += surface->compositor->image.stride * csize; sbuffer += image->stride; } return true; @@ -949,8 +1049,8 @@ static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const Sw static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity) { - auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; @@ -968,8 +1068,8 @@ static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region) { - auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; @@ -994,7 +1094,7 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.lumaValue); + return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.luma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -1002,7 +1102,7 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.lumaValue); + return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.luma); } } } else { @@ -1034,14 +1134,15 @@ static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* t /* Rect Linear Gradient */ /************************************************************************/ -static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t (*blender)(uint8_t*)) { if (fill->linear.len < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); if (!sbuffer) return false; @@ -1051,12 +1152,12 @@ static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& re auto dst = buffer; auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } buffer += surface->stride; - cbuffer += surface->stride; + cbuffer += surface->stride * csize; } return true; } @@ -1066,7 +1167,7 @@ static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBo { if (fill->linear.len < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); @@ -1089,7 +1190,7 @@ static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& reg { if (fill->linear.len < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); @@ -1108,7 +1209,7 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); + return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.luma); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill); @@ -1122,29 +1223,30 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, /* Rle Linear Gradient */ /************************************************************************/ -static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t (*blender)(uint8_t*)) { if (fill->linear.len < FLT_EPSILON) return false; auto span = rle->spans; - auto cbuffer = surface->compositor->image.data; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { fillFetchLinear(fill, buffer, span->y, span->x, span->len); - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto src = buffer; if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { auto ialpha = 255 - span->coverage; - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } @@ -1163,7 +1265,7 @@ static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleD if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; fillFetchLinear(fill, buffer, span->y, span->x, span->len); if (span->coverage == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst) { @@ -1191,10 +1293,10 @@ static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* r for (uint32_t i = 0; i < rle->size; ++i, ++span) { if (span->coverage == 255) { - fillFetchLinear(fill, surface->buffer + span->y * surface->stride + span->x, span->y, span->x, span->len); + fillFetchLinear(fill, surface->buf32 + span->y * surface->stride + span->x, span->y, span->x, span->len); } else { fillFetchLinear(fill, buf, span->y, span->x, span->len); - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; for (uint32_t x = 0; x < span->len; ++x) { dst[x] = INTERPOLATE(span->coverage, buf[x], dst[x]); } @@ -1214,7 +1316,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); + return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.luma); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill); @@ -1228,14 +1330,15 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c /* Rect Radial Gradient */ /************************************************************************/ -static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t(*blender)(uint8_t*)) { if (fill->radial.a < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); if (!sbuffer) return false; @@ -1245,12 +1348,12 @@ static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& re auto dst = buffer; auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } buffer += surface->stride; - cbuffer += surface->stride; + cbuffer += surface->stride * csize; } return true; } @@ -1260,7 +1363,7 @@ static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBo { if (fill->radial.a < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); @@ -1283,7 +1386,7 @@ static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& reg { if (fill->radial.a < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + 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); @@ -1303,7 +1406,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); + return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.luma); } } else { if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill); @@ -1317,28 +1420,29 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, /* RLE Radial Gradient */ /************************************************************************/ -static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t(*blender)(uint8_t*)) { if (fill->radial.a < FLT_EPSILON) return false; auto span = rle->spans; - auto cbuffer = surface->compositor->image.data; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { fillFetchRadial(fill, buffer, span->y, span->x, span->len); - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto src = buffer; if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, blender(cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blendMethod(*cmp)), *dst); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { + auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blender(cmp)), *dst); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } @@ -1356,7 +1460,7 @@ static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleD if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; fillFetchRadial(fill, buffer, span->y, span->x, span->len); if (span->coverage == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst) { @@ -1383,7 +1487,7 @@ static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* r auto span = rle->spans; for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage == 255) { fillFetchRadial(fill, dst, span->y, span->x, span->len); } else { @@ -1408,7 +1512,7 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); + return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.luma); } } else { if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill); @@ -1417,7 +1521,6 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c return false; } - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -1429,47 +1532,65 @@ void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) #elif defined(THORVG_NEON_VECTOR_SUPPORT) neonRasterRGBA32(dst, val, offset, len); #else - cRasterRGBA32(dst, val, offset, len); + cRasterPixels(dst, val, offset, len); #endif } bool rasterCompositor(SwSurface* surface) { - if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) { + if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { surface->blender.join = _abgrJoin; - surface->blender.lumaValue = _abgrLumaValue; - } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { + surface->blender.luma = _abgrLuma; + } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) { surface->blender.join = _argbJoin; - surface->blender.lumaValue = _argbLumaValue; + surface->blender.luma = _argbLuma; } else { - //What Color Space ??? + TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs); return false; } return true; } -bool rasterClear(SwSurface* surface) +bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { - if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false; + if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false; - if (surface->w == surface->stride) { - rasterRGBA32(surface->buffer, 0x00000000, 0, surface->w * surface->h); - } else { - for (uint32_t i = 0; i < surface->h; i++) { - rasterRGBA32(surface->buffer + surface->stride * i, 0x00000000, 0, surface->w); + //full clear + if (surface->channelSize == sizeof(uint32_t)) { + if (w == surface->stride) { + rasterRGBA32(surface->buf32 + (surface->stride * y), 0x00000000, 0, w * h); + } else { + auto buffer = surface->buf32 + (surface->stride * y + x); + for (uint32_t i = 0; i < h; i++) { + rasterRGBA32(buffer + (surface->stride * i), 0x00000000, 0, w); + } + } + //partial clear + } else if (surface->channelSize == sizeof(uint8_t)) { + if (w == surface->stride) { + _rasterGrayscale8(surface->buf8 + (surface->stride * y), 0x00, 0, w * h); + } else { + auto buffer = surface->buf8 + (surface->stride * y + x); + for (uint32_t i = 0; i < h; i++) { + _rasterGrayscale8(buffer + (surface->stride * i), 0x00, 0, w); + } } } return true; } -void rasterUnpremultiply(SwSurface* surface) +void rasterUnpremultiply(Surface* surface) { + if (surface->channelSize != sizeof(uint32_t)) return; + + TVGLOG("SW_ENGINE", "Unpremultiply [Size: %d x %d]", surface->w, surface->h); + //OPTIMIZE_ME: +SIMD for (uint32_t y = 0; y < surface->h; y++) { - auto buffer = surface->buffer + surface->stride * y; + auto buffer = surface->buf32 + surface->stride * y; for (uint32_t x = 0; x < surface->w; ++x) { uint8_t a = buffer[x] >> 24; if (a == 255) { @@ -1487,11 +1608,37 @@ void rasterUnpremultiply(SwSurface* surface) } } } + surface->premultiplied = false; +} + + +void rasterPremultiply(Surface* surface) +{ + if (surface->channelSize != sizeof(uint32_t)) return; + + TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h); + + //OPTIMIZE_ME: +SIMD + auto buffer = surface->buf32; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + auto a = (c >> 24); + *dst = (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); + } + } + surface->premultiplied = true; } bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) { + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale gradient!"); + return false; + } + if (!shape->fill) return false; if (shape->fastTrack) { @@ -1507,6 +1654,11 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) { + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale gradient!"); + return false; + } + if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false; if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); @@ -1519,40 +1671,60 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = _multiplyAlpha(r, a); - g = _multiplyAlpha(g, a); - b = _multiplyAlpha(b, a); + r = _multiply(r, a); + g = _multiply(g, a); + b = _multiply(b, a); } - auto color = surface->blender.join(r, g, b, a); - - if (shape->fastTrack) return _rasterRect(surface, shape->bbox, color, a); - else return _rasterRle(surface, shape->rle, color, a); + if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a); + else return _rasterRle(surface, shape->rle, r, g, b, a); } bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = _multiplyAlpha(r, a); - g = _multiplyAlpha(g, a); - b = _multiplyAlpha(b, a); + r = _multiply(r, a); + g = _multiply(g, a); + b = _multiply(b, a); } - auto color = surface->blender.join(r, g, b, a); - - return _rasterRle(surface, shape->strokeRle, color, a); + return _rasterRle(surface, shape->strokeRle, r, g, b, a); } -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) { + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale image!"); + return false; + } + //Verify Boundary if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return false; //TOOD: switch (image->format) - //TODO: case: _rasterRGBImage() - //TODO: case: _rasterGrayscaleImage() - //TODO: case: _rasterAlphaImage() - return _rasterRGBAImage(surface, image, transform, bbox, opacity); + //TODO: case: _rasterRGBImageMesh() + //TODO: case: _rasterGrayscaleImageMesh() + //TODO: case: _rasterAlphaImageMesh() + if (mesh && mesh->triangleCnt > 0) return _transformedRGBAImageMesh(surface, image, mesh, transform, &bbox, opacity); + else return _rasterRGBAImage(surface, image, transform, bbox, opacity); +} + + +bool rasterConvertCS(Surface* surface, ColorSpace to) +{ + //TOOD: Support SIMD accelerations + auto from = surface->cs; + + if ((from == ColorSpace::ABGR8888 && to == ColorSpace::ARGB8888) || (from == ColorSpace::ABGR8888S && to == ColorSpace::ARGB8888S)) { + surface->cs = to; + return cRasterABGRtoARGB(surface); + } + if ((from == ColorSpace::ARGB8888 && to == ColorSpace::ABGR8888) || (from == ColorSpace::ARGB8888S && to == ColorSpace::ABGR8888S)) { + surface->cs = to; + return cRasterARGBtoABGR(surface); + } + + return false; } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h index 7a129a3a80..cf658a6abb 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -82,9 +82,15 @@ static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_ } -static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color) +static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); + 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); @@ -125,13 +131,19 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u } -static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color) +static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); auto span = rle->spans; uint32_t src; for (uint32_t i = 0; i < rle->size; ++i) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h index de6b35fd64..ad2fc57f24 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -20,45 +20,95 @@ * SOFTWARE. */ - -static void inline cRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +template +static void inline cRasterPixels(PIXEL_T* dst, uint32_t val, uint32_t offset, int32_t len) { dst += offset; while (len--) *dst++ = val; } -static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color) +static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { auto span = rle->spans; - uint32_t src; - - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - - if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); - else src = color; - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, a); + uint32_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); + else src = color; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + } + } + //8bit grayscale + } 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]; + if (span->coverage < 255) src = _multiply(span->coverage, a); + else src = a; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + _multiply(*dst, ~src); + } } } return true; } -static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color) +static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto ialpha = _ialpha(color); - for (uint32_t y = 0; y < h; ++y) { - auto dst = &buffer[y * surface->stride]; - for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = color + ALPHA_BLEND(*dst, ialpha); + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->blender.join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = _ialpha(color); + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = color + ALPHA_BLEND(*dst, ialpha); + } + } + //8bit grayscale + } 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) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = a + _multiply(*dst, ~a); + } } } return true; } + + +static bool inline cRasterABGRtoARGB(Surface* surface) +{ + TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); + + auto buffer = surface->buf32; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + //flip Blue, Red channels + *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16); + } + } + return true; +} + + +static bool inline cRasterARGBtoABGR(Surface* surface) +{ + //exactly same with ABGRtoARGB + return cRasterABGRtoARGB(surface); +} diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h index a4b3cdaeb2..33c3d181b5 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -49,8 +49,14 @@ static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32 } -static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color) +static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); auto span = rle->spans; uint32_t src; uint8x8_t *vDst = nullptr; @@ -60,7 +66,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto ialpha = 255 - _alpha(src); if ((((uint32_t) dst) & 0x7) != 0) { @@ -88,9 +94,15 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u } -static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color) +static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); + 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); auto ialpha = 255 - _alpha(color); @@ -116,7 +128,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, for (uint32_t x = 0; x < (w - align) / 2; ++x) vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha)); - + auto leftovers = (w - align) % 2; if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha); } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h index abb57d7c45..52585162fc 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -20,17 +20,6 @@ * SOFTWARE. */ -struct Vertex -{ - Point pt; - Point uv; -}; - -struct Polygon -{ - Vertex vertex[3]; -}; - struct AALine { int32_t x[2]; @@ -80,7 +69,7 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) { #define TEXMAP_TRANSLUCENT #define TEXMAP_MASKING @@ -90,7 +79,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) { #define TEXMAP_MASKING #include "tvgSwRasterTexmapInternal.h" @@ -113,7 +102,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, /* This mapping algorithm is based on Mikael Kalms's. */ -static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) +static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) { float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; @@ -201,9 +190,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const dxdyb = dxdy[0]; xb = x[0] + dy * dxdyb + (off_y * dxdyb); - if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans); + if (blender) { + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans); } else { if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); @@ -222,9 +211,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const // Set right edge X-slope and perform subpixel pre-stepping dxdyb = dxdy[2]; xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); - if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans); + if (blender) { + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans); } else { if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); @@ -251,9 +240,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const ua = u[0] + dy * dudya + (off_y * dudya); va = v[0] + dy * dvdya + (off_y * dvdya); - if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans); + if (blender) { + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans); } else { if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); @@ -275,9 +264,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const ua = u[1] + dy * dudya + (off_y * dudya); va = v[1] + dy * dvdya + (off_y * dvdya); - if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans); + if (blender) { + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans); } else { if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); @@ -287,18 +276,10 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const } -static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region) +static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region) { - //Initialize Y range - float ys = FLT_MAX, ye = -1.0f; - - for (int i = 0; i < 4; i++) { - if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; - if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; - } - - auto yStart = static_cast(ys); - auto yEnd = static_cast(ye); + auto yStart = static_cast(ymin); + auto yEnd = static_cast(ymax); if (!_arrange(image, region, yStart, yEnd)) return nullptr; @@ -521,7 +502,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) auto offset = y * surface->stride; //Left edge - dst = surface->buffer + (offset + line->x[0]); + dst = surface->buf32 + (offset + line->x[0]); if (line->x[0] > 1) pixel = *(dst - 1); else pixel = *dst; @@ -533,10 +514,10 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) } //Right edge - dst = surface->buffer + (offset + line->x[1] - 1); + dst = surface->buf32 + (offset + line->x[1] - 1); if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1); else pixel = *dst; - + pos = width; while ((int32_t)(width - line->length[1]) < pos) { *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel); @@ -562,12 +543,12 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) 0 -- 1 | / | | / | - 3 -- 2 + 3 -- 2 */ -static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*)) { //Exceptions: No dedicated drawing area? - if (!region && image->rle->size == 0) return false; + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; /* Prepare vertices. shift XY coordinates to match the sub-pixeling technique. */ @@ -577,9 +558,15 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; - for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform); + float ys = FLT_MAX, ye = -1.0f; + for (int i = 0; i < 4; i++) { + mathMultiply(&vertices[i].pt, transform); - auto aaSpans = _AASpans(vertices, image, region); + if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; + if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; + } + + auto aaSpans = _AASpans(ys, ye, image, region); if (!aaSpans) return true; Polygon polygon; @@ -589,14 +576,72 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const polygon.vertex[1] = vertices[1]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans); + _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans); //Draw the second polygon polygon.vertex[0] = vertices[1]; polygon.vertex[1] = vertices[2]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans); + _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans); return _apply(surface, aaSpans); } + + +/* + Provide any number of triangles to draw a mesh using the supplied image. + Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one. + Example: + + 0 -- 1 0 -- 1 0 + | / | --> | / / | + | / | | / / | + 2 -- 3 2 1 -- 2 + + Should provide two Polygons, one for each triangle. + // TODO: region? +*/ +static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*)) +{ + //Exceptions: No dedicated drawing area? + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; + + // Step polygons once to transform + auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt); + float ys = FLT_MAX, ye = -1.0f; + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { + transformedTris[i] = mesh->triangles[i]; + mathMultiply(&transformedTris[i].vertex[0].pt, transform); + mathMultiply(&transformedTris[i].vertex[1].pt, transform); + mathMultiply(&transformedTris[i].vertex[2].pt, transform); + + if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y; + else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y; + if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y; + else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y; + if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y; + else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y; + + // Convert normalized UV coordinates to image coordinates + transformedTris[i].vertex[0].uv.x *= (float)image->w; + transformedTris[i].vertex[0].uv.y *= (float)image->h; + transformedTris[i].vertex[1].uv.x *= (float)image->w; + transformedTris[i].vertex[1].uv.y *= (float)image->h; + transformedTris[i].vertex[2].uv.x *= (float)image->w; + transformedTris[i].vertex[2].uv.y *= (float)image->h; + } + + // Get AA spans and step polygons again to draw + auto aaSpans = _AASpans(ys, ye, image, region); + if (aaSpans) { + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { + _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blender, aaSpans); + } + // Apply to surface (note: frees the AA spans) + _apply(surface, aaSpans); + } + free(transformedTris); + + return true; +} diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index 50536299b1..51685fe6e8 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,12 +19,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + { float _dudx = dudx, _dvdx = dvdx; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _xa = xa, _xb = xb, _ua = ua, _va = va; - auto sbuf = image->data; - auto dbuf = surface->buffer; + auto sbuf = image->buf32; + auto dbuf = surface->buf32; int32_t sw = static_cast(image->stride); int32_t sh = image->h; int32_t dw = surface->stride; @@ -36,7 +37,8 @@ SwSpan* span = nullptr; //used only when rle based. #ifdef TEXMAP_MASKING - uint32_t* cmp; + uint8_t* cmp; + auto csize = surface->compositor->image.channelSize; #endif if (!_arrange(image, region, yStart, yEnd)) return; @@ -93,7 +95,7 @@ x = x1; #ifdef TEXMAP_MASKING - cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1]; + cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; #endif //Draw horizontal line while (x++ < x2) { @@ -104,6 +106,9 @@ ab = (int)(255 * (1 - modff(v, &iptr))); iru = uu + 1; irv = vv + 1; + + if (vv >= sh) continue; + px = *(sbuf + (vv * sw) + uu); /* horizontal interpolate */ @@ -126,9 +131,9 @@ px = INTERPOLATE(ab, px, px2); } #if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT) - auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp))); + auto src = ALPHA_BLEND(px, _multiply(opacity, blender(cmp))); #elif defined(TEXMAP_MASKING) - auto src = ALPHA_BLEND(px, blendMethod(*cmp)); + auto src = ALPHA_BLEND(px, blender(cmp)); #elif defined(TEXMAP_TRANSLUCENT) auto src = ALPHA_BLEND(px, opacity); #else @@ -137,7 +142,7 @@ *buf = src + ALPHA_BLEND(*buf, _ialpha(src)); ++buf; #ifdef TEXMAP_MASKING - ++cmp; + cmp += csize; #endif //Step UV horizontally u += _dudx; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp index a1c0032a2e..6223bc8722 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgSwCommon.h" #include "tvgTaskScheduler.h" @@ -60,6 +61,8 @@ struct SwTask : Task } virtual bool dispose() = 0; + virtual bool clip(SwRleData* target) = 0; + virtual SwRleData* rle() = 0; virtual ~SwTask() { @@ -71,23 +74,39 @@ struct SwTask : Task struct SwShapeTask : SwTask { SwShape shape; - const Shape* sdata = nullptr; + const RenderShape* rshape = nullptr; bool cmpStroking = false; + bool clipper = false; + + bool clip(SwRleData* target) override + { + if (shape.fastTrack) rleClipRect(target, &bbox); + else if (shape.rle) rleClipPath(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 { - auto compMethod = CompositeMethod::None; - auto usedAsClip = (sdata->composite(nullptr, &compMethod) == Result::Success) && (compMethod == CompositeMethod::ClipPath); - if (opacity == 0 && !usedAsClip) return; //Invisible + if (opacity == 0 && !clipper) return; //Invisible uint8_t strokeAlpha = 0; auto visibleStroke = false; bool visibleFill = false; auto clipRegion = bbox; - if (HALF_STROKE(sdata->strokeWidth()) > 0) { - sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); - visibleStroke = sdata->strokeFill() || (static_cast(strokeAlpha * opacity / 255) > 0); + if (HALF_STROKE(rshape->strokeWidth()) > 0) { + rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); + visibleStroke = rshape->strokeFill() || (static_cast(strokeAlpha * opacity / 255) > 0); } //This checks also for the case, if the invisible shape turned to visible by alpha. @@ -97,12 +116,12 @@ struct SwShapeTask : SwTask //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { uint8_t alpha = 0; - sdata->fillColor(nullptr, nullptr, nullptr, &alpha); + rshape->fillColor(nullptr, nullptr, nullptr, &alpha); alpha = static_cast(static_cast(alpha) * opacity / 255); - visibleFill = (alpha > 0 || sdata->fill()); - if (visibleFill || visibleStroke || usedAsClip) { + visibleFill = (alpha > 0 || rshape->fill); + if (visibleFill || visibleStroke || clipper) { shapeReset(&shape); - if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; + if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; } } @@ -112,16 +131,16 @@ struct SwShapeTask : SwTask //Fill if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { - if (visibleFill || usedAsClip) { + if (visibleFill || clipper) { /* We assume that if stroke width is bigger than 2, shape outline below stroke could be full covered by stroke drawing. Thus it turns off antialising in that condition. Also, it shouldn't be dash style. */ - auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true; + auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst); - if (!shapeGenRle(&shape, sdata, antiAlias)) goto err; + if (!shapeGenRle(&shape, rshape, antiAlias)) goto err; } - if (auto fill = sdata->fill()) { + if (auto fill = rshape->fill) { auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; if (ctable) shapeResetFill(&shape); if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; @@ -133,10 +152,10 @@ struct SwShapeTask : SwTask //Stroke if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (visibleStroke) { - shapeResetStroke(&shape, sdata, transform); - if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err; + shapeResetStroke(&shape, rshape, transform); + if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err; - if (auto fill = sdata->strokeFill()) { + if (auto fill = rshape->strokeFill()) { auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; if (ctable) shapeResetStrokeFill(&shape); if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; @@ -148,27 +167,21 @@ struct SwShapeTask : SwTask } } + //Clear current task memorypool here if the clippers would use the same memory pool + shapeDelOutline(&shape, mpool, tid); + //Clip Path for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { - auto clipper = &static_cast(*clip)->shape; + auto clipper = static_cast(*clip); //Clip shape rle - if (shape.rle) { - if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox); - else if (clipper->rle) rleClipPath(shape.rle, clipper->rle); - else goto err; - } + if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip stroke rle - if (shape.strokeRle) { - if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox); - else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle); - else goto err; - } + if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; } - goto end; + return; err: shapeReset(&shape); - end: shapeDelOutline(&shape, mpool, tid); } @@ -180,35 +193,116 @@ struct SwShapeTask : SwTask }; +struct SwSceneTask : SwTask +{ + Array scene; //list of paints render data (SwTask) + SwRleData* sceneRle = nullptr; + + bool clip(SwRleData* target) override + { + //Only one shape + if (scene.count == 1) { + return static_cast(*scene.data)->clip(target); + } + + //More than one shapes + if (sceneRle) rleClipPath(target, sceneRle); + else TVGLOG("SW_ENGINE", "No clippers in a scene?"); + + return true; + } + + SwRleData* rle() override + { + return sceneRle; + } + + void run(unsigned tid) override + { + //TODO: Skip the run if the scene hans't changed. + if (!sceneRle) sceneRle = static_cast(calloc(1, sizeof(SwRleData))); + else rleReset(sceneRle); + + //Merge shapes if it has more than one shapes + if (scene.count > 1) { + //Merge first two clippers + auto clipper1 = static_cast(*scene.data); + auto clipper2 = static_cast(*(scene.data + 1)); + + rleMerge(sceneRle, clipper1->rle(), clipper2->rle()); + + //Unify the remained clippers + for (auto rd = scene.data + 2; rd < (scene.data + scene.count); ++rd) { + auto clipper = static_cast(*rd); + rleMerge(sceneRle, sceneRle, clipper->rle()); + } + } + } + + bool dispose() override + { + rleFree(sceneRle); + return true; + } +}; + + struct SwImageTask : SwTask { SwImage image; + Surface* source; //Image source + const RenderMesh* mesh = nullptr; //Should be valid ptr in action + + bool clip(SwRleData* 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; + //Convert colorspace if it's not aligned. + if (source->owner) { + if (source->cs != surface->cs) rasterConvertCS(source, surface->cs); + if (!source->premultiplied) rasterPremultiply(source); + } + + image.data = source->data; + image.w = source->w; + image.h = source->h; + image.stride = source->stride; + image.channelSize = source->channelSize; + //Invisible shape turned to visible by alpha. if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) { imageReset(&image); if (!image.data || image.w == 0 || image.h == 0) goto end; - if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; + if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end; - if (clips.count > 0) { + // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now + if (mesh->triangleCnt == 0 && clips.count > 0) { if (!imageGenRle(&image, bbox, false)) goto end; if (image.rle) { + //Clear current task memorypool here if the clippers would use the same memory pool + imageDelOutline(&image, mpool, tid); for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { - auto clipper = &static_cast(*clip)->shape; - if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox); - else if (clipper->rle) rleClipPath(image.rle, clipper->rle); - else goto err; + auto clipper = static_cast(*clip); + if (!clipper->clip(image.rle)) goto err; } + return; } } } goto end; - err: rleReset(image.rle); end: @@ -232,6 +326,31 @@ static void _termEngine() } +static void _renderFill(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +{ + uint8_t r, g, b, a; + if (auto fill = task->rshape->fill) { + rasterGradientShape(surface, &task->shape, fill->identifier()); + } else { + task->rshape->fillColor(&r, &g, &b, &a); + a = static_cast((opacity * (uint32_t) a) / 255); + if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); + } +} + +static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +{ + uint8_t r, g, b, a; + if (auto strokeFill = task->rshape->strokeFill()) { + rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); + } else { + if (task->rshape->strokeColor(&r, &g, &b, &a)) { + a = static_cast((opacity * (uint32_t) a) / 255); + if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); + } + } +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -293,17 +412,20 @@ bool SwRenderer::viewport(const RenderRegion& vp) } -bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace) +bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs) { - if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false; + if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false; if (!surface) surface = new SwSurface; - surface->buffer = buffer; + surface->data = data; surface->stride = stride; surface->w = w; surface->h = h; - surface->cs = colorSpace; + surface->cs = cs; + surface->channelSize = CHANNEL_SIZE(cs); + surface->premultiplied = true; + surface->owner = true; vport.x = vport.y = 0; vport.w = surface->w; @@ -315,9 +437,10 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t bool SwRenderer::preRender() { - return rasterClear(surface); + return rasterClear(surface, 0, 0, surface->w, surface->h); } + void SwRenderer::clearCompositors() { //Free Composite Caches @@ -333,7 +456,7 @@ void SwRenderer::clearCompositors() bool SwRenderer::postRender() { //Unmultiply alpha if needed - if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { + if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) { rasterUnpremultiply(surface); } @@ -354,7 +477,7 @@ bool SwRenderer::renderImage(RenderData data) if (task->opacity == 0) return true; - return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); + return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity); } @@ -373,7 +496,7 @@ bool SwRenderer::renderShape(RenderData data) //Do Stroking Composition if (task->cmpStroking) { opacity = 255; - cmp = target(task->bounds()); + cmp = target(task->bounds(), colorSpace()); beginComposite(cmp, CompositeMethod::None, task->opacity); //No Stroking Composition } else { @@ -381,23 +504,12 @@ bool SwRenderer::renderShape(RenderData data) } //Main raster stage - uint8_t r, g, b, a; - - if (auto fill = task->sdata->fill()) { - rasterGradientShape(surface, &task->shape, fill->identifier()); - } else { - task->sdata->fillColor(&r, &g, &b, &a); - a = static_cast((opacity * (uint32_t) a) / 255); - if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); - } - - if (auto strokeFill = task->sdata->strokeFill()) { - rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); + if (task->rshape->stroke && task->rshape->stroke->strokeFirst) { + _renderStroke(task, surface, opacity); + _renderFill(task, surface, opacity); } else { - if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) { - a = static_cast((opacity * (uint32_t) a) / 255); - if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); - } + _renderFill(task, surface, opacity); + _renderStroke(task, surface, opacity); } if (task->cmpStroking) endComposite(cmp); @@ -450,7 +562,7 @@ bool SwRenderer::mempool(bool shared) } -Compositor* SwRenderer::target(const RenderRegion& region) +Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) { auto x = region.x; auto y = region.y; @@ -460,13 +572,15 @@ Compositor* SwRenderer::target(const RenderRegion& region) auto sh = static_cast(surface->h); //Out of boundary - if (x > sw || y > sh) return nullptr; + 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.data; p < (compositors.data + compositors.count); ++p) { - if ((*p)->compositor->valid) { + if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) { cmp = *p; break; } @@ -475,17 +589,16 @@ Compositor* SwRenderer::target(const RenderRegion& region) //New Composition if (!cmp) { cmp = new SwSurface; - if (!cmp) goto err; //Inherits attributes from main surface *cmp = *surface; cmp->compositor = new SwCompositor; - if (!cmp->compositor) goto err; - //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H - cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h); - if (!cmp->compositor->image.data) goto err; + //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); } @@ -507,30 +620,16 @@ Compositor* SwRenderer::target(const RenderRegion& region) cmp->compositor->image.h = surface->h; cmp->compositor->image.direct = true; - //We know partial clear region - cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x); - cmp->w = w; - cmp->h = h; - - rasterClear(cmp); - - //Recover context - cmp->buffer = cmp->compositor->image.data; + cmp->data = cmp->compositor->image.data; cmp->w = cmp->compositor->image.w; cmp->h = cmp->compositor->image.h; + rasterClear(cmp, x, y, w, h); + //Switch render target surface = cmp; return cmp->compositor; - -err: - if (cmp) { - if (cmp->compositor) delete(cmp->compositor); - delete(cmp); - } - - return nullptr; } @@ -547,13 +646,20 @@ bool SwRenderer::endComposite(Compositor* cmp) //Default is alpha blending if (p->method == CompositeMethod::None) { - return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity); + return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity); } return true; } +ColorSpace SwRenderer::colorSpace() +{ + if (surface) return surface->cs; + else return ColorSpace::Unsupported; +} + + bool SwRenderer::dispose(RenderData data) { auto task = static_cast(data); @@ -576,14 +682,15 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, //Finish previous task if it has duplicated request. task->done(); - if (clips.count > 0) { - //Guarantee composition targets get ready. - for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { - static_cast(*clip)->done(); - } - task->clips = clips; + //TODO: Failed threading them. It would be better if it's possible. + //See: https://github.com/thorvg/thorvg/issues/1409 + //Guarantee composition targets get ready. + for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { + static_cast(*clip)->done(); } + task->clips = clips; + if (transform) { if (!task->transform) task->transform = static_cast(malloc(sizeof(Matrix))); *task->transform = transform->m; @@ -612,44 +719,52 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, } -RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) { //prepare task auto task = static_cast(data); - if (!task) { - task = new SwImageTask; - if (flags & RenderUpdateFlag::Image) { - task->image.data = image->buffer; - task->image.w = image->w; - task->image.h = image->h; - task->image.stride = image->stride; - } + if (!task) task = new SwImageTask; + if (flags & RenderUpdateFlag::Image) { + task->source = surface; + task->mesh = mesh; } return prepareCommon(task, transform, opacity, clips, flags); } -RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) { //prepare task - auto task = static_cast(data); - if (!task) { - task = new SwShapeTask; - task->sdata = &sdata; + auto task = static_cast(data); + if (!task) task = new SwSceneTask; + task->scene = scene; + + //TODO: Failed threading them. It would be better if it's possible. + //See: https://github.com/thorvg/thorvg/issues/1409 + //Guarantee composition targets get ready. + for (auto task = scene.data; task < (scene.data + scene.count); ++task) { + static_cast(*task)->done(); } return prepareCommon(task, transform, opacity, clips, flags); } -SwRenderer::SwRenderer():mpool(globalMpool) +RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) { + //prepare task + auto task = static_cast(data); + if (!task) { + task = new SwShapeTask; + task->rshape = &rshape; + } + task->clipper = clipper; + + return prepareCommon(task, transform, opacity, clips, flags); } -uint32_t SwRenderer::colorSpace() +SwRenderer::SwRenderer():mpool(globalMpool) { - if (surface) return surface->cs; - return tvg::SwCanvas::ARGB8888; } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h index cab93f9e1c..36614fa1c1 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SW_RENDERER_H_ #define _TVG_SW_RENDERER_H_ @@ -35,8 +36,9 @@ namespace tvg class SwRenderer : public RenderMethod { public: - RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; - RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; + RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; @@ -45,19 +47,18 @@ public: RenderRegion region(RenderData data) override; RenderRegion viewport() override; bool viewport(const RenderRegion& vp) override; + ColorSpace colorSpace() override; bool clear() override; bool sync() override; - bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace); + 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) override; + Compositor* target(const RenderRegion& region, ColorSpace cs) override; bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override; bool endComposite(Compositor* cmp) override; void clearCompositors(); - uint32_t colorSpace() override; - static SwRenderer* gen(); static bool init(uint32_t threads); static int32_t init(); diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp index 63e7fc9447..50dec208b2 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -773,60 +773,59 @@ static int _genRle(RleWorker& rw) } -SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt) +static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; - auto spans = targetRle->spans; - auto end = targetRle->spans + targetRle->size; + auto spans = target->spans; + auto end = target->spans + target->size; auto clipSpans = clip->spans; auto clipEnd = clip->spans + clip->size; - while (spanCnt > 0 && spans < end) { - if (clipSpans == clipEnd) { - spans = end; - break; - } + while (spans < end && clipSpans < clipEnd) { + //align y cooridnates. if (clipSpans->y > spans->y) { ++spans; continue; } - if (spans->y != clipSpans->y) { + if (spans->y > clipSpans->y) { ++clipSpans; continue; } - auto sx1 = spans->x; - auto sx2 = sx1 + spans->len; - auto cx1 = clipSpans->x; - auto cx2 = cx1 + clipSpans->len; - if (cx1 < sx1 && cx2 < sx1) { - ++clipSpans; - continue; - } - else if (sx1 < cx1 && sx2 < cx1) { - ++spans; - continue; - } - auto x = sx1 > cx1 ? sx1 : cx1; - auto len = (sx2 < cx2 ? sx2 : cx2) - x; - if (len) { - auto spansCorverage = spans->coverage; - auto clipSpansCoverage = clipSpans->coverage; - out->x = sx1 > cx1 ? sx1 : cx1; - out->len = (sx2 < cx2 ? sx2 : cx2) - out->x; - out->y = spans->y; - out->coverage = (uint8_t)(((spansCorverage * clipSpansCoverage) + 0xff) >> 8); - ++out; - --spanCnt; + //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; + auto sx2 = sx1 + spans->len; + auto cx1 = temp->x; + auto cx2 = cx1 + temp->len; + + //The span must be left(x1) to right(x2) direction. Not intersected. + if (cx2 < sx1 || sx2 < cx1) { + ++temp; + continue; + } + + //clip span region. + auto x = sx1 > cx1 ? sx1 : cx1; + auto len = (sx2 < cx2 ? sx2 : cx2) - x; + if (len > 0) { + out->x = x; + out->y = temp->y; + out->len = len; + out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8); + ++out; + --outSpansCnt; + } + ++temp; } - if (sx2 < cx2) ++spans; - else ++clipSpans; + ++spans; } return out; } -SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt) +static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; auto spans = targetRle->spans; @@ -836,7 +835,7 @@ SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSp auto maxx = minx + static_cast(bbox->max.x - bbox->min.x) - 1; auto maxy = miny + static_cast(bbox->max.y - bbox->min.y) - 1; - while (spanCnt && spans < end) { + while (outSpansCnt > 0 && spans < end) { if (spans->y > maxy) { spans = end; break; @@ -853,18 +852,58 @@ SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSp out->x = spans->x; out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1); } - if (out->len != 0) { + if (out->len > 0) { out->y = spans->y; out->coverage = spans->coverage; ++out; + --outSpansCnt; } ++spans; - --spanCnt; } return out; } +static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans) +{ + auto out = outSpans; + auto spans1 = clip1->spans; + auto end1 = clip1->spans + clip1->size; + auto spans2 = clip2->spans; + auto end2 = clip2->spans + clip2->size; + + //list two spans up in y order + //TODO: Remove duplicated regions? + while (spans1 < end1 && spans2 < end2) { + while (spans1 < end1 && spans1->y <= spans2->y) { + *out = *spans1; + ++spans1; + ++out; + } + if (spans1 >= end1) break; + while (spans2 < end2 && spans2->y <= spans1->y) { + *out = *spans2; + ++spans2; + ++out; + } + } + + //Leftovers + while (spans1 < end1) { + *out = *spans1; + ++spans1; + ++out; + } + while (spans2 < end2) { + *out = *spans2; + ++spans2; + ++out; + } + + return out; +} + + void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) { free(rle->spans); @@ -1001,6 +1040,28 @@ error: } +SwRleData* 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))); + rle->spans = static_cast(malloc(sizeof(SwSpan) * height)); + rle->size = height; + rle->alloc = height; + + auto span = rle->spans; + for (uint16_t i = 0; i < height; ++i, ++span) { + span->x = bbox->min.x; + span->y = bbox->min.y + i; + span->len = width; + span->coverage = 255; + } + + return rle; +} + + void rleReset(SwRleData* rle) { if (!rle) return; @@ -1016,12 +1077,50 @@ void rleFree(SwRleData* rle) } +void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2) +{ + if (!rle || (!clip1 && !clip2)) return; + if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return; + + TVGLOG("SW_ENGINE", "Unifying Rle!"); + + //clip1 is empty, just copy clip2 + if (!clip1 || clip1->size == 0) { + if (clip2) { + auto spans = static_cast(malloc(sizeof(SwSpan) * (clip2->size))); + memcpy(spans, clip2->spans, clip2->size); + _replaceClipSpan(rle, spans, clip2->size); + } else { + _replaceClipSpan(rle, nullptr, 0); + } + return; + } + + //clip2 is empty, just copy clip1 + if (!clip2 || clip2->size == 0) { + if (clip1) { + auto spans = static_cast(malloc(sizeof(SwSpan) * (clip1->size))); + memcpy(spans, clip1->spans, clip1->size); + _replaceClipSpan(rle, spans, clip1->size); + } else { + _replaceClipSpan(rle, nullptr, 0); + } + return; + } + + auto spanCnt = clip1->size + clip2->size; + auto spans = static_cast(malloc(sizeof(SwSpan) * spanCnt)); + auto spansEnd = _mergeSpansRegion(clip1, clip2, spans); + + _replaceClipSpan(rle, spans, spansEnd - spans); +} + + void rleClipPath(SwRleData *rle, const SwRleData *clip) { if (rle->size == 0 || clip->size == 0) return; auto spanCnt = rle->size > clip->size ? rle->size : clip->size; auto spans = static_cast(malloc(sizeof(SwSpan) * (spanCnt))); - if (!spans) return; auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt); _replaceClipSpan(rle, spans, spansEnd - spans); @@ -1034,7 +1133,6 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip) { if (rle->size == 0) return; auto spans = static_cast(malloc(sizeof(SwSpan) * (rle->size))); - if (!spans) return; auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size); _replaceClipSpan(rle, spans, spansEnd - spans); diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp index e5b540bcc3..7462a7b7ea 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgSwCommon.h" #include "tvgBezier.h" #include @@ -266,13 +267,13 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct } -static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform) +static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform) { - const PathCommand* cmds = nullptr; - auto cmdCnt = sdata->pathCommands(&cmds); + const PathCommand* cmds = rshape->path.cmds; + auto cmdCnt = rshape->path.cmdCnt; - const Point* pts = nullptr; - auto ptsCnt = sdata->pathCoords(&pts); + const Point* pts = rshape->path.pts; + auto ptsCnt = rshape->path.ptsCnt; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return nullptr; @@ -285,7 +286,7 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform) dash.curOpGap = false; const float* pattern; - dash.cnt = sdata->strokeDash(&pattern); + dash.cnt = rshape->strokeDash(&pattern); if (dash.cnt == 0) return nullptr; //OPTMIZE ME: Use mempool??? @@ -380,13 +381,13 @@ static bool _axisAlignedRect(const SwOutline* outline) -static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) +static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) { - const PathCommand* cmds = nullptr; - auto cmdCnt = sdata->pathCommands(&cmds); + const PathCommand* cmds = rshape->path.cmds; + auto cmdCnt = rshape->path.cmdCnt; - const Point* pts = nullptr; - auto ptsCnt = sdata->pathCoords(&pts); + const Point* pts = rshape->path.pts; + auto ptsCnt = rshape->path.ptsCnt; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return false; @@ -466,7 +467,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf _outlineEnd(*outline); - outline->fillRule = sdata->fillRule(); + outline->fillRule = rshape->rule; shape->outline = outline; shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline)); @@ -478,9 +479,9 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf /* External Class Implementation */ /************************************************************************/ -bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) { - if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false; + if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false; if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; //Keep it for Rasterization Region @@ -503,7 +504,7 @@ bool shapePrepared(const SwShape* shape) } -bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias) +bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias) { //FIXME: Should we draw it? //Case: Stroke Line @@ -557,18 +558,18 @@ void shapeDelStroke(SwShape* shape) } -void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform) +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform) { if (!shape->stroke) shape->stroke = static_cast(calloc(1, sizeof(SwStroke))); auto stroke = shape->stroke; if (!stroke) return; - strokeReset(stroke, sdata, transform); + strokeReset(stroke, rshape, transform); rleReset(shape->strokeRle); } -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { SwOutline* shapeOutline = nullptr; SwOutline* strokeOutline = nullptr; @@ -576,14 +577,14 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo bool ret = true; //Dash Style Stroke - if (sdata->strokeDash(nullptr) > 0) { - shapeOutline = _genDashOutline(sdata, transform); + if (rshape->strokeDash(nullptr) > 0) { + shapeOutline = _genDashOutline(rshape, transform); if (!shapeOutline) return false; freeOutline = true; //Normal Style stroke } else { if (!shape->outline) { - if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false; + if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false; } shapeOutline = shape->outline; } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp index fa213cc5d3..2f41d5f021 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. +/* + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include "tvgSwCommon.h" @@ -301,7 +302,7 @@ static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) bool intersect = false; /* Only intersect borders if between two line_to's and both - lines are long enough (line length is zero fur curves). */ + lines are long enough (line length is zero for curves). */ if (border->movable && lineLength > 0) { //compute minimum required length of lines SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta))); @@ -381,9 +382,16 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to) if (delta.zero()) return; //compute length of line - auto lineLength = mathLength(delta); auto angle = mathAtan(delta); + /* The lineLength is used to determine the intersection of strokes outlines. + The scale needs to be reverted since the stroke width has not been scaled. + An alternative option is to scale the width of the stroke properly by + calculating the mixture of the sx/sy rating on the stroke direction. */ + delta.x = static_cast(delta.x / stroke.sx); + delta.y = static_cast(delta.y / stroke.sy); + auto lineLength = mathLength(delta); + delta = {static_cast(stroke.width), 0}; mathRotate(delta, angle + SW_ANGLE_PI2); SCALE(stroke, delta); @@ -825,7 +833,7 @@ void strokeFree(SwStroke* stroke) } -void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform) +void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform) { if (transform) { stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f)); @@ -834,11 +842,11 @@ void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform) stroke->sx = stroke->sy = 1.0f; } - stroke->width = HALF_STROKE(sdata->strokeWidth()); - stroke->cap = sdata->strokeCap(); + stroke->width = HALF_STROKE(rshape->strokeWidth()); + stroke->cap = rshape->strokeCap(); //Save line join: it can be temporarily changed when stroking curves... - stroke->joinSaved = stroke->join = sdata->strokeJoin(); + stroke->joinSaved = stroke->join = rshape->strokeJoin(); stroke->borders[0].ptsCnt = 0; stroke->borders[0].start = -1; diff --git a/thirdparty/thorvg/src/lib/tvgAccessor.cpp b/thirdparty/thorvg/src/lib/tvgAccessor.cpp index 5ad24f4acf..0c636979b5 100644 --- a/thirdparty/thorvg/src/lib/tvgAccessor.cpp +++ b/thirdparty/thorvg/src/lib/tvgAccessor.cpp @@ -1,13 +1,16 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -23,15 +26,15 @@ /* Internal Class Implementation */ /************************************************************************/ -static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor) +static bool accessChildren(Iterator* it, function func) { while (auto child = it->next()) { //Access the child if (!func(child)) return false; //Access the children of the child - if (auto it2 = itrAccessor.iterator(child)) { - if (!accessChildren(it2, func, itrAccessor)) { + if (auto it2 = IteratorAccessor::iterator(child)) { + if (!accessChildren(it2, func)) { delete(it2); return false; } @@ -41,12 +44,11 @@ static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), Iterat return true; } - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -unique_ptr Accessor::access(unique_ptr picture, bool(*func)(const Paint* paint)) noexcept +unique_ptr Accessor::set(unique_ptr picture, function func) noexcept { auto p = picture.get(); if (!p || !func) return picture; @@ -57,9 +59,8 @@ unique_ptr Accessor::access(unique_ptr picture, bool(*func)(co if (!func(p)) return picture; //Children - IteratorAccessor itrAccessor; - if (auto it = itrAccessor.iterator(p)) { - accessChildren(it, func, itrAccessor); + if (auto it = IteratorAccessor::iterator(p)) { + accessChildren(it, func); delete(it); } return picture; diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/lib/tvgArray.h index 04ddbaee2b..8d67753ae3 100644 --- a/thirdparty/thorvg/src/lib/tvgArray.h +++ b/thirdparty/thorvg/src/lib/tvgArray.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_ARRAY_H_ #define _TVG_ARRAY_H_ diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/lib/tvgBezier.cpp index 95e2055943..6162252235 100644 --- a/thirdparty/thorvg/src/lib/tvgBezier.cpp +++ b/thirdparty/thorvg/src/lib/tvgBezier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include "tvgBezier.h" @@ -114,7 +115,7 @@ float bezAt(const Bezier& bz, float at) auto t = 0.5f; //just in case to prevent an infinite loop - if (at <= 0) return 0.0f; + if (at <= 0) return 0.0f; if (at >= len) return 1.0f; diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/lib/tvgBezier.h index 7d7c84e34e..aa309b098e 100644 --- a/thirdparty/thorvg/src/lib/tvgBezier.h +++ b/thirdparty/thorvg/src/lib/tvgBezier.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_BEZIER_H_ #define _TVG_BEZIER_H_ diff --git a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h index f139def470..3b44db2829 100644 --- a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h +++ b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_BINARY_DESC_H_ #define _TVG_BINARY_DESC_H_ @@ -42,7 +43,7 @@ using TvgBinFlag = TvgBinByte; #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 -//Compress Size +//Compress Size #define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter) #define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter) #define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter) @@ -92,5 +93,6 @@ using TvgBinFlag = TvgBinByte; //Picture #define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70 +#define TVG_TAG_PICTURE_MESH (TvgBinTag)0x71 #endif //_TVG_BINARY_DESC_H_ diff --git a/thirdparty/thorvg/src/lib/tvgCanvas.cpp b/thirdparty/thorvg/src/lib/tvgCanvas.cpp index e8529e47c2..95c66b94df 100644 --- a/thirdparty/thorvg/src/lib/tvgCanvas.cpp +++ b/thirdparty/thorvg/src/lib/tvgCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgCanvasImpl.h" /************************************************************************/ @@ -57,13 +58,21 @@ Result Canvas::clear(bool free) noexcept Result Canvas::draw() noexcept { - return pImpl->draw(); + TVGLOG("COMMON", "Draw S. -------------------------------- Canvas(%p)", this); + auto ret = pImpl->draw(); + TVGLOG("COMMON", "Draw E. -------------------------------- Canvas(%p)", this); + + return ret; } Result Canvas::update(Paint* paint) noexcept { - return pImpl->update(paint, false); + TVGLOG("COMMON", "Update S. ------------------------------ Canvas(%p)", this); + auto ret = pImpl->update(paint, false); + TVGLOG("COMMON", "Update E. ------------------------------ Canvas(%p)", this); + + return ret; } diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h index 0adec8fa52..6f5760f53c 100644 --- a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h +++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_CANVAS_IMPL_H_ #define _TVG_CANVAS_IMPL_H_ diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h index 1d66f2e1a2..1731647c00 100644 --- a/thirdparty/thorvg/src/lib/tvgCommon.h +++ b/thirdparty/thorvg/src/lib/tvgCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_COMMON_H_ #define _TVG_COMMON_H_ @@ -64,8 +65,14 @@ using namespace tvg; enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown }; #ifdef THORVG_LOG_ENABLED - #define TVGLOG(tag, fmt, ...) fprintf(stdout, tag ": " fmt "\n", ##__VA_ARGS__) //Log Message for notifying user some useful info - #define TVGERR(tag, fmt, ...) fprintf(stderr, tag ": " fmt "\n", ##__VA_ARGS__) //Error Message for us to fix it + constexpr auto ErrorColor = "\033[31m"; //red + constexpr auto ErrorBgColor = "\033[41m";//bg red + constexpr auto LogColor = "\033[32m"; //green + constexpr auto LogBgColor = "\033[42m"; //bg green + constexpr auto GreyColor = "\033[90m"; //grey + constexpr auto ResetColors = "\033[0m"; //default + #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) + #define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) #else #define TVGERR(...) #define TVGLOG(...) diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp index eecf239cee..cc7e1ccaed 100644 --- a/thirdparty/thorvg/src/lib/tvgFill.cpp +++ b/thirdparty/thorvg/src/lib/tvgFill.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgFill.h" /************************************************************************/ diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h index fff2475c4f..e90991c9df 100644 --- a/thirdparty/thorvg/src/lib/tvgFill.h +++ b/thirdparty/thorvg/src/lib/tvgFill.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_FILL_H_ #define _TVG_FILL_H_ diff --git a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp index 56feb69541..dbbab516b2 100644 --- a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp +++ b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgCanvasImpl.h" #ifdef THORVG_GL_RASTER_SUPPORT diff --git a/thirdparty/thorvg/src/lib/tvgInitializer.cpp b/thirdparty/thorvg/src/lib/tvgInitializer.cpp index 42997c3493..b7326a9fbc 100644 --- a/thirdparty/thorvg/src/lib/tvgInitializer.cpp +++ b/thirdparty/thorvg/src/lib/tvgInitializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgCommon.h" #include "tvgTaskScheduler.h" #include "tvgLoader.h" @@ -43,6 +44,11 @@ static int _initCnt = 0; static uint16_t _version = 0; +//enum class operation helper +static constexpr bool operator &(CanvasEngine a, CanvasEngine b) +{ + return int(a) & int(b); +} static bool _buildVersionInfo() { @@ -86,12 +92,12 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept { auto nonSupport = true; - if (static_cast(engine) & static_cast(CanvasEngine::Sw)) { + if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; #endif - } else if (static_cast(engine) & static_cast(CanvasEngine::Gl)) { + } else if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; @@ -120,12 +126,12 @@ Result Initializer::term(CanvasEngine engine) noexcept auto nonSupport = true; - if (static_cast(engine) & static_cast(CanvasEngine::Sw)) { + if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::term()) return Result::InsufficientCondition; nonSupport = false; #endif - } else if (static_cast(engine) & static_cast(CanvasEngine::Gl)) { + } else if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::term()) return Result::InsufficientCondition; nonSupport = false; diff --git a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h index 8e566acb72..2347613ed8 100644 --- a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h +++ b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_ITERATOR_ACCESSOR_H_ #define _TVG_ITERATOR_ACCESSOR_H_ @@ -31,7 +32,7 @@ class IteratorAccessor { public: //Utility Method: Iterator Accessor - Iterator* iterator(const Paint* paint) + static Iterator* iterator(const Paint* paint) { return paint->pImpl->iterator(); } diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp index df34af3aa1..3e040c08f1 100644 --- a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp +++ b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include "tvgFill.h" diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h index 0a154aa47d..ee7c0c1193 100644 --- a/thirdparty/thorvg/src/lib/tvgLoadModule.h +++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_LOAD_MODULE_H_ #define _TVG_LOAD_MODULE_H_ @@ -35,8 +36,8 @@ public: float vy = 0; float vw = 0; float vh = 0; - float w = 0, h = 0; //default image size - uint32_t colorSpace = SwCanvas::ARGB8888; + float w = 0, h = 0; //default image size + ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open() virtual ~LoadModule() {} @@ -49,7 +50,7 @@ public: virtual bool read() = 0; virtual bool close() = 0; - virtual unique_ptr bitmap(uint32_t colorSpace) { return nullptr; } + virtual unique_ptr bitmap() { return nullptr; } virtual unique_ptr paint() { return nullptr; } }; diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp index 991c260159..2bbe177844 100644 --- a/thirdparty/thorvg/src/lib/tvgLoader.cpp +++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgLoader.h" #ifdef THORVG_SVG_LOADER_SUPPORT diff --git a/thirdparty/thorvg/src/lib/tvgLoader.h b/thirdparty/thorvg/src/lib/tvgLoader.h index ab32f89a2f..17ff9e2637 100644 --- a/thirdparty/thorvg/src/lib/tvgLoader.h +++ b/thirdparty/thorvg/src/lib/tvgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_LOADER_H_ #define _TVG_LOADER_H_ diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/lib/tvgLzw.cpp index 1aaf37831a..52f4ed6716 100644 --- a/thirdparty/thorvg/src/lib/tvgLzw.cpp +++ b/thirdparty/thorvg/src/lib/tvgLzw.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -258,8 +258,8 @@ struct Dictionary Dictionary() { - /* First 256 dictionary entries are reserved to the byte/ASCII range. - Additional entries follow for the character sequences found in the input. + /* First 256 dictionary entries are reserved to the byte/ASCII range. + Additional entries follow for the character sequences found in the input. Up to 4096 - 256 (MaxDictEntries - FirstCode). */ size = FirstCode; diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/lib/tvgLzw.h index 8e165bdb34..bd4e783fbf 100644 --- a/thirdparty/thorvg/src/lib/tvgLzw.h +++ b/thirdparty/thorvg/src/lib/tvgLzw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_LZW_H_ #define _TVG_LZW_H_ diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h index 74f34fb744..9ab8291b75 100644 --- a/thirdparty/thorvg/src/lib/tvgMath.h +++ b/thirdparty/thorvg/src/lib/tvgMath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_MATH_H_ #define _TVG_MATH_H_ @@ -118,7 +119,7 @@ static inline void mathScale(Matrix* m, float scale) static inline void mathTranslate(Matrix* m, float x, float y) { m->e13 = x; - m->e23 = y; + m->e23 = y; } diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp index c90e95cd33..c7030aaccf 100644 --- a/thirdparty/thorvg/src/lib/tvgPaint.cpp +++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgPaint.h" @@ -161,16 +162,15 @@ bool Paint::Impl::render(RenderMethod& renderer) { Compositor* cmp = nullptr; - //OPTIMIZE_ME: Can we replace the simple AlphaMasking with ClipPath? - /* Note: only ClipPath is processed in update() step. Create a composition image. */ if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { auto region = smethod->bounds(renderer); if (region.w == 0 || region.h == 0) return true; - cmp = renderer.target(region); - renderer.beginComposite(cmp, CompositeMethod::None, 255); - compData->target->pImpl->render(renderer); + cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method)); + if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) { + compData->target->pImpl->render(renderer); + } } if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity); @@ -183,7 +183,7 @@ bool Paint::Impl::render(RenderMethod& renderer) } -void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag) +RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag, bool clipper) { if (renderFlag & RenderUpdateFlag::Transform) { if (!rTransform) return nullptr; @@ -194,9 +194,10 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf } /* 1. Composition Pre Processing */ - void *tdata = nullptr; + RenderData trd = nullptr; //composite target render data RenderRegion viewport; bool compFastTrack = false; + bool childClipper = false; if (compData) { auto target = compData->target; @@ -206,47 +207,50 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle, we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */ auto tryFastTrack = false; - if (method == CompositeMethod::ClipPath) tryFastTrack = true; - else if (method == CompositeMethod::AlphaMask && target->identifier() == TVG_CLASS_ID_SHAPE) { - auto shape = static_cast(target); - uint8_t a; - shape->fillColor(nullptr, nullptr, nullptr, &a); - if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true; - } - if (tryFastTrack) { - RenderRegion viewport2; - if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) { - viewport = renderer.viewport(); - viewport2.intersect(viewport); - renderer.viewport(viewport2); - target->pImpl->ctxFlag |= ContextFlag::FastTrack; + if (target->identifier() == TVG_CLASS_ID_SHAPE) { + if (method == CompositeMethod::ClipPath) tryFastTrack = true; + else if (method == CompositeMethod::AlphaMask) { + auto shape = static_cast(target); + uint8_t a; + shape->fillColor(nullptr, nullptr, nullptr, &a); + if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true; + } + if (tryFastTrack) { + RenderRegion viewport2; + if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) { + viewport = renderer.viewport(); + viewport2.intersect(viewport); + renderer.viewport(viewport2); + target->pImpl->ctxFlag |= ContextFlag::FastTrack; + } } } if (!compFastTrack) { - tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag); - if (method == CompositeMethod::ClipPath) clips.push(tdata); + childClipper = compData->method == CompositeMethod::ClipPath ? true : false; + trd = target->pImpl->update(renderer, pTransform, 255, clips, pFlag, childClipper); + if (childClipper) clips.push(trd); } } /* 2. Main Update */ - void *edata = nullptr; + RenderData rd = nullptr; auto newFlag = static_cast(pFlag | renderFlag); renderFlag = RenderUpdateFlag::None; opacity = (opacity * this->opacity) / 255; if (rTransform && pTransform) { RenderTransform outTransform(pTransform, rTransform); - edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag); + rd = smethod->update(renderer, &outTransform, opacity, clips, newFlag, clipper); } else { auto outTransform = pTransform ? pTransform : rTransform; - edata = smethod->update(renderer, outTransform, opacity, clips, newFlag); + rd = smethod->update(renderer, outTransform, opacity, clips, newFlag, clipper); } /* 3. Composition Post Processing */ if (compFastTrack) renderer.viewport(viewport); - else if (tdata && compData->method == CompositeMethod::ClipPath) clips.pop(); + else if (childClipper) clips.pop(); - return edata; + return rd; } @@ -384,19 +388,6 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept } -Result Paint::composite(const Paint** source, CompositeMethod* method) const noexcept -{ - if (source) *source = pImpl->compSource; - auto met = (pImpl->compSource && pImpl->compSource->pImpl->compData ? - pImpl->compSource->pImpl->compData->method : CompositeMethod::None); - if (method) *method = met; - - if (pImpl->compSource != nullptr && met != CompositeMethod::None) - return Result::Success; - return Result::InsufficientCondition; -} - - Result Paint::opacity(uint8_t o) noexcept { if (pImpl->opacity == o) return Result::Success; diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h index 94239c30a1..d70490c649 100644 --- a/thirdparty/thorvg/src/lib/tvgPaint.h +++ b/thirdparty/thorvg/src/lib/tvgPaint.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_PAINT_H_ #define _TVG_PAINT_H_ @@ -42,7 +43,7 @@ namespace tvg virtual ~StrategyMethod() {} virtual bool dispose(RenderMethod& renderer) = 0; - virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has. + virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has. virtual bool render(RenderMethod& renderer) = 0; virtual bool bounds(float* x, float* y, float* w, float* h) = 0; virtual RenderRegion bounds(RenderMethod& renderer) const = 0; @@ -62,7 +63,6 @@ namespace tvg StrategyMethod* smethod = nullptr; RenderTransform* rTransform = nullptr; Composite* compData = nullptr; - Paint* compSource = nullptr; uint32_t renderFlag = RenderUpdateFlag::None; uint32_t ctxFlag = ContextFlag::Invalid; uint32_t id; @@ -137,7 +137,6 @@ namespace tvg if (!target && method == CompositeMethod::None) return true; compData = static_cast(calloc(1, sizeof(Composite))); } - target->pImpl->compSource = source; compData->target = target; compData->source = source; compData->method = method; @@ -148,7 +147,7 @@ namespace tvg bool scale(float factor); bool translate(float x, float y); bool bounds(float* x, float* y, float* w, float* h, bool transformed); - void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag); + RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag, bool clipper = false); bool render(RenderMethod& renderer); Paint* duplicate(); }; @@ -177,9 +176,9 @@ namespace tvg return inst->dispose(renderer); } - void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag renderFlag) override + RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag renderFlag, bool clipper) override { - return inst->update(renderer, transform, opacity, clips, renderFlag); + return inst->update(renderer, transform, opacity, clips, renderFlag, clipper); } bool render(RenderMethod& renderer) override diff --git a/thirdparty/thorvg/src/lib/tvgPicture.cpp b/thirdparty/thorvg/src/lib/tvgPicture.cpp index 1e04e25435..ad9db96245 100644 --- a/thirdparty/thorvg/src/lib/tvgPicture.cpp +++ b/thirdparty/thorvg/src/lib/tvgPicture.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -107,7 +107,7 @@ Result Picture::size(float* w, float* h) const noexcept const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept { //Try it, If not loaded yet. - pImpl->reload(); + pImpl->load(); if (pImpl->loader) { if (w) *w = static_cast(pImpl->loader->w); @@ -116,6 +116,23 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept if (w) *w = 0; if (h) *h = 0; } - if (pImpl->surface) return pImpl->surface->buffer; + if (pImpl->surface) return pImpl->surface->buf32; else return nullptr; } + + +Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept +{ + if (!triangles && triangleCnt > 0) return Result::InvalidArguments; + if (triangles && triangleCnt == 0) return Result::InvalidArguments; + + pImpl->mesh(triangles, triangleCnt); + return Result::Success; +} + + +uint32_t Picture::mesh(const Polygon** triangles) const noexcept +{ + if (triangles) *triangles = pImpl->rm.triangles; + return pImpl->rm.triangleCnt; +} diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h index b6bc19fa9c..6af7a732e5 100644 --- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h +++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_PICTURE_IMPL_H_ #define _TVG_PICTURE_IMPL_H_ @@ -63,30 +64,28 @@ struct Picture::Impl Paint* paint = nullptr; //vector picture uses Surface* surface = nullptr; //bitmap picture uses - void* rdata = nullptr; //engine data + RenderData rd = nullptr; //engine data float w = 0, h = 0; + RenderMesh rm; //mesh data bool resizing = false; - uint32_t rendererColorSpace = 0; ~Impl() { if (paint) delete(paint); - free(surface); + delete(surface); } bool dispose(RenderMethod& renderer) { bool ret = true; - if (paint) { - ret = paint->pImpl->dispose(renderer); - } else if (surface) { - ret = renderer.dispose(rdata); - rdata = nullptr; - } + if (paint) ret = paint->pImpl->dispose(renderer); + else if (surface) ret = renderer.dispose(rd); + rd = nullptr; + return ret; } - uint32_t reload() + uint32_t load() { if (loader) { if (!paint) { @@ -94,16 +93,21 @@ struct Picture::Impl paint = p.release(); loader->close(); if (w != loader->w || h != loader->h) { + if (!resizing) { + w = loader->w; + h = loader->h; + } loader->resize(paint, w, h); resizing = false; } if (paint) return RenderUpdateFlag::None; } } - free(surface); - if ((surface = loader->bitmap(rendererColorSpace).release())) { - loader->close(); - return RenderUpdateFlag::Image; + if (!surface) { + if ((surface = loader->bitmap().release())) { + loader->close(); + return RenderUpdateFlag::Image; + } } } return RenderUpdateFlag::None; @@ -123,32 +127,31 @@ struct Picture::Impl else return RenderTransform(pTransform, &tmp); } - void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag) + RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) { - rendererColorSpace = renderer.colorSpace(); - auto flag = reload(); + auto flag = load(); if (surface) { auto transform = resizeTransform(pTransform); - rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast(pFlag | flag)); + rd = renderer.prepare(surface, &rm, rd, &transform, opacity, clips, static_cast(pFlag | flag)); } else if (paint) { if (resizing) { loader->resize(paint, w, h); resizing = false; } - rdata = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast(pFlag | flag)); + rd = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast(pFlag | flag), clipper); } - return rdata; + return rd; } bool render(RenderMethod &renderer) { - if (surface) return renderer.renderImage(rdata); + if (surface) return renderer.renderImage(rd); else if (paint) return paint->pImpl->render(renderer); return false; } - bool viewbox(float* x, float* y, float* w, float* h) const + bool viewbox(float* x, float* y, float* w, float* h) { if (!loader) return false; if (x) *x = loader->vx; @@ -168,17 +171,43 @@ struct Picture::Impl bool bounds(float* x, float* y, float* w, float* h) { - if (x) *x = 0; - if (y) *y = 0; - if (w) *w = this->w; - if (h) *h = this->h; - + if (rm.triangleCnt > 0) { + auto triangles = rm.triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; + + for (uint32_t i = 0; i < rm.triangleCnt; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + } else { + if (x) *x = 0; + if (y) *y = 0; + if (w) *w = this->w; + if (h) *h = this->h; + } return true; } RenderRegion bounds(RenderMethod& renderer) { - if (rdata) return renderer.region(rdata); + if (rd) return renderer.region(rd); if (paint) return paint->pImpl->bounds(renderer); return {0, 0, 0, 0}; } @@ -216,15 +245,28 @@ struct Picture::Impl if (paint || surface) return Result::InsufficientCondition; if (loader) loader->close(); loader = LoaderMgr::loader(data, w, h, copy); - if (!loader) return Result::NonSupport; + if (!loader) return Result::FailedAllocation; this->w = loader->w; this->h = loader->h; return Result::Success; } + void mesh(const Polygon* triangles, const uint32_t triangleCnt) + { + if (triangles && triangleCnt > 0) { + this->rm.triangleCnt = triangleCnt; + this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt); + memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt); + } else { + free(this->rm.triangles); + this->rm.triangles = nullptr; + this->rm.triangleCnt = 0; + } + } + Paint* duplicate() { - reload(); + load(); auto ret = Picture::gen(); @@ -233,19 +275,27 @@ struct Picture::Impl dup->loader = loader; if (surface) { - dup->surface = static_cast(malloc(sizeof(Surface))); + dup->surface = new Surface; *dup->surface = *surface; + //TODO: A dupilcation is not a proxy... it needs copy of the pixel data? + dup->surface->owner = false; } dup->w = w; dup->h = h; dup->resizing = resizing; + if (rm.triangleCnt > 0) { + dup->rm.triangleCnt = rm.triangleCnt; + dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt); + memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); + } + return ret.release(); } Iterator* iterator() { - reload(); + load(); return new PictureIterator(paint); } }; diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp index 7f4e1d7e17..a85f60e5d0 100644 --- a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp +++ b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include "tvgFill.h" diff --git a/thirdparty/thorvg/src/lib/tvgRender.cpp b/thirdparty/thorvg/src/lib/tvgRender.cpp index 90f0917e6b..fb9270afe6 100644 --- a/thirdparty/thorvg/src/lib/tvgRender.cpp +++ b/thirdparty/thorvg/src/lib/tvgRender.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgRender.h" diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h index f474d87895..6270fa5316 100644 --- a/thirdparty/thorvg/src/lib/tvgRender.h +++ b/thirdparty/thorvg/src/lib/tvgRender.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_RENDER_H_ #define _TVG_RENDER_H_ @@ -28,18 +29,38 @@ namespace tvg { +using RenderData = void*; +using pixel_t = uint32_t; + enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255}; -struct Surface +struct Surface; + +enum ColorSpace { - //TODO: Union for multiple types - uint32_t* buffer; - uint32_t stride; - uint32_t w, h; - uint32_t cs; + 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. + ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. + ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. + Grayscale8, //One single channel data. + Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace. }; -using RenderData = void*; +struct Surface +{ + union { + pixel_t* data; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels + uint8_t* buf8; //for explicit 8bits grayscale + }; + uint32_t stride; + uint32_t w, h; + ColorSpace cs; + uint8_t channelSize; + + bool premultiplied; //Alpha-premultiplied + bool owner; //Only owner could modify the buffer +}; struct Compositor { @@ -47,6 +68,17 @@ struct Compositor uint32_t opacity; }; +struct RenderMesh +{ + Polygon* triangles = nullptr; + uint32_t triangleCnt = 0; + + ~RenderMesh() + { + free(triangles); + } +}; + struct RenderRegion { int32_t x, y, w, h; @@ -84,13 +116,110 @@ struct RenderTransform RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs); }; +struct RenderStroke +{ + float width = 0.0f; + uint8_t color[4] = {0, 0, 0, 0}; + Fill *fill = nullptr; + float* dashPattern = nullptr; + uint32_t dashCnt = 0; + StrokeCap cap = StrokeCap::Square; + StrokeJoin join = StrokeJoin::Bevel; + bool strokeFirst = false; + + ~RenderStroke() + { + free(dashPattern); + if (fill) delete(fill); + } +}; + +struct RenderShape +{ + struct + { + PathCommand* cmds = nullptr; + uint32_t cmdCnt = 0; + uint32_t reservedCmdCnt = 0; + + Point *pts = nullptr; + uint32_t ptsCnt = 0; + uint32_t reservedPtsCnt = 0; + } path; + + Fill *fill = nullptr; + RenderStroke *stroke = nullptr; + uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a + FillRule rule = FillRule::Winding; + + ~RenderShape() + { + free(path.cmds); + free(path.pts); + + if (fill) delete(fill); + if (stroke) delete(stroke); + } + + void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (r) *r = color[0]; + if (g) *g = color[1]; + if (b) *b = color[2]; + if (a) *a = color[3]; + } + + float strokeWidth() const + { + if (!stroke) return 0; + return stroke->width; + } + + bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (!stroke) return false; + + if (r) *r = stroke->color[0]; + if (g) *g = stroke->color[1]; + if (b) *b = stroke->color[2]; + if (a) *a = stroke->color[3]; + + return true; + } + + const Fill* strokeFill() const + { + if (!stroke) return nullptr; + return stroke->fill; + } + + uint32_t strokeDash(const float** dashPattern) const + { + if (!stroke) return 0; + if (dashPattern) *dashPattern = stroke->dashPattern; + return stroke->dashCnt; + } + + StrokeCap strokeCap() const + { + if (!stroke) return StrokeCap::Square; + return stroke->cap; + } + + StrokeJoin strokeJoin() const + { + if (!stroke) return StrokeJoin::Bevel; + return stroke->join; + } +}; class RenderMethod { public: virtual ~RenderMethod() {} - virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; - virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) = 0; + virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; @@ -99,17 +228,47 @@ public: virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion viewport() = 0; virtual bool viewport(const RenderRegion& vp) = 0; + virtual ColorSpace colorSpace() = 0; virtual bool clear() = 0; virtual bool sync() = 0; - virtual Compositor* target(const RenderRegion& region) = 0; + virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0; virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0; virtual bool endComposite(Compositor* cmp) = 0; - - virtual uint32_t colorSpace() = 0; }; +static inline uint8_t CHANNEL_SIZE(ColorSpace cs) +{ + switch(cs) { + case ColorSpace::ABGR8888: + case ColorSpace::ABGR8888S: + case ColorSpace::ARGB8888: + case ColorSpace::ARGB8888S: + return sizeof(uint32_t); + case ColorSpace::Grayscale8: + return sizeof(uint8_t); + case ColorSpace::Unsupported: + default: + TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs); + return 0; + } +} + +static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method) +{ + switch(method) { + case CompositeMethod::AlphaMask: + case CompositeMethod::InvAlphaMask: + return ColorSpace::Grayscale8; + case CompositeMethod::LumaMask: + return renderer.colorSpace(); + default: + TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method); + return ColorSpace::Unsupported; + } +} + } #endif //_TVG_RENDER_H_ diff --git a/thirdparty/thorvg/src/lib/tvgSaveModule.h b/thirdparty/thorvg/src/lib/tvgSaveModule.h index 3531662fdd..a997b644a7 100644 --- a/thirdparty/thorvg/src/lib/tvgSaveModule.h +++ b/thirdparty/thorvg/src/lib/tvgSaveModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SAVE_MODULE_H_ #define _TVG_SAVE_MODULE_H_ @@ -27,7 +28,7 @@ namespace tvg { -class SaveModule : public IteratorAccessor +class SaveModule { public: virtual ~SaveModule() {} diff --git a/thirdparty/thorvg/src/lib/tvgSaver.cpp b/thirdparty/thorvg/src/lib/tvgSaver.cpp index e71953700c..85b5a37855 100644 --- a/thirdparty/thorvg/src/lib/tvgSaver.cpp +++ b/thirdparty/thorvg/src/lib/tvgSaver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgCommon.h" #include "tvgSaveModule.h" diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp index 9ed7d45d5c..feb45a9d22 100644 --- a/thirdparty/thorvg/src/lib/tvgScene.cpp +++ b/thirdparty/thorvg/src/lib/tvgScene.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgSceneImpl.h" /************************************************************************/ diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h index b6c68262c6..9ce4d0b37d 100644 --- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h +++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SCENE_IMPL_H_ #define _TVG_SCENE_IMPL_H_ @@ -60,6 +61,7 @@ struct Scene::Impl Array paints; uint8_t opacity; //for composition RenderMethod* renderer = nullptr; //keep it for explicit clear + RenderData rd = nullptr; Scene* scene = nullptr; Impl(Scene* s) : scene(s) @@ -79,9 +81,11 @@ struct Scene::Impl (*paint)->pImpl->dispose(renderer); } + auto ret = renderer.dispose(rd); this->renderer = nullptr; + this->rd = nullptr; - return true; + return ret; } bool needComposition(uint32_t opacity) @@ -101,23 +105,29 @@ struct Scene::Impl return false; } - void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flag) + RenderData update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flag, bool clipper) { /* Overriding opacity value. If this scene is half-translucent, It must do intermeidate composition with that opacity value. */ this->opacity = static_cast(opacity); if (needComposition(opacity)) opacity = 255; - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - (*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast(flag)); - } - - /* FXIME: it requires to return list of children engine data - This is necessary for scene composition */ - this->renderer = &renderer; - return nullptr; + if (clipper) { + Array rds; + rds.reserve(paints.count); + for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { + rds.push((*paint)->pImpl->update(renderer, transform, opacity, clips, flag, true)); + } + rd = renderer.prepare(rds, rd, transform, opacity, clips, flag); + return rd; + } else { + for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { + (*paint)->pImpl->update(renderer, transform, opacity, clips, flag, false); + } + return nullptr; + } } bool render(RenderMethod& renderer) @@ -125,7 +135,7 @@ struct Scene::Impl Compositor* cmp = nullptr; if (needComposition(opacity)) { - cmp = renderer.target(bounds(renderer)); + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); renderer.beginComposite(cmp, CompositeMethod::None, opacity); } diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp index e57f2eafb2..a8354375c9 100644 --- a/thirdparty/thorvg/src/lib/tvgShape.cpp +++ b/thirdparty/thorvg/src/lib/tvgShape.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f; /* External Class Implementation */ /************************************************************************/ -Shape :: Shape() : pImpl(new Impl(this)) +Shape :: Shape() : pImpl(new Impl()) { Paint::pImpl->id = TVG_CLASS_ID_SHAPE; Paint::pImpl->method(new PaintMethod(pImpl)); @@ -59,8 +59,7 @@ uint32_t Shape::identifier() noexcept Result Shape::reset() noexcept { - pImpl->path.reset(); - pImpl->flag = RenderUpdateFlag::Path; + pImpl->reset(); return Result::Success; } @@ -70,9 +69,9 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept { if (!cmds) return 0; - *cmds = pImpl->path.cmds; + *cmds = pImpl->rs.path.cmds; - return pImpl->path.cmdCnt; + return pImpl->rs.path.cmdCnt; } @@ -80,9 +79,9 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept { if (!pts) return 0; - *pts = pImpl->path.pts; + *pts = pImpl->rs.path.pts; - return pImpl->path.ptsCnt; + return pImpl->rs.path.ptsCnt; } @@ -90,10 +89,8 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* { if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments; - pImpl->path.grow(cmdCnt, ptsCnt); - pImpl->path.append(cmds, cmdCnt, pts, ptsCnt); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->grow(cmdCnt, ptsCnt); + pImpl->append(cmds, cmdCnt, pts, ptsCnt); return Result::Success; } @@ -101,9 +98,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* Result Shape::moveTo(float x, float y) noexcept { - pImpl->path.moveTo(x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->moveTo(x, y); return Result::Success; } @@ -111,9 +106,7 @@ Result Shape::moveTo(float x, float y) noexcept Result Shape::lineTo(float x, float y) noexcept { - pImpl->path.lineTo(x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->lineTo(x, y); return Result::Success; } @@ -121,9 +114,7 @@ Result Shape::lineTo(float x, float y) noexcept Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept { - pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); return Result::Success; } @@ -131,9 +122,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float Result Shape::close() noexcept { - pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->close(); return Result::Success; } @@ -144,15 +133,13 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept auto rxKappa = rx * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA; - pImpl->path.grow(6, 13); - pImpl->path.moveTo(cx, cy - ry); - pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); - pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); - pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); - pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); - pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->grow(6, 13); + pImpl->moveTo(cx, cy - ry); + pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); + pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); + pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); + pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); + pImpl->close(); return Result::Success; } @@ -174,10 +161,10 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa Point start = {radius * cosf(startAngle), radius * sinf(startAngle)}; if (pie) { - pImpl->path.moveTo(cx, cy); - pImpl->path.lineTo(start.x + cx, start.y + cy); + pImpl->moveTo(cx, cy); + pImpl->lineTo(start.x + cx, start.y + cy); } else { - pImpl->path.moveTo(start.x + cx, start.y + cy); + pImpl->moveTo(start.x + cx, start.y + cy); } for (int i = 0; i < nCurves; ++i) { @@ -204,14 +191,12 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy}; Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy}; - pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y); + pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y); startAngle = endAngle; } - if (pie) pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + if (pie) pImpl->close(); return Result::Success; } @@ -228,48 +213,47 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) //rectangle if (rx == 0 && ry == 0) { - pImpl->path.grow(5, 4); - pImpl->path.moveTo(x, y); - pImpl->path.lineTo(x + w, y); - pImpl->path.lineTo(x + w, y + h); - pImpl->path.lineTo(x, y + h); - pImpl->path.close(); + pImpl->grow(5, 4); + pImpl->moveTo(x, y); + pImpl->lineTo(x + w, y); + pImpl->lineTo(x + w, y + h); + pImpl->lineTo(x, y + h); + pImpl->close(); //circle } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) { return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry); } else { auto hrx = rx * 0.5f; auto hry = ry * 0.5f; - pImpl->path.grow(10, 17); - pImpl->path.moveTo(x + rx, y); - pImpl->path.lineTo(x + w - rx, y); - pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); - pImpl->path.lineTo(x + w, y + h - ry); - pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); - pImpl->path.lineTo(x + rx, y + h); - pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); - pImpl->path.lineTo(x, y + ry); - pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); - pImpl->path.close(); + pImpl->grow(10, 17); + pImpl->moveTo(x + rx, y); + pImpl->lineTo(x + w - rx, y); + pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); + pImpl->lineTo(x + w, y + h - ry); + pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); + pImpl->lineTo(x + rx, y + h); + pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); + pImpl->lineTo(x, y + ry); + pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); + pImpl->close(); } - pImpl->flag |= RenderUpdateFlag::Path; - return Result::Success; } +//TODO: kill alpha at TVG 1.0, because we also have opacity Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept { - pImpl->color[0] = r; - pImpl->color[1] = g; - pImpl->color[2] = b; - pImpl->color[3] = a; + pImpl->rs.color[0] = r; + pImpl->rs.color[1] = g; + pImpl->rs.color[2] = b; + pImpl->rs.color[3] = a; pImpl->flag |= RenderUpdateFlag::Color; - if (pImpl->fill) { - delete(pImpl->fill); - pImpl->fill = nullptr; + if (pImpl->rs.fill) { + delete(pImpl->rs.fill); + pImpl->rs.fill = nullptr; pImpl->flag |= RenderUpdateFlag::Gradient; } @@ -282,8 +266,8 @@ Result Shape::fill(unique_ptr f) noexcept auto p = f.release(); if (!p) return Result::MemoryCorruption; - if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill); - pImpl->fill = p; + if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill); + pImpl->rs.fill = p; pImpl->flag |= RenderUpdateFlag::Gradient; return Result::Success; @@ -292,17 +276,23 @@ Result Shape::fill(unique_ptr f) noexcept Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { - if (r) *r = pImpl->color[0]; - if (g) *g = pImpl->color[1]; - if (b) *b = pImpl->color[2]; - if (a) *a = pImpl->color[3]; + pImpl->rs.fillColor(r, g, b, a); return Result::Success; } + const Fill* Shape::fill() const noexcept { - return pImpl->fill; + return pImpl->rs.fill; +} + + +Result Shape::order(bool strokeFirst) noexcept +{ + if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation; + + return Result::Success; } @@ -316,8 +306,7 @@ Result Shape::stroke(float width) noexcept float Shape::strokeWidth() const noexcept { - if (!pImpl->stroke) return 0; - return pImpl->stroke->width; + return pImpl->rs.strokeWidth(); } @@ -331,12 +320,7 @@ Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { - if (!pImpl->stroke) return Result::InsufficientCondition; - - if (r) *r = pImpl->stroke->color[0]; - if (g) *g = pImpl->stroke->color[1]; - if (b) *b = pImpl->stroke->color[2]; - if (a) *a = pImpl->stroke->color[3]; + if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition; return Result::Success; } @@ -350,9 +334,7 @@ Result Shape::stroke(unique_ptr f) noexcept const Fill* Shape::strokeFill() const noexcept { - if (!pImpl->stroke) return nullptr; - - return pImpl->stroke->fill; + return pImpl->rs.strokeFill(); } @@ -373,11 +355,7 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept uint32_t Shape::strokeDash(const float** dashPattern) const noexcept { - if (!pImpl->stroke) return 0; - - if (dashPattern) *dashPattern = pImpl->stroke->dashPattern; - - return pImpl->stroke->dashCnt; + return pImpl->rs.strokeDash(dashPattern); } @@ -399,23 +377,19 @@ Result Shape::stroke(StrokeJoin join) noexcept StrokeCap Shape::strokeCap() const noexcept { - if (!pImpl->stroke) return StrokeCap::Square; - - return pImpl->stroke->cap; + return pImpl->rs.strokeCap(); } StrokeJoin Shape::strokeJoin() const noexcept { - if (!pImpl->stroke) return StrokeJoin::Bevel; - - return pImpl->stroke->join; + return pImpl->rs.strokeJoin(); } Result Shape::fill(FillRule r) noexcept { - pImpl->rule = r; + pImpl->rs.rule = r; return Result::Success; } @@ -423,5 +397,5 @@ Result Shape::fill(FillRule r) noexcept FillRule Shape::fillRule() const noexcept { - return pImpl->rule; + return pImpl->rs.rule; } diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h index 738b21ed70..da288756fb 100644 --- a/thirdparty/thorvg/src/lib/tvgShapeImpl.h +++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SHAPE_IMPL_H_ #define _TVG_SHAPE_IMPL_H_ @@ -29,243 +30,155 @@ /* Internal Class Implementation */ /************************************************************************/ -struct ShapeStroke +struct Shape::Impl { - float width; - uint8_t color[4]; - Fill *fill; - float* dashPattern; - uint32_t dashCnt; - StrokeCap cap; - StrokeJoin join; - - void copy(const ShapeStroke* src) + RenderShape rs; //shape data + RenderData rd = nullptr; //engine data + uint32_t flag = RenderUpdateFlag::None; + + bool dispose(RenderMethod& renderer) { - width = src->width; - dashCnt = src->dashCnt; - cap = src->cap; - join = src->join; - - memcpy(color, src->color, sizeof(color)); - if (dashCnt > 0) { - dashPattern = static_cast(malloc(sizeof(float) * dashCnt)); - memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt); - } - if (src->fill) fill = src->fill->duplicate(); + auto ret = renderer.dispose(rd); + rd = nullptr; + return ret; } - void clear() + bool render(RenderMethod& renderer) { - if (dashPattern) free(dashPattern); - if (fill) delete(fill); + return renderer.renderShape(rd); } -}; - -struct ShapePath -{ - PathCommand* cmds = nullptr; - uint32_t cmdCnt = 0; - uint32_t reservedCmdCnt = 0; - - Point *pts = nullptr; - uint32_t ptsCnt = 0; - uint32_t reservedPtsCnt = 0; - - ~ShapePath() + RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) { - if (cmds) free(cmds); - if (pts) free(pts); + rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast(pFlag | flag), clipper); + flag = RenderUpdateFlag::None; + return rd; } - ShapePath() + RenderRegion bounds(RenderMethod& renderer) { + return renderer.region(rd); } - void duplicate(const ShapePath* src) + bool bounds(float* x, float* y, float* w, float* h) { - if (src->cmdCnt == 0 || src->ptsCnt == 0) return; - - cmdCnt = src->cmdCnt; - reservedCmdCnt = src->reservedCmdCnt; - ptsCnt = src->ptsCnt; - reservedPtsCnt = src->reservedPtsCnt; + //Path bounding size + if (rs.path.ptsCnt > 0 ) { + Point min = { rs.path.pts[0].x, rs.path.pts[0].y }; + Point max = { rs.path.pts[0].x, rs.path.pts[0].y }; + + for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) { + if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x; + if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y; + if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x; + if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y; + } - cmds = static_cast(malloc(sizeof(PathCommand) * reservedCmdCnt)); - if (!cmds) return; - memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt); + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + } - pts = static_cast(malloc(sizeof(Point) * reservedPtsCnt)); - if (!pts) { - free(cmds); - return; + //Stroke feathering + if (rs.stroke) { + if (x) *x -= rs.stroke->width * 0.5f; + if (y) *y -= rs.stroke->width * 0.5f; + if (w) *w += rs.stroke->width; + if (h) *h += rs.stroke->width; } - memcpy(pts, src->pts, sizeof(Point) * ptsCnt); + return rs.path.ptsCnt > 0 ? true : false; } void reserveCmd(uint32_t cmdCnt) { - if (cmdCnt <= reservedCmdCnt) return; - reservedCmdCnt = cmdCnt; - cmds = static_cast(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt)); + if (cmdCnt <= rs.path.reservedCmdCnt) return; + rs.path.reservedCmdCnt = cmdCnt; + rs.path.cmds = static_cast(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt)); } void reservePts(uint32_t ptsCnt) { - if (ptsCnt <= reservedPtsCnt) return; - reservedPtsCnt = ptsCnt; - pts = static_cast(realloc(pts, sizeof(Point) * reservedPtsCnt)); + if (ptsCnt <= rs.path.reservedPtsCnt) return; + rs.path.reservedPtsCnt = ptsCnt; + rs.path.pts = static_cast(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt)); } void grow(uint32_t cmdCnt, uint32_t ptsCnt) { - reserveCmd(this->cmdCnt + cmdCnt); - reservePts(this->ptsCnt + ptsCnt); + reserveCmd(rs.path.cmdCnt + cmdCnt); + reservePts(rs.path.ptsCnt + ptsCnt); } void reset() { - cmdCnt = 0; - ptsCnt = 0; - } - - void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) - { - memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); - memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt); - this->cmdCnt += cmdCnt; - this->ptsCnt += ptsCnt; - } + rs.path.cmdCnt = 0; + rs.path.ptsCnt = 0; - void moveTo(float x, float y) - { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); - - cmds[cmdCnt++] = PathCommand::MoveTo; - pts[ptsCnt++] = {x, y}; + flag = RenderUpdateFlag::Path; } - void lineTo(float x, float y) + void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); + memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); + memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt); + rs.path.cmdCnt += cmdCnt; + rs.path.ptsCnt += ptsCnt; - cmds[cmdCnt++] = PathCommand::LineTo; - pts[ptsCnt++] = {x, y}; + flag |= RenderUpdateFlag::Path; } - void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) + void moveTo(float x, float y) { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2); + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - cmds[cmdCnt++] = PathCommand::CubicTo; - pts[ptsCnt++] = {cx1, cy1}; - pts[ptsCnt++] = {cx2, cy2}; - pts[ptsCnt++] = {x, y}; - } + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - void close() - { - if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return; - - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - cmds[cmdCnt++] = PathCommand::Close; + flag |= RenderUpdateFlag::Path; } - bool bounds(float* x, float* y, float* w, float* h) const + void lineTo(float x, float y) { - if (ptsCnt == 0) return false; + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - Point min = { pts[0].x, pts[0].y }; - Point max = { pts[0].x, pts[0].y }; + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - for (uint32_t i = 1; i < ptsCnt; ++i) { - if (pts[i].x < min.x) min.x = pts[i].x; - if (pts[i].y < min.y) min.y = pts[i].y; - if (pts[i].x > max.x) max.x = pts[i].x; - if (pts[i].y > max.y) max.y = pts[i].y; - } - - if (x) *x = min.x; - if (y) *y = min.y; - if (w) *w = max.x - min.x; - if (h) *h = max.y - min.y; - - return true; + flag |= RenderUpdateFlag::Path; } -}; - -struct Shape::Impl -{ - ShapePath path; - Fill *fill = nullptr; - ShapeStroke *stroke = nullptr; - uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a - FillRule rule = FillRule::Winding; - RenderData rdata = nullptr; //engine data - Shape *shape = nullptr; - uint32_t flag = RenderUpdateFlag::None; - - Impl(Shape* s) : shape(s) - { - } - - ~Impl() - { - if (fill) delete(fill); - if (stroke) { - stroke->clear(); - free (stroke); - } - } - - bool dispose(RenderMethod& renderer) + void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { - auto ret = renderer.dispose(rdata); - rdata = nullptr; - return ret; - } + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2); - bool render(RenderMethod& renderer) - { - return renderer.renderShape(rdata); - } + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo; + rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1}; + rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2}; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag) - { - this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast(pFlag | flag)); - flag = RenderUpdateFlag::None; - return this->rdata; + flag |= RenderUpdateFlag::Path; } - RenderRegion bounds(RenderMethod& renderer) + void close() { - return renderer.region(rdata); - } + if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return; - bool bounds(float* x, float* y, float* w, float* h) - { - auto ret = path.bounds(x, y, w, h); + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close; - //Stroke feathering - if (stroke) { - if (x) *x -= stroke->width * 0.5f; - if (y) *y -= stroke->width * 0.5f; - if (w) *w += stroke->width; - if (h) *h += stroke->width; - } - return ret; + flag |= RenderUpdateFlag::Path; } bool strokeWidth(float width) { //TODO: Size Exception? - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->width = width; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->width = width; flag |= RenderUpdateFlag::Stroke; return true; @@ -273,8 +186,8 @@ struct Shape::Impl bool strokeCap(StrokeCap cap) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->cap = cap; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->cap = cap; flag |= RenderUpdateFlag::Stroke; return true; @@ -282,8 +195,8 @@ struct Shape::Impl bool strokeJoin(StrokeJoin join) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->join = join; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->join = join; flag |= RenderUpdateFlag::Stroke; return true; @@ -291,17 +204,17 @@ struct Shape::Impl bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->fill) { - delete(stroke->fill); - stroke->fill = nullptr; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill) { + delete(rs.stroke->fill); + rs.stroke->fill = nullptr; flag |= RenderUpdateFlag::GradientStroke; } - stroke->color[0] = r; - stroke->color[1] = g; - stroke->color[2] = b; - stroke->color[3] = a; + rs.stroke->color[0] = r; + rs.stroke->color[1] = g; + rs.stroke->color[2] = b; + rs.stroke->color[3] = a; flag |= RenderUpdateFlag::Stroke; @@ -313,9 +226,9 @@ struct Shape::Impl auto p = f.release(); if (!p) return Result::MemoryCorruption; - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->fill && stroke->fill != p) delete(stroke->fill); - stroke->fill = p; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); + rs.stroke->fill = p; flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::GradientStroke; @@ -327,23 +240,32 @@ struct Shape::Impl { //Reset dash if (!pattern && cnt == 0) { - free(stroke->dashPattern); - stroke->dashPattern = nullptr; + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; } else { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->dashCnt != cnt) { - free(stroke->dashPattern); - stroke->dashPattern = nullptr; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->dashCnt != cnt) { + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; } - if (!stroke->dashPattern) { - stroke->dashPattern = static_cast(malloc(sizeof(float) * cnt)); - if (!stroke->dashPattern) return false; + if (!rs.stroke->dashPattern) { + rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * cnt)); + if (!rs.stroke->dashPattern) return false; } for (uint32_t i = 0; i < cnt; ++i) { - stroke->dashPattern[i] = pattern[i]; + rs.stroke->dashPattern[i] = pattern[i]; } } - stroke->dashCnt = cnt; + rs.stroke->dashCnt = cnt; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeFirst(bool strokeFirst) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->strokeFirst = strokeFirst; flag |= RenderUpdateFlag::Stroke; return true; @@ -354,29 +276,53 @@ struct Shape::Impl auto ret = Shape::gen(); auto dup = ret.get()->pImpl; - dup->rule = rule; + dup->rs.rule = rs.rule; //Color - memcpy(dup->color, color, sizeof(color)); + memcpy(dup->rs.color, rs.color, sizeof(rs.color)); dup->flag = RenderUpdateFlag::Color; //Path - dup->path.duplicate(&path); + if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) { + dup->rs.path.cmdCnt = rs.path.cmdCnt; + dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt; + dup->rs.path.ptsCnt = rs.path.ptsCnt; + dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt; + + dup->rs.path.cmds = static_cast(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt)); + if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt); + + dup->rs.path.pts = static_cast(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt)); + if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt); + } dup->flag |= RenderUpdateFlag::Path; //Stroke - if (stroke) { - dup->stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - dup->stroke->copy(stroke); + if (rs.stroke) { + dup->rs.stroke = new RenderStroke(); + dup->rs.stroke->width = rs.stroke->width; + dup->rs.stroke->dashCnt = rs.stroke->dashCnt; + dup->rs.stroke->cap = rs.stroke->cap; + dup->rs.stroke->join = rs.stroke->join; + dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst; + memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); + + if (rs.stroke->dashCnt > 0) { + dup->rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * rs.stroke->dashCnt)); + memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt); + } + dup->flag |= RenderUpdateFlag::Stroke; - if (stroke->fill) + if (rs.stroke->fill) { + dup->rs.stroke->fill = rs.stroke->fill->duplicate(); dup->flag |= RenderUpdateFlag::GradientStroke; + } } //Fill - if (fill) { - dup->fill = fill->duplicate(); + if (rs.fill) { + dup->rs.fill = rs.fill->duplicate(); dup->flag |= RenderUpdateFlag::Gradient; } diff --git a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp index b5cae424f7..626bd51a2e 100644 --- a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp +++ b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgCanvasImpl.h" #ifdef THORVG_SW_RASTER_SUPPORT @@ -84,7 +85,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t auto renderer = static_cast(Canvas::pImpl->renderer); if (!renderer) return Result::MemoryCorruption; - if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments; + if (!renderer->target(buffer, stride, w, h, static_cast(cs))) return Result::InvalidArguments; //Paints must be updated again with this new target. Canvas::pImpl->needRefresh(); diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp index 8c07dc3859..019468083d 100644 --- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp +++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include @@ -99,13 +100,12 @@ struct TaskQueue { }; -class TaskSchedulerImpl +struct TaskSchedulerImpl { -public: - unsigned threadCnt; + uint32_t threadCnt; vector threads; vector taskQueues; - atomic idx{0}; + atomic idx{0}; TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt) { @@ -135,7 +135,7 @@ public: } if (!success && !taskQueues[i].pop(&task)) break; - (*task)(i); + (*task)(i + 1); } } diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h index 163e387f29..ce2016f561 100644 --- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h +++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_TASK_SCHEDULER_H_ #define _TVG_TASK_SCHEDULER_H_ @@ -44,8 +45,8 @@ struct Task private: mutex mtx; condition_variable cv; - bool ready{true}; - bool pending{false}; + bool ready = true; + bool pending = false; public: virtual ~Task() = default; @@ -78,11 +79,9 @@ private: pending = true; } - friend class TaskSchedulerImpl; + friend struct TaskSchedulerImpl; }; - - } #endif //_TVG_TASK_SCHEDULER_H_ diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp index 1fb0681814..b0a9fdd579 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -23,42 +23,14 @@ #include "tvgLoader.h" #include "tvgPngLoader.h" -static inline uint32_t PREMULTIPLY(uint32_t c) -{ - auto a = (c >> 24); - return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); -} +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ -static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = PREMULTIPLY(*src); - } - } -} - - -static inline uint32_t CHANGE_COLORSPACE(uint32_t c) -{ - return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); -} - - -static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = CHANGE_COLORSPACE(*src); - } - } -} - +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ PngLoader::PngLoader() { @@ -84,6 +56,7 @@ bool PngLoader::open(const string& path) w = (float)image->width; h = (float)image->height; + cs = ColorSpace::ARGB8888; return true; } @@ -96,6 +69,7 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) w = (float)image->width; h = (float)image->height; + cs = ColorSpace::ARGB8888; return true; } @@ -117,8 +91,6 @@ bool PngLoader::read() } content = reinterpret_cast(buffer); - _premultiply(reinterpret_cast(buffer), image->width, image->height); - return true; } @@ -128,20 +100,20 @@ bool PngLoader::close() return true; } -unique_ptr PngLoader::bitmap(uint32_t colorSpace) +unique_ptr PngLoader::bitmap() { if (!content) return nullptr; - if (this->colorSpace != colorSpace) { - this->colorSpace = colorSpace; - _changeColorSpace(content, w, h); - } - auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = content; + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; + surface->buf32 = content; surface->stride = w; surface->w = w; surface->h = h; - surface->cs = colorSpace; + surface->cs = cs; + surface->channelSize = sizeof(uint32_t); + surface->owner = true; + surface->premultiplied = false; return unique_ptr(surface); } diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h index f8c0daa61c..5354e1bdd6 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h +++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_PNG_LOADER_H_ #define _TVG_PNG_LOADER_H_ @@ -36,7 +37,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap(uint32_t colorSpace) override; + unique_ptr bitmap() override; private: png_imagep image = nullptr; diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp index f64b7110fe..6edda86cc1 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -28,24 +28,6 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline uint32_t CHANGE_COLORSPACE(uint32_t c) -{ - return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); -} - - -static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = CHANGE_COLORSPACE(*src); - } - } -} - - void JpgLoader::clear() { jpgdDelete(decoder); @@ -79,6 +61,7 @@ bool JpgLoader::open(const string& path) w = static_cast(width); h = static_cast(height); + cs = ColorSpace::ARGB8888; return true; } @@ -104,6 +87,7 @@ bool JpgLoader::open(const char* data, uint32_t size, bool copy) w = static_cast(width); h = static_cast(height); + cs = ColorSpace::ARGB8888; return true; } @@ -128,22 +112,22 @@ bool JpgLoader::close() } -unique_ptr JpgLoader::bitmap(uint32_t colorSpace) +unique_ptr JpgLoader::bitmap() { this->done(); if (!image) return nullptr; - if (this->colorSpace != colorSpace) { - this->colorSpace = colorSpace; - _changeColorSpace(reinterpret_cast(image), w, h); - } - auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = reinterpret_cast(image); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; + surface->buf8 = image; surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); - surface->cs = colorSpace; + surface->cs = cs; + surface->channelSize = sizeof(uint32_t); + surface->premultiplied = true; + surface->owner = true; return unique_ptr(surface); } diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h index c47cb6704f..6d2febe94f 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_JPG_LOADER_H_ #define _TVG_JPG_LOADER_H_ @@ -44,7 +45,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap(uint32_t colorSpace) override; + unique_ptr bitmap() override; void run(unsigned tid) override; }; diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp index 56b40acf0b..6ea2efb054 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -80,7 +80,7 @@ enum jpgd_status enum { JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, - JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384 + JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384 }; // Input stream interface. @@ -151,7 +151,7 @@ public: // If JPGD_SUCCESS is returned you may then call decode() on each scanline. int begin_decoding(); // Returns the next scan line. - // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). + // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4). // Returns JPGD_SUCCESS if a scan line has been returned. // Returns JPGD_DONE if all scan lines have been returned. @@ -1246,7 +1246,7 @@ void jpeg_decoder::read_sof_marker() uint32_t num_left = get_bits(16); if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */ - + m_image_y_size = get_bits(16); if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT); @@ -1326,7 +1326,7 @@ void jpeg_decoder::read_sos_marker() } num_left -= 3; - while (num_left) { /* read past whatever is num_left */ + while (num_left) { /* read past whatever is num_left */ get_bits(8); num_left--; } @@ -1411,7 +1411,7 @@ int jpeg_decoder::process_markers() stop_decoding(JPGD_UNEXPECTED_MARKER); break; } - default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ + default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ skip_variable_marker(); break; } @@ -1441,7 +1441,7 @@ void jpeg_decoder::locate_soi_marker() if (lastchar == 0xFF) { if (thischar == M_SOI) break; - else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end + else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end } } @@ -1460,7 +1460,7 @@ void jpeg_decoder::locate_sof_marker() switch (c) { case M_SOF2: m_progressive_flag = true; case M_SOF0: /* baseline DCT */ - case M_SOF1: { /* extended sequential DCT */ + case M_SOF1: { /* extended sequential DCT */ read_sof_marker(); break; } @@ -1671,7 +1671,7 @@ void jpeg_decoder::transform_mcu_expand(int mcu_row) JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1); JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64); - int max_zag = m_mcu_block_max_zag[mcu_block++] - 1; + int max_zag = m_mcu_block_max_zag[mcu_block++] - 1; if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis switch (s_max_rc[max_zag]) { @@ -1789,7 +1789,7 @@ void jpeg_decoder::load_next_row() p[0] = pDC[0]; memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t)); - for (i = 63; i > 0; i--) { + for (i = 63; i > 0; i--) { if (p[g_ZAG[i]]) break; } @@ -1809,7 +1809,7 @@ void jpeg_decoder::load_next_row() if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) { block_y_mcu_ofs = 0; block_x_mcu[component_id] += m_comp_h_samp[component_id]; - } + } } } if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row); @@ -1865,7 +1865,7 @@ void jpeg_decoder::process_restart() static inline int dequantize_ac(int c, int q) -{ +{ c *= q; return c; } @@ -1910,7 +1910,7 @@ void jpeg_decoder::decode_next_row() while (n--) p[g_ZAG[kt++]] = 0; } k += r; - } + } s = JPGD_HUFF_EXTEND(extra_bits, s); JPGD_ASSERT(k < 64); p[g_ZAG[k]] = static_cast(dequantize_ac(s, q[k])); //s * q[k]; @@ -2204,7 +2204,7 @@ int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len) } else *pScan_line = m_pScan_line_1; break; - } + } case JPGD_YH2V1: { H2V1Convert(); *pScan_line = m_pScan_line_0; @@ -2609,11 +2609,11 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in int p1 = 1 << pD->m_successive_low; int m1 = static_cast(-1) << pD->m_successive_low; jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); - + JPGD_ASSERT(pD->m_spectral_end <= 63); - + k = pD->m_spectral_start; - + if (pD->m_eob_run == 0) { for ( ; k <= pD->m_spectral_end; k++) { s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h index ca9cb35c32..030fdc2946 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp index 19c1dd6668..5f5e72b0dd 100644 --- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp +++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include "tvgLoader.h" @@ -28,22 +29,6 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline uint32_t CHANGE_COLORSPACE(uint32_t c) -{ - return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); -} - - -static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = CHANGE_COLORSPACE(*src); - } - } -} /************************************************************************/ /* External Class Implementation */ @@ -73,6 +58,8 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) } else content = const_cast(data); + cs = ColorSpace::ARGB8888; + return true; } @@ -89,20 +76,20 @@ bool RawLoader::close() } -unique_ptr RawLoader::bitmap(uint32_t colorSpace) +unique_ptr RawLoader::bitmap() { if (!content) return nullptr; - if (this->colorSpace != colorSpace) { - this->colorSpace = colorSpace; - _changeColorSpace(content, w, h); - } - auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = content; + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; + surface->buf32 = content; surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); - surface->cs = colorSpace; + surface->cs = cs; + surface->channelSize = sizeof(uint32_t); + surface->premultiplied = true; + surface->owner = true; return unique_ptr(surface); } diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h index 8789b0cf51..69f9bdc47a 100644 --- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h +++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_RAW_LOADER_H_ #define _TVG_RAW_LOADER_H_ @@ -35,7 +36,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap(uint32_t colorSpace) override; + unique_ptr bitmap() override; }; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp index 478ba5d3d1..694e6d1ebf 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2022 - 2023 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 @@ -32,13 +32,13 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) { if (from == nullptr) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). - if (from->curColorSet && !((int)to->flags & (int)SvgStyleFlags::Color)) { + if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) { to->color = from->color; to->curColorSet = true; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color); + to->flags = (to->flags | SvgStyleFlags::Color); } //Fill - if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) { + if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; @@ -46,21 +46,21 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->fill.paint.url) free(to->fill.paint.url); to->fill.paint.url = strdup(from->fill.paint.url); } - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill); + to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Fill); } - if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) { + if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity); + to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::FillOpacity); } - if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) { + if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) { to->fill.fillRule = from->fill.fillRule; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule); + to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); + to->flags = (to->flags | SvgStyleFlags::FillRule); } //Stroke - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) { + if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; @@ -68,45 +68,45 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->stroke.paint.url) free(to->stroke.paint.url); to->stroke.paint.url = strdup(from->stroke.paint.url); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Stroke); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) { + if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) { + if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) { to->stroke.width = from->stroke.width; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); + to->flags = (to->flags | SvgStyleFlags::StrokeWidth); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) { + if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) { if (from->stroke.dash.array.count > 0) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { to->stroke.dash.array.push(from->stroke.dash.array.data[i]); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); + to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); } } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) { + if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) { to->stroke.cap = from->stroke.cap; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Cap); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); + to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) { + if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) { to->stroke.join = from->stroke.join; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); + to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); } //Opacity //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' - if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) { + if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) { to->opacity = from->opacity; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::Opacity); } } @@ -118,11 +118,11 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute - if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) { + if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) { to->transform = (Matrix*)malloc(sizeof(Matrix)); if (to->transform) { *to->transform = *from->transform; - to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform); + to->style->flags = (to->style->flags | SvgStyleFlags::Transform); } } //Copy style attribute @@ -155,12 +155,12 @@ SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType t SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) { - if (!style) return nullptr; + if (!style || !title) return nullptr; auto child = style->child.data; for (uint32_t i = 0; i < style->child.count; ++i, ++child) { if ((*child)->type == SvgNodeType::CssStyle) { - if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + if ((*child)->id && !strcmp((*child)->id, title)) return (*child); } } return nullptr; @@ -175,9 +175,6 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style) if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) { cssCopyStyleAttr(*child, cssNode); } - if (auto cssNode = cssFindStyleNode(style, nullptr)) { - cssCopyStyleAttr(*child, cssNode); - } cssUpdateStyle(*child, style); } } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h index 66477c1a32..228c5996da 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2022 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index bc350a0eb8..f9f08cdb81 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -137,11 +137,11 @@ static constexpr struct }; -static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) +static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) { if (!strcmp(*content, "none")) { *align = AspectRatioAlign::None; - return true; + return; } for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { @@ -158,8 +158,6 @@ static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp } else if (!strcmp(*content, "slice")) { *meetOrSlice = AspectRatioMeetOrSlice::Slice; } - - return true; } @@ -254,6 +252,36 @@ static SvgMaskType _toMaskType(const char* str) } +//The default rendering order: fill, stroke, markers +//If any is omitted, will be rendered in its default order after the specified ones. +static bool _toPaintOrder(const char* str) +{ + uint8_t position = 1; + uint8_t strokePosition = 0; + uint8_t fillPosition = 0; + + while (*str != '\0') { + str = _skipSpace(str, nullptr); + if (!strncmp(str, "fill", 4)) { + fillPosition = position++; + str += 4; + } else if (!strncmp(str, "stroke", 6)) { + strokePosition = position++; + str += 6; + } else if (!strncmp(str, "markers", 7)) { + str += 7; + } else { + return _toPaintOrder("fill stroke"); + } + } + + if (fillPosition == 0) fillPosition = position++; + if (strokePosition == 0) strokePosition = position++; + + return fillPosition < strokePosition; +} + + #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \ static Type _to##Name1(const char* str) \ { \ @@ -833,20 +861,40 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) if (!strcmp(key, "width")) { doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent); + } else { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width); + } } else if (!strcmp(key, "height")) { doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); + if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent); + } else { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height); + } } else if (!strcmp(key, "viewBox")) { if (_parseNumber(&value, &doc->vx)) { if (_parseNumber(&value, &doc->vy)) { if (_parseNumber(&value, &doc->vw)) { - _parseNumber(&value, &doc->vh); - loader->svgParse->global.h = doc->vh; + if (_parseNumber(&value, &doc->vh)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox); + loader->svgParse->global.h = doc->vh; + } + loader->svgParse->global.w = doc->vw; } - loader->svgParse->global.w = doc->vw; + loader->svgParse->global.y = doc->vy; } - loader->svgParse->global.y = doc->vy; + loader->svgParse->global.x = doc->vx; + } + if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) { + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox); + TVGLOG("SVG", "Negative values of the width and/or height - the attribute invalidated."); + } + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { + loader->svgParse->global.x = loader->svgParse->global.y = 0.0f; + loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; } - loader->svgParse->global.x = doc->vx; } else if (!strcmp(key, "preserveAspectRatio")) { _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); } else if (!strcmp(key, "style")) { @@ -890,7 +938,7 @@ static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, co static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { SvgStyleProperty* style = node->style; - style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint); + style->fill.flags = (style->fill.flags | SvgFillFlags::Paint); _handlePaintAttr(&style->fill.paint, value); } @@ -898,47 +946,47 @@ static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { SvgStyleProperty* style = node->style; - style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint); + style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint); _handlePaintAttr(&style->stroke.paint, value); } static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity); node->style->stroke.opacity = _toOpacity(value); } static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash); _parseDashArray(loader, value, &node->style->stroke.dash); } static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); } static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap); node->style->stroke.cap = _toLineCap(value); } static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join); node->style->stroke.join = _toLineJoin(value); } static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule); + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule); node->style->fill.fillRule = _toFillRule(value); } @@ -951,7 +999,7 @@ static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity); + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity); node->style->fill.opacity = _toOpacity(value); } @@ -1001,6 +1049,13 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, } +static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder); + node->style->paintOrder = _toPaintOrder(value); +} + + static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { auto cssClass = &node->style->cssClass; @@ -1051,7 +1106,8 @@ static constexpr struct STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath), STYLE_DEF(mask, Mask, SvgStyleFlags::Mask), STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType), - STYLE_DEF(display, Display, SvgStyleFlags::Display) + STYLE_DEF(display, Display, SvgStyleFlags::Display), + STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder) }; @@ -1071,8 +1127,8 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { if (style) { styleTags[i].tagHandler(loader, node, value); - node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag); - } else if (!((int)node->style->flags & (int)styleTags[i].flag)) { + node->style->flags = (node->style->flags | styleTags[i].flag); + } else if (!(node->style->flags & styleTags[i].flag)) { styleTags[i].tagHandler(loader, node, value); } return true; @@ -1253,6 +1309,8 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type) node->style->stroke.join = StrokeJoin::Miter; node->style->stroke.scale = 1.0; + node->style->paintOrder = _toPaintOrder("fill stroke"); + //Default display is true("inline"). node->display = true; @@ -1291,22 +1349,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha if (!loader->svgParse->node) return nullptr; SvgDocNode* doc = &(loader->svgParse->node->node.doc); - loader->svgParse->global.w = 0; - loader->svgParse->global.h = 0; + loader->svgParse->global.w = 1.0f; + loader->svgParse->global.h = 1.0f; doc->align = AspectRatioAlign::XMidYMid; doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; + doc->viewFlag = SvgViewFlag::None; func(buf, bufLength, _attrParseSvgNode, loader); - if (loader->svgParse->global.w == 0) { - if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1; - else loader->svgParse->global.w = doc->w; - } - if (loader->svgParse->global.h == 0) { - if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1; - else loader->svgParse->global.h = doc->h; + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { + if (doc->viewFlag & SvgViewFlag::Width) { + loader->svgParse->global.w = doc->w; + } + if (doc->viewFlag & SvgViewFlag::Height) { + loader->svgParse->global.h = doc->h; + } } - return loader->svgParse->node; } @@ -1868,524 +1926,308 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id) } -static void _cloneGradStops(Array& dst, const Array& src) +static constexpr struct { - for (uint32_t i = 0; i < src.count; ++i) { - dst.push(src.data[i]); - } -} + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} useTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)}, + {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)}, + {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)} +}; -static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from) +static void _cloneNode(SvgNode* from, SvgNode* parent, int depth); +static bool _attrParseUseNode(void* data, const char* key, const char* value) { - if (!from) return nullptr; + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode *defs, *nodeFrom, *node = loader->svgParse->node; + char* id; - auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); - if (!grad) return nullptr; + SvgUseNode* use = &(node->node.use); + int sz = strlen(key); + unsigned char* array = (unsigned char*)use; + for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) { + if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) { + *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type); - grad->type = from->type; - grad->id = from->id ? _copyId(from->id) : nullptr; - grad->ref = from->ref ? _copyId(from->ref) : nullptr; - grad->spread = from->spread; - grad->userSpace = from->userSpace; + if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true; + else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true; - if (from->transform) { - grad->transform = (Matrix*)calloc(1, sizeof(Matrix)); - if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix)); + return true; + } } - if (grad->type == SvgGradientType::Linear) { - grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); - if (!grad->linear) goto error_grad_alloc; - memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient)); - } else if (grad->type == SvgGradientType::Radial) { - grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient)); - if (!grad->radial) goto error_grad_alloc; - memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient)); + if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + id = _idFromHref(value); + defs = _getDefsNode(node); + nodeFrom = _findNodeById(defs, id); + if (nodeFrom) { + _cloneNode(nodeFrom, node, 0); + if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + free(id); + } else { + //some svg export software include element at the end of the file + //if so the 'from' element won't be found now and we have to repeat finding + //after the whole file is parsed + _postpone(loader->cloneNodes, node, id); + } + } else { + return _attrParseGNode(data, key, value); } + return true; +} - _cloneGradStops(grad->stops, from->stops); - return grad; +static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Use); -error_grad_alloc: - if (grad) { - grad->clear(); - free(grad); - } - return nullptr; + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->node.use.isWidthSet = false; + loader->svgParse->node->node.use.isHeightSet = false; + + func(buf, bufLength, _attrParseUseNode, loader); + return loader->svgParse->node; } -static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent) +//TODO: Implement 'text' primitive +static constexpr struct { - if (parent == nullptr) return; - //Inherit the property of parent if not present in child. - if (!child->curColorSet) { - child->color = parent->color; - child->curColorSet = parent->curColorSet; - } - //Fill - if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) { - child->fill.paint.color = parent->fill.paint.color; - child->fill.paint.none = parent->fill.paint.none; - child->fill.paint.curColor = parent->fill.paint.curColor; - if (parent->fill.paint.url) { - if (child->fill.paint.url) free(child->fill.paint.url); - child->fill.paint.url = _copyId(parent->fill.paint.url); - } - } - if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) { - child->fill.opacity = parent->fill.opacity; - } - if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) { - child->fill.fillRule = parent->fill.fillRule; - } - //Stroke - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) { - child->stroke.paint.color = parent->stroke.paint.color; - child->stroke.paint.none = parent->stroke.paint.none; - child->stroke.paint.curColor = parent->stroke.paint.curColor; - if (parent->stroke.paint.url) { - if (child->stroke.paint.url) free(child->stroke.paint.url); - child->stroke.paint.url = _copyId(parent->stroke.paint.url); - } else { - child->stroke.paint.url = nullptr; - } - } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) { - child->stroke.opacity = parent->stroke.opacity; - } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) { - child->stroke.width = parent->stroke.width; - } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) { - if (parent->stroke.dash.array.count > 0) { - child->stroke.dash.array.clear(); - child->stroke.dash.array.reserve(parent->stroke.dash.array.count); - for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) { - child->stroke.dash.array.push(parent->stroke.dash.array.data[i]); - } - } - } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) { - child->stroke.cap = parent->stroke.cap; + const char* tag; + int sz; + FactoryMethod tagHandler; +} graphicsTags[] = { + {"use", sizeof("use"), _createUseNode}, + {"circle", sizeof("circle"), _createCircleNode}, + {"ellipse", sizeof("ellipse"), _createEllipseNode}, + {"path", sizeof("path"), _createPathNode}, + {"polygon", sizeof("polygon"), _createPolygonNode}, + {"rect", sizeof("rect"), _createRectNode}, + {"polyline", sizeof("polyline"), _createPolylineNode}, + {"line", sizeof("line"), _createLineNode}, + {"image", sizeof("image"), _createImageNode} +}; + + +static constexpr struct +{ + const char* tag; + int sz; + FactoryMethod tagHandler; +} groupTags[] = { + {"defs", sizeof("defs"), _createDefsNode}, + {"g", sizeof("g"), _createGNode}, + {"svg", sizeof("svg"), _createSvgNode}, + {"mask", sizeof("mask"), _createMaskNode}, + {"clipPath", sizeof("clipPath"), _createClipPathNode}, + {"style", sizeof("style"), _createCssStyleNode}, + {"symbol", sizeof("symbol"), _createSymbolNode} +}; + + +#define FIND_FACTORY(Short_Name, Tags_Array) \ + static FactoryMethod \ + _find##Short_Name##Factory(const char* name) \ + { \ + unsigned int i; \ + int sz = strlen(name); \ + \ + for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ + if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \ + return Tags_Array[i].tagHandler; \ + } \ + } \ + return nullptr; \ } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) { - child->stroke.join = parent->stroke.join; + +FIND_FACTORY(Group, groupTags) +FIND_FACTORY(Graphics, graphicsTags) + + +FillSpread _parseSpreadValue(const char* value) +{ + auto spread = FillSpread::Pad; + + if (!strcmp(value, "reflect")) { + spread = FillSpread::Reflect; + } else if (!strcmp(value, "repeat")) { + spread = FillSpread::Repeat; } + + return spread; } -static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) +static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { - if (from == nullptr) return; - //Copy the properties of 'from' only if they were explicitly set (not the default ones). - if (from->curColorSet) { - to->color = from->color; - to->curColorSet = true; - } - //Fill - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags); - if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) { - to->fill.paint.color = from->fill.paint.color; - to->fill.paint.none = from->fill.paint.none; - to->fill.paint.curColor = from->fill.paint.curColor; - if (from->fill.paint.url) { - if (to->fill.paint.url) free(to->fill.paint.url); - to->fill.paint.url = _copyId(from->fill.paint.url); - } - } - if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) { - to->fill.opacity = from->fill.opacity; + radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage); + if (!loader->svgParse->gradient.parsedFx) { + radial->fx = radial->cx; + radial->isFxPercentage = radial->isCxPercentage; } - if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) { - to->fill.fillRule = from->fill.fillRule; - } - //Stroke - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags); - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) { - to->stroke.paint.color = from->stroke.paint.color; - to->stroke.paint.none = from->stroke.paint.none; - to->stroke.paint.curColor = from->stroke.paint.curColor; - if (from->stroke.paint.url) { - if (to->stroke.paint.url) free(to->stroke.paint.url); - to->stroke.paint.url = _copyId(from->stroke.paint.url); - } else { - to->stroke.paint.url = nullptr; - } - } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) { - to->stroke.opacity = from->stroke.opacity; - } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) { - to->stroke.width = from->stroke.width; - } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) { - if (from->stroke.dash.array.count > 0) { - to->stroke.dash.array.clear(); - to->stroke.dash.array.reserve(from->stroke.dash.array.count); - for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { - to->stroke.dash.array.push(from->stroke.dash.array.data[i]); - } - } - } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) { - to->stroke.cap = from->stroke.cap; - } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) { - to->stroke.join = from->stroke.join; +} + + +static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage); + if (!loader->svgParse->gradient.parsedFy) { + radial->fy = radial->cy; + radial->isFyPercentage = radial->isCyPercentage; } } -static void _copyAttr(SvgNode* to, const SvgNode* from) +static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { - //Copy matrix attribute - if (from->transform) { - to->transform = (Matrix*)malloc(sizeof(Matrix)); - if (to->transform) *to->transform = *from->transform; - } - //Copy style attribute - _styleCopy(to->style, from->style); - to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags); - if (from->style->clipPath.url) { - if (to->style->clipPath.url) free(to->style->clipPath.url); - to->style->clipPath.url = strdup(from->style->clipPath.url); - } - if (from->style->mask.url) { - if (to->style->mask.url) free(to->style->mask.url); - to->style->mask.url = strdup(from->style->mask.url); - } - - //Copy node attribute - switch (from->type) { - case SvgNodeType::Circle: { - to->node.circle.cx = from->node.circle.cx; - to->node.circle.cy = from->node.circle.cy; - to->node.circle.r = from->node.circle.r; - break; - } - case SvgNodeType::Ellipse: { - to->node.ellipse.cx = from->node.ellipse.cx; - to->node.ellipse.cy = from->node.ellipse.cy; - to->node.ellipse.rx = from->node.ellipse.rx; - to->node.ellipse.ry = from->node.ellipse.ry; - break; - } - case SvgNodeType::Rect: { - to->node.rect.x = from->node.rect.x; - to->node.rect.y = from->node.rect.y; - to->node.rect.w = from->node.rect.w; - to->node.rect.h = from->node.rect.h; - to->node.rect.rx = from->node.rect.rx; - to->node.rect.ry = from->node.rect.ry; - to->node.rect.hasRx = from->node.rect.hasRx; - to->node.rect.hasRy = from->node.rect.hasRy; - break; - } - case SvgNodeType::Line: { - to->node.line.x1 = from->node.line.x1; - to->node.line.y1 = from->node.line.y1; - to->node.line.x2 = from->node.line.x2; - to->node.line.y2 = from->node.line.y2; - break; - } - case SvgNodeType::Path: { - if (from->node.path.path) { - if (to->node.path.path) free(to->node.path.path); - to->node.path.path = strdup(from->node.path.path); - } - break; - } - case SvgNodeType::Polygon: { - if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) { - to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float)); - memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float)); - } - break; - } - case SvgNodeType::Polyline: { - if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) { - to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float)); - memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float)); - } - break; - } - case SvgNodeType::Image: { - to->node.image.x = from->node.image.x; - to->node.image.y = from->node.image.y; - to->node.image.w = from->node.image.w; - to->node.image.h = from->node.image.h; - if (from->node.image.href) { - if (to->node.image.href) free(to->node.image.href); - to->node.image.href = strdup(from->node.image.href); - } - break; - } - default: { - break; - } - } + radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage); + loader->svgParse->gradient.parsedFx = true; } -static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) +static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { - /* Exception handling: Prevent invalid SVG data input. - The size is the arbitrary value, we need an experimental size. */ - if (depth == 8192) { - TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); - return; - } - - SvgNode* newNode; - if (!from || !parent || from == parent) return; - - newNode = _createNode(parent, from->type); - if (!newNode) return; - - _styleInherit(newNode->style, parent->style); - _copyAttr(newNode, from); - - auto child = from->child.data; - for (uint32_t i = 0; i < from->child.count; ++i, ++child) { - _cloneNode(*child, newNode, depth + 1); - } + radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage); + loader->svgParse->gradient.parsedFy = true; } -static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) +static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) { - for (uint32_t i = 0; i < cloneNodes->count; ++i) { - auto nodeIdPair = cloneNodes->data[i]; - auto defs = _getDefsNode(nodeIdPair.node); - auto nodeFrom = _findNodeById(defs, nodeIdPair.id); - if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); - _cloneNode(nodeFrom, nodeIdPair.node, 0); - if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { - nodeIdPair.node->node.use.symbol = nodeFrom; - } - free(nodeIdPair.id); - } + radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage); } -static constexpr struct -{ - const char* tag; - SvgParserLengthType type; - int sz; - size_t offset; -} useTags[] = { - {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)}, - {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)}, - {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)}, - {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)} -}; - - -static bool _attrParseUseNode(void* data, const char* key, const char* value) +static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - SvgLoaderData* loader = (SvgLoaderData*)data; - SvgNode *defs, *nodeFrom, *node = loader->svgParse->node; - char* id; - - SvgUseNode* use = &(node->node.use); - int sz = strlen(key); - unsigned char* array = (unsigned char*)use; - for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) { - if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) { - *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type); - - if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true; - else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true; - - return true; - } - } - - if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { - id = _idFromHref(value); - defs = _getDefsNode(node); - nodeFrom = _findNodeById(defs, id); - if (nodeFrom) { - _cloneNode(nodeFrom, node, 0); - if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; - free(id); - } else { - //some svg export software include element at the end of the file - //if so the 'from' element won't be found now and we have to repeat finding - //after the whole file is parsed - _postpone(loader->cloneNodes, node, id); - } - } else { - return _attrParseGNode(data, key, value); - } - return true; + if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w; } -static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - loader->svgParse->node = _createNode(parent, SvgNodeType::Use); - - if (!loader->svgParse->node) return nullptr; - - loader->svgParse->node->node.use.isWidthSet = false; - loader->svgParse->node->node.use.isHeightSet = false; - - func(buf, bufLength, _attrParseUseNode, loader); - return loader->svgParse->node; + if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h; } -//TODO: Implement 'text' primitive -static constexpr struct +static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - const char* tag; - int sz; - FactoryMethod tagHandler; -} graphicsTags[] = { - {"use", sizeof("use"), _createUseNode}, - {"circle", sizeof("circle"), _createCircleNode}, - {"ellipse", sizeof("ellipse"), _createEllipseNode}, - {"path", sizeof("path"), _createPathNode}, - {"polygon", sizeof("polygon"), _createPolygonNode}, - {"rect", sizeof("rect"), _createRectNode}, - {"polyline", sizeof("polyline"), _createPolylineNode}, - {"line", sizeof("line"), _createLineNode}, - {"image", sizeof("image"), _createImageNode} -}; + if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w; +} -static constexpr struct +static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - const char* tag; - int sz; - FactoryMethod tagHandler; -} groupTags[] = { - {"defs", sizeof("defs"), _createDefsNode}, - {"g", sizeof("g"), _createGNode}, - {"svg", sizeof("svg"), _createSvgNode}, - {"mask", sizeof("mask"), _createMaskNode}, - {"clipPath", sizeof("clipPath"), _createClipPathNode}, - {"style", sizeof("style"), _createCssStyleNode}, - {"symbol", sizeof("symbol"), _createSymbolNode} -}; - - -#define FIND_FACTORY(Short_Name, Tags_Array) \ - static FactoryMethod \ - _find##Short_Name##Factory(const char* name) \ - { \ - unsigned int i; \ - int sz = strlen(name); \ - \ - for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ - if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \ - return Tags_Array[i].tagHandler; \ - } \ - } \ - return nullptr; \ - } - -FIND_FACTORY(Group, groupTags) -FIND_FACTORY(Graphics, graphicsTags) + if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h; +} -FillSpread _parseSpreadValue(const char* value) +static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - auto spread = FillSpread::Pad; - - if (!strcmp(value, "reflect")) { - spread = FillSpread::Reflect; - } else if (!strcmp(value, "repeat")) { - spread = FillSpread::Repeat; - } - - return spread; + // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html + if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); } -static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage); - if (!loader->svgParse->gradient.parsedFx) { - radial->fx = radial->cx; - radial->isFxPercentage = radial->isCxPercentage; + if (!radial->isCxPercentage) { + if (userSpace) radial->cx /= loader->svgParse->global.w; + else radial->cx *= loader->svgParse->global.w; } } - -static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) -{ - radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage); - if (!loader->svgParse->gradient.parsedFy) { - radial->fy = radial->cy; - radial->isFyPercentage = radial->isCyPercentage; + +static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isCyPercentage) { + if (userSpace) radial->cy /= loader->svgParse->global.h; + else radial->cy *= loader->svgParse->global.h; } } -static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage); - loader->svgParse->gradient.parsedFx = true; + if (!radial->isFxPercentage) { + if (userSpace) radial->fx /= loader->svgParse->global.w; + else radial->fx *= loader->svgParse->global.w; + } } -static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage); - loader->svgParse->gradient.parsedFy = true; + if (!radial->isFyPercentage) { + if (userSpace) radial->fy /= loader->svgParse->global.h; + else radial->fy *= loader->svgParse->global.h; + } } -static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { - radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage); + if (!radial->isRPercentage) { + if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + } } -static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { - if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w; + to->radial->cx = from->radial->cx; + to->radial->isCxPercentage = from->radial->isCxPercentage; + to->flags = (to->flags | SvgGradientFlags::Cx); } -static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { - if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h; + to->radial->cy = from->radial->cy; + to->radial->isCyPercentage = from->radial->isCyPercentage; + to->flags = (to->flags | SvgGradientFlags::Cy); } -static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { - if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w; + to->radial->fx = from->radial->fx; + to->radial->isFxPercentage = from->radial->isFxPercentage; + to->flags = (to->flags | SvgGradientFlags::Fx); } -static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { - if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h; + to->radial->fy = from->radial->fy; + to->radial->isFyPercentage = from->radial->isFyPercentage; + to->flags = (to->flags | SvgGradientFlags::Fy); } -static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from) { - // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html - if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); + to->radial->r = from->radial->r; + to->radial->isRPercentage = from->radial->isRPercentage; + to->flags = (to->flags | SvgGradientFlags::R); } typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value); +typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from); typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace); -#define RADIAL_DEF(Name, Name1) \ +#define RADIAL_DEF(Name, Name1, Flag) \ { \ -#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \ +#Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \ } @@ -2394,13 +2236,16 @@ static constexpr struct const char* tag; int sz; radialMethod tagHandler; + radialInheritMethod tagInheritHandler; radialMethodRecalc tagRecalc; + radialMethodRecalc tagInheritedRecalc; + SvgGradientFlags flag; } radialTags[] = { - RADIAL_DEF(cx, Cx), - RADIAL_DEF(cy, Cy), - RADIAL_DEF(fx, Fx), - RADIAL_DEF(fy, Fy), - RADIAL_DEF(r, R) + RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx), + RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy), + RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx), + RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy), + RADIAL_DEF(r, R, SvgGradientFlags::R) }; @@ -2414,6 +2259,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) { radialTags[i].tagHandler(loader, radial, value); + grad->flags = (grad->flags | radialTags[i].flag); return true; } } @@ -2423,11 +2269,13 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char grad->id = _copyId(value); } else if (!strcmp(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); } else if (!strcmp(key, "gradientUnits")) { if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2443,6 +2291,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); loader->svgParse->styleGrad = grad; + grad->flags = SvgGradientFlags::None; grad->type = SvgGradientType::Radial; grad->userSpace = false; grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient)); @@ -2485,10 +2334,10 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) if (!strcmp(key, "stop-opacity")) { stop->a = _toOpacity(value); - loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); } else if (!strcmp(key, "stop-color")) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); - loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } else { return false; } @@ -2505,11 +2354,11 @@ static bool _attrParseStops(void* data, const char* key, const char* value) if (!strcmp(key, "offset")) { stop->offset = _toOffset(value); } else if (!strcmp(key, "stop-opacity")) { - if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) { stop->a = _toOpacity(value); } } else if (!strcmp(key, "stop-color")) { - if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); } } else if (!strcmp(key, "style")) { @@ -2570,13 +2419,82 @@ static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear } +static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isX1Percentage) { + if (userSpace) linear->x1 /= loader->svgParse->global.w; + else linear->x1 *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isX2Percentage) { + if (userSpace) linear->x2 /= loader->svgParse->global.w; + else linear->x2 *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isY1Percentage) { + if (userSpace) linear->y1 /= loader->svgParse->global.h; + else linear->y1 *= loader->svgParse->global.h; + } +} + + +static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isY2Percentage) { + if (userSpace) linear->y2 /= loader->svgParse->global.h; + else linear->y2 *= loader->svgParse->global.h; + } +} + + +static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->x1 = from->linear->x1; + to->linear->isX1Percentage = from->linear->isX1Percentage; + to->flags = (to->flags | SvgGradientFlags::X1); +} + + +static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->x2 = from->linear->x2; + to->linear->isX2Percentage = from->linear->isX2Percentage; + to->flags = (to->flags | SvgGradientFlags::X2); +} + + +static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y1 = from->linear->y1; + to->linear->isY1Percentage = from->linear->isY1Percentage; + to->flags = (to->flags | SvgGradientFlags::Y1); +} + + +static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y2 = from->linear->y2; + to->linear->isY2Percentage = from->linear->isY2Percentage; + to->flags = (to->flags | SvgGradientFlags::Y2); +} + + typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value); +typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from); typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace); -#define LINEAR_DEF(Name, Name1) \ +#define LINEAR_DEF(Name, Name1, Flag) \ { \ -#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \ +#Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \ } @@ -2585,12 +2503,15 @@ static constexpr struct const char* tag; int sz; Linear_Method tagHandler; + Linear_Inherit_Method tagInheritHandler; Linear_Method_Recalc tagRecalc; + Linear_Method_Recalc tagInheritedRecalc; + SvgGradientFlags flag; } linear_tags[] = { - LINEAR_DEF(x1, X1), - LINEAR_DEF(y1, Y1), - LINEAR_DEF(x2, X2), - LINEAR_DEF(y2, Y2) + LINEAR_DEF(x1, X1, SvgGradientFlags::X1), + LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1), + LINEAR_DEF(x2, X2, SvgGradientFlags::X2), + LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2) }; @@ -2604,91 +2525,460 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) { linear_tags[i].tagHandler(loader, linear, value); + grad->flags = (grad->flags | linear_tags[i].flag); return true; } } - - if (!strcmp(key, "id")) { - if (grad->id && value) free(grad->id); - grad->id = _copyId(value); - } else if (!strcmp(key, "spreadMethod")) { - grad->spread = _parseSpreadValue(value); - } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { - if (grad->ref && value) free(grad->ref); - grad->ref = _idFromHref(value); - } else if (!strcmp(key, "gradientUnits")) { - if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; - } else if (!strcmp(key, "gradientTransform")) { - grad->transform = _parseTransformationMatrix(value); - } else { - return false; + + if (!strcmp(key, "id")) { + if (grad->id && value) free(grad->id); + grad->id = _copyId(value); + } else if (!strcmp(key, "spreadMethod")) { + grad->spread = _parseSpreadValue(value); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); + } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + if (grad->ref && value) free(grad->ref); + grad->ref = _idFromHref(value); + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); + } else if (!strcmp(key, "gradientTransform")) { + grad->transform = _parseTransformationMatrix(value); + } else { + return false; + } + + return true; +} + + +static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) +{ + auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); + loader->svgParse->styleGrad = grad; + + grad->flags = SvgGradientFlags::None; + grad->type = SvgGradientType::Linear; + grad->userSpace = false; + grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); + if (!grad->linear) { + grad->clear(); + free(grad); + return nullptr; + } + /** + * Default value of x2 is 100% - transformed to the global percentage + */ + grad->linear->x2 = 1.0f; + grad->linear->isX2Percentage = true; + + simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader); + + for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { + linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace); + } + + return loader->svgParse->styleGrad; +} + + +#define GRADIENT_DEF(Name, Name1) \ + { \ +#Name, sizeof(#Name), _create##Name1 \ + } + + +/** + * In the case when the gradients lengths are given as numbers (not percentages) + * in the current user coordinate system, they are recalculated into percentages + * related to the canvas width and height. + */ +static constexpr struct +{ + const char* tag; + int sz; + GradientFactoryMethod tagHandler; +} gradientTags[] = { + GRADIENT_DEF(linearGradient, LinearGradient), + GRADIENT_DEF(radialGradient, RadialGradient) +}; + + +static GradientFactoryMethod _findGradientFactory(const char* name) +{ + int sz = strlen(name); + + for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) { + if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) { + return gradientTags[i].tagHandler; + } + } + return nullptr; +} + + +static void _cloneGradStops(Array& dst, const Array& src) +{ + for (uint32_t i = 0; i < src.count; ++i) { + dst.push(src.data[i]); + } +} + + +static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from) +{ + if (!to || !from) return; + + if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) { + to->spread = from->spread; + to->flags = (to->flags | SvgGradientFlags::SpreadMethod); + } + bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits); + if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) { + to->userSpace = from->userSpace; + to->flags = (to->flags | SvgGradientFlags::GradientUnits); + } + + if (!to->transform && from->transform) { + to->transform = (Matrix*)malloc(sizeof(Matrix)); + if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix)); + } + + if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) { + for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { + bool coordSet = to->flags & linear_tags[i].flag; + if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) { + linear_tags[i].tagInheritHandler(to, from); + } + + //GradUnits not set directly, coord set + if (!gradUnitSet && coordSet) { + linear_tags[i].tagRecalc(loader, to->linear, to->userSpace); + } + //GradUnits set, coord not set directly + if (to->userSpace == from->userSpace) continue; + if (gradUnitSet && !coordSet) { + linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace); + } + } + } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) { + for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { + bool coordSet = (to->flags & radialTags[i].flag); + if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) { + radialTags[i].tagInheritHandler(to, from); + } + + //GradUnits not set directly, coord set + if (!gradUnitSet && coordSet) { + radialTags[i].tagRecalc(loader, to->radial, to->userSpace); + } + //GradUnits set, coord not set directly + if (to->userSpace == from->userSpace) continue; + if (gradUnitSet && !coordSet) { + radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace); + } + } + } + + if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops); +} + + +static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from) +{ + if (!from) return nullptr; + + auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); + if (!grad) return nullptr; + + grad->type = from->type; + grad->id = from->id ? _copyId(from->id) : nullptr; + grad->ref = from->ref ? _copyId(from->ref) : nullptr; + grad->spread = from->spread; + grad->userSpace = from->userSpace; + grad->flags = from->flags; + + if (from->transform) { + grad->transform = (Matrix*)calloc(1, sizeof(Matrix)); + if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix)); + } + + if (grad->type == SvgGradientType::Linear) { + grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); + if (!grad->linear) goto error_grad_alloc; + memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient)); + } else if (grad->type == SvgGradientType::Radial) { + grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient)); + if (!grad->radial) goto error_grad_alloc; + memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient)); + } + + _cloneGradStops(grad->stops, from->stops); + + return grad; + + error_grad_alloc: + if (grad) { + grad->clear(); + free(grad); + } + return nullptr; +} + + +static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent) +{ + if (parent == nullptr) return; + //Inherit the property of parent if not present in child. + if (!child->curColorSet) { + child->color = parent->color; + child->curColorSet = parent->curColorSet; + } + if (!(child->flags & SvgStyleFlags::PaintOrder)) { + child->paintOrder = parent->paintOrder; + } + //Fill + if (!(child->fill.flags & SvgFillFlags::Paint)) { + child->fill.paint.color = parent->fill.paint.color; + child->fill.paint.none = parent->fill.paint.none; + child->fill.paint.curColor = parent->fill.paint.curColor; + if (parent->fill.paint.url) { + if (child->fill.paint.url) free(child->fill.paint.url); + child->fill.paint.url = _copyId(parent->fill.paint.url); + } + } + if (!(child->fill.flags & SvgFillFlags::Opacity)) { + child->fill.opacity = parent->fill.opacity; + } + if (!(child->fill.flags & SvgFillFlags::FillRule)) { + child->fill.fillRule = parent->fill.fillRule; + } + //Stroke + if (!(child->stroke.flags & SvgStrokeFlags::Paint)) { + child->stroke.paint.color = parent->stroke.paint.color; + child->stroke.paint.none = parent->stroke.paint.none; + child->stroke.paint.curColor = parent->stroke.paint.curColor; + if (parent->stroke.paint.url) { + if (child->stroke.paint.url) free(child->stroke.paint.url); + child->stroke.paint.url = _copyId(parent->stroke.paint.url); + } + } + if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) { + child->stroke.opacity = parent->stroke.opacity; + } + if (!(child->stroke.flags & SvgStrokeFlags::Width)) { + child->stroke.width = parent->stroke.width; + } + if (!(child->stroke.flags & SvgStrokeFlags::Dash)) { + if (parent->stroke.dash.array.count > 0) { + child->stroke.dash.array.clear(); + child->stroke.dash.array.reserve(parent->stroke.dash.array.count); + for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) { + child->stroke.dash.array.push(parent->stroke.dash.array.data[i]); + } + } + } + if (!(child->stroke.flags & SvgStrokeFlags::Cap)) { + child->stroke.cap = parent->stroke.cap; + } + if (!(child->stroke.flags & SvgStrokeFlags::Join)) { + child->stroke.join = parent->stroke.join; } - - return true; } -static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) +static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) { - auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); - loader->svgParse->styleGrad = grad; - - grad->type = SvgGradientType::Linear; - grad->userSpace = false; - grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); - if (!grad->linear) { - grad->clear(); - free(grad); - return nullptr; + if (from == nullptr) return; + //Copy the properties of 'from' only if they were explicitly set (not the default ones). + if (from->curColorSet) { + to->color = from->color; + to->curColorSet = true; } - /** - * Default value of x2 is 100% - transformed to the global percentage - */ - grad->linear->x2 = 1.0f; - grad->linear->isX2Percentage = true; + if (from->flags & SvgStyleFlags::PaintOrder) { + to->paintOrder = from->paintOrder; + } + //Fill + to->fill.flags = (to->fill.flags | from->fill.flags); + if (from->fill.flags & SvgFillFlags::Paint) { + to->fill.paint.color = from->fill.paint.color; + to->fill.paint.none = from->fill.paint.none; + to->fill.paint.curColor = from->fill.paint.curColor; + if (from->fill.paint.url) { + if (to->fill.paint.url) free(to->fill.paint.url); + to->fill.paint.url = _copyId(from->fill.paint.url); + } + } + if (from->fill.flags & SvgFillFlags::Opacity) { + to->fill.opacity = from->fill.opacity; + } + if (from->fill.flags & SvgFillFlags::FillRule) { + to->fill.fillRule = from->fill.fillRule; + } + //Stroke + to->stroke.flags = (to->stroke.flags | from->stroke.flags); + if (from->stroke.flags & SvgStrokeFlags::Paint) { + to->stroke.paint.color = from->stroke.paint.color; + to->stroke.paint.none = from->stroke.paint.none; + to->stroke.paint.curColor = from->stroke.paint.curColor; + if (from->stroke.paint.url) { + if (to->stroke.paint.url) free(to->stroke.paint.url); + to->stroke.paint.url = _copyId(from->stroke.paint.url); + } + } + if (from->stroke.flags & SvgStrokeFlags::Opacity) { + to->stroke.opacity = from->stroke.opacity; + } + if (from->stroke.flags & SvgStrokeFlags::Width) { + to->stroke.width = from->stroke.width; + } + if (from->stroke.flags & SvgStrokeFlags::Dash) { + if (from->stroke.dash.array.count > 0) { + to->stroke.dash.array.clear(); + to->stroke.dash.array.reserve(from->stroke.dash.array.count); + for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { + to->stroke.dash.array.push(from->stroke.dash.array.data[i]); + } + } + } + if (from->stroke.flags & SvgStrokeFlags::Cap) { + to->stroke.cap = from->stroke.cap; + } + if (from->stroke.flags & SvgStrokeFlags::Join) { + to->stroke.join = from->stroke.join; + } +} - simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader); - for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { - linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace); +static void _copyAttr(SvgNode* to, const SvgNode* from) +{ + //Copy matrix attribute + if (from->transform) { + to->transform = (Matrix*)malloc(sizeof(Matrix)); + if (to->transform) *to->transform = *from->transform; + } + //Copy style attribute + _styleCopy(to->style, from->style); + to->style->flags = (to->style->flags | from->style->flags); + if (from->style->clipPath.url) { + if (to->style->clipPath.url) free(to->style->clipPath.url); + to->style->clipPath.url = strdup(from->style->clipPath.url); + } + if (from->style->mask.url) { + if (to->style->mask.url) free(to->style->mask.url); + to->style->mask.url = strdup(from->style->mask.url); } - return loader->svgParse->styleGrad; + //Copy node attribute + switch (from->type) { + case SvgNodeType::Circle: { + to->node.circle.cx = from->node.circle.cx; + to->node.circle.cy = from->node.circle.cy; + to->node.circle.r = from->node.circle.r; + break; + } + case SvgNodeType::Ellipse: { + to->node.ellipse.cx = from->node.ellipse.cx; + to->node.ellipse.cy = from->node.ellipse.cy; + to->node.ellipse.rx = from->node.ellipse.rx; + to->node.ellipse.ry = from->node.ellipse.ry; + break; + } + case SvgNodeType::Rect: { + to->node.rect.x = from->node.rect.x; + to->node.rect.y = from->node.rect.y; + to->node.rect.w = from->node.rect.w; + to->node.rect.h = from->node.rect.h; + to->node.rect.rx = from->node.rect.rx; + to->node.rect.ry = from->node.rect.ry; + to->node.rect.hasRx = from->node.rect.hasRx; + to->node.rect.hasRy = from->node.rect.hasRy; + break; + } + case SvgNodeType::Line: { + to->node.line.x1 = from->node.line.x1; + to->node.line.y1 = from->node.line.y1; + to->node.line.x2 = from->node.line.x2; + to->node.line.y2 = from->node.line.y2; + break; + } + case SvgNodeType::Path: { + if (from->node.path.path) { + if (to->node.path.path) free(to->node.path.path); + to->node.path.path = strdup(from->node.path.path); + } + break; + } + case SvgNodeType::Polygon: { + if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) { + to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float)); + memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float)); + } + break; + } + case SvgNodeType::Polyline: { + if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) { + to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float)); + memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float)); + } + break; + } + case SvgNodeType::Image: { + to->node.image.x = from->node.image.x; + to->node.image.y = from->node.image.y; + to->node.image.w = from->node.image.w; + to->node.image.h = from->node.image.h; + if (from->node.image.href) { + if (to->node.image.href) free(to->node.image.href); + to->node.image.href = strdup(from->node.image.href); + } + break; + } + default: { + break; + } + } } -#define GRADIENT_DEF(Name, Name1) \ - { \ -#Name, sizeof(#Name), _create##Name1 \ +static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) +{ + /* Exception handling: Prevent invalid SVG data input. + The size is the arbitrary value, we need an experimental size. */ + if (depth == 8192) { + TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); + return; } + SvgNode* newNode; + if (!from || !parent || from == parent) return; -/** - * In the case when the gradients lengths are given as numbers (not percentages) - * in the current user coordinate system, they are recalculated into percentages - * related to the canvas width and height. - */ -static constexpr struct -{ - const char* tag; - int sz; - GradientFactoryMethod tagHandler; -} gradientTags[] = { - GRADIENT_DEF(linearGradient, LinearGradient), - GRADIENT_DEF(radialGradient, RadialGradient) -}; + newNode = _createNode(parent, from->type); + if (!newNode) return; + _styleInherit(newNode->style, parent->style); + _copyAttr(newNode, from); -static GradientFactoryMethod _findGradientFactory(const char* name) -{ - int sz = strlen(name); + auto child = from->child.data; + for (uint32_t i = 0; i < from->child.count; ++i, ++child) { + _cloneNode(*child, newNode, depth + 1); + } +} - for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) { - if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) { - return gradientTags[i].tagHandler; + +static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) +{ + for (uint32_t i = 0; i < cloneNodes->count; ++i) { + auto nodeIdPair = cloneNodes->data[i]; + auto defs = _getDefsNode(nodeIdPair.node); + auto nodeFrom = _findNodeById(defs, nodeIdPair.id); + if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); + _cloneNode(nodeFrom, nodeIdPair.node, 0); + if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { + nodeIdPair.node->node.use.symbol = nodeFrom; } + free(nodeIdPair.id); } - return nullptr; } @@ -2738,6 +3028,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, //Parse the empty tag attrs = content; while ((attrs != nullptr) && *attrs != '>') attrs++; + if (empty) attrs--; } if (attrs) { @@ -2942,7 +3233,7 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle) } -static SvgStyleGradient* _gradientDup(Array* gradients, const char* id) +static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array* gradients, const char* id) { SvgStyleGradient* result = nullptr; @@ -2960,8 +3251,7 @@ static SvgStyleGradient* _gradientDup(Array* gradients, const gradList = gradients->data; for (uint32_t i = 0; i < gradients->count; ++i) { if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) { - if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops); - //TODO: Properly inherit other property + _inheritGradient(loader, result, *gradList); break; } ++gradList; @@ -2972,27 +3262,33 @@ static SvgStyleGradient* _gradientDup(Array* gradients, const } -static void _updateGradient(SvgNode* node, Array* gradients) +static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array* gradients) { if (node->child.count > 0) { auto child = node->child.data; for (uint32_t i = 0; i < node->child.count; ++i, ++child) { - _updateGradient(*child, gradients); + _updateGradient(loader, *child, gradients); } } else { if (node->style->fill.paint.url) { - if (node->style->fill.paint.gradient) { - node->style->fill.paint.gradient->clear(); - free(node->style->fill.paint.gradient); + auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url); + if (newGrad) { + if (node->style->fill.paint.gradient) { + node->style->fill.paint.gradient->clear(); + free(node->style->fill.paint.gradient); + } + node->style->fill.paint.gradient = newGrad; } - node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url); } if (node->style->stroke.paint.url) { - if (node->style->stroke.paint.gradient) { - node->style->stroke.paint.gradient->clear(); - free(node->style->stroke.paint.gradient); + auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url); + if (newGrad) { + if (node->style->stroke.paint.gradient) { + node->style->stroke.paint.gradient->clear(); + free(node->style->stroke.paint.gradient); + } + node->style->stroke.paint.gradient = newGrad; } - node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url); } } } @@ -3181,7 +3477,8 @@ SvgLoader::~SvgLoader() void SvgLoader::run(unsigned tid) { //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering - if (renderingDisabled) { + if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { + TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); root = Scene::gen(); return; } @@ -3199,12 +3496,12 @@ void SvgLoader::run(unsigned tid) _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); - if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); - if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); - _updateStyle(loaderData.doc, nullptr); + + if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); + if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); } - root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath); + root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag); } @@ -3217,34 +3514,78 @@ bool SvgLoader::header() if (!loaderData.svgParse) return false; loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault; + viewFlag = SvgViewFlag::None; simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData)); if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) { - //Return the brief resource info such as viewbox: - vx = loaderData.doc->node.doc.vx; - vy = loaderData.doc->node.doc.vy; - w = vw = loaderData.doc->node.doc.vw; - h = vh = loaderData.doc->node.doc.vh; - - //Override size - if (loaderData.doc->node.doc.w > 0) { + viewFlag = loaderData.doc->node.doc.viewFlag; + align = loaderData.doc->node.doc.align; + meetOrSlice = loaderData.doc->node.doc.meetOrSlice; + + if (viewFlag & SvgViewFlag::Viewbox) { + vx = loaderData.doc->node.doc.vx; + vy = loaderData.doc->node.doc.vy; + vw = loaderData.doc->node.doc.vw; + vh = loaderData.doc->node.doc.vh; + + if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w; + else { + w = loaderData.doc->node.doc.vw; + if (viewFlag & SvgViewFlag::WidthInPercent) { + w *= loaderData.doc->node.doc.w; + viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent); + } + viewFlag = (viewFlag | SvgViewFlag::Width); + } + if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h; + else { + h = loaderData.doc->node.doc.vh; + if (viewFlag & SvgViewFlag::HeightInPercent) { + h *= loaderData.doc->node.doc.h; + viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent); + } + viewFlag = (viewFlag | SvgViewFlag::Height); + } + //In case no viewbox and width/height data is provided the completion of loading + //has to be forced, in order to establish this data based on the whole picture. + } else { + //Before loading, set default viewbox & size if they are empty + vx = vy = 0.0f; + if (viewFlag & SvgViewFlag::Width) { + vw = w = loaderData.doc->node.doc.w; + } else { + vw = 1.0f; + if (viewFlag & SvgViewFlag::WidthInPercent) { + w = loaderData.doc->node.doc.w; + } else w = 1.0f; + } + + if (viewFlag & SvgViewFlag::Height) { + vh = h = loaderData.doc->node.doc.h; + } else { + vh = 1.0f; + if (viewFlag & SvgViewFlag::HeightInPercent) { + h = loaderData.doc->node.doc.h; + } else h = 1.0f; + } + + run(0); + + //Override viewbox & size again after svg loading. + vx = loaderData.doc->node.doc.vx; + vy = loaderData.doc->node.doc.vy; + vw = loaderData.doc->node.doc.vw; + vh = loaderData.doc->node.doc.vh; w = loaderData.doc->node.doc.w; - if (vw < FLT_EPSILON) vw = w; - } - if (loaderData.doc->node.doc.h > 0) { h = loaderData.doc->node.doc.h; - if (vh < FLT_EPSILON) vh = h; } - align = loaderData.doc->node.doc.align; - meetOrSlice = loaderData.doc->node.doc.meetOrSlice; - } else { - TVGLOG("SVG", "No SVG File. There is no "); - return false; + return true; } - return true; + TVGLOG("SVG", "No SVG File. There is no "); + return false; } @@ -3304,6 +3645,9 @@ bool SvgLoader::read() { if (!content || size == 0) return false; + //the loading has been already completed in header() + if (root) return true; + TaskScheduler::request(this); return true; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h index c6fdde55af..5c74184ec8 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SVG_LOADER_H_ #define _TVG_SVG_LOADER_H_ @@ -50,9 +51,9 @@ public: unique_ptr paint() override; private: + SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; - bool renderingDisabled = false; bool header(); void clear(); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index 3588cabf0b..dec9fadebe 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SVG_LOADER_COMMON_H_ #define _TVG_SVG_LOADER_COMMON_H_ @@ -79,6 +80,16 @@ enum class SvgFillFlags ClipPath = 0x16 }; +constexpr bool operator &(SvgFillFlags a, SvgFillFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b) +{ + return SvgFillFlags(int(a) | int(b)); +} + enum class SvgStrokeFlags { Paint = 0x1, @@ -91,6 +102,17 @@ enum class SvgStrokeFlags Dash = 0x80, }; +constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return SvgStrokeFlags(int(a) | int(b)); +} + + enum class SvgGradientType { Linear, @@ -114,9 +136,20 @@ enum class SvgStyleFlags ClipPath = 0x1000, Mask = 0x2000, MaskType = 0x4000, - Display = 0x8000 + Display = 0x8000, + PaintOrder = 0x10000 }; +constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b) +{ + return SvgStyleFlags(int(a) | int(b)); +} + enum class SvgStopStyleFlags { StopDefault = 0x0, @@ -124,6 +157,42 @@ enum class SvgStopStyleFlags StopColor = 0x02 }; +constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return SvgStopStyleFlags(int(a) | int(b)); +} + +enum class SvgGradientFlags +{ + None = 0x0, + GradientUnits = 0x1, + SpreadMethod = 0x2, + X1 = 0x4, + X2 = 0x8, + Y1 = 0x10, + Y2 = 0x20, + Cx = 0x40, + Cy = 0x80, + R = 0x100, + Fx = 0x200, + Fy = 0x400 +}; + +constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b) +{ + return SvgGradientFlags(int(a) | int(b)); +} + enum class SvgFillRule { Winding = 0, @@ -145,6 +214,31 @@ enum class SvgParserLengthType Other }; +enum class SvgViewFlag +{ + None = 0x0, + Width = 0x01, //viewPort width + Height = 0x02, //viewPort height + Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set + WidthInPercent = 0x08, + HeightInPercent = 0x10 +}; + +constexpr bool operator &(SvgViewFlag a, SvgViewFlag b) +{ + return static_cast(a) & static_cast(b); +} + +constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) | int(b)); +} + +constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) ^ int(b)); +} + enum class AspectRatioAlign { None, @@ -167,12 +261,13 @@ enum class AspectRatioMeetOrSlice struct SvgDocNode { - float w; - float h; + float w; //unit: point or in percentage see: SvgViewFlag + float h; //unit: point or in percentage see: SvgViewFlag float vx; float vy; float vw; float vh; + SvgViewFlag viewFlag; SvgNode* defs; SvgNode* style; AspectRatioAlign align; @@ -339,6 +434,7 @@ struct SvgStyleGradient SvgLinearGradient* linear; Matrix* transform; Array stops; + SvgGradientFlags flags; bool userSpace; void clear() @@ -384,6 +480,7 @@ struct SvgStyleProperty SvgColor color; bool curColorSet; char* cssClass; + bool paintOrder; //true if default (fill, stroke), false otherwise SvgStyleFlags flags; }; @@ -456,6 +553,11 @@ struct SvgLoaderData bool style = false; }; +struct Box +{ + float x, y, w, h; +}; + /* * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017 * diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp index a09a2797d0..e044931b51 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -52,7 +52,6 @@ #include #include -#include #include #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" @@ -545,11 +544,6 @@ bool svgPathToTvgPath(const char* svgPath, Array& cmds, Array& cmds, Array _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); @@ -255,7 +249,6 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox node->style->clipPath.applying = true; auto comp = Shape::gen(); - if (node->transform) comp->transform(*node->transform); auto child = compNode->child.data; auto valid = false; //Composite only when valid shapes are existed @@ -264,6 +257,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true; } + if (node->transform) { + auto m = comp->transform(); + m = mathMultiply(node->transform, &m); + comp->transform(m); + } + if (valid) paint->composite(move(comp), CompositeMethod::ClipPath); node->style->clipPath.applying = false; @@ -331,6 +330,8 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri //Apply the fill rule vg->fill((tvg::FillRule)style->fill.fillRule); + //Rendering order + vg->order(!style->paintOrder); //Apply node opacity if (style->opacity < 255) vg->opacity(style->opacity); @@ -444,9 +445,11 @@ enum class imageMimeTypeEncoding base64 = 0x1, utf8 = 0x2 }; + constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return static_cast(static_cast(a) | static_cast(b)); } + constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return (static_cast(a) & static_cast(b)); } @@ -755,24 +758,45 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, } +static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) +{ + bool validWidth = (viewFlag & SvgViewFlag::Width); + bool validHeight = (viewFlag & SvgViewFlag::Height); + + float x, y; + scene->bounds(&x, &y, &vBox.w, &vBox.h, false); + if (!validWidth && !validHeight) { + vBox.x = x; + vBox.y = y; + } else { + if (validWidth) vBox.w = w; + if (validHeight) vBox.h = h; + } + + //the size would have 1x1 or percentage values. + if (!validWidth) w *= vBox.w; + if (!validHeight) h *= vBox.h; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath) +unique_ptr svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) { //TODO: aspect ratio is valid only if viewBox was set - if (!node || (node->type != SvgNodeType::Doc)) return nullptr; + if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr; - Box vBox = {vx, vy, vw, vh}; - auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0); + auto docNode = _sceneBuildHelper(loaderData.doc, vBox, svgPath, false, 0); - if (!mathEqual(w, vw) || !mathEqual(h, vh)) { + if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); + + if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) { Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); docNode->transform(m); - } else if (!mathZero(vx) || !mathZero(vy)) { - docNode->translate(-vx, -vy); + } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) { + docNode->translate(-vBox.x, -vBox.y); } auto viewBoxClip = Shape::gen(); @@ -786,5 +810,12 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo auto root = Scene::gen(); root->push(move(compositeLayer)); + loaderData.doc->node.doc.vx = vBox.x; + loaderData.doc->node.doc.vy = vBox.y; + loaderData.doc->node.doc.vw = vBox.w; + loaderData.doc->node.doc.vh = vBox.h; + loaderData.doc->node.doc.w = w; + loaderData.doc->node.doc.h = h; + return root; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h index 311f3c80e6..f6a60f850d 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath); +unique_ptr svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); #endif //_TVG_SVG_SCENE_BUILDER_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp index 7fb108bc21..12d20c9731 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 @@ -94,8 +94,8 @@ float svgUtilStrtof(const char *nPtr, char **endPtr) if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; else goto error; - if (tolower(*(iter + 3)) == 'i') { - if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5; + if (tolower(*(iter)) == 'i') { + if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5; else goto error; } if (endPtr) *endPtr = (char *)(iter); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h index b5e6e1bdb2..6f94367aeb 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp index 0e2c3fa141..77467e071b 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h index e2761ca8da..7333bb09fb 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2020 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp index 01a39b6e17..2b89d6bb59 100644 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp +++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #ifdef _WIN32 @@ -357,27 +358,45 @@ static bool _parsePicture(TvgBinBlock block, Paint* paint) { auto picture = static_cast(paint); - //Case1: Image Picture - if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) { - if (block.length < 2 * SIZE(uint32_t)) return false; + switch (block.type) { + case TVG_TAG_PICTURE_RAW_IMAGE: { + if (block.length < 2 * SIZE(uint32_t)) return false; - auto ptr = block.data; - uint32_t w, h; + auto ptr = block.data; + uint32_t w, h; - READ_UI32(&w, ptr); - ptr += SIZE(uint32_t); - READ_UI32(&h, ptr); - ptr += SIZE(uint32_t); + READ_UI32(&w, ptr); + ptr += SIZE(uint32_t); + READ_UI32(&h, ptr); + ptr += SIZE(uint32_t); - auto size = w * h * SIZE(uint32_t); - if (block.length != 2 * SIZE(uint32_t) + size) return false; + auto size = w * h * SIZE(uint32_t); + if (block.length != 2 * SIZE(uint32_t) + size) return false; - picture->load((uint32_t*) ptr, w, h, true); - return true; - } + picture->load((uint32_t*) ptr, w, h, true); - //Case2: Base Paint Properties - if (_parsePaintProperty(block, picture)) return true; + return true; + } + case TVG_TAG_PICTURE_MESH: { + if (block.length < 1 * SIZE(uint32_t)) return false; + + auto ptr = block.data; + uint32_t meshCnt; + READ_UI32(&meshCnt, ptr); + ptr += SIZE(uint32_t); + + auto size = meshCnt * SIZE(Polygon); + if (block.length != SIZE(uint32_t) + size) return false; + + picture->mesh((Polygon*) ptr, meshCnt); + + return true; + } + //Base Paint Properties + default: { + if (_parsePaintProperty(block, picture)) return true; + } + } //Vector Picture won't be requested since Saver replaces it with the Scene return false; @@ -414,7 +433,7 @@ static Paint* _parsePaint(TvgBinBlock baseBlock) auto ptr = baseBlock.data; - //2. Read Subsquent properties of the current paint. + //2. Read Subsequent properties of the current paint. while (ptr < baseBlock.end) { auto block = _readBlock(ptr); if (block.end > baseBlock.end) return paint; diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h index a0762d0fcc..29fa1025b0 100644 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h +++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp index 95d629d1f6..82c578e6d4 100644 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include #include #include "tvgLoader.h" diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h index 3ae841aa85..b98dff83b0 100644 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h +++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp index 57a21dcce1..1098570140 100644 --- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp +++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "tvgMath.h" #include "tvgSaveModule.h" #include "tvgTvgSaver.h" @@ -169,7 +170,7 @@ bool TvgSaver::saveEncoding(const std::string& path) memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS); //Good optimization, flush to file. - auto fp = _fopen(path.c_str(), "w+"); + auto fp = _fopen(path.c_str(), "wb+"); if (!fp) goto fail; //write header @@ -192,7 +193,7 @@ fail: bool TvgSaver::flushTo(const std::string& path) { - auto fp = _fopen(path.c_str(), "w+"); + auto fp = _fopen(path.c_str(), "wb+"); if (!fp) return false; if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) { @@ -355,7 +356,7 @@ TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform) { - auto it = this->iterator(scene); + auto it = IteratorAccessor::iterator(scene); if (it->count() == 0) { delete(it); return 0; @@ -567,7 +568,7 @@ TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransf /* Picture has either a vector scene or a bitmap. */ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform) { - auto it = this->iterator(picture); + auto it = IteratorAccessor::iterator(picture); //Case - Vector Scene: if (it->count() == 1) { @@ -609,6 +610,20 @@ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* p cnt += writeData(pixels, imgSize); cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + //mesh: currently only available in bitmap image. + const Polygon* triangles = nullptr; + auto triangleCnt = picture->mesh(&triangles); + if (triangles && triangleCnt > 0) { + TvgBinCounter triangleCntSize = SIZE(triangleCnt); + TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]); + + writeTag(TVG_TAG_PICTURE_MESH); + writeCount(triangleCntSize + trianglesSize); + cnt += writeData(&triangleCnt, triangleCntSize); + cnt += writeData(triangles, trianglesSize); + cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + } + //Bitmap picture needs the transform info. cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM); diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h index 4acb35e76a..0824475923 100644 --- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h +++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2021 - 2023 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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_TVGSAVER_H_ #define _TVG_TVGSAVER_H_ diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 34bb3b8e59..4eb7bf47ed 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,9 +1,9 @@ -VERSION=0.8.4 +VERSION=0.9.0 rm -rf AUTHORS inc LICENSE src *.zip curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.zip bsdtar --strip-components=1 -xvf *.zip rm *.zip -rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh +rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh CODEOWNERS find . -type f -name 'meson.build' -delete rm -rf src/bin src/bindings src/examples src/wasm rm -rf src/lib/gl_engine src/loaders/external_jpg src/loaders/png -- cgit v1.2.3