diff --git a/thirdparty/README.md b/thirdparty/README.md index 00d88206057..9186d94127f 100644 --- a/thirdparty/README.md +++ b/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: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 818739b113f..98f35a6e457 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/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 diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index 1b9c2d70d33..20acda7667f 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/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, 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, const std::string& path, uint32_t quality = 100, uint32_t fps = 0) noexcept; + /** * @brief Guarantees that the saving task is finished. * diff --git a/thirdparty/thorvg/src/common/tvgArray.h b/thirdparty/thorvg/src/common/tvgArray.h index 08eb25329cf..1afc647b688 100644 --- a/thirdparty/thorvg/src/common/tvgArray.h +++ b/thirdparty/thorvg/src/common/tvgArray.h @@ -25,6 +25,7 @@ #include #include +#include namespace tvg { diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp index b0a9fdd5799..30a66f9cada 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp @@ -107,9 +107,9 @@ unique_ptr 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; diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp index 61a5dc1c0ff..3cd852a4bb8 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +++ b/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(); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index c6dcf79a484..8fe7b77edd2 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/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(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 diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp index dbcfa754f31..d58dd9e3c5a 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp @@ -20,7 +20,7 @@ * SOFTWARE. */ -#include +#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 - 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) +static float TO_RADIAN(SwFixed angle) { - 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) -{ - //multiply a give value by the CORDIC shrink factor - auto s = abs(x); - int64_t t = (s * static_cast(CORDIC_FACTOR)) + 0x100000000UL; - s = static_cast(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((unsigned long)v.x << shift); - pt.y = static_cast((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(x); - pt.y = static_cast(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; + if (angle == 0 || pt.zero()) return; - auto v = pt; - auto shift = _normalize(v); + Point v = pt.toPoint(); - auto theta = angle; - _rotate(v, theta); + auto radian = TO_RADIAN(angle); + auto cosv = cosf(radian); + auto sinv = sinf(radian); - v.x = _downscale(v.x); - v.y = _downscale(v.y); - - if (shift > 0) { - auto half = static_cast(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((unsigned long)v.x << shift); - pt.y = static_cast((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); + if (pt.x == 0) return abs(pt.y); + if (pt.y == 0) return abs(pt.x); - //general case - auto shift = _normalize(v); - _polarize(v); - v.x = _downscale(v.x); + auto v = pt.toPoint(); + //return static_cast(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f); - if (shift > 0) return (v.x + (static_cast(1) << (shift -1))) >> shift; - return static_cast((uint32_t)v.x << -shift); + /* 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((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; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index 4b1ba59100d..96a0ed35ad8 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/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((sx - rx) * 255.0f); - auto dy = static_cast((sy - ry) * 255.0f); + auto dx = static_cast((sx - rx) * 255.0f); + auto dy = static_cast((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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 049aa3d1d0f..18815e69a94 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/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 diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 4f66cdacc06..ae2ddd2af79 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/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; } } diff --git a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp b/thirdparty/thorvg/src/renderer/tvgAnimation.cpp index 7a29ecf973c..b4ea534b91f 100644 --- a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp +++ b/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(); diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgCanvas.cpp index 2d4cbd048fa..25741f611df 100644 --- a/thirdparty/thorvg/src/renderer/tvgCanvas.cpp +++ b/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; } diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h index f36b4b9b305..2b67681a403 100644 --- a/thirdparty/thorvg/src/renderer/tvgCommon.h +++ b/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; diff --git a/thirdparty/thorvg/src/renderer/tvgFrameModule.h b/thirdparty/thorvg/src/renderer/tvgFrameModule.h index 857c6caeb95..332cca30908 100644 --- a/thirdparty/thorvg/src/renderer/tvgFrameModule.h +++ b/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; } diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.cpp b/thirdparty/thorvg/src/renderer/tvgLoader.cpp index 52bdb91d6a8..65330e9e778 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoader.cpp +++ b/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; } diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 37df906dac5..008e6589b5d 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/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(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; } diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h index 0ee07fedff9..f4721f8e15b 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.h +++ b/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& 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(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 - 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& 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_ diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp index 07877b92de1..e3827956982 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp +++ b/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(pImpl)); } diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp index 9768b5ede09..64d76d35620 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.cpp +++ b/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); } diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index 1231089ff52..6c0a7d5f135 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/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); }; diff --git a/thirdparty/thorvg/src/renderer/tvgSaveModule.h b/thirdparty/thorvg/src/renderer/tvgSaveModule.h index a997b644a71..09e7bde72bb 100644 --- a/thirdparty/thorvg/src/renderer/tvgSaveModule.h +++ b/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; }; diff --git a/thirdparty/thorvg/src/renderer/tvgSaver.cpp b/thirdparty/thorvg/src/renderer/tvgSaver.cpp index 3b4dfddcc23..038d1ad097b 100644 --- a/thirdparty/thorvg/src/renderer/tvgSaver.cpp +++ b/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, const string& path, bool compre } +Result Saver::save(std::unique_ptr 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; diff --git a/thirdparty/thorvg/src/renderer/tvgScene.cpp b/thirdparty/thorvg/src/renderer/tvgScene.cpp index 52e7ea2bf69..cd73913b303 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.cpp +++ b/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(pImpl)); } diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h index 10daa49a99b..b558e95a2f5 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.h +++ b/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) { diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp index efb7666b296..9a64c7df9fa 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.cpp +++ b/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(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); diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h index bb266866d0e..46b2d7d0dbd 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/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); diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 04d1dd0b9af..d4323848da7 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/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