diff --git a/thirdparty/README.md b/thirdparty/README.md index 4572687be2e8..5bc76026c7cc 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.14.0 (ae4e9d003c93325f1eba64319fa9852a0d764b4c, 2024) +- Version: 0.14.1 (70b2f2dad158316dd08166d613b425248b36fd27, 2024) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 6218c18e68bd..4be7e3936df3 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -15,5 +15,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.14.0" +#define THORVG_VERSION_STRING "0.14.1" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index 0b7e9771b907..20f1942a5792 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/thirdparty/thorvg/inc/thorvg.h @@ -631,6 +631,8 @@ class TVG_API Canvas * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished, * the sync() must be called after the draw() regardless of threading. * + * @retval Result::InsufficientCondition: The canvas is either already in sync condition or in a damaged condition (a draw is required before syncing). + * * @see Canvas::draw() */ virtual Result sync() noexcept; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 231410cdacee..05cbdc7f3a09 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -23,6 +23,7 @@ #ifndef _TVG_SW_COMMON_H_ #define _TVG_SW_COMMON_H_ +#include #include "tvgCommon.h" #include "tvgRender.h" diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp index be1662daeb6a..bd0b5ffdcbbf 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp @@ -63,6 +63,66 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f } +static uint32_t _estimateAAMargin(const Fill* fdata) +{ + constexpr float marginScalingFactor = 800.0f; + if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { + auto radius = P(static_cast(fdata))->r; + return mathZero(radius) ? 0 : static_cast(marginScalingFactor / radius); + } + auto grad = P(static_cast(fdata)); + Point p1 {grad->x1, grad->y1}; + Point p2 {grad->x2, grad->y2}; + auto length = mathLength(&p1, &p2); + return mathZero(length) ? 0 : static_cast(marginScalingFactor / length); +} + + +static void _adjustAAMargin(uint32_t& iMargin, uint32_t index) +{ + constexpr float threshold = 0.1f; + constexpr uint32_t iMarginMax = 40; + + auto iThreshold = static_cast(index * threshold); + if (iMargin > iThreshold) iMargin = iThreshold; + if (iMargin > iMarginMax) iMargin = iMarginMax; +} + + +static inline uint32_t _alphaUnblend(uint32_t c) +{ + auto a = (c >> 24); + if (a == 255 || a == 0) return c; + auto invA = 255.0f / static_cast(a); + auto c0 = static_cast(static_cast((c >> 16) & 0xFF) * invA); + auto c1 = static_cast(static_cast((c >> 8) & 0xFF) * invA); + auto c2 = static_cast(static_cast(c & 0xFF) * invA); + + return (a << 24) | (c0 << 16) | (c1 << 8) | c2; +} + + +static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end) +{ + if (begin == 0 || end == 0) return; + + auto i = GRADIENT_STOP_SIZE - end; + auto rgbaEnd = _alphaUnblend(fill->ctable[i]); + auto rgbaBegin = _alphaUnblend(fill->ctable[begin]); + + auto dt = 1.0f / (begin + end + 1.0f); + float t = dt; + while (i != begin) { + auto dist = 255 - static_cast(255 * t); + auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist); + fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); + + if (i == GRADIENT_STOP_SIZE) i = 0; + t += dt; + } +} + + static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity) { if (!fill->ctable) { @@ -88,6 +148,11 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* auto pos = 1.5f * inc; uint32_t i = 0; + //If repeat is true, anti-aliasing must be applied between the last and the first colors. + auto repeat = fill->spread == FillSpread::Repeat; + uint32_t iAABegin = repeat ? _estimateAAMargin(fdata) : 0; + uint32_t iAAEnd = 0; + fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a); while (pos <= pColors->offset) { @@ -97,6 +162,11 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* } for (uint32_t j = 0; j < cnt - 1; ++j) { + if (repeat && j == cnt - 2 && iAAEnd == 0) { + iAAEnd = iAABegin; + _adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i); + } + auto curr = colors + j; auto next = curr + 1; auto delta = 1.0f / (next->offset - curr->offset); @@ -118,14 +188,18 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* } rgba = rgba2; a = a2; + + if (repeat && j == 0) _adjustAAMargin(iAABegin, i - 1); } rgba = ALPHA_BLEND((rgba | 0xff000000), a); for (; i < GRADIENT_STOP_SIZE; ++i) fill->ctable[i] = rgba; - //Make sure the last color stop is represented at the end of the table - fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba; + //For repeat fill spread apply anti-aliasing between the last and first colors, + //othewise make sure the last color stop is represented at the end of the table. + if (repeat) _applyAA(fill, iAABegin, iAAEnd); + else fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba; return true; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index 04f36c727f94..b3507acdc3a9 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -1087,6 +1087,7 @@ static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* im static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { + TVGERR("SW_ENGINE", "Not supported ScaledMaskedImage!"); #if 0 //Enable it when GRAYSCALE image is supported TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); @@ -1100,6 +1101,11 @@ static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, c static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!"); + return false; + } + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; @@ -1130,6 +1136,11 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!"); + return false; + } + 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); @@ -1152,19 +1163,33 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - 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); int32_t miny = 0, maxy = 0; - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - SCALED_IMAGE_RANGE_Y(y) - auto dst = dbuffer; - 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)); + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto buffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + for (auto y = region.min.y; y < region.max.y; ++y, buffer += surface->stride) { + SCALED_IMAGE_RANGE_Y(y) + auto dst = buffer; + 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)); + } + } + } else if (surface->channelSize == sizeof(uint8_t)) { + auto buffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); + for (auto y = region.min.y; y < region.max.y; ++y, buffer += surface->stride) { + SCALED_IMAGE_RANGE_Y(y) + auto dst = buffer; + 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); + *dst = MULTIPLY(A(src), opacity); + } } } return true; @@ -1173,11 +1198,6 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) { - if (surface->channelSize == sizeof(uint8_t)) { - TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!"); - return false; - } - Matrix itransform; if (transform) { diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 0a3f5ef7e7b7..350f33340506 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -20,6 +20,7 @@ * SOFTWARE. */ +#include #include "tvgMath.h" #include "tvgSwCommon.h" #include "tvgTaskScheduler.h" @@ -86,7 +87,7 @@ struct SwShapeTask : SwTask Additionally, the stroke style should not be dashed. */ bool antialiasing(float strokeWidth) { - return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim(); + return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim() || rshape->stroke->color[3] < 255;; } float validStrokeWidth() diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.h b/thirdparty/thorvg/src/renderer/tvgCanvas.h index 9d216e2f3072..81fd1b7d6f19 100644 --- a/thirdparty/thorvg/src/renderer/tvgCanvas.h +++ b/thirdparty/thorvg/src/renderer/tvgCanvas.h @@ -129,7 +129,7 @@ struct Canvas::Impl return Result::Success; } - return Result::InsufficientCondition; + return Result::Unknown; } Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index 8f28d37dbc12..a915d68fec33 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/thirdparty/thorvg/src/renderer/tvgRender.h @@ -103,7 +103,7 @@ struct RenderRegion void intersect(const RenderRegion& rhs); void add(const RenderRegion& rhs); - bool operator==(const RenderRegion& rhs) + bool operator==(const RenderRegion& rhs) const { if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true; return false; diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h index c45995a64d98..ecc58b6cc009 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/thirdparty/thorvg/src/renderer/tvgShape.h @@ -296,6 +296,7 @@ struct Shape::Impl if (!rs.stroke) rs.stroke = new RenderStroke(); if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); rs.stroke->fill = p; + rs.stroke->color[3] = 0; flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::GradientStroke; diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index d08158bfe78b..e964e5ab6d95 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.14.0 +VERSION=0.14.1 cd thirdparty/thorvg/ || true rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/