Răsfoiți Sursa

thorvg: Update to 0.14.10

More fixes to rendering of SVG files with broken text tags.

Also backports upstream patch to fix #97078.

(cherry picked from commit 0c0336f7049fb792c6e0121082b60815ad3cf1e0)
Rémi Verschelde 1 an în urmă
părinte
comite
f273c7c773

+ 1 - 1
thirdparty/README.md

@@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.14.9 (81a0fbfd590873b21e53c3af77969c71d3d9b586, 2024)
+- Version: 0.14.10 (366dcd72850c360b49e841e568fc5a154d7cce9e, 2024)
 - License: MIT
 
 Files extracted from upstream source:

+ 1 - 1
thirdparty/thorvg/inc/config.h

@@ -15,5 +15,5 @@
 // For internal debugging:
 //#define THORVG_LOG_ENABLED
 
-#define THORVG_VERSION_STRING "0.14.9"
+#define THORVG_VERSION_STRING "0.14.10"
 #endif

+ 0 - 25
thirdparty/thorvg/patches/pr2716-text-drawing-reliability.patch

@@ -1,25 +0,0 @@
-From 41d67213607e7ff20b7a3ca833f1cfde9780da65 Mon Sep 17 00:00:00 2001
-From: Hermet Park <[email protected]>
-Date: Sat, 7 Sep 2024 01:35:09 +0900
-Subject: [PATCH] renderer: ++reliability in text drawing
-
-Allow the canvas to pass through
-even if text elements are not properly supported.
-
-issue: https://github.com/thorvg/thorvg/issues/2715
----
- src/renderer/tvgText.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h
-index 746b85bea6..55d33ffd4b 100644
---- a/thirdparty/thorvg/src/renderer/tvgText.h
-+++ b/thirdparty/thorvg/src/renderer/tvgText.h
-@@ -89,6 +89,7 @@ struct Text::Impl
- 
-     bool render(RenderMethod* renderer)
-     {
-+        if (!loader) return true;
-         renderer->blend(paint->blend(), true);
-         return PP(shape)->render(renderer);
-     }

+ 45 - 0
thirdparty/thorvg/patches/pr2740-renderer-crash-hotfix.patch

@@ -0,0 +1,45 @@
+From 8009c75465e5b35da2d5f53532bc65f6df202a3a Mon Sep 17 00:00:00 2001
+From: Hermet Park <[email protected]>
+Date: Tue, 17 Sep 2024 11:35:48 +0900
+Subject: [PATCH] renderer: hotfix a crash
+
+prevent a nullptr memory access
+regression by f5337015e971d24379d2ee664895503ab8945e13
+
+issue: https://github.com/godotengine/godot/issues/97078
+---
+ src/renderer/tvgShape.h                  | 6 ++++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h
+index 221931dee..e120a85c6 100644
+--- a/src/renderer/tvgShape.h
++++ b/src/renderer/tvgShape.h
+@@ -51,8 +51,9 @@ struct Shape::Impl
+ 
+     bool render(RenderMethod* renderer)
+     {
++        if (!rd) return false;
++
+         Compositor* cmp = nullptr;
+-        bool ret;
+ 
+         renderer->blend(shape->blend());
+ 
+@@ -61,7 +62,7 @@ struct Shape::Impl
+             renderer->beginComposite(cmp, CompositeMethod::None, opacity);
+         }
+ 
+-        ret = renderer->renderShape(rd);
++        auto ret = renderer->renderShape(rd);
+         if (cmp) renderer->endComposite(cmp);
+         return ret;
+     }
+@@ -117,6 +118,7 @@ struct Shape::Impl
+ 
+     RenderRegion bounds(RenderMethod* renderer)
+     {
++        if (!rd) return {0, 0, 0, 0};
+         return renderer->region(rd);
+     }
+ 

+ 8 - 0
thirdparty/thorvg/src/common/tvgStr.cpp

@@ -229,6 +229,14 @@ char* strDuplicate(const char *str, size_t n)
     return (char *) memcpy(ret, str, n);
 }
 
+char* strAppend(char* lhs, const char* rhs, size_t n)
+{
+    if (!rhs) return lhs;
+    if (!lhs) return strDuplicate(rhs, n);
+    lhs = (char*)realloc(lhs, strlen(lhs) + n + 1);
+    return strncat(lhs, rhs, n);
+}
+
 char* strDirname(const char* path)
 {
     const char *ptr = strrchr(path, '/');

+ 1 - 0
thirdparty/thorvg/src/common/tvgStr.h

@@ -31,6 +31,7 @@ namespace tvg
 float strToFloat(const char *nPtr, char **endPtr);  //convert to float
 int str2int(const char* str, size_t n);             //convert to integer
 char* strDuplicate(const char *str, size_t n);      //copy the string
+char* strAppend(char* lhs, const char* rhs, size_t n);  //append the rhs to the lhs
 char* strDirname(const char* path);                 //return the full directory name
 
 }

+ 2 - 2
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -2183,6 +2183,7 @@ static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const ch
     //TODO: support the def font and size as used in a system?
     loader->svgParse->node->node.text.fontSize = 10.0f;
     loader->svgParse->node->node.text.fontFamily = nullptr;
+    loader->svgParse->node->node.text.text = nullptr;
 
     func(buf, bufLength, _attrParseTextNode, loader);
 
@@ -3400,8 +3401,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
 static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length)
 {
     auto text = &loader->svgParse->node->node.text;
-    if (text->text) free(text->text);
-    text->text = strDuplicate(content, length);
+    text->text = strAppend(text->text, content, length);
 }
 
 

+ 3 - 8
thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h

@@ -367,7 +367,7 @@ static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNU
 }
 
 //TODO: BlendMethod could remove the alpha parameter.
-static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, uint8_t a)
+static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     //if (s > d) => s - d
     //else => d - s
@@ -404,7 +404,7 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
     return JOIN(255, c1, c2, c3);
 }
 
-static inline uint32_t opBlendDirectMultiply(uint32_t s, uint32_t d, uint8_t a)
+static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // s * d
     auto c1 = MULTIPLY(C1(s), C1(d));
@@ -413,11 +413,6 @@ static inline uint32_t opBlendDirectMultiply(uint32_t s, uint32_t d, uint8_t a)
     return JOIN(255, c1, c2, c3);
 }
 
-static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, uint8_t a)
-{
-    return opBlendDirectMultiply(s, d, a) + ALPHA_BLEND(d, IA(s));
-}
-
 static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // if (2 * d < da) => 2 * s * d,
@@ -570,7 +565,7 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8
 bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
-bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
 void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
 void rasterUnpremultiply(Surface* surface);

+ 59 - 28
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -423,12 +423,11 @@ static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
     auto color = surface->join(r, g, b, a);
     auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
-    auto ialpha = 255 - a;
 
     for (uint32_t y = 0; y < h; ++y) {
         auto dst = &buffer[y * surface->stride];
         for (uint32_t x = 0; x < w; ++x, ++dst) {
-            *dst = surface->blender(color, *dst, ialpha);
+            *dst = surface->blender(color, *dst, 255);
         }
     }
     return true;
@@ -595,17 +594,16 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t
 
     auto span = rle->spans;
     auto color = surface->join(r, g, b, a);
-    auto ialpha = 255 - a;
 
     for (uint32_t i = 0; i < rle->size; ++i, ++span) {
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         if (span->coverage == 255) {
             for (uint32_t x = 0; x < span->len; ++x, ++dst) {
-                *dst = surface->blender(color, *dst, ialpha);
+                *dst = surface->blender(color, *dst, 255);
             }
         } else {
             for (uint32_t x = 0; x < span->len; ++x, ++dst) {
-                auto tmp = surface->blender(color, *dst, ialpha);
+                auto tmp = surface->blender(color, *dst, 255);
                 *dst = INTERPOLATE(tmp, *dst, span->coverage);
             }
         }
@@ -813,9 +811,8 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
             for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
                 SCALED_IMAGE_RANGE_X
                 auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-                if (opacity < 255) src = ALPHA_BLEND(src, opacity);
                 auto tmp = surface->blender(src, *dst, 255);
-                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
+                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src)));
             }
         }
     }
@@ -981,18 +978,12 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima
         auto alpha = MULTIPLY(span->coverage, opacity);
         if (alpha == 255) {
             for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
-                *dst = surface->blender(*img, *dst, IA(*img));
-            }
-        } else if (opacity == 255) {
-            for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
-                auto tmp = surface->blender(*img, *dst, 255);
-                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(*img)));
+                *dst = surface->blender(*img, *dst, 255);
             }
         } else {
             for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
-                auto src = ALPHA_BLEND(*img, opacity);
-                auto tmp = surface->blender(src, *dst, IA(src));
-                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
+                auto tmp = surface->blender(*img, *dst, 255);
+                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(*img)));
             }
         }
     }
@@ -1164,9 +1155,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
         for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
             SCALED_IMAGE_RANGE_X
             auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
-            if (opacity < 255) ALPHA_BLEND(src, opacity);
             auto tmp = surface->blender(src, *dst, 255);
-            *dst = INTERPOLATE(tmp, *dst, A(src));
+            *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src)));
         }
     }
     return true;
@@ -1348,11 +1338,13 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
             auto src = sbuffer;
             if (opacity == 255) {
                 for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
-                    *dst = MULTIPLY(A(*src), alpha(cmp));
+                    auto tmp = MULTIPLY(A(*src), alpha(cmp));
+                    *dst = tmp + MULTIPLY(*dst, 255 - tmp);
                 }
             } else {
                 for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
-                    *dst = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp)));
+                    auto tmp = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp)));
+                    *dst = tmp + MULTIPLY(*dst, 255 - tmp);
                 }
             }
             buffer += surface->stride;
@@ -1384,9 +1376,8 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
             }
         } else {
             for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
-                auto tmp = ALPHA_BLEND(*src, opacity);
-                auto tmp2 = surface->blender(tmp, *dst, 255);
-                *dst = INTERPOLATE(tmp2, *dst, A(tmp));
+                auto tmp = surface->blender(*src, *dst, 255);
+                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(*src)));
             }
         }
         dbuffer += surface->stride;
@@ -1442,12 +1433,52 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
 }
 
 
+static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
+{
+    if (surface->channelSize == sizeof(uint8_t)) {
+        TVGERR("SW_ENGINE", "Not supported grayscale image!");
+        return false;
+    }
+
+    auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+    auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+    auto csize = surface->compositor->image.channelSize;
+    auto alpha = surface->alpha(surface->compositor->method);
+    auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+    auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
+    auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+
+    for (uint32_t y = 0; y < h; ++y) {
+        auto dst = buffer;
+        auto cmp = cbuffer;
+        auto src = sbuffer;
+        if (opacity == 255) {
+            for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+                auto tmp = ALPHA_BLEND(*src, alpha(cmp));
+                *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, A(tmp));
+            }
+        } else {
+            for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+                auto tmp = ALPHA_BLEND(*src, alpha(cmp));
+                *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, MULTIPLY(opacity, A(tmp)));
+            }
+        }
+        buffer += surface->stride;
+        cbuffer += surface->compositor->image.stride * csize;
+        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)) return _rasterDirectMattedImage(surface, image, region, opacity);
-        else return _rasterDirectMaskedImage(surface, image, region, opacity);
+        if (_matting(surface)) {
+            if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, region, opacity);
+            else return _rasterDirectMattedImage(surface, image, region, opacity);
+        } else return _rasterDirectMaskedImage(surface, image, region, opacity);
     } else if (_blending(surface)) {
         return _rasterDirectBlendingImage(surface, image, region, opacity);
     } else {
@@ -1843,7 +1874,7 @@ bool rasterCompositor(SwSurface* surface)
 }
 
 
-bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val)
 {
     if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false;
 
@@ -1851,11 +1882,11 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_
     if (surface->channelSize == sizeof(uint32_t)) {
         //full clear
         if (w == surface->stride) {
-            rasterPixel32(surface->buf32, 0x00000000, surface->stride * y, w * h);
+            rasterPixel32(surface->buf32, val, surface->stride * y, w * h);
         //partial clear
         } else {
             for (uint32_t i = 0; i < h; i++) {
-                rasterPixel32(surface->buf32, 0x00000000, (surface->stride * y + x) + (surface->stride * i), w);
+                rasterPixel32(surface->buf32, val, (surface->stride * y + x) + (surface->stride * i), w);
             }
         }
     //8 bits

+ 6 - 3
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp

@@ -446,7 +446,7 @@ bool SwRenderer::renderShape(RenderData data)
 }
 
 
-bool SwRenderer::blend(BlendMethod method, bool direct)
+bool SwRenderer::blend(BlendMethod method)
 {
     if (surface->blendMethod == method) return true;
     surface->blendMethod = method;
@@ -459,7 +459,7 @@ bool SwRenderer::blend(BlendMethod method, bool direct)
             surface->blender = opBlendScreen;
             break;
         case BlendMethod::Multiply:
-            surface->blender = direct ? opBlendDirectMultiply : opBlendMultiply;
+            surface->blender = opBlendMultiply;
             break;
         case BlendMethod::Overlay:
             surface->blender = opBlendOverlay;
@@ -606,7 +606,10 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
     cmp->w = cmp->compositor->image.w;
     cmp->h = cmp->compositor->image.h;
 
-    rasterClear(cmp, x, y, w, h);
+    /* TODO: Currently, only blending might work.
+       Blending and composition must be handled together. */
+    auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000;
+    rasterClear(cmp, x, y, w, h, color);
 
     //Switch render target
     surface = cmp;

+ 1 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h

@@ -46,7 +46,7 @@ public:
     RenderRegion region(RenderData data) override;
     RenderRegion viewport() override;
     bool viewport(const RenderRegion& vp) override;
-    bool blend(BlendMethod method, bool direct) override;
+    bool blend(BlendMethod method) override;
     ColorSpace colorSpace() override;
     const Surface* mainSurface() override;
 

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgPicture.cpp

@@ -73,7 +73,7 @@ bool Picture::Impl::needComposition(uint8_t opacity)
 bool Picture::Impl::render(RenderMethod* renderer)
 {
     bool ret = false;
-    renderer->blend(picture->blend(), true);
+    renderer->blend(picture->blend());
 
     if (surface) return renderer->renderImage(rd);
     else if (paint) {

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -296,7 +296,7 @@ public:
     virtual RenderRegion region(RenderData data) = 0;
     virtual RenderRegion viewport() = 0;
     virtual bool viewport(const RenderRegion& vp) = 0;
-    virtual bool blend(BlendMethod method, bool direct = false) = 0;
+    virtual bool blend(BlendMethod method) = 0;
     virtual ColorSpace colorSpace() = 0;
     virtual const Surface* mainSurface() = 0;
 

+ 5 - 3
thirdparty/thorvg/src/renderer/tvgShape.h

@@ -51,17 +51,18 @@ struct Shape::Impl
 
     bool render(RenderMethod* renderer)
     {
+        if (!rd) return false;
+
         Compositor* cmp = nullptr;
-        bool ret;
 
-        renderer->blend(shape->blend(), !needComp);
+        renderer->blend(shape->blend());
 
         if (needComp) {
             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
             renderer->beginComposite(cmp, CompositeMethod::None, opacity);
         }
 
-        ret = renderer->renderShape(rd);
+        auto ret = renderer->renderShape(rd);
         if (cmp) renderer->endComposite(cmp);
         return ret;
     }
@@ -117,6 +118,7 @@ struct Shape::Impl
 
     RenderRegion bounds(RenderMethod* renderer)
     {
+        if (!rd) return {0, 0, 0, 0};
         return renderer->region(rd);
     }
 

+ 1 - 1
thirdparty/thorvg/src/renderer/tvgText.h

@@ -90,7 +90,7 @@ struct Text::Impl
     bool render(RenderMethod* renderer)
     {
         if (!loader) return true;
-        renderer->blend(paint->blend(), true);
+        renderer->blend(paint->blend());
         return PP(shape)->render(renderer);
     }
 

+ 1 - 1
thirdparty/thorvg/update-thorvg.sh

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-VERSION=0.14.9
+VERSION=0.14.10
 # Uncomment and set a git hash to use specific commit instead of tag.
 #GIT_COMMIT=