浏览代码

ThorVG: update from v0.12.1 to v0.12.3

https://github.com/thorvg/thorvg/releases/tag/v0.12.3

+ Full Changelog:
  https://github.com/thorvg/thorvg/compare/v0.12.1...v0.12.3

Godot-related SVG bug fixes:

+ svg_loader: Add missing transform functions skewX and skewY.
  thorvg/thorvg#1928
+ sw_engine: Rectified dash line drawing issue.
  thorvg/thorvg#1932

(cherry picked from commit 73589f6db604c9c93d3c5ab4cc2cd51f9628256f)
Martin Capitanio 1 年之前
父节点
当前提交
8f4e66d1a2

+ 1 - 1
thirdparty/README.md

@@ -825,7 +825,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.12.1 (d761e3c5622c0ffba2e5bb40da05751e2451e495, 2024)
+- Version: 0.12.3 (9d79f0ccef632fd3b43b8ea02def529b6a8d2288, 2024)
 - License: MIT
 
 Files extracted from upstream source:

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

@@ -9,5 +9,5 @@
 // For internal debugging:
 //#define THORVG_LOG_ENABLED
 
-#define THORVG_VERSION_STRING "0.12.1"
+#define THORVG_VERSION_STRING "0.12.3"
 #endif

+ 70 - 0
thirdparty/thorvg/src/common/tvgLock.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2024 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_LOCK_H_
+#define _TVG_LOCK_H_
+
+#ifdef THORVG_THREAD_SUPPORT
+
+#include <mutex>
+
+namespace tvg {
+
+    struct Key
+    {
+        std::mutex mtx;
+    };
+
+    struct ScopedLock
+    {
+        Key* key = nullptr;
+
+        ScopedLock(Key& key)
+        {
+            key.mtx.lock();
+            this->key = &key;
+        }
+
+        ~ScopedLock()
+        {
+            key->mtx.unlock();
+        }
+    };
+
+}
+
+#else //THORVG_THREAD_SUPPORT
+
+namespace tvg {
+
+    struct Key {};
+
+    struct ScopedLock
+    {
+        ScopedLock(Key& key) {}
+    };
+
+}
+
+#endif //THORVG_THREAD_SUPPORT
+
+#endif //_TVG_LOCK_H_

+ 18 - 34
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -683,32 +683,6 @@ static constexpr struct
 };
 
 
-static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
-{
-    auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
-    auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
-    auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
-
-    auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
-    auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
-    auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
-
-    auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
-    auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
-    auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
-
-    dst->e11 = a11;
-    dst->e12 = a12;
-    dst->e13 = a13;
-    dst->e21 = a21;
-    dst->e22 = a22;
-    dst->e23 = a23;
-    dst->e31 = a31;
-    dst->e32 = a32;
-    dst->e33 = a33;
-}
-
-
 /* parse transform attribute
  * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
  */
@@ -751,14 +725,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
         if (state == MatrixState::Matrix) {
             if (ptCount != 6) goto error;
             Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
-            _matrixCompose(matrix, &tmp, matrix);
+            *matrix = mathMultiply(matrix, &tmp);
         } else if (state == MatrixState::Translate) {
             if (ptCount == 1) {
                 Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
             } else if (ptCount == 2) {
                 Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
             } else goto error;
         } else if (state == MatrixState::Rotate) {
             //Transform to signed.
@@ -768,14 +742,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
             auto s = sinf(points[0] * (M_PI / 180.0));
             if (ptCount == 1) {
                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
             } else if (ptCount == 3) {
                 Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
                 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
                 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
-                _matrixCompose(matrix, &tmp, matrix);
+                *matrix = mathMultiply(matrix, &tmp);
             } else {
                 goto error;
             }
@@ -785,7 +759,17 @@ static Matrix* _parseTransformationMatrix(const char* value)
             auto sy = sx;
             if (ptCount == 2) sy = points[1];
             Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
-            _matrixCompose(matrix, &tmp, matrix);
+            *matrix = mathMultiply(matrix, &tmp);
+        } else if (state == MatrixState::SkewX) {
+            if (ptCount != 1) goto error;
+            auto deg = tanf(points[0] * (M_PI / 180.0));
+            Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
+            *matrix = mathMultiply(matrix, &tmp);
+        } else if (state == MatrixState::SkewY) {
+            if (ptCount != 1) goto error;
+            auto deg = tanf(points[0] * (M_PI / 180.0));
+            Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
+            *matrix = mathMultiply(matrix, &tmp);
         }
     }
     return matrix;

+ 15 - 13
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -257,8 +257,8 @@ static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride,
     auto ry2 = ry + 1;
     if (ry2 >= h) ry2 = h - 1;
 
-    auto dx = static_cast<size_t>((sx - rx) * 255.0f);
-    auto dy = static_cast<size_t>((sy - ry) * 255.0f);
+    auto dx = static_cast<uint8_t>((sx - rx) * 255.0f);
+    auto dy = static_cast<uint8_t>((sy - ry) * 255.0f);
 
     auto c1 = img[rx + ry * w];
     auto c2 = img[rx2 + ry * w];
@@ -281,21 +281,23 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
     int32_t maxx = (int32_t)sx + n;
     if (maxx >= (int32_t)w) maxx = w;
 
+    int32_t inc = (n / 2) + 1;
+    n = 0;
+
     auto src = img + minx + miny * stride;
 
-    for (auto y = miny; y < maxy; ++y) {
+    for (auto y = miny; y < maxy; y += inc) {
         auto p = src;
-        for (auto x = minx; x < maxx; ++x, ++p) {
-            c[0] += *p >> 24;
-            c[1] += (*p >> 16) & 0xff;
-            c[2] += (*p >> 8) & 0xff;
-            c[3] += *p & 0xff;
+        for (auto x = minx; x < maxx; x += inc, p += inc) {
+            c[0] += A(*p);
+            c[1] += C1(*p);
+            c[2] += C2(*p);
+            c[3] += C3(*p);
+            ++n;
         }
-        src += stride;
+        src += (stride * inc);
     }
 
-    n = (maxy - miny) * (maxx - minx);
-
     c[0] /= n;
     c[1] /= n;
     c[2] /= n;
@@ -1855,7 +1857,7 @@ void rasterUnpremultiply(Surface* surface)
 
 void rasterPremultiply(Surface* surface)
 {
-    unique_lock<mutex> lock{surface->mtx};
+    ScopedLock lock(surface->key);
     if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return;
     surface->premultiplied = true;
 
@@ -1936,7 +1938,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con
 
 bool rasterConvertCS(Surface* surface, ColorSpace to)
 {
-    unique_lock<mutex> lock{surface->mtx};
+    ScopedLock lock(surface->key);
     if (surface->cs == to) return true;
 
     //TOOD: Support SIMD accelerations

+ 4 - 4
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h

@@ -528,8 +528,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
                     vv = (int) v;
                     if (vv >= sh) continue;
 
-                    ar = (int)(255 * (1 - modff(u, &iptr)));
-                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
+                    ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
                     iru = uu + 1;
                     irv = vv + 1;
 
@@ -576,8 +576,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
                     uu = (int) u;
                     vv = (int) v;
 
-                    ar = (int)(255 * (1 - modff(u, &iptr)));
-                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
+                    ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
                     iru = uu + 1;
                     irv = vv + 1;
 

+ 2 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp

@@ -425,6 +425,8 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h,
 {
     if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false;
 
+    clearCompositors();
+
     if (!surface) surface = new SwSurface;
 
     surface->data = data;
@@ -474,7 +476,6 @@ bool SwRenderer::postRender()
     }
     tasks.clear();
 
-    clearCompositors();
     return true;
 }
 

+ 7 - 38
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -64,12 +64,16 @@ static void _outlineEnd(SwOutline& outline)
 {
     if (outline.pts.empty()) return;
     outline.cntrs.push(outline.pts.count - 1);
+    outline.closed.push(false);
 }
 
 
 static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
 {
-    if (outline.pts.count > 0) outline.cntrs.push(outline.pts.count - 1);
+    if (outline.pts.count > 0) {
+        outline.cntrs.push(outline.pts.count - 1);
+        outline.closed.push(false);
+    }
 
     outline.pts.push(mathTransform(to, transform));
     outline.types.push(SW_CURVE_TYPE_POINT);
@@ -128,7 +132,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
             _outlineLineTo(*dash.outline, to, transform);
         }
     } else {
-        while (len > dash.curLen) {
+        while (len - dash.curLen > 0.0001f) {
             Line left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
@@ -185,7 +189,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
         }
     } else {
-        while (len > dash.curLen) {
+        while ((len - dash.curLen) > 0.0001f) {
             Bezier left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
@@ -315,21 +319,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
 
     dash.outline = mpoolReqDashOutline(mpool, tid);
 
-    //smart reservation
-    auto closeCnt = 0;
-    auto moveCnt = 0;
-
-    for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
-        if (*cmd == PathCommand::Close) ++closeCnt;
-        else if (*cmd == PathCommand::MoveTo) ++moveCnt;
-    }
-
-    //No idea exact count.... Reserve Approximitely 20x...
-    //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
-    dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1));
-    dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1));
-    dash.outline->cntrs.grow(20 * (moveCnt + 1));
-
     while (cmdCnt-- > 0) {
         switch (*cmds) {
             case PathCommand::Close: {
@@ -435,29 +424,9 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
     //No actual shape data
     if (cmdCnt == 0 || ptsCnt == 0) return false;
 
-    //smart reservation
-    auto moveCnt = 0;
-    auto closeCnt = 0;
-
-    for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
-        if (*cmd == PathCommand::Close) ++closeCnt;
-        else if (*cmd == PathCommand::MoveTo) ++moveCnt;
-    }
-
     shape->outline = mpoolReqOutline(mpool, tid);
     auto outline = shape->outline;
 
-    //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
-    outline->pts.grow(ptsCnt + closeCnt + 1);
-    outline->types.grow(ptsCnt + closeCnt + 1);
-    outline->cntrs.grow(moveCnt + 1);
-
-    //Dash outlines are always opened.
-    //Only normal outlines use this information, it sholud be same to their contour counts.
-    outline->closed.reserve(outline->cntrs.reserved);
-
-    memset(outline->closed.data, 0x0, sizeof(bool) * outline->closed.reserved);
-
     //Generate Outlines
     while (cmdCnt-- > 0) {
         switch (*cmds) {

+ 9 - 8
thirdparty/thorvg/src/renderer/tvgLoader.cpp

@@ -24,6 +24,7 @@
 
 #include "tvgInlist.h"
 #include "tvgLoader.h"
+#include "tvgLock.h"
 
 #ifdef THORVG_SVG_LOADER_SUPPORT
     #include "tvgSvgLoader.h"
@@ -65,7 +66,7 @@ uint64_t HASH_KEY(const char* data, uint64_t size)
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-static mutex mtx;
+static Key key;
 static Inlist<LoadModule> _activeLoaders;
 
 
@@ -211,7 +212,7 @@ static LoadModule* _findByType(const string& mimeType)
 
 static LoadModule* _findFromCache(const string& path)
 {
-    unique_lock<mutex> lock{mtx};
+    ScopedLock lock(key);
 
     auto loader = _activeLoaders.head;
 
@@ -231,7 +232,7 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
     auto type = _convert(mimeType);
     if (type == FileType::Unknown) return nullptr;
 
-    unique_lock<mutex> lock{mtx};
+    ScopedLock lock(key);
     auto loader = _activeLoaders.head;
 
     auto key = HASH_KEY(data, size);
@@ -279,7 +280,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
     if (!loader) return false;
     if (loader->close()) {
         {
-            unique_lock<mutex> lock{mtx};
+            ScopedLock lock(key);
             _activeLoaders.remove(loader);
         }
         delete(loader);
@@ -298,7 +299,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
         if (loader->open(path)) {
             loader->hashpath = strdup(path.c_str());
             {
-                unique_lock<mutex> lock{mtx};
+                ScopedLock lock(key);
                 _activeLoaders.back(loader);
             }
             return loader;
@@ -340,7 +341,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
         if (auto loader = _findByType(mimeType)) {
             if (loader->open(data, size, copy)) {
                 loader->hashkey = HASH_KEY(data, size);                
-                unique_lock<mutex> lock{mtx};
+                ScopedLock lock(key);
                 _activeLoaders.back(loader);
                 return loader;
             } else {
@@ -356,7 +357,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
                 if (loader->open(data, size, copy)) {
                     loader->hashkey = HASH_KEY(data, size);
                     {
-                        unique_lock<mutex> lock{mtx};
+                        ScopedLock lock(key);
                         _activeLoaders.back(loader);
                     }
                     return loader;
@@ -379,7 +380,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool
     if (loader->open(data, w, h, copy)) {
         loader->hashkey = HASH_KEY((const char*)data, w * h);
         {
-            unique_lock<mutex> lock{mtx};
+            ScopedLock lock(key);
             _activeLoaders.back(loader);
         }
         return loader;

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

@@ -23,9 +23,9 @@
 #ifndef _TVG_RENDER_H_
 #define _TVG_RENDER_H_
 
-#include <mutex>
 #include "tvgCommon.h"
 #include "tvgArray.h"
+#include "tvgLock.h"
 
 namespace tvg
 {
@@ -54,7 +54,7 @@ struct Surface
         uint32_t* buf32;            //for explicit 32bits channels
         uint8_t*  buf8;             //for explicit 8bits grayscale
     };
-    mutex mtx;                      //used for thread safety
+    Key key;                        //a reserved lock for the thread safety
     uint32_t stride = 0;
     uint32_t w = 0, h = 0;
     ColorSpace cs = ColorSpace::Unsupported;

+ 5 - 7
thirdparty/thorvg/src/renderer/tvgShape.cpp

@@ -130,11 +130,11 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
     auto ryKappa = ry * PATH_KAPPA;
 
     pImpl->grow(6, 13);
-    pImpl->moveTo(cx + rx, cy);
+    pImpl->moveTo(cx, cy - ry);
+    pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
     pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
     pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
     pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
-    pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
     pImpl->close();
 
     return Result::Success;
@@ -216,21 +216,19 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
         pImpl->lineTo(x, y + h);
         pImpl->close();
     //circle
-    } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
-        return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
     } else {
         auto hrx = rx * PATH_KAPPA;
         auto hry = ry * PATH_KAPPA;
         pImpl->grow(10, 17);
-        pImpl->moveTo(x + rx, y);
-        pImpl->lineTo(x + w - rx, y);
-        pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
+        pImpl->moveTo(x + w, y + ry);
         pImpl->lineTo(x + w, y + h - ry);
         pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
         pImpl->lineTo(x + rx, y + h);
         pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
         pImpl->lineTo(x, y + ry);
         pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
+        pImpl->lineTo(x + w - rx, y);
+        pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
         pImpl->close();
     }
 

+ 41 - 19
thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp

@@ -20,19 +20,28 @@
  * SOFTWARE.
  */
 
-#include <thread>
-#include <atomic>
-#include <condition_variable>
 #include "tvgArray.h"
 #include "tvgInlist.h"
 #include "tvgTaskScheduler.h"
 
+#ifdef THORVG_THREAD_SUPPORT
+    #include <thread>
+    #include <atomic>
+#endif
+
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
 namespace tvg {
 
+struct TaskSchedulerImpl;
+static TaskSchedulerImpl* inst = nullptr;
+
+#ifdef THORVG_THREAD_SUPPORT
+
+static thread_local bool _async = true;
+
 struct TaskQueue {
     Inlist<Task>             taskDeque;
     mutex                    mtx;
@@ -61,7 +70,7 @@ struct TaskQueue {
     void complete()
     {
         {
-            unique_lock<mutex> lock{mtx};
+            lock_guard<mutex> lock{mtx};
             done = true;
         }
         ready.notify_all();
@@ -84,7 +93,7 @@ struct TaskQueue {
     void push(Task* task)
     {
         {
-            unique_lock<mutex> lock{mtx};
+            lock_guard<mutex> lock{mtx};
             taskDeque.back(task);
         }
         ready.notify_one();
@@ -92,25 +101,22 @@ struct TaskQueue {
 };
 
 
-static thread_local bool _async = true;  //toggle async tasking for each thread on/off
-
-
 struct TaskSchedulerImpl
 {
     Array<thread*>                 threads;
     Array<TaskQueue*>              taskQueues;
     atomic<uint32_t>               idx{0};
 
-    TaskSchedulerImpl(unsigned threadCnt)
+    TaskSchedulerImpl(uint32_t threadCnt)
     {
         threads.reserve(threadCnt);
         taskQueues.reserve(threadCnt);
 
-        for (unsigned i = 0; i < threadCnt; ++i) {
+        for (uint32_t i = 0; i < threadCnt; ++i) {
             taskQueues.push(new TaskQueue);
             threads.push(new thread);
         }
-        for (unsigned i = 0; i < threadCnt; ++i) {
+        for (uint32_t i = 0; i < threadCnt; ++i) {
             *threads.data[i] = thread([&, i] { run(i); });
         }
     }
@@ -136,7 +142,7 @@ struct TaskSchedulerImpl
         //Thread Loop
         while (true) {
             auto success = false;
-            for (unsigned x = 0; x < threads.count * 2; ++x) {
+            for (uint32_t x = 0; x < threads.count * 2; ++x) {
                 if (taskQueues[(i + x) % threads.count]->tryPop(&task)) {
                     success = true;
                     break;
@@ -154,7 +160,7 @@ struct TaskSchedulerImpl
         if (threads.count > 0 && _async) {
             task->prepare();
             auto i = idx++;
-            for (unsigned n = 0; n < threads.count; ++n) {
+            for (uint32_t n = 0; n < threads.count; ++n) {
                 if (taskQueues[(i + n) % threads.count]->tryPush(task)) return;
             }
             taskQueues[i % threads.count]->push(task);
@@ -163,17 +169,33 @@ struct TaskSchedulerImpl
             task->run(0);
         }
     }
+
+    uint32_t threadCnt()
+    {
+        return threads.count;
+    }
 };
 
-}
+#else //THORVG_THREAD_SUPPORT
 
-static TaskSchedulerImpl* inst = nullptr;
+static bool _async = true;
+
+struct TaskSchedulerImpl
+{
+    TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {}
+    void request(Task* task) { task->run(0); }
+    uint32_t threadCnt() { return 0; }
+};
+
+#endif //THORVG_THREAD_SUPPORT
+
+} //namespace
 
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
 
-void TaskScheduler::init(unsigned threads)
+void TaskScheduler::init(uint32_t threads)
 {
     if (inst) return;
     inst = new TaskSchedulerImpl(threads);
@@ -182,7 +204,6 @@ void TaskScheduler::init(unsigned threads)
 
 void TaskScheduler::term()
 {
-    if (!inst) return;
     delete(inst);
     inst = nullptr;
 }
@@ -194,14 +215,15 @@ void TaskScheduler::request(Task* task)
 }
 
 
-unsigned TaskScheduler::threads()
+uint32_t TaskScheduler::threads()
 {
-    if (inst) return inst->threads.count;
+    if (inst) return inst->threadCnt();
     return 0;
 }
 
 
 void TaskScheduler::async(bool on)
 {
+    //toggle async tasking for each thread on/off
     _async = on;
 }

+ 33 - 13
thirdparty/thorvg/src/renderer/tvgTaskScheduler.h

@@ -25,22 +25,13 @@
 
 #include <mutex>
 #include <condition_variable>
+
 #include "tvgCommon.h"
 #include "tvgInlist.h"
 
-namespace tvg
-{
-
-struct Task;
+namespace tvg {
 
-struct TaskScheduler
-{
-    static unsigned threads();
-    static void init(unsigned threads);
-    static void term();
-    static void request(Task* task);
-    static void async(bool on);
-};
+#ifdef THORVG_THREAD_SUPPORT
 
 struct Task
 {
@@ -86,7 +77,36 @@ private:
     friend struct TaskSchedulerImpl;
 };
 
-}
+#else  //THORVG_THREAD_SUPPORT
+
+struct Task
+{
+public:
+    INLIST_ITEM(Task);
+
+    virtual ~Task() = default;
+    void done() {}
+
+protected:
+    virtual void run(unsigned tid) = 0;
+
+private:
+    friend struct TaskSchedulerImpl;
+};
+
+#endif  //THORVG_THREAD_SUPPORT
+
+
+struct TaskScheduler
+{
+    static uint32_t threads();
+    static void init(uint32_t threads);
+    static void term();
+    static void request(Task* task);
+    static void async(bool on);
+};
+
+}  //namespace
 
 #endif //_TVG_TASK_SCHEDULER_H_
  

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

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-VERSION=0.12.1
+VERSION=0.12.3
 
 cd thirdparty/thorvg/ || true
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/