Quellcode durchsuchen

thorvg: Update to 0.15.10

Jakub Marcowski vor 5 Monaten
Ursprung
Commit
0c15c106e0

+ 1 - 2
thirdparty/README.md

@@ -944,7 +944,7 @@ Patches:
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.15.8 (bd8c2fca7663a22fba7a339937cb60f2f6247a2e, 2025)
+- Version: 0.15.10 (bca94d244c67f573c6eddc27d783d9a6b1ef2f1b, 2025)
 - License: MIT
 
 Files extracted from upstream source:
@@ -955,7 +955,6 @@ Files extracted from upstream source:
 Patches:
 
 - `0001-revert-tvglines-bezier-precision.patch` (GH-96658)
-- `0002-gcc15-include-fix.patch` (GH-102022)
 
 
 ## tinyexr

+ 1 - 0
thirdparty/thorvg/AUTHORS

@@ -38,3 +38,4 @@ Elliott Sales de Andrade <[email protected]>
 Kelly Loh <[email protected]>
 Dragoș Tiselice <[email protected]>
 Marcin Baszczewski <[email protected]>
+Fabian Blatz <[email protected]>

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

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

+ 0 - 12
thirdparty/thorvg/patches/0002-gcc15-include-fix.patch

@@ -1,12 +0,0 @@
-diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
-index 8e3ab4e6ce..f515a03136 100644
---- a/thirdparty/thorvg/inc/thorvg.h
-+++ b/thirdparty/thorvg/inc/thorvg.h
-@@ -1,6 +1,7 @@
- #ifndef _THORVG_H_
- #define _THORVG_H_
- 
-+#include <cstdint>
- #include <functional>
- #include <memory>
- #include <string>

+ 1 - 1
thirdparty/thorvg/src/common/tvgStr.cpp

@@ -183,7 +183,7 @@ float strToFloat(const char *nPtr, char **endPtr)
         auto scale = 1.0f;
 
         while (exponentPart >= 8U) {
-            scale *= 1E8;
+            scale *= 1E8f;
             exponentPart -= 8U;
         }
         while (exponentPart > 0U) {

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

@@ -200,7 +200,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
     isPercentage = false;
 
     if (strstr(str, "%")) {
-        parsedValue = parsedValue / 100.0;
+        parsedValue = parsedValue / 100.0f;
         isPercentage = true;
     }
     else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
@@ -225,7 +225,7 @@ static float _toOffset(const char* str)
     auto ptr = strstr(str, "%");
 
     if (ptr) {
-        parsedValue = parsedValue / 100.0;
+        parsedValue = parsedValue / 100.0f;
         if (end != ptr || (end + 1) != strEnd) return 0;
     } else if (end != strEnd) return 0;
 

+ 8 - 8
thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp

@@ -402,10 +402,10 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         case 'q':
         case 'Q': {
             Point p[3];
-            float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
-            float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
-            float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
-            float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
+            float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0f / 3.0f);
+            float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0f / 3.0f);
+            float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0f / 3.0f);
+            float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0f / 3.0f);
             cmds->push(PathCommand::CubicTo);
             p[0] = {ctrl_x0, ctrl_y0};
             p[1] = {ctrl_x1, ctrl_y1};
@@ -428,10 +428,10 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
             } else {
                 ctrl = *cur;
             }
-            float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
-            float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
-            float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
-            float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
+            float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0f / 3.0f);
+            float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0f / 3.0f);
+            float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0f / 3.0f);
+            float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0f / 3.0f);
             cmds->push(PathCommand::CubicTo);
             p[0] = {ctrl_x0, ctrl_y0};
             p[1] = {ctrl_x1, ctrl_y1};

+ 1 - 1
thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp

@@ -26,7 +26,7 @@
 
 #ifdef _WIN32
     #include <malloc.h>
-#elif defined(__linux__)
+#elif defined(__linux__) || defined(__ZEPHYR__)
     #include <alloca.h>
 #else
     #include <stdlib.h>

+ 28 - 20
thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h

@@ -23,8 +23,8 @@
 #ifndef _TVG_SW_COMMON_H_
 #define _TVG_SW_COMMON_H_
 
-#include <algorithm>
 #include "tvgCommon.h"
+#include "tvgMath.h"
 #include "tvgRender.h"
 
 #define SW_CURVE_TYPE_POINT 0
@@ -379,10 +379,13 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
 
 static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
-    //A + B - 2AB
-    auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
-    auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
-    auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
+    // (s + d) - (2 * s * d)
+    auto c1 = C1(s) + C1(d) - 2 * MULTIPLY(C1(s), C1(d));
+    tvg::clamp(c1, 0, 255);
+    auto c2 = C2(s) + C2(d) - 2 * MULTIPLY(C2(s), C2(d));
+    tvg::clamp(c2, 0, 255);
+    auto c3 = C3(s) + C3(d) - 2 * MULTIPLY(C3(s), C3(d));
+    tvg::clamp(c3, 0, 255);
     return JOIN(255, c1, c2, c3);
 }
 
@@ -444,10 +447,10 @@ static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
 static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
     // d / (1 - s)
-    auto is = 0xffffffff - s;
-    auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d);
-    auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d);
-    auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d);
+    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);
     return JOIN(255, c1, c2, c3);
 }
 
@@ -455,14 +458,17 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
 {
     // 1 - (1 - d) / s
     auto id = 0xffffffff - d;
-    auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id));
-    auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id));
-    auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id));
+    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);
+
     return JOIN(255, c1, c2, c3);
 }
 
 static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
 {
+    // if (s < sa), (2 * s * d)
+    // else (sa * da) - 2 * (da - s) * (sa - d)
     auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
     auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
     auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
@@ -472,9 +478,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 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
-    auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
-    auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
+    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));
     return JOIN(255, c1, c2, c3);
 }
 
@@ -578,14 +584,16 @@ bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
 uint32_t rasterUnpremultiply(uint32_t data);
 
 bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
-bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect);
+bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect);
+void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform);
 bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct);
-bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
-bool effectFillPrepare(RenderEffectFill* effect);
+bool effectDropShadowRegion(RenderEffectDropShadow* effect);
+void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform);
+void effectFillUpdate(RenderEffectFill* effect);
 bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
-bool effectTintPrepare(RenderEffectTint* effect);
+void effectTintUpdate(RenderEffectTint* effect);
 bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
-bool effectTritonePrepare(RenderEffectTritone* effect);
+void effectTritoneUpdate(RenderEffectTritone* effect);
 bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct);
 
 #endif /* _TVG_SW_COMMON_H_ */

+ 74 - 64
thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp

@@ -32,39 +32,27 @@ struct SwGaussianBlur
     static constexpr int MAX_LEVEL = 3;
     int level;
     int kernel[MAX_LEVEL];
+    int extends;
 };
 
 
-static void _gaussianExtendRegion(RenderRegion& region, int extra, int8_t direction)
+static inline int _gaussianEdgeWrap(int end, int idx)
 {
-    //bbox region expansion for feathering
-    if (direction != 2) {
-        region.x = -extra;
-        region.w = extra * 2;
-    }
-    if (direction != 1) {
-        region.y = -extra;
-        region.h = extra * 2;
-    }
-}
-
-
-static int _gaussianEdgeWrap(int end, int idx)
-{
-    auto r = idx % end;
-    return (r < 0) ? end + r : r;
+    auto r = idx % (end + 1);
+    return (r < 0) ? (end + 1) + r : r;
 }
 
 
-static int _gaussianEdgeExtend(int end, int idx)
+static inline int _gaussianEdgeExtend(int end, int idx)
 {
     if (idx < 0) return 0;
-    else if (idx >= end) return end - 1;
+    else if (idx > end) return end;
     return idx;
 }
 
 
-static int _gaussianRemap(int end, int idx, int border)
+template<int border>
+static inline int _gaussianRemap(int end, int idx)
 {
     if (border == 1) return _gaussianEdgeWrap(end, idx);
     return _gaussianEdgeExtend(end, idx);
@@ -72,7 +60,8 @@ static int _gaussianRemap(int end, int idx, int border)
 
 
 //TODO: SIMD OPTIMIZATION?
-static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, int border, bool flipped)
+template<int border = 0>
+static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
 {
     if (flipped) {
         src += (bbox.min.x * stride + bbox.min.y) << 2;
@@ -83,6 +72,7 @@ static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t
     }
 
     auto iarr = 1.0f / (dimension + dimension + 1);
+    auto end = w - 1;
 
     #pragma omp parallel for
     for (int y = 0; y < h; ++y) {
@@ -94,7 +84,7 @@ static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t
 
         //initial accumulation
         for (int x = l; x < r; ++x) {
-            auto id = (_gaussianRemap(w, x, border) + p) * 4;
+            auto id = (_gaussianRemap<border>(end, x) + p) * 4;
             acc[0] += src[id++];
             acc[1] += src[id++];
             acc[2] += src[id++];
@@ -102,16 +92,17 @@ static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t
         }
         //perform filtering
         for (int x = 0; x < w; ++x, ++r, ++l) {
-            auto rid = (_gaussianRemap(w, r, border) + p) * 4;
-            auto lid = (_gaussianRemap(w, l, border) + p) * 4;
+            auto rid = (_gaussianRemap<border>(end, r) + p) * 4;
+            auto lid = (_gaussianRemap<border>(end, l) + p) * 4;
             acc[0] += src[rid++] - src[lid++];
             acc[1] += src[rid++] - src[lid++];
             acc[2] += src[rid++] - src[lid++];
             acc[3] += src[rid] - src[lid];
-            dst[i++] = static_cast<uint8_t>(acc[0] * iarr + 0.5f);
-            dst[i++] = static_cast<uint8_t>(acc[1] * iarr + 0.5f);
-            dst[i++] = static_cast<uint8_t>(acc[2] * iarr + 0.5f);
-            dst[i++] = static_cast<uint8_t>(acc[3] * iarr + 0.5f);
+            //ignored rounding for the performance. It should be originally: acc[idx] * iarr + 0.5f
+            dst[i++] = static_cast<uint8_t>(acc[0] * iarr);
+            dst[i++] = static_cast<uint8_t>(acc[1] * iarr);
+            dst[i++] = static_cast<uint8_t>(acc[2] * iarr);
+            dst[i++] = static_cast<uint8_t>(acc[3] * iarr);
         }
     }
 }
@@ -142,24 +133,41 @@ static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
 }
 
 
-bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
+bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
+{
+    //bbox region expansion for feathering
+    auto& region = params->extend;
+    auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
+
+    if (params->direction != 2) {
+        region.x = -extra;
+        region.w = extra * 2;
+    }
+    if (params->direction != 1) {
+        region.y = -extra;
+        region.h = extra * 2;
+    }
+
+    return true;
+}
+
+
+void effectGaussianBlurUpdate(RenderEffectGaussianBlur* params, const Matrix& transform)
 {
-    auto rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
+    if (!params->rd) params->rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
+    auto rd = static_cast<SwGaussianBlur*>(params->rd);
 
-    auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
+    //compute box kernel sizes
+    auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
+    rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
 
     //invalid
-    if (extends == 0) {
-        free(rd);
-        return false;
+    if (rd->extends == 0) {
+        params->valid = false;
+        return;
     }
 
-    _gaussianExtendRegion(params->extend, extends, params->direction);
-
-    params->rd = rd;
     params->valid = true;
-
-    return true;
 }
 
 
@@ -184,7 +192,7 @@ bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffec
     //horizontal
     if (params->direction != 2) {
         for (int i = 0; i < data->level; ++i) {
-            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], params->border, false);
+            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], false);
             std::swap(front, back);
             swapped = !swapped;
         }
@@ -196,7 +204,7 @@ bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffec
         std::swap(front, back);
 
         for (int i = 0; i < data->level; ++i) {
-            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, h, w, bbox, data->kernel[i], params->border, true);
+            _gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, h, w, bbox, data->kernel[i], true);
             std::swap(front, back);
             swapped = !swapped;
         }
@@ -231,6 +239,7 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
         dst += (bbox.min.y * stride + bbox.min.x);
     }
     auto iarr = 1.0f / (dimension + dimension + 1);
+    auto end = w - 1;
 
     #pragma omp parallel for
     for (int y = 0; y < h; ++y) {
@@ -242,15 +251,16 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
 
         //initial accumulation
         for (int x = l; x < r; ++x) {
-            auto id = _gaussianEdgeExtend(w, x) + p;
+            auto id = _gaussianEdgeExtend(end, x) + p;
             acc += A(src[id]);
         }
         //perform filtering
         for (int x = 0; x < w; ++x, ++r, ++l) {
-            auto rid = _gaussianEdgeExtend(w, r) + p;
-            auto lid = _gaussianEdgeExtend(w, l) + p;
+            auto rid = _gaussianEdgeExtend(end, r) + p;
+            auto lid = _gaussianEdgeExtend(end, l) + p;
             acc += A(src[rid]) - A(src[lid]);
-            dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr + 0.5f));
+            //ignored rounding for the performance. It should be originally: acc * iarr
+            dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr));
         }
     }
 }
@@ -281,9 +291,13 @@ static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& r
 }
 
 
-static void _dropShadowExtendRegion(RenderRegion& region, int extra, SwPoint& offset)
+bool effectDropShadowRegion(RenderEffectDropShadow* params)
 {
     //bbox region expansion for feathering
+    auto& region = params->extend;
+    auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
+    auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
+
     region.x = -extra;
     region.w = extra * 2;
     region.y = -extra;
@@ -293,20 +307,24 @@ static void _dropShadowExtendRegion(RenderRegion& region, int extra, SwPoint& of
     region.y = std::min(region.y + (int32_t)offset.y, region.y);
     region.w += abs(offset.x);
     region.h += abs(offset.y);
+
+    return true;
 }
 
 
-bool effectDropShadowPrepare(RenderEffectDropShadow* params)
+void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transform)
 {
-    auto rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
+    if (!params->rd) params->rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
+    auto rd = static_cast<SwDropShadow*>(params->rd);
 
     //compute box kernel sizes
-    auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
+    auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
+    rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
 
     //invalid
-    if (extends == 0 || params->color[3] == 0) {
-        free(rd);
-        return false;
+    if (rd->extends == 0 || params->color[3] == 0) {
+        params->valid = false;
+        return;
     }
 
     //offset
@@ -317,13 +335,7 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
         rd->offset = {0, 0};
     }
 
-    //bbox region expansion for feathering
-    _dropShadowExtendRegion(params->extend, extends, rd->offset);
-
-    params->rd = rd;
     params->valid = true;
-
-    return true;
 }
 
 
@@ -405,10 +417,9 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
 /* Fill Implementation                                                  */
 /************************************************************************/
 
-bool effectFillPrepare(RenderEffectFill* params)
+void effectFillUpdate(RenderEffectFill* params)
 {
     params->valid = true;
-    return true;
 }
 
 
@@ -456,10 +467,9 @@ bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
 /* Tint Implementation                                                  */
 /************************************************************************/
 
-bool effectTintPrepare(RenderEffectTint* params)
+void effectTintUpdate(RenderEffectTint* params)
 {
     params->valid = true;
-    return true;
 }
 
 
@@ -529,10 +539,10 @@ static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
     }
 }
 
-bool effectTritonePrepare(RenderEffectTritone* params)
+
+void effectTritoneUpdate(RenderEffectTritone* params)
 {
     params->valid = true;
-    return true;
 }
 
 

+ 1 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -22,7 +22,7 @@
 
 #ifdef _WIN32
     #include <malloc.h>
-#elif defined(__linux__)
+#elif defined(__linux__) || defined(__ZEPHYR__)
     #include <alloca.h>
 #else
     #include <stdlib.h>

+ 30 - 229
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h

@@ -75,197 +75,8 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
 
 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;
-
-#if 0 //Enable it when GRAYSCALE image is supported
-    auto maskOp = _getMaskOp(surface->compositor->method);
-    auto direct = _direct(surface->compositor->method);
-    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->buf8;
-    int32_t sw = static_cast<int32_t>(image->stride);
-    int32_t sh = 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;
-    SwSpan* span = nullptr;         //used only when rle based.
-
-    if (!_arrange(image, region, yStart, yEnd)) return false;
-
-    //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;
-        }
-    }
-
-    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;
-
-        //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;
-
-        //Range allowed
-        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
-
-            //Perform subtexel pre-stepping on UV
-            dx = 1 - (_xa - x1);
-            u = _ua + dx * _dudx;
-            v = _va + dx * _dvdx;
-
-            x = x1;
-
-            auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
-            auto dst = &surface->buf8[y * surface->stride + x1];
-
-            if (opacity == 255) {
-                //Draw horizontal line
-                while (x++ < x2) {
-                    uu = (int) u;
-                    if (uu >= sw) continue;
-                    vv = (int) v;
-                    if (vv >= sh) continue;
-
-                    ar = (int)(255 * (1 - modff(u, &iptr)));
-                    ab = (int)(255 * (1 - modff(v, &iptr)));
-                    iru = uu + 1;
-                    irv = vv + 1;
-
-                    px = *(sbuf + (vv * sw) + uu);
-
-                    /* horizontal interpolate */
-                    if (iru < sw) {
-                        /* right pixel */
-                        int px2 = *(sbuf + (vv * sw) + iru);
-                        px = INTERPOLATE(px, px2, ar);
-                    }
-                    /* vertical interpolate */
-                    if (irv < sh) {
-                        /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
-
-                        /* horizontal interpolate */
-                        if (iru < sw) {
-                            /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
-                            px2 = INTERPOLATE(px2, px3, ar);
-                        }
-                        px = INTERPOLATE(px, px2, ab);
-                    }
-                    if (direct) {
-                        auto tmp = maskOp(px, *cmp, 0);  //not use alpha
-                        *dst = tmp + MULTIPLY(*dst, ~tmp);
-                        ++dst;
-                    } else {
-                        *cmp = maskOp(px, *cmp, ~px);
-                    }
-                    ++cmp;
-
-                    //Step UV horizontally
-                    u += _dudx;
-                    v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
-                }
-            } else {
-                //Draw horizontal line
-                while (x++ < x2) {
-                    uu = (int) u;
-                    if (uu >= sw) continue;
-                    vv = (int) v;
-                    if (vv >= sh) continue;
-
-                    ar = (int)(255 * (1 - modff(u, &iptr)));
-                    ab = (int)(255 * (1 - modff(v, &iptr)));
-                    iru = uu + 1;
-                    irv = vv + 1;
-
-                    px = *(sbuf + (vv * sw) + uu);
-
-                    /* horizontal interpolate */
-                    if (iru < sw) {
-                        /* right pixel */
-                        int px2 = *(sbuf + (vv * sw) + iru);
-                        px = INTERPOLATE(px, px2, ar);
-                    }
-                    /* vertical interpolate */
-                    if (irv < sh) {
-                        /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
-
-                        /* horizontal interpolate */
-                        if (iru < sw) {
-                            /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
-                            px2 = INTERPOLATE(px2, px3, ar);
-                        }
-                        px = INTERPOLATE(px, px2, ab);
-                    }
-
-                    if (direct) {
-                        auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
-                        *dst = tmp + MULTIPLY(*dst, ~tmp);
-                        ++dst;
-                    } else {
-                        auto tmp = MULTIPLY(px, opacity);
-                        *cmp = maskOp(tmp, *cmp, ~px);
-                    }
-                    ++cmp;
-
-                    //Step UV horizontally
-                    u += _dudx;
-                    v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
-                }
-            }
-        }
-
-        //Step along both edges
-        _xa += _dxdya;
-        _xb += _dxdyb;
-        _ua += _dudya;
-        _va += _dvdya;
-
-        if (!region && spanIdx >= image->rle->size) break;
-
-        ++y;
-    }
-    xa = _xa;
-    xb = _xb;
-    ua = _ua;
-    va = _va;
-
-    return true;
-#endif
 }
 
 
@@ -276,9 +87,8 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
     float _xa = xa, _xb = xb, _ua = ua, _va = va;
     auto sbuf = image->buf32;
     auto dbuf = surface->buf32;
-    int32_t sw = static_cast<int32_t>(image->stride);
-    int32_t sh = image->h;
-    int32_t dw = surface->stride;
+    int32_t sw = static_cast<int32_t>(image->w);
+    int32_t sh = static_cast<int32_t>(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;
@@ -335,7 +145,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
             u = _ua + dx * _dudx;
             v = _va + dx * _dvdx;
 
-            buf = dbuf + ((y * dw) + x1);
+            buf = dbuf + ((y * surface->stride) + x1);
 
             x = x1;
 
@@ -343,32 +153,32 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
                 //Draw horizontal line
                 while (x++ < x2) {
                     uu = (int) u;
-                    if (uu >= sw) continue;
                     vv = (int) v;
-                    if (vv >= sh) 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;
 
-                    px = *(sbuf + (vv * sw) + uu);
+                    px = *(sbuf + (vv * image->stride) + uu);
 
                     /* horizontal interpolate */
                     if (iru < sw) {
                         /* right pixel */
-                        int px2 = *(sbuf + (vv * sw) + iru);
+                        int px2 = *(sbuf + (vv * image->stride) + iru);
                         px = INTERPOLATE(px, px2, ar);
                     }
                     /* vertical interpolate */
                     if (irv < sh) {
                         /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
+                        int px2 = *(sbuf + (irv * image->stride) + uu);
 
                         /* horizontal interpolate */
                         if (iru < sw) {
                             /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
+                            int px3 = *(sbuf + (irv * image->stride) + iru);
                             px2 = INTERPOLATE(px2, px3, ar);
                         }
                         px = INTERPOLATE(px, px2, ab);
@@ -379,39 +189,37 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
                     //Step UV horizontally
                     u += _dudx;
                     v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
                 }
             } else {
                 //Draw horizontal line
                 while (x++ < x2) {
                     uu = (int) u;
-                    if (uu >= sw) continue;
                     vv = (int) v;
-                    if (vv >= sh) 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;
 
-                    px = *(sbuf + (vv * sw) + uu);
+                    px = *(sbuf + (vv * image->stride) + uu);
 
                     /* horizontal interpolate */
                     if (iru < sw) {
                         /* right pixel */
-                        int px2 = *(sbuf + (vv * sw) + iru);
+                        int px2 = *(sbuf + (vv * image->stride) + iru);
                         px = INTERPOLATE(px, px2, ar);
                     }
                     /* vertical interpolate */
                     if (irv < sh) {
                         /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
+                        int px2 = *(sbuf + (irv * image->stride) + uu);
 
                         /* horizontal interpolate */
                         if (iru < sw) {
                             /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
+                            int px3 = *(sbuf + (irv * image->stride) + iru);
                             px2 = INTERPOLATE(px2, px3, ar);
                         }
                         px = INTERPOLATE(px, px2, ab);
@@ -423,8 +231,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
                     //Step UV horizontally
                     u += _dudx;
                     v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
                 }
             }
         }
@@ -453,9 +259,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
     float _xa = xa, _xb = xb, _ua = ua, _va = va;
     auto sbuf = image->buf32;
     auto dbuf = surface->buf32;
-    int32_t sw = static_cast<int32_t>(image->stride);
-    int32_t sh = image->h;
-    int32_t dw = surface->stride;
+    int32_t sw = static_cast<int32_t>(image->w);
+    int32_t sh = static_cast<int32_t>(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;
@@ -517,7 +322,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
             u = _ua + dx * _dudx;
             v = _va + dx * _dvdx;
 
-            buf = dbuf + ((y * dw) + x1);
+            buf = dbuf + ((y * surface->stride) + x1);
 
             x = x1;
 
@@ -527,32 +332,32 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
                 //Draw horizontal line
                 while (x++ < x2) {
                     uu = (int) u;
-                    if (uu >= sw) continue;
                     vv = (int) v;
-                    if (vv >= sh) continue;
+
+                    if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
 
                     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 * sw) + iru);
+                        int px2 = *(sbuf + (vv * image->stride) + iru);
                         px = INTERPOLATE(px, px2, ar);
                     }
                     /* vertical interpolate */
                     if (irv < sh) {
                         /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
+                        int px2 = *(sbuf + (irv * image->stride) + uu);
 
                         /* horizontal interpolate */
                         if (iru < sw) {
                             /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
+                            int px3 = *(sbuf + (irv * image->stride) + iru);
                             px2 = INTERPOLATE(px2, px3, ar);
                         }
                         px = INTERPOLATE(px, px2, ab);
@@ -570,8 +375,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
                     //Step UV horizontally
                     u += _dudx;
                     v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
                 }
             } else {
                 //Draw horizontal line
@@ -579,30 +382,30 @@ 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;
+
                     ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
                     ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
                     iru = uu + 1;
                     irv = vv + 1;
 
-                    if (vv >= sh) continue;
-
                     px = *(sbuf + (vv * sw) + uu);
 
                     /* horizontal interpolate */
                     if (iru < sw) {
                         /* right pixel */
-                        int px2 = *(sbuf + (vv * sw) + iru);
+                        int px2 = *(sbuf + (vv * image->stride) + iru);
                         px = INTERPOLATE(px, px2, ar);
                     }
                     /* vertical interpolate */
                     if (irv < sh) {
                         /* bottom pixel */
-                        int px2 = *(sbuf + (irv * sw) + uu);
+                        int px2 = *(sbuf + (irv * image->stride) + uu);
 
                         /* horizontal interpolate */
                         if (iru < sw) {
                             /* bottom right pixel */
-                            int px3 = *(sbuf + (irv * sw) + iru);
+                            int px3 = *(sbuf + (irv * image->stride) + iru);
                             px2 = INTERPOLATE(px2, px3, ar);
                         }
                         px = INTERPOLATE(px, px2, ab);
@@ -620,8 +423,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
                     //Step UV horizontally
                     u += _dudx;
                     v += _dvdx;
-                    //range over?
-                    if ((uint32_t)v >= image->h) break;
                 }
             }
         }

+ 18 - 10
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp

@@ -574,7 +574,7 @@ SwSurface* SwRenderer::request(int channelSize, bool square)
         cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h);
         cmp->w = cmp->compositor->image.w = w;
         cmp->h = cmp->compositor->image.h = h;
-        cmp->compositor->image.stride = w;
+        cmp->stride = cmp->compositor->image.stride = w;
         cmp->compositor->image.direct = true;
         cmp->compositor->valid = true;
         cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
@@ -655,23 +655,31 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
 }
 
 
-bool SwRenderer::prepare(RenderEffect* effect)
+void SwRenderer::prepare(RenderEffect* effect, const Matrix& transform)
 {
     switch (effect->type) {
-        case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(effect));
-        case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect));
-        case SceneEffect::Fill: return effectFillPrepare(static_cast<RenderEffectFill*>(effect));
-        case SceneEffect::Tint: return effectTintPrepare(static_cast<RenderEffectTint*>(effect));
-        case SceneEffect::Tritone: return effectTritonePrepare(static_cast<RenderEffectTritone*>(effect));
-        default: return false;
+        case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast<RenderEffectGaussianBlur*>(effect), transform); break;
+        case SceneEffect::DropShadow: effectDropShadowUpdate(static_cast<RenderEffectDropShadow*>(effect), transform); break;
+        case SceneEffect::Fill: effectFillUpdate(static_cast<RenderEffectFill*>(effect)); break;
+        case SceneEffect::Tint: effectTintUpdate(static_cast<RenderEffectTint*>(effect)); break;
+        case SceneEffect::Tritone: effectTritoneUpdate(static_cast<RenderEffectTritone*>(effect)); break;
+        default: break;
     }
 }
 
 
-bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct)
+bool SwRenderer::region(RenderEffect* effect)
 {
-    if (!effect->valid) return false;
+    switch (effect->type) {
+        case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast<RenderEffectGaussianBlur*>(effect));
+        case SceneEffect::DropShadow: return effectDropShadowRegion(static_cast<RenderEffectDropShadow*>(effect));
+        default: return false;
+    }
+}
+
 
+bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool direct)
+{
     auto p = static_cast<SwCompositor*>(cmp);
 
     if (p->image.channelSize != sizeof(uint32_t)) {

+ 3 - 2
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h

@@ -60,8 +60,9 @@ public:
     bool endComposite(RenderCompositor* cmp) override;
     void clearCompositors();
 
-    bool prepare(RenderEffect* effect) override;
-    bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
+    void prepare(RenderEffect* effect, const Matrix& transform) override;
+    bool region(RenderEffect* effect) override;
+    bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
 
     static SwRenderer* gen();
     static bool init(uint32_t threads);

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

@@ -95,7 +95,7 @@ float Animation::duration() const noexcept
 
 Result Animation::segment(float begin, float end) noexcept
 {
-    if (begin < 0.0 || end > 1.0 || begin > end) return Result::InvalidArguments;
+    if (begin < 0.0f || end > 1.0f || begin > end) return Result::InvalidArguments;
 
     auto loader = pImpl->picture->pImpl->loader;
     if (!loader) return Result::InsufficientCondition;

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

@@ -76,7 +76,7 @@ struct Fill::Impl
         ret->pImpl->cnt = cnt;
         ret->pImpl->spread = spread;
         ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
-        memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
+        if (cnt > 0) memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
         if (transform) {
             ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
             *ret->pImpl->transform = *transform;

+ 4 - 3
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -352,7 +352,7 @@ struct RenderEffectTint : RenderEffect
         inst->white[0] = va_arg(args, int);
         inst->white[1] = va_arg(args, int);
         inst->white[2] = va_arg(args, int);
-        inst->intensity = (uint8_t)(va_arg(args, double) * 2.55f);
+        inst->intensity = (uint8_t)(va_arg(args, double) * 2.55);
         inst->type = SceneEffect::Tint;
         return inst;
     }
@@ -413,8 +413,9 @@ public:
     virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
     virtual bool endComposite(RenderCompositor* cmp) = 0;
 
-    virtual bool prepare(RenderEffect* effect) = 0;
-    virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0;
+    virtual void prepare(RenderEffect* effect, const Matrix& transform) = 0;
+    virtual bool region(RenderEffect* effect) = 0;
+    virtual bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0;
 };
 
 static inline bool MASK_REGION_MERGING(CompositeMethod method)

+ 8 - 2
thirdparty/thorvg/src/renderer/tvgScene.h

@@ -121,6 +121,12 @@ struct Scene::Impl
             paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
         }
 
+        if (effects) {
+            for (auto e = effects->begin(); e < effects->end(); ++e) {
+                renderer->prepare(*e, transform);
+            }
+        }
+
         return nullptr;
     }
 
@@ -146,7 +152,7 @@ struct Scene::Impl
                 //Notify the possiblity of the direct composition of the effect result to the origin surface.
                 auto direct = (effects->count == 1) & (compFlag == CompositionFlag::PostProcessing);
                 for (auto e = effects->begin(); e < effects->end(); ++e) {
-                    renderer->effect(cmp, *e, direct);
+                    if ((*e)->valid) renderer->render(cmp, *e, direct);
                 }
             }
             renderer->endComposite(cmp);
@@ -179,7 +185,7 @@ struct Scene::Impl
         if (effects) {
             for (auto e = effects->begin(); e < effects->end(); ++e) {
                 auto effect = *e;
-                if (effect->valid || renderer->prepare(effect)) {
+                if (effect->valid && renderer->region(effect)) {
                     ex = std::min(ex, effect->extend.x);
                     ey = std::min(ey, effect->extend.y);
                     ew = std::max(ew, effect->extend.w);

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

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