diff --git a/thirdparty/README.md b/thirdparty/README.md index 1872e7ac36d..169a3be5391 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -944,7 +944,7 @@ Patches: ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.15.10 (bca94d244c67f573c6eddc27d783d9a6b1ef2f1b, 2025) +- Version: 0.15.11 (61360cf6db0a05a8dd2ebdcc44d4cbbc315692ec, 2025) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index b2b1cb6be57..5bf2913d726 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.10" +#define THORVG_VERSION_STRING "0.15.11" #endif diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index b5e90e7b404..44f610e1537 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -3382,6 +3382,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, if (node->type != SvgNodeType::Defs || !empty) { loader->stack.push(node); } + loader->latestGradient = nullptr; } else if ((method = _findGraphicsFactory(tagName))) { if (loader->stack.count > 0) parent = loader->stack.last(); else parent = loader->doc; @@ -3392,6 +3393,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, loader->stack.push(defs); loader->currentGraphicsNode = node; } + loader->latestGradient = nullptr; } else if ((gradientMethod = _findGradientFactory(tagName))) { SvgStyleGradient* gradient; gradient = gradientMethod(loader, attrs, attrsLength); @@ -3417,8 +3419,9 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, loader->svgParse->flags = SvgStopStyleFlags::StopDefault; simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader); loader->latestGradient->stops.push(loader->svgParse->gradStop); - } else if (!isIgnoreUnsupportedLogElements(tagName)) { - TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); + } else { + loader->latestGradient = nullptr; + if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); } } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index a45a996ad91..53c28049813 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -448,9 +448,9 @@ static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint { // d / (1 - s) s = 0xffffffff - s; - auto c1 = (C1(s) == 0) ? C1(d) : std::min(C1(d) * 255 / C1(s), 255); - auto c2 = (C2(s) == 0) ? C2(d) : std::min(C2(d) * 255 / C2(s), 255); - auto c3 = (C3(s) == 0) ? C3(d) : std::min(C3(d) * 255 / C3(s), 255); + auto c1 = C1(d) == 0 ? 0 : (C1(s) == 0 ? 255 : std::min(C1(d) * 255 / C1(s), 255)); + auto c2 = C2(d) == 0 ? 0 : (C2(s) == 0 ? 255 : std::min(C2(d) * 255 / C2(s), 255)); + auto c3 = C3(d) == 0 ? 0 : (C3(s) == 0 ? 255 : std::min(C3(d) * 255 / C3(s), 255)); return JOIN(255, c1, c2, c3); } @@ -458,9 +458,9 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8 { // 1 - (1 - d) / s auto id = 0xffffffff - d; - auto c1 = (C1(s) == 0) ? C1(d) : 255 - std::min(C1(id) * 255 / C1(s), 255); - auto c2 = (C2(s) == 0) ? C2(d) : 255 - std::min(C2(id) * 255 / C2(s), 255); - auto c3 = (C3(s) == 0) ? C3(d) : 255 - std::min(C3(id) * 255 / C3(s), 255); + auto c1 = C1(d) == 255 ? 255 : (C1(s) == 0 ? 0 : 255 - std::min(C1(id) * 255 / C1(s), 255)); + auto c2 = C2(d) == 255 ? 255 : (C2(s) == 0 ? 0 : 255 - std::min(C2(id) * 255 / C2(s), 255)); + auto c3 = C3(d) == 255 ? 255 : (C3(s) == 0 ? 0 : 255 - std::min(C3(id) * 255 / C3(s), 255)); return JOIN(255, c1, c2, c3); } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp index 4be40c885dc..fe311ef3454 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -266,10 +266,10 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i } -static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct) +static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct) { - src += (region.min.y * stride + region.min.x); - dst += (region.min.y * stride + region.min.x); + src += (region.min.y * sstride + region.min.x); + dst += (region.min.y * dstride + region.min.x); auto w = region.max.x - region.min.x; auto h = region.max.y - region.min.y; @@ -279,14 +279,14 @@ static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& r if (region.min.x + offset.x < 0) src -= offset.x; else dst += offset.x; - if (region.min.y + offset.y < 0) src -= (offset.y * stride); - else dst += (offset.y * stride); + 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); - src += stride; - dst += stride; + src += sstride; + dst += dstride; } } @@ -389,14 +389,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, stride, bbox, data->offset, opacity, direct); + _dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, stride, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[0]->buf32); return true; } //draw to the intermediate surface rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h); - _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct); + _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, stride, bbox, data->offset, opacity, direct); std::swap(cmp->image.buf32, buffer[1]->buf32); //compositing shadow and body diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index cce97cffb3c..5e29d2e23af 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -1813,7 +1813,7 @@ bool rasterConvertCS(RenderSurface* surface, ColorSpace to) //TODO: SIMD OPTIMIZATION? void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped) { - constexpr int BLOCK = 8; //experimental decision + constexpr int32_t BLOCK = 8; //experimental decision if (flipped) { src += ((bbox.min.x * stride) + bbox.min.y); @@ -1824,16 +1824,16 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32 } #pragma omp parallel for - for (int x = 0; x < w; x += BLOCK) { + for (int32_t x = 0; x < w; x += BLOCK) { auto bx = std::min(w, x + BLOCK) - x; auto in = &src[x]; auto out = &dst[x * stride]; - for (int y = 0; y < h; y += BLOCK) { + for (int32_t y = 0; y < h; y += BLOCK) { auto p = &in[y * stride]; auto q = &out[y]; auto by = std::min(h, y + BLOCK) - y; - for (int xx = 0; xx < bx; ++xx) { - for (int yy = 0; yy < by; ++yy) { + for (int32_t xx = 0; xx < bx; ++xx) { + for (int32_t yy = 0; yy < by; ++yy) { *q = *p; p += stride; ++q; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 5b9719d2bc7..c70fdcfa1ac 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -121,8 +121,7 @@ struct SwShapeTask : SwTask auto visibleFill = false; //This checks also for the case, if the invisible shape turned to visible by alpha. - auto prepareShape = false; - if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true; + auto prepareShape = !shapePrepared(&shape) && flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient); //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { @@ -186,6 +185,7 @@ struct SwShapeTask : SwTask err: bbox.reset(); shapeReset(&shape); + rleReset(shape.strokeRle); shapeDelOutline(&shape, mpool, tid); } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp index cd9f40485ca..8290927aa6e 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp @@ -482,7 +482,7 @@ static bool _setCell(RleWorker& rw, SwPoint pos) static bool _startCell(RleWorker& rw, SwPoint pos) { if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; - if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x; + if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x - 1; rw.area = 0; rw.cover = 0; @@ -866,6 +866,8 @@ void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size) SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) { + if (!outline) return nullptr; + constexpr auto RENDER_POOL_SIZE = 16384L; constexpr auto BAND_SIZE = 40; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 4408db0b86f..ce46b582848 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -543,7 +543,6 @@ void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid) void shapeReset(SwShape* shape) { rleReset(shape->rle); - rleReset(shape->strokeRle); shape->fastTrack = false; shape->bbox.reset(); } diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.cpp b/thirdparty/thorvg/src/renderer/tvgLoader.cpp index e69fb678a3a..333c9bf6cf7 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoader.cpp +++ b/thirdparty/thorvg/src/renderer/tvgLoader.cpp @@ -267,7 +267,11 @@ bool LoaderMgr::term() auto loader = _activeLoaders.head; //clean up the remained font loaders which is globally used. - while (loader && loader->type == FileType::Ttf) { + while (loader) { + if (loader->type != FileType::Ttf) { + loader = loader->next; + continue; + } auto ret = loader->close(); auto tmp = loader; loader = loader->next; diff --git a/thirdparty/thorvg/src/renderer/tvgText.cpp b/thirdparty/thorvg/src/renderer/tvgText.cpp index a09bb302e57..2b2aea316e1 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.cpp +++ b/thirdparty/thorvg/src/renderer/tvgText.cpp @@ -62,7 +62,11 @@ Result Text::load(const std::string& path) noexcept { #ifdef THORVG_FILE_IO_SUPPORT bool invalid; //invalid path - if (!LoaderMgr::loader(path, &invalid)) { + auto loader = LoaderMgr::loader(path, &invalid); + if (loader) { + if (loader->sharing > 0) --loader->sharing; //font loading doesn't mean sharing. + return Result::Success; + } else { if (invalid) return Result::InvalidArguments; else return Result::NonSupport; } diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index cd36e1626ee..10ed335461d 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.15.10 +VERSION=0.15.11 # Uncomment and set a git hash to use specific commit instead of tag. #GIT_COMMIT=