Pārlūkot izejas kodu

Merge pull request #105093 from Chubercik/thorvg-0.15.12

thorvg: Update to 0.15.12
Thaddeus Crews 4 mēneši atpakaļ
vecāks
revīzija
e90fb666a2

+ 2 - 1
thirdparty/README.md

@@ -955,7 +955,7 @@ Patches:
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.15.11 (61360cf6db0a05a8dd2ebdcc44d4cbbc315692ec, 2025)
+- Version: 0.15.12 (91bd6f35b94e92abfc1a320632e66cd124943524, 2025)
 - License: MIT
 
 Files extracted from upstream source:
@@ -966,6 +966,7 @@ Files extracted from upstream source:
 Patches:
 
 - `0001-revert-tvglines-bezier-precision.patch` (GH-96658)
+- `0002-png-explicit-variable-scope.patch` (GH-105093)
 
 
 ## tinyexr

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

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

+ 13 - 0
thirdparty/thorvg/patches/0002-png-explicit-variable-scope.patch

@@ -0,0 +1,13 @@
+diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
+index 71bf25a62b..c362403125 100644
+--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
++++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
+@@ -88,7 +88,7 @@ bool PngLoader::read()
+ 
+     if (w == 0 || h == 0) return false;
+ 
+-    if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
++    if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) {
+         image->format = PNG_FORMAT_BGRA;
+         surface.cs = ColorSpace::ARGB8888S;
+     } else {

+ 1 - 1
thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp

@@ -88,7 +88,7 @@ bool PngLoader::read()
 
     if (w == 0 || h == 0) return false;
 
-    if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
+    if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) {
         image->format = PNG_FORMAT_BGRA;
         surface.cs = ColorSpace::ARGB8888S;
     } else {

+ 1 - 1
thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp

@@ -56,7 +56,7 @@ WebpLoader::WebpLoader() : ImageLoader(FileType::Webp)
 
 WebpLoader::~WebpLoader()
 {
-    this->done();
+    done();
 
     if (freeData) free(data);
     data = nullptr;

+ 1 - 0
thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp

@@ -63,6 +63,7 @@ JpgLoader::JpgLoader() : ImageLoader(FileType::Jpg)
 
 JpgLoader::~JpgLoader()
 {
+    done();
     clear();
     free(surface.buf8);
 }

+ 60 - 1
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -405,6 +405,27 @@ static char* _idFromUrl(const char* url)
 }
 
 
+static size_t _srcFromUrl(const char* url, char*& src)
+{
+    src = (char*)strchr(url, '(');
+    auto close = strchr(url, ')');
+    if (!src || !close || src >= close) return 0;
+
+    src = strchr(src, '\'');
+    if (!src || src >= close) return 0;
+    ++src;
+
+    close = strchr(src, '\'');
+    if (!close || close == src) return 0;
+    --close;
+
+    while (src < close && *src == ' ') ++src;
+    while (src < close && *close == ' ') --close;
+
+    return close - src + 1;
+}
+
+
 static unsigned char _parseColor(const char* value, char** end)
 {
     float r;
@@ -2005,6 +2026,42 @@ static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const c
 }
 
 
+static char* _unquote(const char* str)
+{
+    auto len = str ? strlen(str) : 0;
+    if (len >= 2 && str[0] == '\'' && str[len - 1] == '\'') return strDuplicate(str + 1, len - 2);
+    return strdup(str);
+}
+
+
+static bool _attrParseFontFace(void* data, const char* key, const char* value)
+{
+    if (!key || !value) return false;
+
+    key = _skipSpace(key, nullptr);
+    value = _skipSpace(value, nullptr);
+
+    auto loader = (SvgLoaderData*)data;
+    auto& font = loader->fonts.last();
+
+    if (!strcmp(key, "font-family")) {
+        if (font.name) free(font.name);
+        font.name = _unquote(value);
+    } else if (!strcmp(key, "src")) {
+        font.srcLen = _srcFromUrl(value, font.src);
+    }
+
+    return true;
+}
+
+
+static void _createFontFace(SvgLoaderData* loader, const char* buf, unsigned bufLength, parseAttributes func)
+{
+    loader->fonts.push(FontFace());
+    func(buf, bufLength, _attrParseFontFace, loader);
+}
+
+
 static SvgNode* _getDefsNode(SvgNode* node)
 {
     if (!node) return nullptr;
@@ -3455,6 +3512,8 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte
             TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
         } else if (!strcmp(tag, "all")) {
             if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
+        } else if (!strcmp(tag, "@font-face")) { //css at-rule specifying font
+            _createFontFace(loader, attrs, attrsLength, simpleXmlParseW3CAttribute);
         } else if (!isIgnoreUnsupportedLogElements(tag)) {
             TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
         }
@@ -3829,7 +3888,7 @@ SvgLoader::SvgLoader() : ImageLoader(FileType::Svg)
 
 SvgLoader::~SvgLoader()
 {
-    this->done();
+    done();
     clear();
 }
 

+ 9 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h

@@ -555,6 +555,14 @@ struct SvgNodeIdPair
     char *id;
 };
 
+struct FontFace
+{
+    char* name = nullptr;
+    char* src = nullptr;
+    size_t srcLen = 0;
+    char* decoded = nullptr;
+};
+
 enum class OpenedTagType : uint8_t
 {
     Other = 0,
@@ -574,6 +582,7 @@ struct SvgLoaderData
     Array<SvgNodeIdPair> cloneNodes;
     Array<SvgNodeIdPair> nodesToStyle;
     Array<char*> images;        //embedded images
+    Array<FontFace> fonts;
     int level = 0;
     bool result = false;
     OpenedTagType openedTag = OpenedTagType::Other;

+ 37 - 8
thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp

@@ -573,8 +573,6 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
     if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
     auto picture = Picture::gen();
 
-    TaskScheduler::async(false);    //force to load a picture on the same thread
-
     const char* href = node->node.image.href;
     if (!strncmp(href, "data:", sizeof("data:") - 1)) {
         href += sizeof("data:") - 1;
@@ -586,14 +584,12 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
             auto size = b64Decode(href, strlen(href), &decoded);
             if (picture->load(decoded, size, mimetype, false) != Result::Success) {
                 free(decoded);
-                TaskScheduler::async(true);
                 return nullptr;
             }
         } else {
             auto size = svgUtilURLDecode(href, &decoded);
             if (picture->load(decoded, size, mimetype, false) != Result::Success) {
                 free(decoded);
-                TaskScheduler::async(true);
                 return nullptr;
             }
         }
@@ -605,7 +601,6 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
         const char *dot = strrchr(href, '.');
         if (dot && !strcmp(dot, ".svg")) {
             TVGLOG("SVG", "Embedded svg file is disabled.");
-            TaskScheduler::async(true);
             return nullptr;
         }
         string imagePath = href;
@@ -614,13 +609,10 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
             imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
         }
         if (picture->load(imagePath) != Result::Success) {
-            TaskScheduler::async(true);
             return nullptr;
         }
     }
 
-    TaskScheduler::async(true);
-
     float w, h;
     Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
     if (picture->size(&w, &h) == Result::Success && w  > 0 && h > 0) {
@@ -900,6 +892,41 @@ static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, floa
     if (!validHeight) h *= vBox.h;
 }
 
+
+static void _loadFonts(Array<FontFace>& fonts)
+{
+    if (fonts.empty()) return;
+
+    static constexpr struct {
+        const char* prefix;
+        size_t len;
+    } prefixes[] = {
+        {"data:font/ttf;base64,", sizeof("data:font/ttf;base64,") - 1},
+        {"data:application/font-ttf;base64,", sizeof("data:application/font-ttf;base64,") - 1}
+    };
+
+    for (uint32_t i = 0; i < fonts.count; ++i) {
+        auto p = &fonts[i];
+        if (!p->name) continue;
+
+        size_t shift = 0;
+        for (const auto& prefix : prefixes) {
+            if (p->srcLen > prefix.len && !memcmp(p->src, prefix.prefix, prefix.len)) {
+                shift = prefix.len;
+                break;
+            }
+        }
+        if (shift == 0) {
+            TVGLOG("SVG", "The embedded font \"%s\" data not loaded properly.", p->name);
+            continue;
+        }
+
+        auto size = b64Decode(p->src + shift, p->srcLen - shift, &p->decoded);
+
+        if (Text::load(p->name, p->decoded, size) != Result::Success) TVGERR("SVG", "Error while loading the ttf font named \"%s\".", p->name);
+    }
+}
+
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
@@ -910,6 +937,8 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
 
     if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr;
 
+    _loadFonts(loaderData.fonts);
+
     auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0);
 
     if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);

+ 8 - 0
thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp

@@ -483,6 +483,14 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
     do {
         char* sep = (char*)strchr(buf, ':');
         next = (char*)strchr(buf, ';');
+
+        if (auto src = strstr(buf, "src")) {//src tag from css font-face contains extra semicolon
+            if (src < sep) {
+                if (next + 1 < end) next = (char*)strchr(next + 1, ';');
+                else return true;
+            }
+        }
+
         if (sep >= end) {
             next = nullptr;
             sep = nullptr;

+ 4 - 4
thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp

@@ -314,10 +314,10 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
     }
 
     if (fastTrack) {
-        renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
-        renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));
-        renderRegion.min.y = static_cast<SwCoord>(nearbyint(yMin / 64.0f));
-        renderRegion.max.y = static_cast<SwCoord>(nearbyint(yMax / 64.0f));
+        renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
+        renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
+        renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
+        renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
     } else {
         renderRegion.min.x = xMin >> 6;
         renderRegion.max.x = (xMax + 63) >> 6;

+ 2 - 3
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -102,7 +102,6 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
 {
     Line cur = {dash.ptCur, *to};
     auto len = cur.length();
-
     if (tvg::zero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
     //draw the current line fully
@@ -117,7 +116,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
         }
     //draw the current line partially
     } else {
-        while (len - dash.curLen > 0.0001f) {
+        while (len - dash.curLen > DASH_PATTERN_THRESHOLD) {
             Line left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
@@ -178,7 +177,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
         }
     //draw the current line partially
     } else {
-        while ((len - dash.curLen) > 0.0001f) {
+        while ((len - dash.curLen) > DASH_PATTERN_THRESHOLD) {
             Bezier left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;

+ 15 - 6
thirdparty/thorvg/src/renderer/tvgLoadModule.h

@@ -23,6 +23,8 @@
 #ifndef _TVG_LOAD_MODULE_H_
 #define _TVG_LOAD_MODULE_H_
 
+#include <atomic>
+#include "tvgCommon.h"
 #include "tvgRender.h"
 #include "tvgInlist.h"
 
@@ -38,7 +40,7 @@ struct LoadModule
     };
 
     FileType type;                                  //current loader file type
-    uint16_t sharing = 0;                           //reference count
+    atomic<uint16_t> sharing{};                     //reference count
     bool readied = false;                           //read done already.
     bool pathcache = false;                         //cached by path
 
@@ -77,7 +79,7 @@ struct LoadModule
 
 struct ImageLoader : LoadModule
 {
-    static ColorSpace cs;                           //desired value
+    static atomic<ColorSpace> cs;                   //desired value
 
     float w = 0, h = 0;                             //default image size
     RenderSurface surface;
@@ -95,14 +97,21 @@ struct ImageLoader : LoadModule
 };
 
 
-struct FontLoader : LoadModule
+struct FontMetrics
 {
-    float scale = 1.0f;
+    //TODO: add necessary metrics
+    float minw;
+};
+
 
+struct FontLoader : LoadModule
+{
     FontLoader(FileType type) : LoadModule(type) {}
 
-    virtual bool request(Shape* shape, char* text) = 0;
-    virtual bool transform(Paint* paint, float fontSize, bool italic) = 0;
+    using LoadModule::read;
+
+    virtual bool read(Shape* shape, char* text, FontMetrics& out) = 0;
+    virtual float transform(Paint* paint, FontMetrics& mertrics, float fontSize, bool italic) = 0;
 };
 
 #endif //_TVG_LOAD_MODULE_H_

+ 14 - 13
thirdparty/thorvg/src/renderer/tvgLoader.cpp

@@ -22,6 +22,7 @@
 
 #include <string.h>
 
+#include <atomic>
 #include "tvgInlist.h"
 #include "tvgLoader.h"
 #include "tvgLock.h"
@@ -66,9 +67,10 @@ uintptr_t HASH_KEY(const char* data)
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-ColorSpace ImageLoader::cs = ColorSpace::ARGB8888;
+//TODO: remove it.
+atomic<ColorSpace> ImageLoader::cs{ColorSpace::ARGB8888};
 
-static Key key;
+static Key _key;
 static Inlist<LoadModule> _activeLoaders;
 
 
@@ -215,7 +217,7 @@ static LoadModule* _findByType(const string& mimeType)
 
 static LoadModule* _findFromCache(const string& path)
 {
-    ScopedLock lock(key);
+    ScopedLock lock(_key);
 
     auto loader = _activeLoaders.head;
 
@@ -235,11 +237,10 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
     auto type = _convert(mimeType);
     if (type == FileType::Unknown) return nullptr;
 
-    ScopedLock lock(key);
-    auto loader = _activeLoaders.head;
-
     auto key = HASH_KEY(data);
+    ScopedLock lock(_key);
 
+    auto loader = _activeLoaders.head;
     while (loader) {
         if (loader->type == type && loader->hashkey == key) {
             ++loader->sharing;
@@ -285,9 +286,9 @@ bool LoaderMgr::term()
 bool LoaderMgr::retrieve(LoadModule* loader)
 {
     if (!loader) return false;
+
     if (loader->close()) {
         if (loader->cached()) {
-            ScopedLock lock(key);
             _activeLoaders.remove(loader);
         }
         delete(loader);
@@ -316,7 +317,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
                 loader->hashpath = strdup(path.c_str());
                 loader->pathcache = true;
                 {
-                    ScopedLock lock(key);
+                    ScopedLock lock(_key);
                     _activeLoaders.back(loader);
                 }
             }
@@ -332,7 +333,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
                     loader->hashpath = strdup(path.c_str());
                     loader->pathcache = true;
                     {
-                        ScopedLock lock(key);
+                        ScopedLock lock(_key);
                         _activeLoaders.back(loader);
                     }
                 }
@@ -390,7 +391,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
             if (loader->open(data, size, copy)) {
                 if (allowCache) {
                     loader->hashkey = HASH_KEY(data);
-                    ScopedLock lock(key);
+                    ScopedLock lock(_key);
                     _activeLoaders.back(loader);
                 }
                 return loader;
@@ -407,7 +408,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
             if (loader->open(data, size, copy)) {
                 if (allowCache) {
                     loader->hashkey = HASH_KEY(data);
-                    ScopedLock lock(key);
+                    ScopedLock lock(_key);
                     _activeLoaders.back(loader);
                 }
                 return loader;
@@ -433,7 +434,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool
     if (loader->open(data, w, h, copy)) {
         if (!copy) {
             loader->hashkey = HASH_KEY((const char*)data);
-            ScopedLock lock(key);
+            ScopedLock lock(_key);
             _activeLoaders.back(loader);
         }
         return loader;
@@ -455,7 +456,7 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size,
     if (loader->open(data, size, copy)) {
         loader->hashpath = strdup(name);
         loader->pathcache = true;
-        ScopedLock lock(key);
+        ScopedLock lock(_key);
         _activeLoaders.back(loader);
         return loader;
     }

+ 2 - 0
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -35,6 +35,8 @@ namespace tvg
 using RenderData = void*;
 using pixel_t = uint32_t;
 
+#define DASH_PATTERN_THRESHOLD 0.001f
+
 enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
 enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8};  //Composition Purpose
 

+ 1 - 12
thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp

@@ -40,8 +40,6 @@ static TaskSchedulerImpl* inst = nullptr;
 
 #ifdef THORVG_THREAD_SUPPORT
 
-static thread_local bool _async = true;
-
 struct TaskQueue {
     Inlist<Task>             taskDeque;
     mutex                    mtx;
@@ -157,7 +155,7 @@ struct TaskSchedulerImpl
     void request(Task* task)
     {
         //Async
-        if (threads.count > 0 && _async) {
+        if (threads.count > 0) {
             task->prepare();
             auto i = idx++;
             for (uint32_t n = 0; n < threads.count; ++n) {
@@ -178,8 +176,6 @@ struct TaskSchedulerImpl
 
 #else //THORVG_THREAD_SUPPORT
 
-static bool _async = true;
-
 struct TaskSchedulerImpl
 {
     TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {}
@@ -220,10 +216,3 @@ uint32_t TaskScheduler::threads()
     if (inst) return inst->threadCnt();
     return 0;
 }
-
-
-void TaskScheduler::async(bool on)
-{
-    //toggle async tasking for each thread on/off
-    _async = on;
-}

+ 0 - 1
thirdparty/thorvg/src/renderer/tvgTaskScheduler.h

@@ -103,7 +103,6 @@ struct TaskScheduler
     static void init(uint32_t threads);
     static void term();
     static void request(Task* task);
-    static void async(bool on);
 };
 
 }  //namespace

+ 10 - 8
thirdparty/thorvg/src/renderer/tvgText.h

@@ -24,6 +24,7 @@
 #define _TVG_TEXT_H
 
 #include <cstring>
+#include "tvgMath.h"
 #include "tvgShape.h"
 #include "tvgFill.h"
 #include "tvgLoader.h"
@@ -33,6 +34,7 @@ struct Text::Impl
     FontLoader* loader = nullptr;
     Text* paint;
     Shape* shape;
+    FontMetrics metrics;
     char* utf8 = nullptr;
     float fontSize;
     bool italic = false;
@@ -40,6 +42,7 @@ struct Text::Impl
 
     Impl(Text* p) : paint(p), shape(Shape::gen().release())
     {
+        shape->fill(FillRule::EvenOdd);
     }
 
     ~Impl()
@@ -94,27 +97,26 @@ struct Text::Impl
         return PP(shape)->render(renderer);
     }
 
-    bool load()
+    float load()
     {
-        if (!loader) return false;
+        if (!loader) return 0.0f;
 
-        loader->request(shape, utf8);
         //reload
         if (changed) {
-            loader->read();
+            loader->read(shape, utf8, metrics);
             changed = false;
         }
-        return loader->transform(shape, fontSize, italic);
+        return loader->transform(shape, metrics, fontSize, italic);
     }
 
     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
     {
-        if (!load()) return nullptr;
+        auto scale = 1.0f / load();
+        if (tvg::zero(scale)) return nullptr;
 
         //transform the gradient coordinates based on the final scaled font.
         auto fill = P(shape)->rs.fill;
         if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) {
-            auto scale = 1.0f / loader->scale;
             if (fill->type() == Type::LinearGradient) {
                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
                 P(static_cast<LinearGradient*>(fill))->y1 *= scale;
@@ -134,7 +136,7 @@ struct Text::Impl
 
     bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
     {
-        if (!load()) return false;
+        if (load() == 0.0f) return false;
         PP(shape)->bounds(x, y, w, h, true, true, false);
         return true;
     }

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

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