Browse Source

Merge pull request #86623 from capnm/231229_horvg_from_v0.11.2_to_0.11.6

ThorVG: update from v0.11.2 to v0.11.6
Rémi Verschelde 1 year ago
parent
commit
2d90ebd3e4
28 changed files with 382 additions and 590 deletions
  1. 1 1
      thirdparty/README.md
  2. 1 1
      thirdparty/thorvg/inc/config.h
  3. 31 6
      thirdparty/thorvg/inc/thorvg.h
  4. 1 0
      thirdparty/thorvg/src/common/tvgArray.h
  5. 3 3
      thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
  6. 5 1
      thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
  7. 10 1
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h
  8. 50 222
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
  9. 115 201
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
  10. 3 1
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
  11. 4 0
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
  12. 3 3
      thirdparty/thorvg/src/renderer/tvgAnimation.cpp
  13. 4 4
      thirdparty/thorvg/src/renderer/tvgCanvas.cpp
  14. 1 1
      thirdparty/thorvg/src/renderer/tvgCommon.h
  15. 3 4
      thirdparty/thorvg/src/renderer/tvgFrameModule.h
  16. 2 2
      thirdparty/thorvg/src/renderer/tvgLoader.cpp
  17. 61 20
      thirdparty/thorvg/src/renderer/tvgPaint.cpp
  18. 14 83
      thirdparty/thorvg/src/renderer/tvgPaint.h
  19. 0 1
      thirdparty/thorvg/src/renderer/tvgPicture.cpp
  20. 6 13
      thirdparty/thorvg/src/renderer/tvgRender.cpp
  21. 3 3
      thirdparty/thorvg/src/renderer/tvgRender.h
  22. 1 0
      thirdparty/thorvg/src/renderer/tvgSaveModule.h
  23. 47 1
      thirdparty/thorvg/src/renderer/tvgSaver.cpp
  24. 0 1
      thirdparty/thorvg/src/renderer/tvgScene.cpp
  25. 3 2
      thirdparty/thorvg/src/renderer/tvgScene.h
  26. 5 10
      thirdparty/thorvg/src/renderer/tvgShape.cpp
  27. 2 1
      thirdparty/thorvg/src/renderer/tvgShape.h
  28. 3 4
      thirdparty/thorvg/update-thorvg.sh

+ 1 - 1
thirdparty/README.md

@@ -861,7 +861,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.11.2 (b01fe9bf4461146304d3520d6dc699cf580a3744, 2023)
+- Version: 0.11.6 (3dba4f12f8f05f86acbc51096ca3a15f5d96bc06, 2023)
 - 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.11.2"
+#define THORVG_VERSION_STRING "0.11.6"
 #endif

+ 31 - 6
thirdparty/thorvg/inc/thorvg.h

@@ -1664,14 +1664,14 @@ public:
      * @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame().
      *
      * @retval Result::Success Successfully set the frame.
-     * @retval Result::InsufficientCondition No animatable data loaded from the Picture.
-     * @retval Result::NonSupport The Picture data does not support animations.
+     * @retval Result::InsufficientCondition if the given @p no is the same as the current frame value.
+     * @retval Result::NonSupport The current Picture data does not support animations.
      *
      * @see totalFrame()
      *
      * @BETA_API
      */
-    Result frame(uint32_t no) noexcept;
+    Result frame(float no) noexcept;
 
     /**
      * @brief Retrieves a picture instance associated with this animation instance.
@@ -1695,12 +1695,12 @@ public:
      *
      * @note If the Picture is not properly configured, this function will return 0.
      *
-     * @see Animation::frame(uint32_t no)
+     * @see Animation::frame(float no)
      * @see Animation::totalFrame()
      *
      * @BETA_API
      */
-    uint32_t curFrame() const noexcept;
+    float curFrame() const noexcept;
 
     /**
      * @brief Retrieves the total number of frames in the animation.
@@ -1712,7 +1712,7 @@ public:
      *
      * @BETA_API
      */
-    uint32_t totalFrame() const noexcept;
+    float totalFrame() const noexcept;
 
     /**
      * @brief Retrieves the duration of the animation in seconds.
@@ -1784,6 +1784,31 @@ public:
      */
     Result save(std::unique_ptr<Paint> paint, const std::string& path, bool compress = true) noexcept;
 
+    /**
+     * @brief Export the provided animation data to the specified file path.
+     *
+     * This function exports the given animation data to the provided file path. You can also specify the desired frame rate in frames per second (FPS) by providing the fps parameter.
+     *
+     * @param[in] animation The animation to be saved, including all associated properties.
+     * @param[in] path The path to the file where the animation will be saved.
+     * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended).
+     * @param[in] fps The desired frames per second (FPS). For example, to encode data at 60 FPS, pass 60. Pass 0 to keep the original frame data.
+     *
+     * @return Result::Success if the export succeeds.
+     * @return Result::InsufficientCondition if there are ongoing resource-saving operations.
+     * @return Result::NonSupport if an attempt is made to save the file with an unknown extension or in an unsupported format.
+     * @return Result::MemoryCorruption in case of an internal error.
+     * @return Result::Unknown if attempting to save an empty paint.
+     *
+     * @note A higher frames per second (FPS) would result in a larger file size. It is recommended to use the default value.
+     * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards.
+     *
+     * @see Saver::sync()
+     *
+     * @note: Experimental API
+     */
+    Result save(std::unique_ptr<Animation> animation, const std::string& path, uint32_t quality = 100, uint32_t fps = 0) noexcept;
+
     /**
      * @brief Guarantees that the saving task is finished.
      *

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

@@ -25,6 +25,7 @@
 
 #include <memory.h>
 #include <cstdint>
+#include <cstdlib>
 
 namespace tvg
 {

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

@@ -107,9 +107,9 @@ unique_ptr<Surface> PngLoader::bitmap()
     //TODO: It's better to keep this surface instance in the loader side
     auto surface = new Surface;
     surface->buf32 = content;
-    surface->stride = w;
-    surface->w = w;
-    surface->h = h;
+    surface->stride = (uint32_t)w;
+    surface->w = (uint32_t)w;
+    surface->h = (uint32_t)h;
     surface->cs = cs;
     surface->channelSize = sizeof(uint32_t);
     surface->owner = true;

+ 5 - 1
thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp

@@ -1456,7 +1456,11 @@ void jpeg_decoder::locate_sof_marker()
     int c = process_markers();
 
     switch (c) {
-        case M_SOF2: m_progressive_flag = true;
+        case M_SOF2: {
+            m_progressive_flag = true;
+            read_sof_marker();
+            break;
+        }
         case M_SOF0:  /* baseline DCT */
         case M_SOF1: { /* extended sequential DCT */
           read_sof_marker();

+ 10 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h

@@ -43,11 +43,16 @@ static double timeStamp()
 #define SW_ANGLE_PI (180L << 16)
 #define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
 #define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
-#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
 
 using SwCoord = signed long;
 using SwFixed = signed long long;
 
+
+static inline float TO_FLOAT(SwCoord val)
+{
+    return static_cast<float>(val) / 64.0f;
+}
+
 struct SwPoint
 {
     SwCoord x, y;
@@ -92,6 +97,10 @@ struct SwPoint
         else return false;
     }
 
+    Point toPoint() const
+    {
+        return {TO_FLOAT(x),  TO_FLOAT(y)};
+    }
 };
 
 struct SwSize

+ 50 - 222
thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp

@@ -20,7 +20,7 @@
  * SOFTWARE.
  */
 
-#include <math.h>
+#include "tvgMath.h"
 #include "tvgSwCommon.h"
 
 
@@ -28,173 +28,9 @@
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-//clz: count leading zero’s
-#if defined(_MSC_VER) && !defined(__clang__)
-    #include <intrin.h>
-    static uint32_t __inline _clz(uint32_t value)
-    {
-        unsigned long leadingZero = 0;
-        if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
-        else return 32;
-    }
-#else
-    #define _clz(x) __builtin_clz((x))
-#endif
-
-
-constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL;       //the Cordic shrink factor 0.858785336480436 * 2^32
-
-//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
-constexpr static auto ATAN_MAX = 23;
-constexpr static SwFixed ATAN_TBL[] = {
-    1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
-    14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
-    57L, 29L, 14L, 7L, 4L, 2L, 1L};
-
-static inline SwCoord SATURATE(const SwCoord x)
-{
-    return (x >> (sizeof(SwCoord) * 8 - 1));
-}
-
-
-static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
-{
-    return (((x) + ((n)/2)) & ~((n)-1));
-}
-
-
-static SwCoord _downscale(SwFixed x)
+static float TO_RADIAN(SwFixed angle)
 {
-    //multiply a give value by the CORDIC shrink factor
-    auto s = abs(x);
-    int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
-    s = static_cast<SwFixed>(t >> 32);
-    if (x < 0) s = -s;
-    return s;
-}
-
-
-static int32_t _normalize(SwPoint& pt)
-{
-    /* the highest bit in overflow-safe vector components
-       MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
-    constexpr auto SAFE_MSB = 29;
-
-    auto v = pt;
-
-    //High order bit(MSB)
-    int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
-
-    if (shift <= SAFE_MSB) {
-        shift = SAFE_MSB - shift;
-        pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
-        pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
-    } else {
-        shift -= SAFE_MSB;
-        pt.x = v.x >> shift;
-        pt.y = v.y >> shift;
-        shift = -shift;
-    }
-    return shift;
-}
-
-
-static void _polarize(SwPoint& pt)
-{
-    auto v = pt;
-    SwFixed theta;
-
-    //Get the vector into [-PI/4, PI/4] sector
-    if (v.y > v.x) {
-        if (v.y > -v.x) {
-            auto tmp = v.y;
-            v.y = -v.x;
-            v.x = tmp;
-            theta = SW_ANGLE_PI2;
-        } else {
-            theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
-            v.x = -v.x;
-            v.y = -v.y;
-        }
-    } else {
-        if (v.y < -v.x) {
-            theta = -SW_ANGLE_PI2;
-            auto tmp = -v.y;
-            v.y = v.x;
-            v.x = tmp;
-        } else {
-            theta = 0;
-        }
-    }
-
-    auto atan = ATAN_TBL;
-    uint32_t i;
-    SwFixed j;
-
-    //Pseudorotations. with right shifts
-    for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
-        if (v.y > 0) {
-            auto tmp = v.x + ((v.y + j) >> i);
-            v.y = v.y - ((v.x + j) >> i);
-            v.x = tmp;
-            theta += *atan++;
-        } else {
-            auto tmp = v.x - ((v.y + j) >> i);
-            v.y = v.y + ((v.x + j) >> i);
-            v.x = tmp;
-            theta -= *atan++;
-        }
-    }
-
-    //round theta
-    if (theta >= 0) theta =  PAD_ROUND(theta, 32);
-    else theta = -PAD_ROUND(-theta, 32);
-
-    pt.x = v.x;
-    pt.y = theta;
-}
-
-
-static void _rotate(SwPoint& pt, SwFixed theta)
-{
-    SwFixed x = pt.x;
-    SwFixed y = pt.y;
-
-    //Rotate inside [-PI/4, PI/4] sector
-    while (theta < -SW_ANGLE_PI4) {
-        auto tmp = y;
-        y = -x;
-        x = tmp;
-        theta += SW_ANGLE_PI2;
-    }
-
-    while (theta > SW_ANGLE_PI4) {
-        auto tmp = -y;
-        y = x;
-        x = tmp;
-        theta -= SW_ANGLE_PI2;
-    }
-
-    auto atan = ATAN_TBL;
-    uint32_t i;
-    SwFixed j;
-
-    for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
-        if (theta < 0) {
-            auto tmp = x + ((y + j) >> i);
-            y = y - ((x + j) >> i);
-            x = tmp;
-            theta += *atan++;
-        } else {
-            auto tmp = x - ((y + j) >> i);
-            y = y + ((x + j) >> i);
-            x = tmp;
-            theta -= *atan++;
-        }
-    }
-
-    pt.x = static_cast<SwCoord>(x);
-    pt.y = static_cast<SwCoord>(y);
+    return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
 }
 
 
@@ -214,11 +50,17 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
     auto d2 = base[1] - base[2];
     auto d3 = base[0] - base[1];
 
+    if (d1 == d2 || d2 == d3) {
+        if (d3.small()) angleIn = angleMid = angleOut = 0;
+        else angleIn = angleMid = angleOut = mathAtan(d3);
+        return true;
+    }
+
     if (d1.small()) {
         if (d2.small()) {
             if (d3.small()) {
-                //basically a point.
-                //do nothing to retain original direction
+                angleIn = angleMid = angleOut = 0;
+                return true;
             } else {
                 angleIn = angleMid = angleOut = mathAtan(d3);
             }
@@ -320,77 +162,63 @@ int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
 
 void mathRotate(SwPoint& pt, SwFixed angle)
 {
-    if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
-
-    auto v  = pt;
-    auto shift = _normalize(v);
+    if (angle == 0 || pt.zero()) return;
 
-    auto theta = angle;
-   _rotate(v, theta);
+    Point v = pt.toPoint();
 
-    v.x = _downscale(v.x);
-    v.y = _downscale(v.y);
+    auto radian = TO_RADIAN(angle);
+    auto cosv = cosf(radian);
+    auto sinv = sinf(radian);
 
-    if (shift > 0) {
-        auto half = static_cast<int32_t>(1L << (shift - 1));
-        pt.x = (v.x + half + SATURATE(v.x)) >> shift;
-        pt.y = (v.y + half + SATURATE(v.y)) >> shift;
-    } else {
-        shift = -shift;
-        pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
-        pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
-    }
+    pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f));
+    pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f));
 }
 
+
 SwFixed mathTan(SwFixed angle)
 {
-    SwPoint v = {CORDIC_FACTOR >> 8, 0};
-    _rotate(v, angle);
-    return mathDivide(v.y, v.x);
+    if (angle == 0) return 0;
+    return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
 }
 
 
 SwFixed mathAtan(const SwPoint& pt)
 {
-    if (pt.x == 0 && pt.y == 0) return 0;
-
-    auto v = pt;
-    _normalize(v);
-    _polarize(v);
-
-    return v.y;
+    if (pt.zero()) return 0;
+    return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
 }
 
 
 SwFixed mathSin(SwFixed angle)
 {
+    if (angle == 0) return 0;
     return mathCos(SW_ANGLE_PI2 - angle);
 }
 
 
 SwFixed mathCos(SwFixed angle)
 {
-    SwPoint v = {CORDIC_FACTOR >> 8, 0};
-    _rotate(v, angle);
-    return (v.x + 0x80L) >> 8;
+    return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
 }
 
 
 SwFixed mathLength(const SwPoint& pt)
 {
-    auto v = pt;
+    if (pt.zero()) return 0;
 
     //trivial case
-    if (v.x == 0) return abs(v.y);
-    if (v.y == 0) return abs(v.x);
-
-    //general case
-    auto shift = _normalize(v);
-    _polarize(v);
-    v.x = _downscale(v.x);
-
-    if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
-    return static_cast<SwFixed>((uint32_t)v.x << -shift);
+    if (pt.x == 0) return abs(pt.y);
+    if (pt.y == 0) return abs(pt.x);
+
+    auto v = pt.toPoint();
+    //return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f);
+
+    /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+       With alpha = 1, beta = 3/8, giving results with the largest error less
+       than 7% compared to the exact value. */
+    if (v.x < 0) v.x = -v.x;
+    if (v.y < 0) v.y = -v.y;
+    return static_cast<SwFixed>((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f));
 }
 
 
@@ -401,22 +229,22 @@ void mathSplitCubic(SwPoint* base)
     base[6].x = base[3].x;
     c = base[1].x;
     d = base[2].x;
-    base[1].x = a = (base[0].x + c) / 2;
-    base[5].x = b = (base[3].x + d) / 2;
-    c = (c + d) / 2;
-    base[2].x = a = (a + c) / 2;
-    base[4].x = b = (b + c) / 2;
-    base[3].x = (a + b) / 2;
+    base[1].x = a = (base[0].x + c) >> 1;
+    base[5].x = b = (base[3].x + d) >> 1;
+    c = (c + d) >> 1;
+    base[2].x = a = (a + c) >> 1;
+    base[4].x = b = (b + c) >> 1;
+    base[3].x = (a + b) >> 1;
 
     base[6].y = base[3].y;
     c = base[1].y;
     d = base[2].y;
-    base[1].y = a = (base[0].y + c) / 2;
-    base[5].y = b = (base[3].y + d) / 2;
-    c = (c + d) / 2;
-    base[2].y = a = (a + c) / 2;
-    base[4].y = b = (b + c) / 2;
-    base[3].y = (a + b) / 2;
+    base[1].y = a = (base[0].y + c) >> 1;
+    base[5].y = b = (base[3].y + d) >> 1;
+    c = (c + d) >> 1;
+    base[2].y = a = (a + c) >> 1;
+    base[4].y = b = (b + c) >> 1;
+    base[3].y = (a + b) >> 1;
 }
 
 

+ 115 - 201
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -248,17 +248,17 @@ static inline uint32_t _sampleSize(float scale)
 
 //Bilinear Interpolation
 //OPTIMIZE_ME: Skip the function pointer access
-static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED uint32_t n, TVG_UNUSED uint32_t n2)
+static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED int32_t miny, TVG_UNUSED int32_t maxy, TVG_UNUSED int32_t n)
 {
-    auto rx = (uint32_t)(sx);
-    auto ry = (uint32_t)(sy);
+    auto rx = (size_t)(sx);
+    auto ry = (size_t)(sy);
     auto rx2 = rx + 1;
     if (rx2 >= w) rx2 = w - 1;
     auto ry2 = ry + 1;
     if (ry2 >= h) ry2 = h - 1;
 
-    auto dx = static_cast<uint32_t>((sx - rx) * 255.0f);
-    auto dy = static_cast<uint32_t>((sy - ry) * 255.0f);
+    auto dx = static_cast<size_t>((sx - rx) * 255.0f);
+    auto dy = static_cast<size_t>((sy - ry) * 255.0f);
 
     auto c1 = img[rx + ry * w];
     auto c2 = img[rx2 + ry * w];
@@ -271,18 +271,21 @@ static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride,
 
 //2n x 2n Mean Kernel
 //OPTIMIZE_ME: Skip the function pointer access
-static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, uint32_t n, uint32_t n2)
+static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, TVG_UNUSED float sy, int32_t miny, int32_t maxy, int32_t n)
 {
-    uint32_t rx = lroundf(sx);
-    uint32_t ry = lroundf(sy);
-    uint32_t c[4] = {0, 0, 0, 0};
-    auto src = img + rx - n + (ry - n) * stride;
+    size_t c[4] = {0, 0, 0, 0};
 
-    for (auto y = ry - n; y < ry + n; ++y) {
-        if (y >= h) continue;
+    int32_t minx = (int32_t)sx - n;
+    if (minx < 0) minx = 0;
+
+    int32_t maxx = (int32_t)sx + n;
+    if (maxx >= (int32_t)w) maxx = w;
+
+    auto src = img + minx + miny * stride;
+
+    for (auto y = miny; y < maxy; ++y) {
         auto p = src;
-        for (auto x = rx - n; x < rx + n; ++x, ++p) {
-            if (x >= w) continue;
+        for (auto x = minx; x < maxx; ++x, ++p) {
             c[0] += *p >> 24;
             c[1] += (*p >> 16) & 0xff;
             c[2] += (*p >> 8) & 0xff;
@@ -290,9 +293,14 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
         }
         src += stride;
     }
-    for (auto i = 0; i < 4; ++i) {
-        c[i] = (c[i] >> 2) / n2;
-    }
+
+    n = (maxy - miny) * (maxx - minx);
+
+    c[0] /= n;
+    c[1] /= n;
+    c[2] /= n;
+    c[3] /= n;
+
     return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
 }
 
@@ -660,34 +668,39 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
 /* RLE Scaled Image                                                     */
 /************************************************************************/
 
+#define SCALED_IMAGE_RANGE_Y(y) \
+    auto sy = (y) * itransform->e22 + itransform->e23; \
+    auto my = (int32_t)round(sy); \
+    if (my < 0 || (uint32_t)sy >= image->h) continue; \
+    if (scaleMethod == _interpDownScaler) { \
+        miny = my - (int32_t)sampleSize; \
+        if (miny < 0) miny = 0; \
+        maxy = my + (int32_t)sampleSize; \
+        if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \
+    }
+
+#define SCALED_IMAGE_RANGE_X \
+    auto sx = x * itransform->e11 + itransform->e13; \
+    if ((int32_t)round(sx) < 0 || (uint32_t) sx >= image->w) continue;
+
+
 #if 0 //Enable it when GRAYSCALE image is supported
 static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
 {
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
     auto span = image->rle->spans;
+    int32_t miny = 0, maxy = 0;
 
     for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto sy = span->y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(span->y)
         auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
         auto a = MULTIPLY(span->coverage, opacity);
-        if (a == 255) {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                *cmp = maskOp(src, *cmp, ~src);
-            }
-        } else {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = MULTIPLY(src, a);
-                *cmp = maskOp(tmp, *cmp, ~tmp);
-            }
+        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (a < 255) src = MULTIPLY(src, a);
+            *cmp = maskOp(src, *cmp, ~src);
         }
     }
     return true;
@@ -698,31 +711,20 @@ static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage*
 {
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
     auto span = image->rle->spans;
+    int32_t miny = 0, maxy = 0;
 
-    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto sy = span->y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+    for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {        
+        SCALED_IMAGE_RANGE_Y(span->y)
         auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
         auto dst = &surface->buf8[span->y * surface->stride + span->x];
         auto a = MULTIPLY(span->coverage, opacity);
-        if (a == 255) {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = maskOp(src, *cmp, 0);  //not use alpha
-                *dst = tmp + MULTIPLY(*dst, ~tmp);
-            }
-        } else {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = maskOp(MULTIPLY(src, a), *cmp, 0);  //not use alpha
-                *dst = tmp + MULTIPLY(*dst, ~tmp);
-            }
+        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (a < 255) src = MULTIPLY(src, a);
+            src = maskOp(src, *cmp, 0);  //not use alpha
+            *dst = src + MULTIPLY(*dst, ~src);
         }
     }
     return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
@@ -752,32 +754,20 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
     auto span = image->rle->spans;
     auto csize = surface->compositor->image.channelSize;
     auto alpha = surface->alpha(surface->compositor->method);
-
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto sy = span->y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(span->y)
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
         auto a = MULTIPLY(span->coverage, opacity);
-        if (a == 255) {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto tmp = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha(cmp));
-                *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
-            }
-        } else {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = ALPHA_BLEND(src, MULTIPLY(alpha(cmp), a));
-                *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
-            }
+        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a));
+            *dst = src + ALPHA_BLEND(*dst, IA(src));
         }
     }
 
@@ -790,34 +780,24 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
     auto span = image->rle->spans;
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto sy = span->y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(span->y)
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         auto alpha = MULTIPLY(span->coverage, opacity);
         if (alpha == 255) {
             for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+                SCALED_IMAGE_RANGE_X
+                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
                 auto tmp = surface->blender(src, *dst, 255);
                 *dst = INTERPOLATE(tmp, *dst, A(src));
             }
-        } else if (opacity == 255) {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = surface->blender(src, *dst, 255);
-                *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
-            }
         } else {
             for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
+                SCALED_IMAGE_RANGE_X
+                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+                if (opacity < 255) src = ALPHA_BLEND(src, opacity);
                 auto tmp = surface->blender(src, *dst, 255);
                 *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
             }
@@ -832,27 +812,17 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
     auto span = image->rle->spans;
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
-        auto sy = span->y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(span->y)
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
         auto alpha = MULTIPLY(span->coverage, opacity);
-        if (alpha == 255) {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                *dst = src + ALPHA_BLEND(*dst, IA(src));
-            }
-        } else {
-            for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha);
-                *dst = src + ALPHA_BLEND(*dst, IA(src));
-            }
+        for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (alpha < 255) src = ALPHA_BLEND(src, alpha);
+            *dst = src + ALPHA_BLEND(*dst, IA(src));
         }
     }
     return true;
@@ -1067,29 +1037,18 @@ static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage*
 {
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
     auto cstride = surface->compositor->image.stride;
     auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
+    int32_t miny = 0, maxy = 0;
 
     for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto sy = y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(y)
         auto cmp = cbuffer;
-        if (opacity == 255) {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                *cmp = maskOp(src, *cmp, ~src);
-            }
-        } else {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = MULTIPLY(src, opacity);
-                *cmp = maskOp(tmp, *cmp, ~tmp);
-            }
+        for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (opacity < 255) src = MULTIPLY(src, opacity);
+            *cmp = maskOp(src, *cmp, ~src);
         }
         cbuffer += cstride;
     }
@@ -1101,33 +1060,21 @@ static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* im
 {
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
     auto cstride = surface->compositor->image.stride;
     auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
     auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
+    int32_t miny = 0, maxy = 0;
 
     for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto sy = y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(y)
         auto cmp = cbuffer;
         auto dst = dbuffer;
-        if (opacity == 255) {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = maskOp(src, *cmp, 0);  //not use alpha
-                *dst = tmp + MULTIPLY(*dst, ~tmp);
-            }
-        } else {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = MULTIPLY(src, opacity);
-                auto tmp2 = maskOp(tmp, *cmp, 0);  //not use alpha
-                *dst = tmp2 + MULTIPLY(*dst, ~tmp2);
-            }
+        for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (opacity < 255) src = MULTIPLY(src, opacity);
+            src = maskOp(src, *cmp, 0);  //not use alpha
+            *dst = src + MULTIPLY(*dst, ~src);
         }
         cbuffer += cstride;
         dbuffer += surface->stride;
@@ -1160,29 +1107,17 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
 
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto sy = y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(y)
         auto dst = dbuffer;
         auto cmp = cbuffer;
-        if (opacity == 255) {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto temp = ALPHA_BLEND(src, alpha(cmp));
-                *dst = temp + ALPHA_BLEND(*dst, IA(temp));
-            }
-        } else {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto temp = ALPHA_BLEND(src, MULTIPLY(opacity, alpha(cmp)));
-                *dst = temp + ALPHA_BLEND(*dst, IA(temp));
-            }
+        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp)));
+            *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
         }
         dbuffer += surface->stride;
         cbuffer += surface->compositor->image.stride * csize;
@@ -1196,28 +1131,17 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
     auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
-        auto sy = y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(y)
         auto dst = dbuffer;
-        if (opacity == 255) {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                auto tmp = surface->blender(src, *dst, 255);
-                *dst = INTERPOLATE(tmp, *dst, A(src));
-            }
-        } else {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
-                auto tmp = surface->blender(src, *dst, 255);
-                *dst = INTERPOLATE(tmp, *dst, A(src));
-            }
+        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (opacity < 255) ALPHA_BLEND(src, opacity);
+            auto tmp = surface->blender(src, *dst, 255);
+            *dst = INTERPOLATE(tmp, *dst, A(src));
         }
     }
     return true;
@@ -1229,26 +1153,16 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
     auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
     auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
     auto sampleSize = _sampleSize(image->scale);
-    auto sampleSize2 = sampleSize * sampleSize;
+    int32_t miny = 0, maxy = 0;
 
     for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
-        auto sy = y * itransform->e22 + itransform->e23;
-        if ((uint32_t)sy >= image->h) continue;
+        SCALED_IMAGE_RANGE_Y(y)
         auto dst = dbuffer;
-        if (opacity == 255) {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
-                *dst = src + ALPHA_BLEND(*dst, IA(src));
-            }
-        } else {
-            for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-                auto sx = x * itransform->e11 + itransform->e13;
-                if ((uint32_t)sx >= image->w) continue;
-                auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
-                *dst = src + ALPHA_BLEND(*dst, IA(src));
-            }
+        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+            SCALED_IMAGE_RANGE_X
+            auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
+            if (opacity < 255) src = ALPHA_BLEND(src, opacity);
+            *dst = src + ALPHA_BLEND(*dst, IA(src));
         }
     }
     return true;

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

@@ -139,7 +139,9 @@ struct SwShapeTask : SwTask
             visibleFill = (alpha > 0 || rshape->fill);
             if (visibleFill || clipper) {
                 shapeReset(&shape);
-                if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
+                if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
+                    visibleFill = false;
+                }
             }
         }
         //Fill

+ 4 - 0
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -558,11 +558,15 @@ void shapeReset(SwShape* shape)
 void shapeFree(SwShape* shape)
 {
     rleFree(shape->rle);
+    shape->rle = nullptr;
+
     shapeDelFill(shape);
 
     if (shape->stroke) {
         rleFree(shape->strokeRle);
+        shape->strokeRle = nullptr;
         strokeFree(shape->stroke);
+        shape->stroke = nullptr;
     }
 }
 

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

@@ -62,7 +62,7 @@ Animation::Animation() : pImpl(new Impl)
 }
 
 
-Result Animation::frame(uint32_t no) noexcept
+Result Animation::frame(float no) noexcept
 {
     auto loader = pImpl->picture->pImpl->loader.get();
 
@@ -80,7 +80,7 @@ Picture* Animation::picture() const noexcept
 }
 
 
-uint32_t Animation::curFrame() const noexcept
+float Animation::curFrame() const noexcept
 {
     auto loader = pImpl->picture->pImpl->loader.get();
 
@@ -91,7 +91,7 @@ uint32_t Animation::curFrame() const noexcept
 }
 
 
-uint32_t Animation::totalFrame() const noexcept
+float Animation::totalFrame() const noexcept
 {
     auto loader = pImpl->picture->pImpl->loader.get();
 

+ 4 - 4
thirdparty/thorvg/src/renderer/tvgCanvas.cpp

@@ -63,9 +63,9 @@ Result Canvas::clear(bool free) noexcept
 
 Result Canvas::draw() noexcept
 {
-    TVGLOG("COMMON", "Draw S. -------------------------------- Canvas(%p)", this);
+    TVGLOG("RENDERER", "Draw S. -------------------------------- Canvas(%p)", this);
     auto ret = pImpl->draw();
-    TVGLOG("COMMON", "Draw E. -------------------------------- Canvas(%p)", this);
+    TVGLOG("RENDERER", "Draw E. -------------------------------- Canvas(%p)", this);
 
     return ret;
 }
@@ -73,9 +73,9 @@ Result Canvas::draw() noexcept
 
 Result Canvas::update(Paint* paint) noexcept
 {
-    TVGLOG("COMMON", "Update S. ------------------------------ Canvas(%p)", this);
+    TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this);
     auto ret = pImpl->update(paint, false);
-    TVGLOG("COMMON", "Update E. ------------------------------ Canvas(%p)", this);
+    TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this);
 
     return ret;
 }

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

@@ -62,7 +62,7 @@ using namespace tvg;
 #define TVG_CLASS_ID_LINEAR    4
 #define TVG_CLASS_ID_RADIAL    5
 
-enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Unknown };
+enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown };
 
 using Size = Point;
 

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

@@ -33,10 +33,9 @@ class FrameModule: public LoadModule
 public:
     virtual ~FrameModule() {}
 
-    virtual bool frame(uint32_t frameNo) = 0;   //set the current frame number
-
-    virtual uint32_t totalFrame() = 0;      //return the total frame count
-    virtual uint32_t curFrame() = 0;        //return the current frame number
+    virtual bool frame(float no) = 0;       //set the current frame number
+    virtual float totalFrame() = 0;         //return the total frame count
+    virtual float curFrame() = 0;           //return the current frame number
     virtual float duration() = 0;           //return the animation duration in seconds
 
     virtual bool animatable() override { return true; }

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgLoader.cpp

@@ -136,7 +136,7 @@ static LoadModule* _find(FileType type)
             break;
         }
     }
-    TVGLOG("LOADER", "%s format is not supported", format);
+    TVGLOG("RENDERER", "%s format is not supported", format);
 #endif
     return nullptr;
 }
@@ -170,7 +170,7 @@ static LoadModule* _findByType(const string& mimeType)
     else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
     else if (mimeType == "webp") type = FileType::Webp;
     else {
-        TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
+        TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
         return nullptr;
     }
 

+ 61 - 20
thirdparty/thorvg/src/renderer/tvgPaint.cpp

@@ -22,11 +22,22 @@
 
 #include "tvgMath.h"
 #include "tvgPaint.h"
+#include "tvgShape.h"
+#include "tvgPicture.h"
+#include "tvgScene.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
+#define PAINT_METHOD(ret, METHOD) \
+    switch (id) { \
+        case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
+        case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
+        case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
+        default: ret = {}; \
+    }
+
 
 static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport)
 {
@@ -93,9 +104,36 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform,
 }
 
 
+RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const
+{
+    RenderRegion ret;
+    PAINT_METHOD(ret, bounds(renderer));
+    return ret;
+}
+
+
+bool Paint::Impl::dispose(RenderMethod& renderer)
+{
+    if (compData) compData->target->pImpl->dispose(renderer);
+
+    bool ret;
+    PAINT_METHOD(ret, dispose(renderer));
+    return ret;
+}
+
+
+Iterator* Paint::Impl::iterator()
+{
+    Iterator* ret;
+    PAINT_METHOD(ret, iterator());
+    return ret;
+}
+
+
 Paint* Paint::Impl::duplicate()
 {
-    auto ret = smethod->duplicate();
+    Paint* ret;
+    PAINT_METHOD(ret, duplicate());
 
     //duplicate Transform
     if (rTransform) {
@@ -165,8 +203,10 @@ bool Paint::Impl::render(RenderMethod& renderer)
     /* Note: only ClipPath is processed in update() step.
         Create a composition image. */
     if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
-        auto region = smethod->bounds(renderer);
-        if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
+        RenderRegion region;
+        PAINT_METHOD(region, bounds(renderer));
+
+        if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
         if (region.w == 0 || region.h == 0) return true;
         cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
         if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@@ -177,7 +217,9 @@ bool Paint::Impl::render(RenderMethod& renderer)
     if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
 
     renderer.blend(blendMethod);
-    auto ret = smethod->render(renderer);
+
+    bool ret;
+    PAINT_METHOD(ret, render(renderer));
 
     if (cmp) renderer.endComposite(cmp);
 
@@ -189,10 +231,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
 {
     if (renderFlag & RenderUpdateFlag::Transform) {
         if (!rTransform) return nullptr;
-        if (!rTransform->update()) {
-            delete(rTransform);
-            rTransform = nullptr;
-        }
+        rTransform->update();
     }
 
     /* 1. Composition Pre Processing */
@@ -239,18 +278,13 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
     }
 
     /* 2. Main Update */
-    RenderData rd = nullptr;
     auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
     renderFlag = RenderUpdateFlag::None;
     opacity = MULTIPLY(opacity, this->opacity);
 
-    if (rTransform && pTransform) {
-        RenderTransform outTransform(pTransform, rTransform);
-        rd = smethod->update(renderer, &outTransform, clips, opacity, newFlag, clipper);
-    } else {
-        auto outTransform = pTransform ? pTransform : rTransform;
-        rd = smethod->update(renderer, outTransform, clips, opacity, newFlag, clipper);
-    }
+    RenderData rd = nullptr;
+    RenderTransform outTransform(pTransform, rTransform);
+    PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
 
     /* 3. Composition Post Processing */
     if (compFastTrack) renderer.viewport(viewport);
@@ -263,9 +297,13 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
 bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
 {
     Matrix* m = nullptr;
+    bool ret;
 
     //Case: No transformed, quick return!
-    if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
+    if (!transformed || !(m = this->transform())) {
+        PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
+        return ret;
+    }
 
     //Case: Transformed
     auto tx = 0.0f;
@@ -273,7 +311,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
     auto tw = 0.0f;
     auto th = 0.0f;
 
-    auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
+    PAINT_METHOD(ret, bounds(&tx, &ty, &tw, &th, stroking));
 
     //Get vertices
     Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
@@ -307,7 +345,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
 /* External Class Implementation                                        */
 /************************************************************************/
 
-Paint :: Paint() : pImpl(new Impl())
+Paint :: Paint() : pImpl(new Impl(this))
 {
 }
 
@@ -419,7 +457,10 @@ uint32_t Paint::identifier() const noexcept
 
 Result Paint::blend(BlendMethod method) const noexcept
 {
-    pImpl->blendMethod = method;
+    if (pImpl->blendMethod != method) {
+        pImpl->blendMethod = method;
+        pImpl->renderFlag |= RenderUpdateFlag::Blend;
+    }
 
     return Result::Success;
 }

+ 14 - 83
thirdparty/thorvg/src/renderer/tvgPaint.h

@@ -38,19 +38,6 @@ namespace tvg
         virtual void begin() = 0;
     };
 
-    struct StrategyMethod
-    {
-        virtual ~StrategyMethod() {}
-
-        virtual bool dispose(RenderMethod& renderer) = 0;     //return true if the deletion is allowed.
-        virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0;   //Return engine data if it has.
-        virtual bool render(RenderMethod& renderer) = 0;
-        virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
-        virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
-        virtual Paint* duplicate() = 0;
-        virtual Iterator* iterator() = 0;
-    };
-
     struct Composite
     {
         Paint* target;
@@ -60,7 +47,7 @@ namespace tvg
 
     struct Paint::Impl
     {
-        StrategyMethod* smethod = nullptr;
+        Paint* paint = nullptr;
         RenderTransform* rTransform = nullptr;
         Composite* compData = nullptr;
         BlendMethod blendMethod = BlendMethod::Normal;              //uint8_t
@@ -70,13 +57,16 @@ namespace tvg
         uint8_t opacity = 255;
         uint8_t refCnt = 0;
 
+        Impl(Paint* pnt) : paint(pnt)
+        {
+        }
+
         ~Impl()
         {
             if (compData) {
-                delete(compData->target);
+                if (P(compData->target)->unref() == 0) delete(compData->target);
                 free(compData);
             }
-            delete(smethod);
             delete(rTransform);
         }
 
@@ -92,11 +82,6 @@ namespace tvg
             return (--refCnt);
         }
 
-        void method(StrategyMethod* method)
-        {
-            smethod = method;
-        }
-
         bool transform(const Matrix& m)
         {
             if (!rTransform) {
@@ -119,29 +104,16 @@ namespace tvg
             return nullptr;
         }
 
-        RenderRegion bounds(RenderMethod& renderer) const
-        {
-            return smethod->bounds(renderer);
-        }
-
-        bool dispose(RenderMethod& renderer)
-        {
-            if (compData) compData->target->pImpl->dispose(renderer);
-            return smethod->dispose(renderer);
-        }
-
-        Iterator* iterator()
-        {
-            return smethod->iterator();
-        }
-
         bool composite(Paint* source, Paint* target, CompositeMethod method)
         {
             //Invalid case
             if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
 
             if (compData) {
-                delete(compData->target);
+                P(compData->target)->unref();
+                if ((compData->target != target) && P(compData->target)->refCnt == 0) {
+                    delete(compData->target);
+                }
                 //Reset scenario
                 if (!target && method == CompositeMethod::None) {
                     free(compData);
@@ -152,12 +124,16 @@ namespace tvg
                 if (!target && method == CompositeMethod::None) return true;
                 compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
             }
+            P(target)->ref();
             compData->target = target;
             compData->source = source;
             compData->method = method;
             return true;
         }
 
+        RenderRegion bounds(RenderMethod& renderer) const;
+        bool dispose(RenderMethod& renderer);
+        Iterator* iterator();
         bool rotate(float degree);
         bool scale(float factor);
         bool translate(float x, float y);
@@ -166,51 +142,6 @@ namespace tvg
         bool render(RenderMethod& renderer);
         Paint* duplicate();
     };
-
-
-    template<class T>
-    struct PaintMethod : StrategyMethod
-    {
-        T* inst = nullptr;
-
-        PaintMethod(T* _inst) : inst(_inst) {}
-        ~PaintMethod() {}
-
-        bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
-        {
-            return inst->bounds(x, y, w, h, stroking);
-        }
-
-        RenderRegion bounds(RenderMethod& renderer) const override
-        {
-            return inst->bounds(renderer);
-        }
-
-        bool dispose(RenderMethod& renderer) override
-        {
-            return inst->dispose(renderer);
-        }
-
-        RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag renderFlag, bool clipper) override
-        {
-            return inst->update(renderer, transform, clips, opacity, renderFlag, clipper);
-        }
-
-        bool render(RenderMethod& renderer) override
-        {
-            return inst->render(renderer);
-        }
-
-        Paint* duplicate() override
-        {
-            return inst->duplicate();
-        }
-
-        Iterator* iterator() override
-        {
-            return inst->iterator();
-        }
-    };
 }
 
 #endif //_TVG_PAINT_H_

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

@@ -62,7 +62,6 @@ RenderUpdateFlag Picture::Impl::load()
 Picture::Picture() : pImpl(new Impl(this))
 {
     Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
-    Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
 }
 
 

+ 6 - 13
thirdparty/thorvg/src/renderer/tvgRender.cpp

@@ -39,12 +39,9 @@ void RenderTransform::override(const Matrix& m)
 }
 
 
-bool RenderTransform::update()
+void RenderTransform::update()
 {
-    if (overriding) return true;
-
-    //Init Status
-    if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false;
+    if (overriding) return;
 
     mathIdentity(&m);
 
@@ -53,17 +50,13 @@ bool RenderTransform::update()
     if (!mathZero(degree)) mathRotate(&m, degree);
 
     mathTranslate(&m, x, y);
-
-    return true;
-}
-
-
-RenderTransform::RenderTransform()
-{
 }
 
 
 RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
 {
-    m = mathMultiply(&lhs->m, &rhs->m);
+    if (lhs && rhs) m = mathMultiply(&lhs->m, &rhs->m);
+    else if (lhs) m = lhs->m;
+    else if (rhs) m = rhs->m;
+    else mathIdentity(&m);
 }

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

@@ -32,7 +32,7 @@ namespace tvg
 using RenderData = void*;
 using pixel_t = uint32_t;
 
-enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
+enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
 
 struct Surface;
 
@@ -123,10 +123,10 @@ struct RenderTransform
     float scale = 1.0f;   //scale factor
     bool overriding = false;  //user transform?
 
-    bool update();
+    void update();
     void override(const Matrix& m);
 
-    RenderTransform();
+    RenderTransform() {}
     RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
 };
 

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

@@ -34,6 +34,7 @@ public:
     virtual ~SaveModule() {}
 
     virtual bool save(Paint* paint, const string& path, bool compress) = 0;
+    virtual bool save(Animation* animation, const string& path, uint32_t quality, uint32_t fps) = 0;
     virtual bool close() = 0;
 };
 

+ 47 - 1
thirdparty/thorvg/src/renderer/tvgSaver.cpp

@@ -26,6 +26,9 @@
 #ifdef THORVG_TVG_SAVER_SUPPORT
     #include "tvgTvgSaver.h"
 #endif
+#ifdef THORVG_GIF_SAVER_SUPPORT
+    #include "tvgGifSaver.h"
+#endif
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -47,6 +50,12 @@ static SaveModule* _find(FileType type)
         case FileType::Tvg: {
 #ifdef THORVG_TVG_SAVER_SUPPORT
             return new TvgSaver;
+#endif
+            break;
+        }
+        case FileType::Gif: {
+#ifdef THORVG_GIF_SAVER_SUPPORT
+            return new GifSaver;
 #endif
             break;
         }
@@ -62,12 +71,16 @@ static SaveModule* _find(FileType type)
             format = "TVG";
             break;
         }
+        case FileType::Gif: {
+            format = "GIF";
+            break;
+        }
         default: {
             format = "???";
             break;
         }
     }
-    TVGLOG("SAVER", "%s format is not supported", format);
+    TVGLOG("RENDERER", "%s format is not supported", format);
 #endif
     return nullptr;
 }
@@ -78,6 +91,8 @@ static SaveModule* _find(const string& path)
     auto ext = path.substr(path.find_last_of(".") + 1);
     if (!ext.compare("tvg")) {
         return _find(FileType::Tvg);
+    } else if (!ext.compare("gif")) {
+        return _find(FileType::Gif);
     }
     return nullptr;
 }
@@ -124,6 +139,37 @@ Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compre
 }
 
 
+Result Saver::save(std::unique_ptr<Animation> animation, const string& path, uint32_t quality, uint32_t fps) noexcept
+{
+    auto a = animation.release();
+    if (!a) return Result::MemoryCorruption;
+
+    if (mathZero(a->totalFrame())) {
+        delete(a);
+        return Result::InsufficientCondition;
+    }
+
+    //Already on saving an other resource.
+    if (pImpl->saveModule) {
+        delete(a);
+        return Result::InsufficientCondition;
+    }
+
+    if (auto saveModule = _find(path)) {
+        if (saveModule->save(a, path, quality, fps)) {
+            pImpl->saveModule = saveModule;
+            return Result::Success;
+        } else {
+            delete(a);
+            delete(saveModule);
+            return Result::Unknown;
+        }
+    }
+    delete(a);
+    return Result::NonSupport;
+}
+
+
 Result Saver::sync() noexcept
 {
     if (!pImpl->saveModule) return Result::InsufficientCondition;

+ 0 - 1
thirdparty/thorvg/src/renderer/tvgScene.cpp

@@ -29,7 +29,6 @@
 Scene::Scene() : pImpl(new Impl(this))
 {
     Paint::pImpl->id = TVG_CLASS_ID_SCENE;
-    Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
 }
 
 

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

@@ -66,7 +66,7 @@ struct Scene::Impl
     RenderData rd = nullptr;
     Scene* scene = nullptr;
     uint8_t opacity;                     //for composition
-    bool needComp;                       //composite or not
+    bool needComp = false;               //composite or not
 
     Impl(Scene* s) : scene(s)
     {
@@ -75,7 +75,7 @@ struct Scene::Impl
     ~Impl()
     {
         for (auto paint : paints) {
-            if (paint->pImpl->unref() == 0) delete(paint);
+            if (P(paint)->unref() == 0) delete(paint);
         }
     }
 
@@ -148,6 +148,7 @@ struct Scene::Impl
         if (needComp) {
             cmp = renderer.target(bounds(renderer), renderer.colorSpace());
             renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+            needComp = false;
         }
 
         for (auto paint : paints) {

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

@@ -35,7 +35,6 @@ constexpr auto PATH_KAPPA = 0.552284f;
 Shape :: Shape() : pImpl(new Impl(this))
 {
     Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
-    Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
 }
 
 
@@ -62,7 +61,7 @@ Result Shape::reset() noexcept
     pImpl->rs.path.cmds.clear();
     pImpl->rs.path.pts.clear();
 
-    pImpl->flag = RenderUpdateFlag::Path;
+    pImpl->flag |= RenderUpdateFlag::Path;
 
     return Result::Success;
 }
@@ -70,18 +69,14 @@ Result Shape::reset() noexcept
 
 uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
 {
-    if (!cmds) return 0;
-
-    *cmds = pImpl->rs.path.cmds.data;
+    if (cmds) *cmds = pImpl->rs.path.cmds.data;
     return pImpl->rs.path.cmds.count;
 }
 
 
 uint32_t Shape::pathCoords(const Point** pts) const noexcept
 {
-    if (!pts) return 0;
-
-    *pts = pImpl->rs.path.pts.data;
+    if (pts) *pts = pImpl->rs.path.pts.data;
     return pImpl->rs.path.pts.count;
 }
 
@@ -224,8 +219,8 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
     } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
         return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
     } else {
-        auto hrx = rx * 0.5f;
-        auto hry = ry * 0.5f;
+        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);

+ 2 - 1
thirdparty/thorvg/src/renderer/tvgShape.h

@@ -38,7 +38,7 @@ struct Shape::Impl
     Shape* shape;
     uint8_t flag = RenderUpdateFlag::None;
     uint8_t opacity;                    //for composition
-    bool needComp;                      //composite or not
+    bool needComp = false;              //composite or not
 
     Impl(Shape* s) : shape(s)
     {
@@ -59,6 +59,7 @@ struct Shape::Impl
         if (needComp) {
             cmp = renderer.target(bounds(renderer), renderer.colorSpace());
             renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+            needComp = false;
         }
         ret = renderer.renderShape(rd);
         if (cmp) renderer.endComposite(cmp);

+ 3 - 4
thirdparty/thorvg/update-thorvg.sh

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-VERSION=0.11.2
+VERSION=0.11.6
 
 cd thirdparty/thorvg/ || true
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
@@ -56,9 +56,8 @@ rm -rfv ../src/renderer/gl_engine
 # Enabled embedded loaders: raw, JPEG, PNG.
 mkdir ../src/loaders
 cp -rv src/loaders/svg src/loaders/raw  ../src/loaders/
-cp -rv src/loaders/svg src/loaders/jpg  ../src/loaders/
-cp -rv src/loaders/svg src/loaders/png  ../src/loaders/
-cp -rv src/loaders/svg src/loaders/external_png  ../src/loaders/
+cp -rv src/loaders/jpg  ../src/loaders/
+cp -rv src/loaders/png src/loaders/external_png  ../src/loaders/
 
 popd
 rm -rf tmp