From b5bb3191c81dcfc3d9cf2b27d9b83baeca5f56d8 Mon Sep 17 00:00:00 2001 From: Jakub Marcowski Date: Fri, 15 Aug 2025 22:48:37 +0200 Subject: [PATCH] thorvg: Update to 0.15.16 --- thirdparty/README.md | 2 +- thirdparty/thorvg/inc/config.h | 2 +- thirdparty/thorvg/inc/thorvg.h | 35 +- thirdparty/thorvg/src/common/tvgMath.h | 11 +- thirdparty/thorvg/src/common/tvgStr.cpp | 7 +- .../thorvg/src/loaders/svg/tvgSvgLoader.cpp | 124 +++--- .../src/loaders/svg/tvgSvgLoaderCommon.h | 5 +- .../src/renderer/sw_engine/tvgSwCommon.h | 50 ++- .../src/renderer/sw_engine/tvgSwFill.cpp | 87 ++--- .../src/renderer/sw_engine/tvgSwImage.cpp | 4 +- .../src/renderer/sw_engine/tvgSwMath.cpp | 4 +- .../src/renderer/sw_engine/tvgSwMemPool.cpp | 28 +- .../renderer/sw_engine/tvgSwPostEffect.cpp | 122 ++++-- .../src/renderer/sw_engine/tvgSwRaster.cpp | 328 ++++++++-------- .../renderer/sw_engine/tvgSwRasterTexmap.h | 358 ++++++------------ .../src/renderer/sw_engine/tvgSwRenderer.cpp | 77 +++- .../src/renderer/sw_engine/tvgSwRenderer.h | 3 +- .../src/renderer/sw_engine/tvgSwRle.cpp | 93 ++--- .../src/renderer/sw_engine/tvgSwShape.cpp | 34 +- thirdparty/thorvg/src/renderer/tvgCommon.h | 2 - thirdparty/thorvg/src/renderer/tvgPaint.cpp | 140 ++++--- thirdparty/thorvg/src/renderer/tvgPaint.h | 3 +- thirdparty/thorvg/src/renderer/tvgScene.h | 44 ++- thirdparty/thorvg/src/renderer/tvgText.h | 5 +- thirdparty/thorvg/update-thorvg.sh | 2 +- 25 files changed, 780 insertions(+), 790 deletions(-) diff --git a/thirdparty/README.md b/thirdparty/README.md index 57a9b6266f5..6b6402e83cc 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1034,7 +1034,7 @@ Files extracted from upstream source: ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.15.13 (c597365b99f27cb46e2a5ac2942da45bb73d5a55, 2025) +- Version: 0.15.16 (e15069de7afcc5e853edf1561e69d9b8383e2c6c, 2025) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 59d95d11ac6..5b19cc1df42 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -15,5 +15,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.15.13" +#define THORVG_VERSION_STRING "0.15.16" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index f515a031367..e39f0061e69 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/thirdparty/thorvg/inc/thorvg.h @@ -260,10 +260,14 @@ enum class Type : uint8_t /** * @brief A data structure representing a point in two-dimensional space. + * + * This structure defines a single point using Cartesian coordinates. + * It is typically used for specifying positions or coordinates in 2D graphics. */ struct Point { - float x, y; + float x; ///< The x-coordinate of the point. + float y; ///< The y-coordinate of the point. }; @@ -453,7 +457,7 @@ public: * * @return The class type ID of the Paint instance. * - * @since Experimental API + * @note Experimental API */ virtual Type type() const noexcept = 0; @@ -462,7 +466,7 @@ public: * * This is reserved to specify an paint instance in a scene. * - * @since Experimental API + * @note Experimental API */ uint32_t id = 0; @@ -568,7 +572,7 @@ public: * * @return The class type ID of the Fill instance. * - * @since Experimental API + * @note Experimental API */ virtual Type type() const noexcept = 0; @@ -758,7 +762,7 @@ public: * * @return The class type ID of the LinearGradient instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; @@ -823,7 +827,7 @@ public: * * @return The class type ID of the LinearGradient instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; @@ -1223,7 +1227,7 @@ public: * * @return The class type ID of the Shape instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; @@ -1361,7 +1365,7 @@ public: * * @return The class type ID of the Picture instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; @@ -1461,7 +1465,7 @@ public: * * @return The class type ID of the Scene instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; @@ -1493,13 +1497,22 @@ public: * It sets the font name, size and optionally the style. * * @param[in] name The name of the font. This should correspond to a font available in the canvas. + * If set to @c nullptr, ThorVG will attempt to select a fallback font available on the system. * @param[in] size The size of the font in points. This determines how large the text will appear. * @param[in] style The style of the font. It can be used to set the font to 'italic'. * If not specified, the default style is used. Only 'italic' style is supported currently. * * @retval Result::InsufficientCondition when the specified @p name cannot be found. * - * @note Experimental API + * @note If the @p name is not specified, ThorVG will select any available font candidate. + * @since 1.0 + * + * @code + * // Tip for fallback support to use any available font. + * if (text->font("Arial", 24) != tvg::Result::Success) { + * text->font(nullptr, 24); + * } + * @endcode */ Result font(const char* name, float size, const char* style = nullptr) noexcept; @@ -1619,7 +1632,7 @@ public: * * @return The class type ID of the Text instance. * - * @since Experimental API + * @note Experimental API */ Type type() const noexcept override; diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h index a917998256f..513a3895702 100644 --- a/thirdparty/thorvg/src/common/tvgMath.h +++ b/thirdparty/thorvg/src/common/tvgMath.h @@ -85,10 +85,17 @@ bool identity(const Matrix* m); Matrix operator*(const Matrix& lhs, const Matrix& rhs); bool operator==(const Matrix& lhs, const Matrix& rhs); + +static inline float radian(const Matrix& m) +{ + return fabsf(tvg::atan2(m.e21, m.e11)); +} + + static inline bool rightAngle(const Matrix& m) { - auto radian = fabsf(tvg::atan2(m.e21, m.e11)); - if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true; + auto radian = tvg::radian(m); + if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true; return false; } diff --git a/thirdparty/thorvg/src/common/tvgStr.cpp b/thirdparty/thorvg/src/common/tvgStr.cpp index 957fe18d532..315757630df 100644 --- a/thirdparty/thorvg/src/common/tvgStr.cpp +++ b/thirdparty/thorvg/src/common/tvgStr.cpp @@ -229,11 +229,12 @@ char* strAppend(char* lhs, const char* rhs, size_t n) char* strDirname(const char* path) { - const char *ptr = strrchr(path, '/'); + auto ptr = strrchr(path, '/'); #ifdef _WIN32 - if (ptr) ptr = strrchr(ptr + 1, '\\'); + auto ptr2 = strrchr(ptr ? ptr : path, '\\'); + if (ptr2) ptr = ptr2; #endif - int len = int(ptr + 1 - path); // +1 to include '/' + auto len = ptr ? size_t(ptr - path + 1) : SIZE_MAX; return strDuplicate(path, len); } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index cad68106083..12f55eb5306 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -393,8 +393,8 @@ static char* _idFromUrl(const char* url) ++open; --close; - //trim the rest of the spaces if any - while (open < close && *close == ' ') --close; + //trim the rest of the spaces and the quote marks if any + while (open < close && (*close == ' ' || *close == '\'' || *close == '\"')) --close; //quick verification for (auto id = open; id < close; id++) { @@ -927,43 +927,6 @@ error: } -static void _postpone(Array& nodes, SvgNode *node, char* id) -{ - nodes.push({node, id}); -} - - -/* -// TODO - remove? -static constexpr struct -{ - const char* tag; - int sz; - SvgLengthType type; -} lengthTags[] = { - LENGTH_DEF(%, SvgLengthType::Percent), - LENGTH_DEF(px, SvgLengthType::Px), - LENGTH_DEF(pc, SvgLengthType::Pc), - LENGTH_DEF(pt, SvgLengthType::Pt), - LENGTH_DEF(mm, SvgLengthType::Mm), - LENGTH_DEF(cm, SvgLengthType::Cm), - LENGTH_DEF(in, SvgLengthType::In) -}; - -static float _parseLength(const char* str, SvgLengthType* type) -{ - float value; - int sz = strlen(str); - - *type = SvgLengthType::Px; - for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) { - if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type; - } - value = svgUtilStrtof(str, nullptr); - return value; -} -*/ - static bool _parseStyleAttr(void* data, const char* key, const char* value); static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style); @@ -1215,7 +1178,7 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char cssCopyStyleAttr(node, cssNode); } - if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass); + if (!cssClassFound) loader->nodesToStyle.push({node, *cssClass}); } @@ -2108,6 +2071,23 @@ static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc) } +static bool _checkPostponed(SvgNode* node, SvgNode* cloneNode, int depth) +{ + if (node == cloneNode) return true; + + if (depth == 512) { + TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); + return false; + } + + for (uint32_t i = 0; i < node->child.count; ++i) { + if (_checkPostponed(node->child[i], cloneNode, depth + 1)) return true; + } + + return false; +} + + static constexpr struct { const char* tag; @@ -2149,17 +2129,30 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) nodeFrom = _findNodeById(defs, id); if (nodeFrom) { if (!_findParentById(node, id, loader->doc)) { - _cloneNode(nodeFrom, node, 0); - if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + //Check if none of nodeFrom's children are in the cloneNodes list + auto postpone = false; + for (auto pair = loader->cloneNodes.head; pair; pair = pair->next) { + if (_checkPostponed(nodeFrom, pair->node, 1)) { + postpone = true; + loader->cloneNodes.back(new((SvgNodeIdPair*)malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id)); + break; + } + } + //None of the children of nodeFrom are on the cloneNodes list, so it can be cloned immediately + if (!postpone) { + _cloneNode(nodeFrom, node, 0); + if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + free(id); + } } else { TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id); + free(id); } - 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); + loader->cloneNodes.back(new((SvgNodeIdPair*)malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id)); } } else { return _attrParseGNode(data, key, value); @@ -3322,22 +3315,39 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) } -static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) +static void _clonePostponedNodes(Inlist* cloneNodes, SvgNode* doc) { - for (uint32_t i = 0; i < cloneNodes->count; ++i) { - auto nodeIdPair = (*cloneNodes)[i]; - auto defs = _getDefsNode(nodeIdPair.node); - auto nodeFrom = _findNodeById(defs, nodeIdPair.id); - if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); - if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) { - _cloneNode(nodeFrom, nodeIdPair.node, 0); - if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { - nodeIdPair.node->node.use.symbol = nodeFrom; + auto nodeIdPair = cloneNodes->front(); + while (nodeIdPair) { + if (!_findParentById(nodeIdPair->node, nodeIdPair->id, doc)) { + //Check if none of nodeFrom's children are in the cloneNodes list + auto postpone = false; + auto nodeFrom = _findNodeById(_getDefsNode(nodeIdPair->node), nodeIdPair->id); + if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair->id); + if (nodeFrom) { + for (auto pair = cloneNodes->head; pair; pair = pair->next) { + if (_checkPostponed(nodeFrom, pair->node, 1)) { + postpone = true; + cloneNodes->back(nodeIdPair); + break; + } + } + } + //Since none of the child nodes of nodeFrom are present in the cloneNodes list, it can be cloned immediately + if (!postpone) { + _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); + free(nodeIdPair); } } else { - TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair.id); + TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair->id); + free(nodeIdPair->id); + free(nodeIdPair); } - free(nodeIdPair.id); + nodeIdPair = cloneNodes->front(); } } @@ -3917,7 +3927,7 @@ void SvgLoader::run(unsigned tid) if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle); if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle); - if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); + if (!loaderData.cloneNodes.empty()) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index 696c84ad7d2..77c0abb8b00 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -25,6 +25,7 @@ #include "tvgCommon.h" #include "tvgArray.h" +#include "tvgInlist.h" struct SvgNode; struct SvgStyleGradient; @@ -551,6 +552,8 @@ struct SvgParser struct SvgNodeIdPair { + INLIST_ITEM(SvgNodeIdPair); + SvgNodeIdPair(SvgNode* n, char* i) : node{n}, id{i} {} SvgNode* node; char *id; }; @@ -579,7 +582,7 @@ struct SvgLoaderData Array gradients; Array gradientStack; //For stops SvgParser* svgParse = nullptr; - Array cloneNodes; + Inlist cloneNodes; Array nodesToStyle; Array images; //embedded images Array fonts; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 53c28049813..184c0484fcd 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -35,6 +35,7 @@ using SwCoord = signed long; using SwFixed = signed long long; +#define SW_COLOR_TABLE 1024 static inline float TO_FLOAT(SwCoord val) @@ -128,6 +129,19 @@ struct SwBBox { min.x = min.y = max.x = max.y = 0; } + + int32_t w() const { return max.x - min.x; } + int32_t h() const { return max.y - min.y; } +}; + +using Area = long; + +struct SwCell +{ + int32_t x; + int32_t cover; + Area area; + SwCell *next; }; struct SwFill @@ -150,7 +164,7 @@ struct SwFill SwRadial radial; }; - uint32_t* ctable; + uint32_t ctable[SW_COLOR_TABLE]; FillSpread spread; bool solid = false; //solid color fill with the last color from colorStops @@ -269,7 +283,7 @@ struct SwSurface : RenderSurface blender = rhs->blender; compositor = rhs->compositor; blendMethod = rhs->blendMethod; - } + } }; struct SwCompositor : RenderCompositor @@ -281,11 +295,22 @@ struct SwCompositor : RenderCompositor bool valid; }; +struct SwCellPool +{ + #define DEFAULT_POOL_SIZE 16368 + + uint32_t size; + SwCell* buffer; + + SwCellPool() : size(DEFAULT_POOL_SIZE), buffer((SwCell*)malloc(DEFAULT_POOL_SIZE)) {} + ~SwCellPool() { free(buffer); } +}; + struct SwMpool { SwOutline* outline; SwOutline* strokeOutline; - SwOutline* dashOutline; + SwCellPool* cellPool; unsigned allocSize; }; @@ -478,9 +503,9 @@ static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8 static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) { //(255 - 2 * s) * (d * d) + (2 * s * b) - auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + MULTIPLY(std::min(255, 2 * C1(s)), C1(d)); - auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + MULTIPLY(std::min(255, 2 * C2(s)), C2(d)); - auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + MULTIPLY(std::min(255, 2 * C3(s)), C3(d)); + auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + std::min(255, 2 * MULTIPLY(C1(s), C1(d))); + auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + std::min(255, 2 * MULTIPLY(C2(s), C2(d))); + auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + std::min(255, 2 * MULTIPLY(C3(s), C3(d))); return JOIN(255, c1, c2, c3); } @@ -506,7 +531,7 @@ bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee); void shapeReset(SwShape* shape); bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); bool shapePrepared(const SwShape* shape); -bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); +bool shapeGenRle(SwShape* shape, const RenderShape* rshape, SwMpool* mpool, unsigned tid, bool antiAlias); void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform); bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); @@ -525,7 +550,7 @@ 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 imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); +bool imageGenRle(SwImage* image, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); void imageFree(SwImage* image); @@ -548,7 +573,7 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. -SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); +SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias); SwRle* rleRender(const SwBBox* bbox); void rleFree(SwRle* rle); void rleReset(SwRle* rle); @@ -565,11 +590,16 @@ SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx); void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx); SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx); void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); +SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); +bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); +bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); +bool rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, uint8_t opacity); +bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); +bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp index e012465b708..25d76aa7102 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp @@ -29,7 +29,6 @@ /************************************************************************/ #define RADIAL_A_THRESHOLD 0.0005f -#define GRADIENT_STOP_SIZE 1024 #define FIXPT_BITS 8 #define FIXPT_SIZE (1<ctable[i]); auto rgbaBegin = _alphaUnblend(fill->ctable[begin]); @@ -117,7 +116,7 @@ static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end) auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist); fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); - if (i == GRADIENT_STOP_SIZE) i = 0; + if (i == SW_COLOR_TABLE) i = 0; t += dt; } } @@ -127,11 +126,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* { if (fill->solid) return true; - if (!fill->ctable) { - fill->ctable = static_cast(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); - if (!fill->ctable) return false; - } - const Fill::ColorStop* colors; auto cnt = fdata->colorStops(&colors); if (cnt == 0 || !colors) return false; @@ -145,8 +139,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* auto g = pColors->g; auto b = pColors->b; auto rgba = surface->join(r, g, b, a); - - auto inc = 1.0f / static_cast(GRADIENT_STOP_SIZE); + auto inc = 1.0f / static_cast(SW_COLOR_TABLE); auto pos = 1.5f * inc; uint32_t i = 0; @@ -166,7 +159,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* for (uint32_t j = 0; j < cnt - 1; ++j) { if (repeat && j == cnt - 2 && iAAEnd == 0) { iAAEnd = iAABegin; - _adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i); + _adjustAAMargin(iAAEnd, SW_COLOR_TABLE - i); } auto curr = colors + j; @@ -177,7 +170,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* auto rgba2 = surface->join(next->r, next->g, next->b, a2); - while (pos < next->offset && i < GRADIENT_STOP_SIZE) { + while (pos < next->offset && i < SW_COLOR_TABLE) { auto t = (pos - curr->offset) * delta; auto dist = static_cast(255 * t); auto dist2 = 255 - dist; @@ -195,13 +188,13 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* } rgba = ALPHA_BLEND((rgba | 0xff000000), a); - for (; i < GRADIENT_STOP_SIZE; ++i) + for (; i < SW_COLOR_TABLE; ++i) fill->ctable[i] = rgba; //For repeat fill spread apply anti-aliasing between the last and first colors, //othewise make sure the last color stop is represented at the end of the table. if (repeat) _applyAA(fill, iAABegin, iAAEnd); - else fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba; + else fill->ctable[SW_COLOR_TABLE - 1] = rgba; return true; } @@ -277,10 +270,12 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr //This condition fulfills the SVG 1.1 std: //the focal point, if outside the end circle, is moved to be on the end circle //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes - if (fill->radial.a < 0) { + constexpr float precision = 0.01f; + if (fill->radial.a <= precision) { auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy); - fill->radial.fx = cx + r * (fx - cx) / dist; - fill->radial.fy = cy + r * (fy - cy) / dist; + //retract focal point slightly from edge to avoid numerical errors: + fill->radial.fx = cx + r * (1.0f - precision) * (fx - cx) / dist; + fill->radial.fy = cy + r * (1.0f - precision) * (fy - cy) / dist; fill->radial.dx = cx - fill->radial.fx; fill->radial.dy = cy - fill->radial.fy; // Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA @@ -325,20 +320,20 @@ static inline uint32_t _clamp(const SwFill* fill, int32_t pos) { switch (fill->spread) { case FillSpread::Pad: { - if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1; + if (pos >= SW_COLOR_TABLE) pos = SW_COLOR_TABLE - 1; else if (pos < 0) pos = 0; break; } case FillSpread::Repeat: { - pos = pos % GRADIENT_STOP_SIZE; - if (pos < 0) pos = GRADIENT_STOP_SIZE + pos; + pos = pos % SW_COLOR_TABLE; + if (pos < 0) pos = SW_COLOR_TABLE + pos; break; } case FillSpread::Reflect: { - auto limit = GRADIENT_STOP_SIZE * 2; + auto limit = SW_COLOR_TABLE * 2; pos = pos % limit; if (pos < 0) pos = limit + pos; - if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1); + if (pos >= SW_COLOR_TABLE) pos = (limit - pos - 1); break; } } @@ -355,7 +350,7 @@ static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos) static inline uint32_t _pixel(const SwFill* fill, float pos) { - auto i = static_cast(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f); + auto i = static_cast(pos * (SW_COLOR_TABLE - 1) + 0.5f); return fill->ctable[_clamp(fill, i)]; } @@ -550,8 +545,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 //Rotation float rx = x + 0.5f; float ry = y + 0.5f; - float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); - float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); + float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (opacity == 255) { if (tvg::zero(inc)) { @@ -578,7 +573,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } else { uint32_t counter = 0; while (counter++ < len) { - *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp)); + *dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, alpha(cmp)); ++dst; t += inc; cmp += csize; @@ -609,7 +604,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } else { uint32_t counter = 0; while (counter++ < len) { - *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp))); + *dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, MULTIPLY(opacity, alpha(cmp))); ++dst; t += inc; cmp += csize; @@ -624,8 +619,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 //Rotation float rx = x + 0.5f; float ry = y + 0.5f; - float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); - float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); + float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE)))); @@ -652,7 +647,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 } else { uint32_t counter = 0; while (counter++ < len) { - auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a); + auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a); *dst = maskOp(src, *dst, ~src); ++dst; t += inc; @@ -666,8 +661,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 //Rotation float rx = x + 0.5f; float ry = y + 0.5f; - float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); - float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); + float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto src = A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE))); @@ -697,7 +692,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32 } else { uint32_t counter = 0; while (counter++ < len) { - auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a); + auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a); auto tmp = maskOp(src, *cmp, 0); *dst = tmp + MULTIPLY(*dst, ~tmp); ++dst; @@ -713,8 +708,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 //Rotation float rx = x + 0.5f; float ry = y + 0.5f; - float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); - float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); + float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); @@ -740,7 +735,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } else { uint32_t counter = 0; while (counter++ < len) { - *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a); + *dst = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, a); ++dst; t += inc; } @@ -753,8 +748,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 //Rotation float rx = x + 0.5f; float ry = y + 0.5f; - float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); - float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1); + float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1); if (tvg::zero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); @@ -791,7 +786,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } else { uint32_t counter = 0; while (counter++ < len) { - auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255); *dst = op2(tmp, *dst, 255); ++dst; t += inc; @@ -812,7 +807,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } else { uint32_t counter = 0; while (counter++ < len) { - auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255); auto tmp2 = op2(tmp, *dst, 255); *dst = INTERPOLATE(tmp2, *dst, a); ++dst; @@ -854,10 +849,6 @@ const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata) void fillReset(SwFill* fill) { - if (fill->ctable) { - free(fill->ctable); - fill->ctable = nullptr; - } fill->translucent = false; fill->solid = false; } @@ -865,9 +856,5 @@ void fillReset(SwFill* fill) void fillFree(SwFill* fill) { - if (!fill) return; - - if (fill->ctable) free(fill->ctable); - - free(fill); + if (fill) free(fill); } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp index d2c02bb932d..15ef9683d46 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp @@ -95,9 +95,9 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg } -bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias) +bool imageGenRle(SwImage* image, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias) { - if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true; + if ((image->rle = rleRender(image->rle, image->outline, renderRegion, mpool, tid, antiAlias))) return true; return false; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp index 442d5f8d198..328a8afab5f 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp @@ -245,9 +245,7 @@ void mathSplitCubic(SwPoint* base) void mathSplitLine(SwPoint* base) { base[2] = base[1]; - - base[1].x = (base[0].x + base[1].x) >> 1; - base[1].y = (base[0].y + base[1].y) >> 1; + base[1] = {(base[0].x >> 1) + (base[1].x >> 1), (base[0].y >> 1) + (base[1].y >> 1)}; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp index 3431f034116..9f52f6a1911 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp @@ -23,11 +23,6 @@ #include "tvgSwCommon.h" -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -62,18 +57,9 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx) } -SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx) +SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx) { - return &mpool->dashOutline[idx]; -} - - -void mpoolRetDashOutline(SwMpool* mpool, unsigned idx) -{ - mpool->dashOutline[idx].pts.clear(); - mpool->dashOutline[idx].cntrs.clear(); - mpool->dashOutline[idx].types.clear(); - mpool->dashOutline[idx].closed.clear(); + return &mpool->cellPool[idx]; } @@ -84,7 +70,8 @@ SwMpool* mpoolInit(uint32_t threads) auto mpool = static_cast(calloc(1, sizeof(SwMpool))); mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); - mpool->dashOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); + mpool->cellPool = new SwCellPool[allocSize]; + mpool->allocSize = allocSize; return mpool; @@ -103,11 +90,6 @@ bool mpoolClear(SwMpool* mpool) mpool->strokeOutline[i].cntrs.reset(); mpool->strokeOutline[i].types.reset(); mpool->strokeOutline[i].closed.reset(); - - mpool->dashOutline[i].pts.reset(); - mpool->dashOutline[i].cntrs.reset(); - mpool->dashOutline[i].types.reset(); - mpool->dashOutline[i].closed.reset(); } return true; @@ -122,7 +104,7 @@ bool mpoolTerm(SwMpool* mpool) free(mpool->outline); free(mpool->strokeOutline); - free(mpool->dashOutline); + delete[](mpool->cellPool); free(mpool); return true; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp index fe311ef3454..057a9966397 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -266,25 +266,87 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i } -static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct) +static void _shift(uint32_t** dst, uint32_t** src, int dstride, int sstride, int wmax, int hmax, const SwBBox& bbox, SwPoint offset, SwSize& size) { - src += (region.min.y * sstride + region.min.x); - dst += (region.min.y * dstride + region.min.x); + size.w = bbox.max.x - bbox.min.x; + size.h = bbox.max.y - bbox.min.y; - auto w = region.max.x - region.min.x; - auto h = region.max.y - region.min.y; - auto translucent = (direct || opacity < 255); + //shift + if (offset.x < 0) { + *src -= offset.x; + size.w += offset.x; + } else { + *dst += offset.x; + size.w -= offset.x; + } - //shift offset - if (region.min.x + offset.x < 0) src -= offset.x; - else dst += offset.x; + if (offset.y < 0) { + *src -= (offset.y * sstride); + size.h += offset.y; + } else { + *dst += (offset.y * dstride); + size.h -= offset.y; + } +} - if (region.min.y + offset.y < 0) src -= (offset.y * sstride); - else dst += (offset.y * dstride); - for (auto y = 0; y < h; ++y) { - if (translucent) rasterTranslucentPixel32(dst, src, w, opacity); - else rasterPixel32(dst, src, w, opacity); +static void _dropShadowNoFilter(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const SwBBox& bbox, const SwPoint& offset, uint32_t color, uint8_t opacity, bool direct) +{ + src += (bbox.min.y * sstride + bbox.min.x); + dst += (bbox.min.y * dstride + bbox.min.x); + + SwSize size; + _shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size); + + for (auto y = 0; y < size.h; ++y) { + auto s2 = src; + auto d2 = dst; + for (int x = 0; x < size.w; ++x, ++d2, ++s2) { + auto a = MULTIPLY(opacity, A(*s2)); + if (!direct || a == 255) *d2 = ALPHA_BLEND(color, a); + else *d2 = INTERPOLATE(color, *d2, a); + } + src += sstride; + dst += dstride; + } +} + + +static void _dropShadowNoFilter(SwImage* dimg, SwImage* simg, const SwBBox& bbox, const SwPoint& offset, uint32_t color) +{ + int dstride = dimg->stride; + int sstride = simg->stride; + + //shadow image + _dropShadowNoFilter(dimg->buf32, simg->buf32, dstride, sstride, dimg->w, dimg->h, bbox, offset, color, 255, false); + + //original image + auto src = simg->buf32 + (bbox.min.y * sstride + bbox.min.x); + auto dst = dimg->buf32 + (bbox.min.y * dstride + bbox.min.x); + + for (auto y = 0; y < (bbox.max.y - bbox.min.y); ++y) { + auto s = src; + auto d = dst; + for (int x = 0; x < (bbox.max.x - bbox.min.x); ++x, ++d, ++s) { + *d = *s + ALPHA_BLEND(*d, IA(*s)); + } + src += sstride; + dst += dstride; + } +} + + +static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const SwBBox& bbox, const SwPoint& offset, uint8_t opacity, bool direct) +{ + src += (bbox.min.y * sstride + bbox.min.x); + dst += (bbox.min.y * dstride + bbox.min.x); + + SwSize size; + _shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size); + + for (auto y = 0; y < size.h; ++y) { + if (direct) rasterTranslucentPixel32(dst, src, size.w, opacity); + else rasterPixel32(dst, src, size.w, opacity); src += sstride; dst += dstride; } @@ -322,18 +384,14 @@ void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transf rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality); //invalid - if (rd->extends == 0 || params->color[3] == 0) { + if (params->color[3] == 0) { params->valid = false; return; } //offset - if (params->distance > 0.0f) { - auto radian = tvg::deg2rad(90.0f - params->angle); - rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))}; - } else { - rd->offset = {0, 0}; - } + auto radian = tvg::deg2rad(90.0f - params->angle) - tvg::radian(transform); + rd->offset = {(int32_t)((params->distance * scale) * cosf(radian)), (int32_t)(-1.0f * (params->distance * scale) * sinf(radian))}; params->valid = true; } @@ -364,6 +422,17 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); + //no filter required + if (data->extends == 0) { + if (direct) { + _dropShadowNoFilter(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, color, opacity, direct); + } else { + _dropShadowNoFilter(buffer[1], &cmp->image, bbox, data->offset, color); + std::swap(cmp->image.buf32, buffer[1]->buf32); + } + return true; + } + //saving the original image in order to overlay it into the filtered image. _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false); std::swap(front, buffer[0]->buf32); @@ -389,14 +458,14 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe //draw to the main surface directly if (direct) { - _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, stride, bbox, data->offset, opacity, direct); + _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[0]->buf32); return true; } //draw to the intermediate surface rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h); - _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, stride, bbox, data->offset, opacity, direct); + _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, buffer[1]->stride, cmp->image.stride, buffer[1]->w, buffer[1]->h, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[1]->buf32); //compositing shadow and body @@ -485,8 +554,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity); - /* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */ - if (direct) { auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); @@ -525,11 +592,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l) { - /* Tritone Formula: - if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone } - else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight } - Where the L is Luminance. */ - if (l < 128) { auto a = std::min(l * 2, 255); return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index 5e29d2e23af..8c6dda6723e 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -227,10 +227,10 @@ static inline SwMask _getMaskOp(CompositeMethod method) } -static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const SwBBox& region) +static bool _compositeMaskImage(SwSurface* surface, const SwImage& image, const SwBBox& region) { auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto sbuffer = image.buf8 + (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; @@ -239,7 +239,7 @@ static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const *dst = *src + MULTIPLY(*dst, ~*src); } dbuffer += surface->stride; - sbuffer += image->stride; + sbuffer += image.stride; } return true; } @@ -339,7 +339,7 @@ static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, } cbuffer += cstride; } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } @@ -387,7 +387,7 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t auto alpha = surface->alpha(surface->compositor->method); TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); - + //32bits channels if (surface->channelSize == sizeof(uint32_t)) { auto color = surface->join(r, g, b, a); @@ -506,7 +506,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas *cmp = maskOp(src, *cmp, ialpha); } } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } @@ -681,45 +681,45 @@ static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uin #define SCALED_IMAGE_RANGE_Y(y) \ auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \ - if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image->h) continue; \ + if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image.h) continue; \ if (scaleMethod == _interpDownScaler) { \ auto my = (int32_t)nearbyint(sy); \ miny = my - (int32_t)sampleSize; \ if (miny < 0) miny = 0; \ maxy = my + (int32_t)sampleSize; \ - if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \ + if (maxy >= (int32_t)image.h) maxy = (int32_t)image.h; \ } #define SCALED_IMAGE_RANGE_X \ auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \ - if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \ + if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image.w) continue; \ -static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method); return false; } -static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method); - auto span = image->rle->spans; + auto span = image.rle->spans; auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.rle->size; ++i, ++span) { SCALED_IMAGE_RANGE_Y(span->y) 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 a = MULTIPLY(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a)); *dst = src + ALPHA_BLEND(*dst, IA(src)); } @@ -728,28 +728,28 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image } -static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - auto span = image->rle->spans; - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto span = image.rle->spans; + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.rle->size; ++i, ++span) { SCALED_IMAGE_RANGE_Y(span->y) auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); auto tmp = surface->blender(src, *dst, 255); *dst = INTERPOLATE(tmp, *dst, A(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); auto tmp = surface->blender(src, *dst, 255); *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src))); } @@ -759,20 +759,20 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima } -static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - auto span = image->rle->spans; - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto span = image.rle->spans; + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.rle->size; ++i, ++span) { SCALED_IMAGE_RANGE_Y(span->y) 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) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); if (alpha < 255) src = ALPHA_BLEND(src, alpha); *dst = src + ALPHA_BLEND(*dst, IA(src)); } @@ -781,46 +781,23 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons } -static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity) -{ - if (surface->channelSize == sizeof(uint8_t)) { - TVGERR("SW_ENGINE", "Not supported scaled rle image!"); - return false; - } - - Matrix itransform; - - if (!inverse(&transform, &itransform)) return true; - - if (_compositing(surface)) { - if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); - else return _rasterScaledMaskedRleImage(surface, image, &itransform, region, opacity); - } else if (_blending(surface)) { - return _rasterScaledBlendingRleImage(surface, image, &itransform, region, opacity); - } else { - return _rasterScaledRleImage(surface, image, &itransform, region, opacity); - } - return false; -} - - /************************************************************************/ /* RLE Direct Image */ /************************************************************************/ -static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity) { TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); - auto span = image->rle->spans; + auto span = image.rle->spans; auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8; auto alpha = surface->alpha(surface->compositor->method); - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.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]; - auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox); auto a = MULTIPLY(span->coverage, opacity); if (a == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { @@ -838,13 +815,13 @@ static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image } -static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity) { - auto span = image->rle->spans; + auto span = image.rle->spans; - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.rle->size; ++i, ++span) { 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 img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox); auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { @@ -861,13 +838,13 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima } -static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +static bool _rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity) { - auto span = image->rle->spans; + auto span = image.rle->spans; - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + for (uint32_t i = 0; i < image.rle->size; ++i, ++span) { 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 img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox); auto alpha = MULTIPLY(span->coverage, opacity); rasterTranslucentPixel32(dst, img, span->len, alpha); } @@ -875,44 +852,25 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint } -static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); return false; } -static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) -{ - if (surface->channelSize == sizeof(uint8_t)) { - TVGERR("SW_ENGINE", "Not supported grayscale rle image!"); - return false; - } - - if (_compositing(surface)) { - if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity); - else return _rasterDirectMaskedRleImage(surface, image, opacity); - } else if (_blending(surface)) { - return _rasterDirectBlendingRleImage(surface, image, opacity); - } else { - return _rasterDirectRleImage(surface, image, opacity); - } - return false; -} - - /************************************************************************/ /*Scaled Image */ /************************************************************************/ -static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!"); return false; } -static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!"); @@ -926,8 +884,8 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; for (auto y = region.min.y; y < region.max.y; ++y) { @@ -936,7 +894,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c auto cmp = cbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp))); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } @@ -947,7 +905,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c } -static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!"); @@ -955,8 +913,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, } auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { @@ -964,7 +922,7 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, auto dst = dbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); auto tmp = surface->blender(src, *dst, 255); *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src))); } @@ -973,10 +931,10 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, } -static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +static bool _rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; - auto sampleSize = _sampleSize(image->scale); + auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image.scale); int32_t miny = 0, maxy = 0; //32bits channels @@ -987,7 +945,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M auto dst = buffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); if (opacity < 255) src = ALPHA_BLEND(src, opacity); *dst = src + ALPHA_BLEND(*dst, IA(src)); } @@ -999,7 +957,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M auto dst = buffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { SCALED_IMAGE_RANGE_X - auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize); *dst = MULTIPLY(A(src), opacity); } } @@ -1008,42 +966,24 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M } -static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity) -{ - Matrix itransform; - - if (!inverse(&transform, &itransform)) return true; - - if (_compositing(surface)) { - if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); - else return _rasterScaledMaskedImage(surface, image, &itransform, region, opacity); - } else if (_blending(surface)) { - return _rasterScaledBlendingImage(surface, image, &itransform, region, opacity); - } else { - return _rasterScaledImage(surface, image, &itransform, region, opacity); - } - return false; -} - - /************************************************************************/ /* Direct Image */ /************************************************************************/ -static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity) { TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image"); return false; } -static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity) { auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); - auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto 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 TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); @@ -1068,7 +1008,7 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c } buffer += surface->stride; cbuffer += surface->compositor->image.stride * csize; - sbuffer += image->stride; + sbuffer += image.stride; } //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { @@ -1090,14 +1030,14 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c } buffer += surface->stride; cbuffer += surface->compositor->image.stride * csize; - sbuffer += image->stride; + sbuffer += image.stride; } } return true; } -static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale image!"); @@ -1105,7 +1045,7 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, } auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; @@ -1122,15 +1062,15 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, } } dbuffer += surface->stride; - sbuffer += image->stride; + sbuffer += image.stride; } return true; } -static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity) { - auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { @@ -1138,17 +1078,18 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S for (auto y = region.min.y; y < region.max.y; ++y) { rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity); dbuffer += surface->stride; - sbuffer += image->stride; + sbuffer += image.stride; } //8bits grayscale + //32 -> 8 direct converting seems an avoidable stage. maybe draw to a masking image after an intermediate scene. Can get rid of this? } else if (surface->channelSize == sizeof(uint8_t)) { auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x]; - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) { + for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image.stride) { auto dst = dbuffer; auto src = sbuffer; if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - *dst = *src + MULTIPLY(*dst, IA(*src)); + *dst = A(*src) + MULTIPLY(*dst, IA(*src)); } } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { @@ -1161,7 +1102,7 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S } -static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale image!"); @@ -1172,7 +1113,7 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* auto w = static_cast(region.max.x - region.min.x); auto csize = surface->compositor->image.channelSize; auto alpha = surface->alpha(surface->compositor->method); - auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox); auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; @@ -1193,46 +1134,12 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* } buffer += surface->stride; cbuffer += surface->compositor->image.stride * csize; - sbuffer += image->stride; + sbuffer += image.stride; } return true; } -//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent] -static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) -{ - if (_compositing(surface)) { - if (_matting(surface)) { - if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, region, opacity); - else return _rasterDirectMattedImage(surface, image, region, opacity); - } else return _rasterDirectMaskedImage(surface, image, region, opacity); - } else if (_blending(surface)) { - return _rasterDirectBlendingImage(surface, image, region, opacity); - } else { - return _rasterDirectImage(surface, image, region, opacity); - } - return false; -} - - -//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed] -static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity) -{ - //RLE Image - if (image->rle) { - if (image->direct) return _directRleImage(surface, image, opacity); - else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity); - else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity); - //Whole Image - } else { - if (image->direct) return _directImage(surface, image, region, opacity); - else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity); - else return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity); - } -} - - /************************************************************************/ /* Rect Gradient */ /************************************************************************/ @@ -1249,7 +1156,7 @@ static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox& fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255); cbuffer += surface->stride; } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } @@ -1422,7 +1329,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* r auto cmp = &cbuffer[span->y * cstride + span->x]; fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage); } - return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox); } @@ -1719,6 +1626,82 @@ void rasterPremultiply(RenderSurface* surface) } +bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity) +{ + Matrix itransform; + + if (!inverse(&transform, &itransform)) return true; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, bbox, opacity); + else return _rasterScaledMaskedImage(surface, image, &itransform, bbox, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingImage(surface, image, &itransform, bbox, opacity); + } else { + return _rasterScaledImage(surface, image, &itransform, bbox, opacity); + } + return false; +} + + +bool rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, uint8_t opacity) +{ + if (_compositing(surface)) { + if (_matting(surface)) { + if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, opacity); + else return _rasterDirectMattedImage(surface, image, bbox, opacity); + } else return _rasterDirectMaskedImage(surface, image, bbox, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingImage(surface, image, bbox, opacity); + } else { + return _rasterDirectImage(surface, image, bbox, opacity); + } + return false; +} + + +bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported scaled rle image!"); + return false; + } + + Matrix itransform; + + if (!inverse(&transform, &itransform)) return true; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, bbox, opacity); + else return _rasterScaledMaskedRleImage(surface, image, &itransform, bbox, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingRleImage(surface, image, &itransform, bbox, opacity); + } else { + return _rasterScaledRleImage(surface, image, &itransform, bbox, opacity); + } + return false; +} + + +bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale rle image!"); + return false; + } + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity); + else return _rasterDirectMaskedRleImage(surface, image, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingRleImage(surface, image, opacity); + } else { + return _rasterDirectRleImage(surface, image, opacity); + } + return false; +} + + bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity) { if (!shape->fill) return false; @@ -1781,15 +1764,6 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint } -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity) -{ - //Outside of the viewport, skip the rendering - if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return true; - - return _rasterImage(surface, image, transform, bbox, opacity); -} - - bool rasterConvertCS(RenderSurface* surface, ColorSpace to) { ScopedLock lock(surface->key); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h index 8415e84aecb..956402a3776 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h @@ -51,94 +51,51 @@ static float dxdya, dxdyb, dudya, dvdya; static float xa, xb, ua, va; -//Y Range exception handling -static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd) +static inline int32_t _modf(float v) { - int32_t regionTop, regionBottom; - - if (region) { - regionTop = region->min.y; - regionBottom = region->max.y; - } else { - regionTop = image->rle->spans->y; - regionBottom = image->rle->spans[image->rle->size - 1].y; - } - - if (yStart >= regionBottom) return false; - - if (yStart < regionTop) yStart = regionTop; - if (yEnd > regionBottom) yEnd = regionBottom; - - return true; + return 255 - ((int(v * 256.0f)) & 255); } -static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) +static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) { TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()"); return false; } -static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) +static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) { 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->buf32; + auto sbuf = image.buf32; auto dbuf = surface->buf32; - int32_t sw = static_cast(image->w); - int32_t sh = static_cast(image->h); + int32_t sw = static_cast(image.w); + int32_t sh = static_cast(image.h); int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = 0; - float dx, u, v, iptr; + float dx, u, v; uint32_t* buf; - SwSpan* span = nullptr; //used only when rle based. - if (!_arrange(image, region, yStart, yEnd)) return; - - //Loop through all lines in the segment - uint32_t spanIdx = 0; - - if (region) { - minx = region->min.x; - maxx = region->max.x; - } else { - span = image->rle->spans; - while (span->y < yStart) { - ++span; - ++spanIdx; - } - } + if (yStart < bbox.min.y) yStart = bbox.min.y; + if (yEnd > bbox.max.y) yEnd = bbox.max.y; y = yStart; while (y < yEnd) { - x1 = (int32_t)_xa; - x2 = (int32_t)_xb; - - if (!region) { - minx = INT32_MAX; - maxx = 0; - //one single row, could be consisted of multiple spans. - while (span->y == y && spanIdx < image->rle->size) { - if (minx > span->x) minx = span->x; - if (maxx < span->x + span->len) maxx = span->x + span->len; - ++span; - ++spanIdx; - } - } - if (x1 < minx) x1 = minx; - if (x2 > maxx) x2 = maxx; + x1 = std::max((SwCoord)_xa, bbox.min.x); + x2 = std::min((SwCoord)_xb, bbox.max.x); //Anti-Aliasing frames - ay = y - aaSpans->yStart; - if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; - if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + if (aaSpans) { + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + } //Range allowed - if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) { //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); @@ -149,89 +106,45 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage x = x1; - if (opacity == 255) { - //Draw horizontal line - while (x++ < x2) { - uu = (int) u; - vv = (int) v; + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + vv = (int) v; - if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; + if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; - ar = (int)(255 * (1 - modff(u, &iptr))); - ab = (int)(255 * (1 - modff(v, &iptr))); - iru = uu + 1; - irv = vv + 1; + ar = _modf(u); + ab = _modf(v); + iru = uu + 1; + irv = vv + 1; - px = *(sbuf + (vv * image->stride) + uu); + px = *(sbuf + (vv * image.stride) + uu); + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * image.stride) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * image.stride) + uu); /* horizontal interpolate */ if (iru < sw) { - /* right pixel */ - int px2 = *(sbuf + (vv * image->stride) + iru); - px = INTERPOLATE(px, px2, ar); + /* bottom right pixel */ + int px3 = *(sbuf + (irv * image.stride) + iru); + px2 = INTERPOLATE(px2, px3, ar); } - /* vertical interpolate */ - if (irv < sh) { - /* bottom pixel */ - int px2 = *(sbuf + (irv * image->stride) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* bottom right pixel */ - int px3 = *(sbuf + (irv * image->stride) + iru); - px2 = INTERPOLATE(px2, px3, ar); - } - px = INTERPOLATE(px, px2, ab); - } - *buf = surface->blender(px, *buf, IA(px)); - ++buf; - - //Step UV horizontally - u += _dudx; - v += _dvdx; + px = INTERPOLATE(px, px2, ab); } - } else { - //Draw horizontal line - while (x++ < x2) { - uu = (int) u; - vv = (int) v; + auto tmp = surface->blender(px, *buf, 255); + *buf = INTERPOLATE(tmp, *buf, MULTIPLY(opacity, A(px))); + ++buf; - if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; - - ar = (int)(255 * (1 - modff(u, &iptr))); - ab = (int)(255 * (1 - modff(v, &iptr))); - iru = uu + 1; - irv = vv + 1; - - px = *(sbuf + (vv * image->stride) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* right pixel */ - int px2 = *(sbuf + (vv * image->stride) + iru); - px = INTERPOLATE(px, px2, ar); - } - /* vertical interpolate */ - if (irv < sh) { - /* bottom pixel */ - int px2 = *(sbuf + (irv * image->stride) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* bottom right pixel */ - int px3 = *(sbuf + (irv * image->stride) + iru); - px2 = INTERPOLATE(px2, px3, ar); - } - px = INTERPOLATE(px, px2, ab); - } - auto src = ALPHA_BLEND(px, opacity); - *buf = surface->blender(src, *buf, IA(src)); - ++buf; - - //Step UV horizontally - u += _dudx; - v += _dvdx; - } + //Step UV horizontally + u += _dudx; + v += _dvdx; } } @@ -241,8 +154,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage _ua += _dudya; _va += _dvdya; - if (!region && spanIdx >= image->rle->size) break; - ++y; } xa = _xa; @@ -252,70 +163,43 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) { 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->buf32; + auto sbuf = image.buf32; auto dbuf = surface->buf32; - int32_t sw = static_cast(image->w); - int32_t sh = static_cast(image->h); + int32_t sw = static_cast(image.w); + int32_t sh = static_cast(image.h); int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = 0; - float dx, u, v, iptr; + float dx, u, v; uint32_t* buf; - SwSpan* span = nullptr; //used only when rle based. //for matting(composition) auto csize = matting ? surface->compositor->image.channelSize: 0; auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; uint8_t* cmp = nullptr; - if (!_arrange(image, region, yStart, yEnd)) return; - - //Loop through all lines in the segment - uint32_t spanIdx = 0; - - if (region) { - minx = region->min.x; - maxx = region->max.x; - } else { - span = image->rle->spans; - while (span->y < yStart) { - ++span; - ++spanIdx; - } - } + if (yStart < bbox.min.y) yStart = bbox.min.y; + if (yEnd > bbox.max.y) yEnd = bbox.max.y; y = yStart; while (y < yEnd) { - x1 = (int32_t)_xa; - x2 = (int32_t)_xb; - - if (!region) { - minx = INT32_MAX; - maxx = 0; - //one single row, could be consisted of multiple spans. - while (span->y == y && spanIdx < image->rle->size) { - if (minx > span->x) minx = span->x; - if (maxx < span->x + span->len) maxx = span->x + span->len; - ++span; - ++spanIdx; - } - } - if (x1 < minx) x1 = minx; - if (x2 > maxx) x2 = maxx; + x1 = std::max((SwCoord)_xa, bbox.min.x); + x2 = std::min((SwCoord)_xb, bbox.max.x); //Anti-Aliasing frames - ay = y - aaSpans->yStart; - if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; - if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + if (aaSpans) { + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + } //Range allowed - if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) { //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); @@ -334,30 +218,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, uu = (int) u; vv = (int) v; - if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; + if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; + + ar = _modf(u); + ab = _modf(v); - ar = (int)(255.0f * (1.0f - modff(u, &iptr))); - ab = (int)(255.0f * (1.0f - modff(v, &iptr))); iru = uu + 1; irv = vv + 1; - px = *(sbuf + (vv * image->stride) + uu); + px = *(sbuf + (vv * image.stride) + uu); /* horizontal interpolate */ if (iru < sw) { /* right pixel */ - int px2 = *(sbuf + (vv * image->stride) + iru); + int px2 = *(sbuf + (vv * image.stride) + iru); px = INTERPOLATE(px, px2, ar); } /* vertical interpolate */ if (irv < sh) { /* bottom pixel */ - int px2 = *(sbuf + (irv * image->stride) + uu); + int px2 = *(sbuf + (irv * image.stride) + uu); /* horizontal interpolate */ if (iru < sw) { /* bottom right pixel */ - int px3 = *(sbuf + (irv * image->stride) + iru); + int px3 = *(sbuf + (irv * image.stride) + iru); px2 = INTERPOLATE(px2, px3, ar); } px = INTERPOLATE(px, px2, ab); @@ -382,30 +267,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, uu = (int) u; vv = (int) v; - if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; + if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue; + + ar = _modf(u); + ab = _modf(v); - ar = (int)(255.0f * (1.0f - modff(u, &iptr))); - ab = (int)(255.0f * (1.0f - modff(v, &iptr))); iru = uu + 1; irv = vv + 1; - px = *(sbuf + (vv * sw) + uu); + px = *(sbuf + (vv * image.stride) + uu); /* horizontal interpolate */ if (iru < sw) { /* right pixel */ - int px2 = *(sbuf + (vv * image->stride) + iru); + int px2 = *(sbuf + (vv * image.stride) + iru); px = INTERPOLATE(px, px2, ar); } /* vertical interpolate */ if (irv < sh) { /* bottom pixel */ - int px2 = *(sbuf + (irv * image->stride) + uu); + int px2 = *(sbuf + (irv * image.stride) + uu); /* horizontal interpolate */ if (iru < sw) { /* bottom right pixel */ - int px3 = *(sbuf + (irv * image->stride) + iru); + int px3 = *(sbuf + (irv * image.stride) + iru); px2 = INTERPOLATE(px2, px3, ar); } px = INTERPOLATE(px, px2, ab); @@ -433,8 +319,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, _ua += _dudya; _va += _dvdya; - if (!region && spanIdx >= image->rle->size) break; - ++y; } xa = _xa; @@ -445,7 +329,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, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) +static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) { 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}; @@ -506,7 +390,6 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; - auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image? auto compositing = _compositing(surface); //Composition required auto blending = _blending(surface); //Blending required @@ -525,7 +408,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const //Draw upper segment if possibly visible if (yi[0] < yi[1]) { - off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; + off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0; xa += (off_y * dxdya); ua += (off_y * dudya); va += (off_y * dvdya); @@ -535,18 +418,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const xb = x[0] + dy * dxdyb + (off_y * dxdyb); if (compositing) { - if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); - else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1); + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1); } else if (blending) { - _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity); } else { - _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false); } upper = true; } //Draw lower segment if possibly visible if (yi[1] < yi[2]) { - off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; + off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0; if (!upper) { xa += (off_y * dxdya); ua += (off_y * dudya); @@ -556,12 +439,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const dxdyb = dxdy[2]; xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); if (compositing) { - if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); - else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2); + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2); } else if (blending) { - _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity); } else { - _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false); } } //Longer edge is on the right side @@ -573,7 +456,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const //Draw upper segment if possibly visible if (yi[0] < yi[1]) { - off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; + off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0; xb += (off_y *dxdyb); // Set slopes along left edge and perform subpixel pre-stepping @@ -586,18 +469,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const va = v[0] + dy * dvdya + (off_y * dvdya); if (compositing) { - if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); - else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3); + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3); } else if (blending) { - _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity); } else { - _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false); } upper = true; } //Draw lower segment if possibly visible if (yi[1] < yi[2]) { - off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; + off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0; if (!upper) xb += (off_y *dxdyb); // Set slopes along left edge and perform subpixel pre-stepping @@ -610,25 +493,20 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const va = v[1] + dy * dvdya + (off_y * dvdya); if (compositing) { - if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); - else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4); + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4); } else if (blending) { - _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity); } else { - _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false); } } } } -static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region) +static AASpans* _AASpans(int yStart, int yEnd) { - auto yStart = static_cast(ymin); - auto yEnd = static_cast(ymax); - - if (!_arrange(image, region, yStart, yEnd)) return nullptr; - auto aaSpans = static_cast(malloc(sizeof(AASpans))); aaSpans->yStart = yStart; aaSpans->yEnd = yEnd; @@ -837,7 +715,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx) } -static bool _apply(SwSurface* surface, AASpans* aaSpans) +static void _apply(SwSurface* surface, AASpans* aaSpans) { auto end = surface->buf32 + surface->h * surface->stride; auto y = aaSpans->yStart; @@ -864,7 +742,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) //exceptional handling. out of memory bound. if (dst + line->length[0] >= end) { - pos += (dst + line->length[0] - end); + pos += static_cast(dst + line->length[0] - end); } while (pos <= line->length[0]) { @@ -881,7 +759,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) pos = line->length[1]; //exceptional handling. out of memory bound. - if (dst - pos < surface->buf32) --pos; + if (dst - pos < surface->buf32) pos = static_cast(dst - surface->buf32); while (pos > 0) { *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos)); @@ -894,8 +772,6 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) free(aaSpans->lines); free(aaSpans); - - return true; } @@ -909,23 +785,19 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) | / | 3 -- 2 */ -static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity) +bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); return false; } - //Exceptions: No dedicated drawing area? - if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true; - - /* Prepare vertices. - shift XY coordinates to match the sub-pixeling technique. */ + //Prepare vertices. Shift XY coordinates to match the sub-pixeling technique. Vertex vertices[4]; vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; - vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}}; - 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)}}; + vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}}; + 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)}}; float ys = FLT_MAX, ye = -1.0f; for (int i = 0; i < 4; i++) { @@ -934,8 +806,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; } - auto aaSpans = _AASpans(ys, ye, image, region); - if (!aaSpans) return true; + auto yStart = std::max(static_cast(ys), bbox.min.y); + auto yEnd = std::min(static_cast(ye), bbox.max.y); + auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd); Polygon polygon; @@ -944,19 +817,20 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const polygon.vertex[1] = vertices[1]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); + _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity); //Draw the second polygon polygon.vertex[0] = vertices[1]; polygon.vertex[1] = vertices[2]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); + _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity); #if 0 if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); } #endif - return _apply(surface, aaSpans); + if (aaSpans) _apply(surface, aaSpans); + return true; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index cad07f6de67..0a9ef3803e0 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -129,7 +129,7 @@ struct SwShapeTask : SwTask if (updateShape) shapeReset(&shape); if (updateFill || clipper) { if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) { - if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err; + if (!shapeGenRle(&shape, rshape, mpool, tid, antialiasing(strokeWidth))) goto err; } else { updateFill = false; renderRegion.reset(); @@ -165,8 +165,9 @@ struct SwShapeTask : SwTask //Clip Path for (auto clip = clips.begin(); clip < clips.end(); ++clip) { auto clipper = static_cast(*clip); - if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle - if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle + auto clipShapeRle = shape.rle ? clipper->clip(shape.rle) : true; + auto clipStrokeRle = shape.strokeRle ? clipper->clip(shape.strokeRle) : true; + if (!clipShapeRle && !clipStrokeRle) goto err; } bbox = renderRegion; //sync @@ -220,7 +221,7 @@ struct SwImageTask : SwTask if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; if (clips.count > 0) { - if (!imageGenRle(&image, bbox, false)) goto end; + if (!imageGenRle(&image, bbox, mpool, tid, false)) goto end; if (image.rle) { //Clear current task memorypool here if the clippers would use the same memory pool imageDelOutline(&image, mpool, tid); @@ -403,7 +404,32 @@ bool SwRenderer::renderImage(RenderData data) if (task->opacity == 0) return true; - return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); + //Outside of the viewport, skip the rendering + auto& bbox = task->bbox; + if (bbox.max.x <= bbox.min.x || bbox.max.y <= bbox.min.y || bbox.min.x >= int32_t(surface->w) || bbox.min.y >= int32_t(surface->h)) return true; + + auto& image = task->image; + + //RLE Image + if (image.rle) { + if (image.direct) return rasterDirectRleImage(surface, image, task->opacity); + else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity); + else { + //create a intermediate buffer for rle clipping + auto cmp = request(sizeof(pixel_t), false); + cmp->compositor->method = CompositeMethod::None; + cmp->compositor->valid = true; + cmp->compositor->image.rle = image.rle; + rasterClear(cmp, (uint32_t)bbox.min.x, (uint32_t)bbox.min.y, (uint32_t)(bbox.max.x - bbox.min.x), (uint32_t)(bbox.max.y - bbox.min.y), 0); + rasterTexmapPolygon(cmp, image, task->transform, bbox, 255); + return rasterDirectRleImage(surface, cmp->compositor->image, task->opacity); + } + //Whole Image + } else { + if (image.direct) return rasterDirectImage(surface, image, bbox, task->opacity); + else if (image.scaled) return rasterScaledImage(surface, image, task->transform, bbox, task->opacity); + else return rasterTexmapPolygon(surface, image, task->transform, bbox, task->opacity); + } } @@ -638,8 +664,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp) //Default is alpha blending if (p->method == CompositeMethod::None) { - Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - return rasterImage(surface, &p->image, m, p->bbox, p->opacity); + return rasterDirectImage(surface, p->image, p->bbox, p->opacity); } return true; @@ -678,13 +703,16 @@ bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool return false; } + //TODO: Support grayscale effects. + if (p->recoverSfc->channelSize != sizeof(uint32_t)) direct = false; + switch (effect->type) { case SceneEffect::GaussianBlur: { return effectGaussianBlur(p, request(surface->channelSize, true), static_cast(effect)); } case SceneEffect::DropShadow: { auto cmp1 = request(surface->channelSize, true); - cmp1->compositor->valid = false; + cmp1->compositor->valid = false; //prevent a conflict with cmp2 request. auto cmp2 = request(surface->channelSize, true); SwSurface* surfaces[] = {cmp1, cmp2}; auto ret = effectDropShadow(p, surfaces, static_cast(effect), direct); @@ -726,8 +754,7 @@ void SwRenderer::dispose(RenderData data) void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) { - if (!surface) return task; - if (flags == RenderUpdateFlag::None) return task; + if (!surface || (transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //invalid //TODO: Failed threading them. It would be better if it's possible. //See: https://github.com/thorvg/thorvg/issues/1409 @@ -738,7 +765,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr task->clips = clips; task->transform = transform; - + //zero size? if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height @@ -757,7 +784,16 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr tasks.push(task); } - TaskScheduler::request(task); + //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. + if (flags & RenderUpdateFlag::Clip) { + for (uint32_t i = 0; i < clips.count; ++i) { + static_cast(clips[i])->done(); + } + } + + if (flags) TaskScheduler::request(task); return task; } @@ -767,10 +803,11 @@ RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Ma { //prepare task auto task = static_cast(data); - if (!task) task = new SwImageTask; - else task->done(); - - task->source = surface; + if (task) task->done(); + else { + task = new SwImageTask; + task->source = surface; + } return prepareCommon(task, transform, clips, opacity, flags); } @@ -780,10 +817,12 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const { //prepare task auto task = static_cast(data); - if (!task) task = new SwShapeTask; - else task->done(); + if (task) task->done(); + else { + task = new SwShapeTask; + task->rshape = &rshape; + } - task->rshape = &rshape; task->clipper = clipper; return prepareCommon(task, transform, clips, opacity, flags); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index 27868abf576..1910deadf36 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -55,6 +55,8 @@ public: bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool mempool(bool shared); + SwSurface* request(int channelSize, bool square); + RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override; bool endComposite(RenderCompositor* cmp) override; @@ -80,7 +82,6 @@ private: SwRenderer(); ~SwRenderer(); - SwSurface* request(int channelSize, bool square); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); }; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp index 1c139d9e753..27a9ba1f397 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp @@ -199,21 +199,11 @@ constexpr auto PIXEL_BITS = 8; //must be at least 6 bits! constexpr auto ONE_PIXEL = (1L << PIXEL_BITS); -using Area = long; - struct Band { SwCoord min, max; }; -struct Cell -{ - SwCoord x; - SwCoord cover; - Area area; - Cell *next; -}; - struct RleWorker { SwRle* rle; @@ -227,7 +217,7 @@ struct RleWorker Area area; SwCoord cover; - Cell* cells; + SwCell* cells; ptrdiff_t maxCells; ptrdiff_t cellsCnt; @@ -242,11 +232,11 @@ struct RleWorker int bandSize; int bandShoot; - void* buffer; - long bufferSize; + SwCell* buffer; + uint32_t bufferSize; - Cell** yCells; - SwCoord yCnt; + SwCell** yCells; + int32_t yCnt; bool invalid; bool antiAlias; @@ -282,12 +272,11 @@ static inline SwCoord SUBPIXELS(const SwCoord x) return SwCoord(((unsigned long) x) << PIXEL_BITS); } -/* - * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' - * algorithm. We use alpha = 1, beta = 3/8, giving us results with a - * largest error less than 7% compared to the exact value. - */ -static inline SwCoord HYPOT(SwPoint pt) + +// Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' algorithm. +// We use alpha = 1, beta = 3/8, giving us results with a largest error +// less than 7% compared to the exact value. +static inline int32_t HYPOT(SwPoint pt) { if (pt.x < 0) pt.x = -pt.x; if (pt.y < 0) pt.y = -pt.y; @@ -295,7 +284,17 @@ static inline SwCoord HYPOT(SwPoint pt) } -static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount) +// Used to prevent integer overflow when calculating the distance between points. +// This function uses 64-bit arithmetic to safely compute the difference between coordinates. +static inline uint32_t SAFE_HYPOT(SwPoint& pt1, SwPoint& pt2) +{ + auto x = uint32_t(abs(int64_t(pt1.x) - int64_t(pt2.x))); + auto y = uint32_t(abs(int64_t(pt1.y) - int64_t(pt2.y))); + return (x > y) ? (x + (3 * y >> 3)) : (y + (3 * x >> 3)); +} + + +static void _horizLine(RleWorker& rw, int32_t x, int32_t y, int32_t area, int32_t aCount) { x += rw.cellMin.x; y += rw.cellMin.y; @@ -401,7 +400,7 @@ static void _sweep(RleWorker& rw) } -static Cell* _findCell(RleWorker& rw) +static SwCell* _findCell(RleWorker& rw) { auto x = rw.cellPos.x; if (x > rw.cellXCnt) x = rw.cellXCnt; @@ -409,7 +408,7 @@ static Cell* _findCell(RleWorker& rw) auto pcell = &rw.yCells[rw.cellPos.y]; while(true) { - Cell* cell = *pcell; + auto cell = *pcell; if (!cell || cell->x > x) break; if (cell->x == x) return cell; pcell = &cell->next; @@ -529,14 +528,12 @@ static bool _lineTo(RleWorker& rw, const SwPoint& to) line[1] = rw.pos; while (true) { - auto diff = line[0] - line[1]; - auto L = HYPOT(diff); - - if (L > SHRT_MAX) { + if (SAFE_HYPOT(line[0], line[1]) > SHRT_MAX) { mathSplitLine(line); ++line; continue; } + auto diff = line[0] - line[1]; e1 = TRUNC(line[1]); e2 = TRUNC(line[0]); @@ -864,21 +861,25 @@ void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size) /* External Class Implementation */ /************************************************************************/ -SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) +SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias) { if (!outline) return nullptr; - - constexpr auto RENDER_POOL_SIZE = 16384L; - constexpr auto BAND_SIZE = 40; - - //TODO: We can preserve several static workers in advance + RleWorker rw; - Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)]; + auto cellPool = mpoolReqCellPool(mpool, tid); + auto reqSize = uint32_t(std::max(renderRegion.w(), renderRegion.h()) * 0.75f) * sizeof(SwCell); //experimental decision + + // grow by 1.25x and align to multiple of sizeof(SwCell) + if (reqSize > cellPool->size) { + cellPool->size = ((reqSize + (reqSize >> 2)) / sizeof(SwCell)) * sizeof(SwCell); + free(cellPool->buffer); + cellPool->buffer = (SwCell*)malloc(cellPool->size); + } //Init Cells - rw.buffer = buffer; - rw.bufferSize = sizeof(buffer); - rw.yCells = reinterpret_cast(buffer); + rw.buffer = cellPool->buffer; + rw.bufferSize = cellPool->size; + rw.yCells = reinterpret_cast(cellPool->buffer); rw.cells = nullptr; rw.maxCells = 0; rw.cellsCnt = 0; @@ -890,7 +891,7 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; rw.outline = const_cast(outline); - rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256 + rw.bandSize = rw.bufferSize / (sizeof(SwCell) * 2); rw.bandShoot = 0; rw.antiAlias = antiAlias; @@ -898,6 +899,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio else rw.rle = rle; //Generate RLE + constexpr auto BAND_SIZE = 40; + Band bands[BAND_SIZE]; Band* band; @@ -920,16 +923,16 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio band = bands; while (band >= bands) { - rw.yCells = static_cast(rw.buffer); + rw.yCells = reinterpret_cast(rw.buffer); rw.yCnt = band->max - band->min; - int cellStart = sizeof(Cell*) * (int)rw.yCnt; - int cellMod = cellStart % sizeof(Cell); + int cellStart = sizeof(SwCell*) * (int)rw.yCnt; + int cellMod = cellStart % sizeof(SwCell); - if (cellMod > 0) cellStart += sizeof(Cell) - cellMod; + if (cellMod > 0) cellStart += sizeof(SwCell) - cellMod; - auto cellsMax = reinterpret_cast((char*)rw.buffer + rw.bufferSize); - rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); + auto cellsMax = reinterpret_cast((char*)rw.buffer + rw.bufferSize); + rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); if (rw.cells >= cellsMax) goto reduce_bands; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 87c2da905a7..08f3c36da28 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -368,7 +368,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans } } - dash.outline = mpoolReqDashOutline(mpool, tid); + mpoolRetOutline(mpool, tid); //retreive the outline cache and use it for dash outline. + dash.outline = mpoolReqOutline(mpool, tid); //must begin with moveTo if (cmds[0] == PathCommand::MoveTo) { @@ -516,7 +517,7 @@ bool shapePrepared(const SwShape* shape) } -bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias) +bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, SwMpool* mpool, unsigned tid, bool antiAlias) { //FIXME: Should we draw it? //Case: Stroke Line @@ -526,7 +527,7 @@ bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool anti if (shape->fastTrack) return true; //Case B: Normal Shape RLE Drawing - if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true; + if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, mpool, tid, antiAlias))) return true; return false; } @@ -587,17 +588,13 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& t bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { SwOutline* shapeOutline = nullptr; - SwOutline* strokeOutline = nullptr; - auto dashStroking = false; - auto ret = true; //Dash style (+trimming) auto trimmed = rshape->strokeTrim(); if (rshape->stroke->dashCnt > 0 || trimmed) { shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid); if (!shapeOutline) return false; - dashStroking = true; - //Normal style + //Trimming & Normal style } else { if (!shape->outline) { if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false; @@ -605,24 +602,11 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& shapeOutline = shape->outline; } - if (!strokeParseOutline(shape->stroke, *shapeOutline)) { - ret = false; - goto clear; - } - - strokeOutline = strokeExportOutline(shape->stroke, mpool, tid); - - if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) { - ret = false; - goto clear; - } - - shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true); - -clear: - if (dashStroking) mpoolRetDashOutline(mpool, tid); + if (!strokeParseOutline(shape->stroke, *shapeOutline)) return false; + auto strokeOutline = strokeExportOutline(shape->stroke, mpool, tid); + auto ret = mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false); + shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, mpool, tid, true); mpoolRetStrokeOutline(mpool, tid); - return ret; } diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h index 527221625b6..8aed701c12a 100644 --- a/thirdparty/thorvg/src/renderer/tvgCommon.h +++ b/thirdparty/thorvg/src/renderer/tvgCommon.h @@ -56,8 +56,6 @@ using namespace tvg; enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown }; -using Size = Point; - #ifdef THORVG_LOG_ENABLED constexpr auto ErrorColor = "\033[31m"; //red constexpr auto ErrorBgColor = "\033[41m";//bg red diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 2c05f5b9f48..ba7ea606b84 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -41,97 +41,88 @@ } -static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& pm, const Matrix& rm, RenderRegion& before) +static bool _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& m, RenderRegion& before) { - //sorting - Point tmp[4]; - Point min = {FLT_MAX, FLT_MAX}; - Point max = {0.0f, 0.0f}; - + Point c[4]; //corners for (int i = 0; i < 4; ++i) { - tmp[i] = pts[i]; - tmp[i] *= rm; - tmp[i] *= pm; - if (tmp[i].x < min.x) min.x = tmp[i].x; - if (tmp[i].x > max.x) max.x = tmp[i].x; - if (tmp[i].y < min.y) min.y = tmp[i].y; - if (tmp[i].y > max.y) max.y = tmp[i].y; + c[i] = pts[i] * m; } - float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)}; - //figure out if the clipper is a superset of the current viewport(before) region - if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) { - //viewport region is same, nothing to do. - return Result::Success; - //figure out if the clipper is totally outside of the viewport - } else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) { - renderer->viewport({0, 0, 0, 0}); - return Result::Success; - } - return Result::InsufficientCondition; + auto pointInConvexQuad = [](const Point& p, const Point* quad) { + auto sign = [](const Point& p1, const Point& p2, const Point& p3) { + return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y); + }; + auto b1 = sign(p, quad[0], quad[1]) < 0.0f; + auto b2 = sign(p, quad[1], quad[2]) < 0.0f; + auto b3 = sign(p, quad[2], quad[3]) < 0.0f; + auto b4 = sign(p, quad[3], quad[0]) < 0.0f; + return ((b1 == b2) && (b2 == b3) && (b3 == b4)); + }; + + if (!pointInConvexQuad({float(before.x), float(before.y)}, c)) return false; + if (!pointInConvexQuad({float(before.x + before.w), float(before.y)}, c)) return false; + if (!pointInConvexQuad({float(before.x + before.w), float(before.y + before.h)}, c)) return false; + if (!pointInConvexQuad({float(before.x), float(before.y + before.h)}, c)) return false; + + //same viewport + return true; } -static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before) +static bool _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before) { /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ auto shape = static_cast(cmpTarget); + //Trimming likely makes the shape non-rectangular + if (P(shape)->rs.strokeTrim()) return false; + //Rectangle Candidates? const Point* pts; auto ptsCnt = shape->pathCoords(&pts); - //nothing to clip - if (ptsCnt == 0) return Result::InvalidArguments; - if (ptsCnt != 4) return Result::InsufficientCondition; - - auto& rm = P(cmpTarget)->transform(); + //No rectangle format + if (ptsCnt != 4) return false; //No rotation and no skewing, still can try out clipping the rect region. - auto tryClip = false; - - if ((!rightAngle(pm) || skewed(pm))) tryClip = true; - if ((!rightAngle(rm) || skewed(rm))) tryClip = true; - - if (tryClip) return _clipRect(renderer, pts, pm, rm, before); + auto tm = pm * cmpTarget->transform(); //Perpendicular Rectangle? - auto pt1 = pts + 0; - auto pt2 = pts + 1; - auto pt3 = pts + 2; - auto pt4 = pts + 3; + if (rightAngle(tm) && !skewed(tm)) { + auto pt1 = pts + 0; + auto pt2 = pts + 1; + auto pt3 = pts + 2; + auto pt4 = pts + 3; - if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) || - (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) { + if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) || + (tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) { - RenderRegion after; + RenderRegion after; - auto v1 = *pt1; - auto v2 = *pt3; - v1 *= rm; - v2 *= rm; - v1 *= pm; - v2 *= pm; + auto v1 = *pt1; + auto v2 = *pt3; + v1 *= tm; + v2 *= tm; - //sorting - if (v1.x > v2.x) std::swap(v1.x, v2.x); - if (v1.y > v2.y) std::swap(v1.y, v2.y); + //sorting + if (v1.x > v2.x) std::swap(v1.x, v2.x); + if (v1.y > v2.y) std::swap(v1.y, v2.y); - after.x = static_cast(v1.x); - after.y = static_cast(v1.y); - after.w = static_cast(ceil(v2.x - after.x)); - after.h = static_cast(ceil(v2.y - after.y)); + after.x = static_cast(nearbyint(v1.x)); + after.y = static_cast(nearbyint(v1.y)); + after.w = static_cast(nearbyint(v2.x)) - after.x; + after.h = static_cast(nearbyint(v2.y)) - after.y; - if (after.w < 0) after.w = 0; - if (after.h < 0) after.h = 0; + if (after.w < 0) after.w = 0; + if (after.h < 0) after.h = 0; - after.intersect(before); - renderer->viewport(after); - - return Result::Success; + after.intersect(before); + renderer->viewport(after); + return true; + } } - return Result::InsufficientCondition; + return _clipRect(renderer, pts, tm, before); } @@ -214,8 +205,12 @@ bool Paint::Impl::render(RenderMethod* renderer) RenderRegion region; PAINT_METHOD(region, bounds(renderer)); - if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer)); - if (region.w == 0 || region.h == 0) return true; + auto cData = compData; + while (cData) { + if (MASK_REGION_MERGING(cData->method)) region.add(P(cData->target)->bounds(renderer)); + if (region.w == 0 || region.h == 0) return true; + cData = P(cData->target)->compData; + } cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method), CompositionFlag::Masking); if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) { compData->target->pImpl->render(renderer); @@ -246,7 +241,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arraytarget; @@ -263,13 +258,13 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayfill() && !(PP(shape)->compData)) { if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) { viewport = renderer->viewport(); - if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { - P(target)->ctxFlag |= ContextFlag::FastTrack; + if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport))) { + P(target)->ctxFlag |= ContextFlag::FastTrack; } } } } - if (compFastTrack == Result::InsufficientCondition) { + if (!compFastTrack) { trd = P(target)->update(renderer, pm, clips, 255, pFlag, false); } } @@ -282,13 +277,12 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayviewport(); /* TODO: Intersect the clipper's clipper, if both are FastTrack. Update the subsequent clipper first and check its ctxFlag. */ - if (!pclip->clipper && _compFastTrack(renderer, this->clipper, pm, viewport) == Result::Success) { + if (!pclip->clipper && static_cast(this->clipper)->strokeWidth() == 0.0f && _compFastTrack(renderer, this->clipper, pm, viewport)) { pclip->ctxFlag |= ContextFlag::FastTrack; - compFastTrack = Result::Success; + compFastTrack = true; } else { trd = pclip->update(renderer, pm, clips, 255, pFlag, true); clips.push(trd); - compFastTrack = Result::InsufficientCondition; } } @@ -303,7 +297,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayviewport(viewport); + if (compFastTrack) renderer->viewport(viewport); else if (this->clipper) clips.pop(); return rd; diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h index ee35adc4676..63b64b4d29e 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.h +++ b/thirdparty/thorvg/src/renderer/tvgPaint.h @@ -74,9 +74,9 @@ namespace tvg } tr; RenderUpdateFlag renderFlag = RenderUpdateFlag::None; BlendMethod blendMethod; + uint16_t refCnt = 0; //reference count uint8_t ctxFlag; uint8_t opacity; - uint8_t refCnt = 0; //reference count Impl(Paint* pnt) : paint(pnt) { @@ -95,7 +95,6 @@ namespace tvg uint8_t ref() { - if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!"); return ++refCnt; } diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h index e2eba0ce783..516148aaef0 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.h +++ b/thirdparty/thorvg/src/renderer/tvgScene.h @@ -97,10 +97,11 @@ struct Scene::Impl //Half translucent requires intermediate composition. if (opacity == 255) return compFlag; - //If scene has several children or only scene, it may require composition. - //OPTIMIZE: the bitmap type of the picture would not need the composition. - //OPTIMIZE: a single paint of a scene would not need the composition. - if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag; + //Only shape or picture may not require composition. + if (paints.size() == 1) { + auto type = paints.front()->type(); + if (type == Type::Shape || type == Type::Picture) return compFlag; + } compFlag |= CompositionFlag::Opacity; @@ -244,7 +245,40 @@ struct Scene::Impl dup->paints.push_back(cdup); } - if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?"); + if (effects) { + dup->effects = new Array; + for (auto p = effects->begin(); p < effects->end(); ++p) { + RenderEffect* ret = nullptr; + switch ((*p)->type) { + case SceneEffect::GaussianBlur: { + ret = new RenderEffectGaussianBlur(*(RenderEffectGaussianBlur*)(*p)); + break; + } + case SceneEffect::DropShadow: { + ret = new RenderEffectDropShadow(*(RenderEffectDropShadow*)(*p)); + break; + } + case SceneEffect::Fill: { + ret = new RenderEffectFill(*(RenderEffectFill*)(*p)); + break; + } + case SceneEffect::Tint: { + ret = new RenderEffectTint(*(RenderEffectTint*)(*p)); + break; + } + case SceneEffect::Tritone: { + ret = new RenderEffectTritone(*(RenderEffectTritone*)(*p)); + break; + } + default: break; + } + if (ret) { + ret->rd = nullptr; + ret->valid = false; + dup->effects->push(ret); + } + } + } return scene; } diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h index 9d4529ab090..c84615d7e6f 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.h +++ b/thirdparty/thorvg/src/renderer/tvgText.h @@ -40,10 +40,7 @@ struct Text::Impl bool italic = false; bool changed = false; - Impl(Text* p) : paint(p), shape(Shape::gen().release()) - { - shape->fill(FillRule::EvenOdd); - } + Impl(Text* p) : paint(p), shape(Shape::gen().release()) {} ~Impl() { diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index a4e0b865cb5..8e3193557ee 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.15.13 +VERSION=0.15.16 # Uncomment and set a git hash to use specific commit instead of tag. #GIT_COMMIT=