From d411b51acf8844edd22a8e95aa9ade3467239435 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 22 Oct 2021 21:04:15 +0900 Subject: [PATCH 01/16] common: introduce iterator +++ auto picture = tvg::Picture::gen(); auto func = [](const tvg::Paint* paint, const tvg::Paint* parent, bool hasChildren) -> int { if (paint->identifier() == Shape::identifier()) //TODO: override properties. //return true to continue, return false to stop. return true; }; picture = tvg::Iteratorv::iterate(move(picture), func); Change-Id: I2ea956cb46b20547239686969c2e403e23fc8943 --- inc/thorvg.h | 10 +++++++++- src/lib/meson.build | 1 + src/lib/tvgInitializer.cpp | 2 +- src/lib/tvgIterator.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/lib/tvgIterator.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 49e112d..127663c 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1533,6 +1533,14 @@ public: _TVG_DECLARE_PRIVATE(Saver); }; + +class TVG_EXPORT Iteratorv final +{ +public: + static std::unique_ptr iterate(std::unique_ptr picture, int(*func)(const Paint* paint, const Paint* parent, bool hasChildren)) noexcept; +}; + + /** @}*/ } //namespace @@ -1541,4 +1549,4 @@ public: } #endif -#endif //_THORVG_H_ +#endif //_THORVG_H_ \ No newline at end of file diff --git a/src/lib/meson.build b/src/lib/meson.build index 9c31700..2d747f7 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -30,6 +30,7 @@ source_file = [ 'tvgFill.cpp', 'tvgGlCanvas.cpp', 'tvgInitializer.cpp', + 'tvgIterator.cpp', 'tvgLinearGradient.cpp', 'tvgLzw.cpp', 'tvgLoader.cpp', diff --git a/src/lib/tvgInitializer.cpp b/src/lib/tvgInitializer.cpp index 83ec50b..f1b1885 100644 --- a/src/lib/tvgInitializer.cpp +++ b/src/lib/tvgInitializer.cpp @@ -149,4 +149,4 @@ Result Initializer::term(CanvasEngine engine) noexcept uint16_t THORVG_VERSION_NUMBER() { return _version; -} +} \ No newline at end of file diff --git a/src/lib/tvgIterator.cpp b/src/lib/tvgIterator.cpp new file mode 100644 index 0000000..d0d4d1d --- /dev/null +++ b/src/lib/tvgIterator.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tvgCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + + + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +unique_ptr Iteratorv::iterate(unique_ptr root, int(*func)(const Paint* paint, const Paint* parent, bool hasChild)) noexcept +{ + //TODO: Preorder traversal + + return nullptr; +} \ No newline at end of file -- 2.7.4 From 6d25bdf34cb9232d4d2668e854d8eb6bc87b1f1d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 17 Dec 2021 12:43:49 +0900 Subject: [PATCH 02/16] sw_engine raster: ++safety of the scaled image rasterization Prevent out of buffer boundary access. @Issues: https://github.com/Samsung/thorvg/pull/1119 Change-Id: I9d007b00f4193448fe2fd05194621190144ad7ce --- src/lib/sw_engine/tvgSwRaster.cpp | 145 +++++++++++++++----------- src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 2 +- 2 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 9ef4cc0..56bc2f7 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -82,8 +82,8 @@ static inline uint32_t _halfScale(float scale) //Bilinear Interpolation static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, float sx, float sy) { - auto rx = static_cast(sx); - auto ry = static_cast(sy); + auto rx = (uint32_t)(sx); + auto ry = (uint32_t)(sy); auto rx2 = rx + 1; if (rx2 >= w) rx2 = w - 1; auto ry2 = ry + 1; @@ -102,20 +102,23 @@ static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, flo //2n x 2n Mean Kernel -static uint32_t _interpDownScaler(const uint32_t *img, uint32_t w, uint32_t h, uint32_t rX, uint32_t rY, uint32_t n) +static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, uint32_t rx, uint32_t ry, uint32_t n) { - uint32_t c[4] = { 0 }; + uint32_t c[4] = {0, 0, 0, 0}; auto n2 = n * n; - auto src = img + rX - n + (rY - n) * w; - for (auto y = rY - n; y < rY + n; ++y) { + auto src = img + rx - n + (ry - n) * stride; + + for (auto y = ry - n; y < ry + n; ++y) { + if (y >= h) continue; auto p = src; - for (auto x = rX - n; x < rX + n; ++x, ++p) { + for (auto x = rx - n; x < rx + n; ++x, ++p) { + if (x >= w) continue; c[0] += *p >> 24; c[1] += (*p >> 16) & 0xff; c[2] += (*p >> 8) & 0xff; c[3] += *p & 0xff; } - src += w; + src += stride; } for (auto i = 0; i < 4; ++i) { c[i] = (c[i] >> 2) / n2; @@ -292,15 +295,15 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), alpha); + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } @@ -308,14 +311,14 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const //Center (Up-Scaled) } else { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); @@ -335,22 +338,22 @@ static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* i //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); + auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), span->coverage); + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } @@ -359,21 +362,21 @@ static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* i //Center (Up-Scaled) } else { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto tmp = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage); auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); @@ -392,27 +395,27 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), alpha); + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } //Center (Up-Scaled) } else { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } @@ -429,21 +432,21 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, //Center (Down-Scaled) if (image->scale < DOWN_SCALE_TOLERANCE) { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); if (sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = _interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale); + auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), span->coverage); + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -451,20 +454,20 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, //Center (Up-Scaled) } else { for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = static_cast(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; auto dst = &surface->buffer[span->y * surface->stride + span->x]; if (span->coverage == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } else { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } @@ -663,13 +666,15 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { for (auto y = region.min.y; y < region.max.y; ++y) { + auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); + if (sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - auto sy = static_cast(y * itransform->e22 + itransform->e23); for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); + if (sx >= image->w) continue; auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp)); - auto sx = static_cast(x * itransform->e11 + itransform->e13); - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), alpha); + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; @@ -678,12 +683,14 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw // Up-Scaled } else { 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; auto dst = dbuffer; auto cmp = cbuffer; - auto sy = fabsf(y * itransform->e22 + itransform->e23); for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp)); - auto sx = fabsf(x * itransform->e11 + itransform->e13); auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } @@ -705,12 +712,14 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { for (auto y = region.min.y; y < region.max.y; ++y) { + auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); + if (sy >= image->h) continue; auto dst = dbuffer; auto cmp = cbuffer; - auto sy = static_cast(y * itransform->e22 + itransform->e23); for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); + if (sx >= image->w) continue; + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp)); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } dbuffer += surface->stride; @@ -719,11 +728,13 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag // Up-Scaled } else { 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; auto dst = dbuffer; auto cmp = cbuffer; - auto sy = fabsf(y * itransform->e22 + itransform->e23); for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) { - auto sx = fabsf(x * itransform->e11 + itransform->e13); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp)); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } @@ -742,11 +753,13 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = static_cast(y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); + if (sy >= image->h) continue; auto dst = dbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale), opacity); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); + if (sx >= image->w) continue; + auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), opacity); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } @@ -754,9 +767,11 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* } else { for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { auto sy = fabsf(y * itransform->e22 + itransform->e23); + if (sy >= image->h) continue; auto dst = dbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = fabsf(x * itransform->e11 + itransform->e13); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), opacity); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } @@ -773,21 +788,25 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con // Down-Scaled if (image->scale < DOWN_SCALE_TOLERANCE) { for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = static_cast(y * itransform->e22 + itransform->e23); + auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); + if (sy >= image->h) continue; auto dst = dbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = static_cast(x * itransform->e11 + itransform->e13); - auto src = _interpDownScaler(image->data, image->w, image->h, sx, sy, halfScale); + auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); + if (sx >= image->w) continue; + auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } } // Up-Scaled } else { for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = fabsf(y * itransform->e22 + itransform->e23); + auto sy = y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; auto dst = dbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = fabsf(x * itransform->e11 + itransform->e13); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy); *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); } diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index f7203e1..782c8f6 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -62,7 +62,7 @@ if (x1 < minx) x1 = minx; if (x2 > maxx) x2 = maxx; - + //Anti-Aliasing frames ay = y - aaSpans->yStart; if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; -- 2.7.4 From 875846decf78623d0cc0e623ef391d66b6fc3d38 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 17 Dec 2021 17:23:55 +0900 Subject: [PATCH 03/16] sw_engine raster: fix a crash at the texmap clipping. Handle correctly duplicated spans from the multiple y span data. Previous logic only expected the one single y span data from the rle. However rle might have multiple y span data if the anti-aliasing is applied. This patch also removed the bad design of the common engine which handles the anti-alising option to ignore the anti-aliasing rle generation. Just realized, it's difficult to control that condition due to scene-composition. Change-Id: I0caab0e22b0bb88bf0c7ed25b58229c216579086 --- src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 33 +++++++++++++++++---------- src/lib/sw_engine/tvgSwRenderer.cpp | 4 +--- src/lib/tvgPaint.cpp | 10 +------- src/lib/tvgRender.h | 2 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index 782c8f6..0c6bf13 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -30,7 +30,7 @@ int32_t dw = surface->stride; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = 0, maxx = 0; + int32_t minx, maxx; float dx, u, v, iptr; uint32_t* buf; SwSpan* span = nullptr; //used only when rle based. @@ -42,24 +42,36 @@ if (!_arrange(image, region, yStart, yEnd)) return; //Loop through all lines in the segment - y = yStart; + uint32_t spanIdx = 0; if (region) { minx = region->min.x; maxx = region->max.x; } else { - span = image->rle->spans + (yStart - image->rle->spans->y); + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } } + y = yStart; + while (y < yEnd) { x1 = _xa; x2 = _xb; if (!region) { - minx = span->x; - maxx = span->x + span->len; + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } } - if (x1 < minx) x1 = minx; if (x2 > maxx) x2 = maxx; @@ -138,12 +150,9 @@ next: _ua += _dudya; _va += _dvdya; - if (span) { - ++span; - y = span->y; - } else { - y++; - } + if (!region && spanIdx >= image->rle->size) break; + + ++y; } xa = _xa; xb = _xb; diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 672b938..78537e7 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -111,13 +111,11 @@ struct SwShapeTask : SwTask //Fill if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { if (visibleFill) { - auto antiAlias = (flags & RenderUpdateFlag::IgnoreAliasing) ? false : true; - /* We assume that if stroke width is bigger than 2, shape outline below stroke could be full covered by stroke drawing. Thus it turns off antialising in that condition. Also, it shouldn't be dash style. */ - if (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) antiAlias = false; + auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true; if (!shapeGenRle(&shape, sdata, antiAlias)) goto err; } diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index d258dd5..30e82fb 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -223,15 +223,7 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf } } if (!compFastTrack) { - //Bad design!: ignore anti-aliasing if the bitmap image is the source of the clip-path! - auto tempFlag = pFlag; - - if (id == TVG_CLASS_ID_PICTURE) { - auto picture = static_cast(compData->source); - if (picture->data(nullptr, nullptr)) tempFlag |= RenderUpdateFlag::IgnoreAliasing; - } - - tdata = target->pImpl->update(renderer, pTransform, 255, clips, tempFlag); + tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag); if (method == CompositeMethod::ClipPath) clips.push(tdata); } } diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index 7621fe2..f927947 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -28,7 +28,7 @@ namespace tvg { -enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255, IgnoreAliasing = 256}; +enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255}; struct Surface { -- 2.7.4 From de923f990fcdbff9e72fcb02e8c7451fc00a50f9 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Fri, 17 Dec 2021 16:04:26 -0800 Subject: [PATCH 04/16] Hide the thorvg LZW implementation. Change-Id: I9fc4049b0d3dd04f22e6327d44c71d663096e366 --- src/lib/tvgLzw.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/tvgLzw.cpp b/src/lib/tvgLzw.cpp index adbcb3f..0049c89 100644 --- a/src/lib/tvgLzw.cpp +++ b/src/lib/tvgLzw.cpp @@ -65,6 +65,7 @@ #include #include "tvgLzw.h" +namespace { //LZW Dictionary helper: constexpr int Nil = -1; constexpr int MaxDictBits = 12; @@ -333,7 +334,7 @@ static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, i } return true; } - +} /************************************************************************/ @@ -424,4 +425,4 @@ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, } -#endif \ No newline at end of file +#endif -- 2.7.4 From 2505b95e91f7c076e617c253c0642711a9ef927b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 22 Dec 2021 16:40:27 +0900 Subject: [PATCH 05/16] sw_engine texmap: ++safety Prevent range over just in case. Change-Id: I27ed449d1292bc8e191d0a6fb751ab27ea3ee125 --- src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index 0c6bf13..4e8d342 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -142,6 +142,8 @@ //Step UV horizontally u += _dudx; v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; } next: //Step along both edges -- 2.7.4 From 3556554a3552ca3ce404b5fd05d341de1ddb7e9d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 23 Dec 2021 10:40:09 +0900 Subject: [PATCH 06/16] sw_engine texmap: fix trivial compiler warnings. Change-Id: I3649d84e83c257df6d51429c52a88a1fc9d6fdd1 warning: suggest braces around initialization of subobject [-Wmissing-braces] --- src/lib/sw_engine/tvgSwRasterTexmap.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index ece14eb..2cf9fb4 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -572,10 +572,10 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const /* Prepare vertices. shift XY coordinates to match the sub-pixeling technique. */ Vertex vertices[4]; - vertices[0] = {{0.0f, 0.0f}, 0.0f, 0.0f}; - vertices[1] = {{float(image->w), 0.0f}, float(image->w), 0.0f}; - vertices[2] = {{float(image->w), float(image->h)}, float(image->w), float(image->h)}; - vertices[3] = {{0.0f, float(image->h)}, 0.0f, float(image->h)}; + vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; + vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}}; + vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; + vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform); -- 2.7.4 From f20cc67676bf0122203da9638592785389ea5c8c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 23 Dec 2021 11:29:58 +0900 Subject: [PATCH 07/16] Revert "common: introduce iterator" This reverts commit e947fef9a4170b601cb92b868ae71dfd675a9173. Bad... This patch was wrongly applied... Change-Id: I5af1b3755ff5a0b44028fc664d8fd8c80c3ed3cd --- inc/thorvg.h | 10 +--------- src/lib/meson.build | 1 - src/lib/tvgInitializer.cpp | 2 +- src/lib/tvgIterator.cpp | 41 ----------------------------------------- 4 files changed, 2 insertions(+), 52 deletions(-) delete mode 100644 src/lib/tvgIterator.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 127663c..49e112d 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1533,14 +1533,6 @@ public: _TVG_DECLARE_PRIVATE(Saver); }; - -class TVG_EXPORT Iteratorv final -{ -public: - static std::unique_ptr iterate(std::unique_ptr picture, int(*func)(const Paint* paint, const Paint* parent, bool hasChildren)) noexcept; -}; - - /** @}*/ } //namespace @@ -1549,4 +1541,4 @@ public: } #endif -#endif //_THORVG_H_ \ No newline at end of file +#endif //_THORVG_H_ diff --git a/src/lib/meson.build b/src/lib/meson.build index 2d747f7..9c31700 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -30,7 +30,6 @@ source_file = [ 'tvgFill.cpp', 'tvgGlCanvas.cpp', 'tvgInitializer.cpp', - 'tvgIterator.cpp', 'tvgLinearGradient.cpp', 'tvgLzw.cpp', 'tvgLoader.cpp', diff --git a/src/lib/tvgInitializer.cpp b/src/lib/tvgInitializer.cpp index f1b1885..83ec50b 100644 --- a/src/lib/tvgInitializer.cpp +++ b/src/lib/tvgInitializer.cpp @@ -149,4 +149,4 @@ Result Initializer::term(CanvasEngine engine) noexcept uint16_t THORVG_VERSION_NUMBER() { return _version; -} \ No newline at end of file +} diff --git a/src/lib/tvgIterator.cpp b/src/lib/tvgIterator.cpp deleted file mode 100644 index d0d4d1d..0000000 --- a/src/lib/tvgIterator.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved. - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "tvgCommon.h" - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -unique_ptr Iteratorv::iterate(unique_ptr root, int(*func)(const Paint* paint, const Paint* parent, bool hasChild)) noexcept -{ - //TODO: Preorder traversal - - return nullptr; -} \ No newline at end of file -- 2.7.4 From 2988e8bea25bfb271f1f0c86f0118b50f98d6847 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 23 Dec 2021 11:34:31 +0900 Subject: [PATCH 08/16] common: Introduced Accessor for traversing the scene-tree. Basically, this Accessor is a utility to debug the Scene structure, by traversing the scene-tree by users. You can search specific nodes to read the property information, figure out the structure of the scene tree and its size. We actually don't recommend you to touch the property unless you really know the each paint's position and role because it's not visible, difficult to understand its anatomy. Also, You must underatnd that modifying the nodes of the scene will be going well with both the art-design structure and the prorgram logic. In this first version, Accessor only supports for the Picture class. @example: auto picture = tvg::Picture::gen(); picture->load("test.svg"); //The callback function from lambda expression. //This function will be called for every paint nodes of the tree. auto f = [](const tvg::Paint* paint) -> bool { if (paint->identifier() == Shape::identifier()) { //override properties? uint8_t r, g, b, a; paint->fillColor(&r, &g, &b, &a); paint->fill(r / 2, g / 2, b / 2, a); } //You can return false, to stop traversing immediately. return true; }; auto accessor = tvg::Accessor::gen(); picture = accessor->access(move(picture), f); ... @Issue: https://github.com/Samsung/thorvg/issues/693 Change-Id: I4f46754de46dfab9bb5b14f58cc659237344822a --- inc/thorvg.h | 45 +++++++++++++ src/examples/Accessor.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++++++ src/examples/meson.build | 1 + src/lib/meson.build | 1 + src/lib/tvgAccessor.cpp | 84 ++++++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 src/examples/Accessor.cpp create mode 100644 src/lib/tvgAccessor.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 49e112d..e542d36 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -50,6 +50,7 @@ protected: \ friend Canvas; \ friend Scene; \ friend Picture; \ + friend Accessor; \ friend IteratorAccessor @@ -61,6 +62,7 @@ class IteratorAccessor; class Scene; class Picture; class Canvas; +class Accessor; /** * @defgroup ThorVG ThorVG @@ -1533,6 +1535,49 @@ public: _TVG_DECLARE_PRIVATE(Saver); }; + +/** + * @class Accessor + * + * @brief The Accessor is a utility class to debug the Scene structure by traversing the scene-tree. + * + * The Accessor helps you search specific nodes to read the property information, figure out the structure of the scene tree and its size. + * + * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure. + * + * @BETA_API + */ +class TVG_EXPORT Accessor final +{ +public: + ~Accessor(); + + /** + * @brief Access the Picture scene stree nodes. + * + * @param[in] picture The picture node to traverse the internal scene-tree. + * @param[in] func The callback function calling for every paint nodes of the Picture. + * + * @return Return the given @p picture instance. + * + * @note The bitmap based picture might not have the scene-tree. + * + * @BETA_API + */ + std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint)) noexcept; + + /** + * @brief Creates a new Accessor object. + * + * @return A new Accessor object. + * + * @BETA_API + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(Accessor); +}; + /** @}*/ } //namespace diff --git a/src/examples/Accessor.cpp b/src/examples/Accessor.cpp new file mode 100644 index 0000000..975f04f --- /dev/null +++ b/src/examples/Accessor.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Common.h" + +/************************************************************************/ +/* Drawing Commands */ +/************************************************************************/ + + +void tvgDrawCmds(tvg::Canvas* canvas) +{ + if (!canvas) return; + + //load the tvg file + auto picture = tvg::Picture::gen(); + if (picture->load(EXAMPLE_DIR"/favorite_on.svg") != tvg::Result::Success) return; + picture->size(WIDTH, HEIGHT); + + auto accessor = tvg::Accessor::gen(); + + //The callback function from lambda expression. + //This function will be called for every paint nodes of the picture tree. + auto f = [](const tvg::Paint* paint) -> bool + { + if (paint->identifier() == tvg::Shape::identifier()) { + auto shape = (tvg::Shape*) paint; + //override color? + uint8_t r, g, b, a; + shape->fillColor(&r, &g, &b, &a); + if (r == 255 && g == 180 && b == 0) + shape->fill(0, 0, 255, a); + } + + //You can return false, to stop traversing immediately. + return true; + }; + + picture = accessor->access(move(picture), f); + + canvas->push(move(picture)); +} + + +/************************************************************************/ +/* Sw Engine Test Code */ +/************************************************************************/ + +static unique_ptr swCanvas; + +void tvgSwTest(uint32_t* buffer) +{ + //Create a Canvas + swCanvas = tvg::SwCanvas::gen(); + swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888); + + /* Push the shape into the Canvas drawing list + When this shape is into the canvas list, the shape could update & prepare + internal data asynchronously for coming rendering. + Canvas keeps this shape node unless user call canvas->clear() */ + tvgDrawCmds(swCanvas.get()); +} + +void drawSwView(void* data, Eo* obj) +{ + if (swCanvas->draw() == tvg::Result::Success) { + swCanvas->sync(); + } +} + + +/************************************************************************/ +/* GL Engine Test Code */ +/************************************************************************/ + +static unique_ptr glCanvas; + +void initGLview(Evas_Object *obj) +{ + static constexpr auto BPP = 4; + + //Create a Canvas + glCanvas = tvg::GlCanvas::gen(); + glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT); + + /* Push the shape into the Canvas drawing list + When this shape is into the canvas list, the shape could update & prepare + internal data asynchronously for coming rendering. + Canvas keeps this shape node unless user call canvas->clear() */ + tvgDrawCmds(glCanvas.get()); +} + +void drawGLview(Evas_Object *obj) +{ + auto gl = elm_glview_gl_api_get(obj); + gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + gl->glClear(GL_COLOR_BUFFER_BIT); + + if (glCanvas->draw() == tvg::Result::Success) { + glCanvas->sync(); + } +} + + +/************************************************************************/ +/* Main Code */ +/************************************************************************/ + +int main(int argc, char **argv) +{ + tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw; + + if (argc > 1) { + if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl; + } + + //Initialize ThorVG Engine + if (tvgEngine == tvg::CanvasEngine::Sw) { + cout << "tvg engine: software" << endl; + } else { + cout << "tvg engine: opengl" << endl; + } + + //Threads Count + auto threads = std::thread::hardware_concurrency(); + + //Initialize ThorVG Engine + if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) { + + elm_init(argc, argv); + + if (tvgEngine == tvg::CanvasEngine::Sw) { + createSwView(); + } else { + createGlView(); + } + + elm_run(); + elm_shutdown(); + + //Terminate ThorVG Engine + tvg::Initializer::term(tvgEngine); + + } else { + cout << "engine is not supported" << endl; + } + return 0; +} \ No newline at end of file diff --git a/src/examples/meson.build b/src/examples/meson.build index 0d53348..31331c8 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -1,6 +1,7 @@ examples_dep = dependency('elementary', required : true) source_file = [ + 'Accessor.cpp', 'AnimateMasking.cpp', 'Arc.cpp', 'Async.cpp', diff --git a/src/lib/meson.build b/src/lib/meson.build index 9c31700..f28760d 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -25,6 +25,7 @@ source_file = [ 'tvgSceneImpl.h', 'tvgShapeImpl.h', 'tvgTaskScheduler.h', + 'tvgAccessor.cpp', 'tvgBezier.cpp', 'tvgCanvas.cpp', 'tvgFill.cpp', diff --git a/src/lib/tvgAccessor.cpp b/src/lib/tvgAccessor.cpp new file mode 100644 index 0000000..085c8a3 --- /dev/null +++ b/src/lib/tvgAccessor.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tvgIteratorAccessor.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor) +{ + while (auto child = it->next()) { + //Access the child + if (!func(child)) return false; + + //Access the children of the child + if (auto it2 = itrAccessor.iterator(child)) { + if (!accessChildren(it2, func, itrAccessor)) { + delete(it2); + return false; + } + delete(it2); + } + } + return true; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +unique_ptr Accessor::access(unique_ptr picture, bool(*func)(const Paint* paint)) noexcept +{ + auto p = picture.get(); + if (!p || !func) return picture; + + //Use the Preorder Tree-Search + + //Root + if (!func(p)) return picture; + + //Children + IteratorAccessor itrAccessor; + if (auto it = itrAccessor.iterator(p)) { + accessChildren(it, func, itrAccessor); + delete(it); + } + return picture; +} + + +Accessor::~Accessor() +{ + +} + + +Accessor::Accessor() +{ + +} + + +unique_ptr Accessor::gen() noexcept +{ + return unique_ptr(new Accessor); +} \ No newline at end of file -- 2.7.4 From ada1526fedd9883dd999d9a4075043d80231882a Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 23 Feb 2023 10:30:44 +0900 Subject: [PATCH 09/16] Bump up 0.7.0 Change-Id: I94c0b425e13a8fae232d1ba9c63cd012ebda157c Signed-off-by: jykeon --- packaging/thorvg.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/thorvg.spec b/packaging/thorvg.spec index b58cec2..f958ea5 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.6.7 +Version: 0.7.0 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4 From 07dfa5dc05a12605f7e60ac3870f8071a736465c Mon Sep 17 00:00:00 2001 From: Michal Maciola Date: Thu, 30 Dec 2021 09:28:27 +0100 Subject: [PATCH 10/16] compositeMethod: introduced LumaMask Introduced CompositeMethod::LumaMask that converts the source pixels to the grayscale (luma value) before alpha blending. Thanks to it, mask works more like typical mask in graphics editor software. Grayscale is calculated with weighted method: (0.0721*B + 0.7154*G + 0.2125*R) * A Introduced surface->blender.lumaValue function Change-Id: Ia0258e2f374aa27acaef9b2f4161af5b04400422 --- inc/thorvg.h | 5 ++-- src/lib/sw_engine/tvgSwCommon.h | 1 + src/lib/sw_engine/tvgSwRaster.cpp | 54 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index e542d36..b08356d 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -145,8 +145,9 @@ enum class TVG_EXPORT CompositeMethod { None = 0, ///< No composition is applied. ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. - AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which intersects with the target is visible. - InvAlphaMask ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible. + AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible. + InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible. + LumaMask ///< @BETA_API The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. }; /** diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index e0ffc1f..4d940eb 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -235,6 +235,7 @@ struct SwImage struct SwBlender { uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + uint32_t (*lumaValue)(uint32_t c); }; struct SwCompositor; diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 56bc2f7..6b5e00a 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -47,6 +47,18 @@ static inline uint32_t _ialpha(uint32_t c) } +static inline uint32_t _abgrLumaValue(uint32_t c) +{ + return ((((c&0xff)*54) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B +} + + +static inline uint32_t _argbLumaValue(uint32_t c) +{ + return ((((c&0xff)*19) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R +} + + static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24 | b << 16 | g << 8 | r); @@ -139,7 +151,7 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; @@ -173,6 +185,8 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color return _rasterMaskedRect(surface, region, color, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterMaskedRect(surface, region, color, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterMaskedRect(surface, region, color, surface->blender.lumaValue); } } else { if (opacity == 255) { @@ -246,6 +260,8 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8 return _rasterMaskedRle(surface, rle, color, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterMaskedRle(surface, rle, color, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterMaskedRle(surface, rle, color, surface->blender.lumaValue); } } else { if (opacity == 255) { @@ -275,6 +291,8 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.lumaValue); } } else { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr); @@ -494,12 +512,16 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); } } } else { @@ -616,12 +638,16 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 return _rasterDirectMaskedRleRGBAImage(surface, image, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.lumaValue); } } } else { @@ -643,6 +669,8 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.lumaValue); } } else { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, nullptr); @@ -832,12 +860,16 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); } } } else { @@ -861,7 +893,7 @@ static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* imag auto w2 = static_cast(region.max.x - region.min.x); auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; @@ -888,7 +920,7 @@ static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const Sw auto w2 = static_cast(region.max.x - region.min.x); auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; @@ -952,12 +984,16 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB return _rasterDirectMaskedRGBAImage(surface, image, region, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.lumaValue); } } } else { @@ -1062,6 +1098,8 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, return _rasterLinearGradientMaskedRect(surface, region, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill); @@ -1166,6 +1204,8 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterLinearGradientMaskedRle(surface, rle, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill); @@ -1253,6 +1293,8 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, return _rasterRadialGradientMaskedRect(surface, region, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill); @@ -1356,6 +1398,8 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterRadialGradientMaskedRle(surface, rle, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); } } else { if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill); @@ -1385,8 +1429,10 @@ bool rasterCompositor(SwSurface* surface) { if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) { surface->blender.join = _abgrJoin; + surface->blender.lumaValue = _abgrLumaValue; } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { surface->blender.join = _argbJoin; + surface->blender.lumaValue = _argbLumaValue; } else { //What Color Space ??? return false; @@ -1500,4 +1546,4 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co //TODO: case: _rasterGrayscaleImage() //TODO: case: _rasterAlphaImage() return _rasterRGBAImage(surface, image, transform, bbox, opacity); -} \ No newline at end of file +} -- 2.7.4 From e1a88e73467cc3fadccb8cbb42700d127f7b4632 Mon Sep 17 00:00:00 2001 From: Michal Maciola <71131832+mmaciola@users.noreply.github.com> Date: Thu, 30 Dec 2021 15:56:25 +0100 Subject: [PATCH 11/16] example: added LumaMasking.cpp (#621) Added an example LumaMasking.cpp that tests CompositeMethod::LumaMask Change-Id: I30d6fde4a209b8a818ad2240025832f44b8d4f84 --- src/examples/LumaMasking.cpp | 217 +++++++++++++++++++++++++++++++++++++++++++ src/examples/meson.build | 1 + 2 files changed, 218 insertions(+) create mode 100644 src/examples/LumaMasking.cpp diff --git a/src/examples/LumaMasking.cpp b/src/examples/LumaMasking.cpp new file mode 100644 index 0000000..8b4c4fb --- /dev/null +++ b/src/examples/LumaMasking.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Common.h" +#include + +/************************************************************************/ +/* Drawing Commands */ +/************************************************************************/ + +void tvgDrawCmds(tvg::Canvas* canvas) +{ + if (!canvas) return; + + //Solid Rectangle + auto shape = tvg::Shape::gen(); + shape->appendRect(0, 0, 400, 400, 0, 0); + shape->fill(255, 0, 0, 255); + + //Mask + auto mask = tvg::Shape::gen(); + mask->appendCircle(200, 200, 125, 125); + mask->fill(255, 0, 0, 255); + shape->composite(move(mask), tvg::CompositeMethod::LumaMask); + canvas->push(move(shape)); + + //SVG + auto svg = tvg::Picture::gen(); + if (svg->load(EXAMPLE_DIR"/cartman.svg") != tvg::Result::Success) return; + svg->opacity(100); + svg->scale(3); + svg->translate(50, 400); + + //Mask2 + auto mask2 = tvg::Shape::gen(); + mask2->appendCircle(150, 500, 75, 75); + mask2->appendRect(150, 500, 200, 200, 30, 30); + mask2->fill(255, 255, 255, 255); + svg->composite(move(mask2), tvg::CompositeMethod::LumaMask); + if (canvas->push(move(svg)) != tvg::Result::Success) return; + + //Star + auto star = tvg::Shape::gen(); + star->fill(80, 80, 80, 255); + star->moveTo(599, 34); + star->lineTo(653, 143); + star->lineTo(774, 160); + star->lineTo(687, 244); + star->lineTo(707, 365); + star->lineTo(599, 309); + star->lineTo(497, 365); + star->lineTo(512, 245); + star->lineTo(426, 161); + star->lineTo(546, 143); + star->close(); + star->stroke(10); + star->stroke(255, 255, 255, 255); + + //Mask3 + auto mask3 = tvg::Shape::gen(); + mask3->appendCircle(600, 200, 125, 125); + mask3->fill(0, 255, 255, 255); + star->composite(move(mask3), tvg::CompositeMethod::LumaMask); + if (canvas->push(move(star)) != tvg::Result::Success) return; + + //Image + ifstream file(EXAMPLE_DIR"/rawimage_200x300.raw"); + if (!file.is_open()) return; + auto data = (uint32_t*) malloc(sizeof(uint32_t) * (200 * 300)); + file.read(reinterpret_cast(data), sizeof (uint32_t) * 200 * 300); + file.close(); + + auto image = tvg::Picture::gen(); + if (image->load(data, 200, 300, true) != tvg::Result::Success) return; + image->translate(500, 400); + + //Mask4 + auto mask4 = tvg::Scene::gen(); + auto mask4_rect = tvg::Shape::gen(); + mask4_rect->appendRect(500, 400, 200, 300, 0, 0); + mask4_rect->fill(255, 255, 255, 255); + auto mask4_circle = tvg::Shape::gen(); + mask4_circle->appendCircle(600, 550, 125, 125); + mask4_circle->fill(128, 0, 128, 224); + if (mask4->push(move(mask4_rect)) != tvg::Result::Success) return; + if (mask4->push(move(mask4_circle)) != tvg::Result::Success) return; + image->composite(move(mask4), tvg::CompositeMethod::LumaMask); + if (canvas->push(move(image)) != tvg::Result::Success) return; + + free(data); +} + + +/************************************************************************/ +/* Sw Engine Test Code */ +/************************************************************************/ + +static unique_ptr swCanvas; + +void tvgSwTest(uint32_t* buffer) +{ + //Create a Canvas + swCanvas = tvg::SwCanvas::gen(); + swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888); + + /* Push the shape into the Canvas drawing list + When this shape is into the canvas list, the shape could update & prepare + internal data asynchronously for coming rendering. + Canvas keeps this shape node unless user call canvas->clear() */ + tvgDrawCmds(swCanvas.get()); +} + +void drawSwView(void* data, Eo* obj) +{ + if (swCanvas->draw() == tvg::Result::Success) { + swCanvas->sync(); + } +} + + +/************************************************************************/ +/* GL Engine Test Code */ +/************************************************************************/ + +static unique_ptr glCanvas; + +void initGLview(Evas_Object *obj) +{ + static constexpr auto BPP = 4; + + //Create a Canvas + glCanvas = tvg::GlCanvas::gen(); + glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT); + + /* Push the shape into the Canvas drawing list + When this shape is into the canvas list, the shape could update & prepare + internal data asynchronously for coming rendering. + Canvas keeps this shape node unless user call canvas->clear() */ + tvgDrawCmds(glCanvas.get()); +} + +void drawGLview(Evas_Object *obj) +{ + auto gl = elm_glview_gl_api_get(obj); + gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + gl->glClear(GL_COLOR_BUFFER_BIT); + + if (glCanvas->draw() == tvg::Result::Success) { + glCanvas->sync(); + } +} + + +/************************************************************************/ +/* Main Code */ +/************************************************************************/ + +int main(int argc, char **argv) +{ + tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw; + + if (argc > 1) { + if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl; + } + + //Initialize ThorVG Engine + if (tvgEngine == tvg::CanvasEngine::Sw) { + cout << "tvg engine: software" << endl; + } else { + cout << "tvg engine: opengl" << endl; + } + + //Threads Count + auto threads = std::thread::hardware_concurrency(); + if (threads > 0) --threads; //Allow the designated main thread capacity + + //Initialize ThorVG Engine + if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) { + + elm_init(argc, argv); + + if (tvgEngine == tvg::CanvasEngine::Sw) { + createSwView(); + } else { + createGlView(); + } + + elm_run(); + elm_shutdown(); + + //Terminate ThorVG Engine + tvg::Initializer::term(tvg::CanvasEngine::Sw); + + } else { + cout << "engine is not supported" << endl; + } + return 0; +} diff --git a/src/examples/meson.build b/src/examples/meson.build index 31331c8..778bc2c 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -18,6 +18,7 @@ source_file = [ 'ImageScaleUp.cpp', 'InvMasking.cpp', 'LinearGradient.cpp', + 'LumaMasking.cpp', 'Masking.cpp', 'MultiCanvas.cpp', 'MultiShapes.cpp', -- 2.7.4 From e9014711363ee7dce9da5b89eb633158ef59ff30 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 31 Dec 2021 12:00:15 +0100 Subject: [PATCH 12/16] svg_loader: composite node splitted on mask and clip nodes The SvgCompositeNode is replaced by the SvgMaskNode and SvgClipNode. This is needed for using Luma/AlphaMask in the svg loader and in the future, when we introduce other mask's features. Change-Id: Ieb90615da7dedcabe296950af20b59ac5d81e6ba --- src/loaders/svg/tvgSvgLoader.cpp | 12 ++++++------ src/loaders/svg/tvgSvgLoaderCommon.h | 10 ++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index def8ae1..24aba19 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1029,7 +1029,7 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; - SvgCompositeNode* comp = &(node->node.comp); + SvgClipNode* clip = &(node->node.clip); if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader); @@ -1039,7 +1039,7 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu if (node->id && value) free(node->id); node->id = _copyId(value); } else if (!strcmp(key, "clipPathUnits")) { - if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false; + if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false; } else { return _parseStyleAttr(loader, key, value, false); } @@ -1051,7 +1051,7 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; SvgNode* node = loader->svgParse->node; - SvgCompositeNode* comp = &(node->node.comp); + SvgMaskNode* mask = &(node->node.mask); if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader); @@ -1061,7 +1061,7 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value) if (node->id && value) free(node->id); node->id = _copyId(value); } else if (!strcmp(key, "maskContentUnits")) { - if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false; + if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false; } else { return _parseStyleAttr(loader, key, value, false); } @@ -1171,7 +1171,7 @@ static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUS loader->svgParse->node = _createNode(parent, SvgNodeType::Mask); if (!loader->svgParse->node) return nullptr; - loader->svgParse->node->node.comp.userSpace = true; + loader->svgParse->node->node.mask.userSpace = true; simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader); @@ -1185,7 +1185,7 @@ static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, cons if (!loader->svgParse->node) return nullptr; loader->svgParse->node->display = false; - loader->svgParse->node->node.comp.userSpace = true; + loader->svgParse->node->node.clip.userSpace = true; simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index cceef91..585e4e1 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -215,7 +215,12 @@ struct SvgPolygonNode float* points; }; -struct SvgCompositeNode +struct SvgClipNode +{ + bool userSpace; +}; + +struct SvgMaskNode { bool userSpace; }; @@ -352,7 +357,8 @@ struct SvgNode SvgPathNode path; SvgLineNode line; SvgImageNode image; - SvgCompositeNode comp; + SvgMaskNode mask; + SvgClipNode clip; } node; bool display; ~SvgNode(); -- 2.7.4 From 0e76934e3e3209f55e8a79a4c01ae2b591c3c0f8 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 4 Jan 2022 17:42:41 -0800 Subject: [PATCH 13/16] sw_engine SwRaster: Initialize uninitialized transform value Change-Id: I12e6a63cebbd765c19e83194587722a19db06fa2 --- src/lib/sw_engine/tvgSwRaster.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 6b5e00a..c51fda9 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -500,9 +500,8 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const { Matrix itransform; - if (transform) { - if (!mathInverse(transform, &itransform)) return false; - } else mathIdentity(&itransform); + if (transform && !mathInverse(transform, &itransform)) return false; + else mathIdentity(&itransform); auto halfScale = _halfScale(image->scale); @@ -848,9 +847,8 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat { Matrix itransform; - if (transform) { - if (!mathInverse(transform, &itransform)) return false; - } else mathIdentity(&itransform); + if (transform && !mathInverse(transform, &itransform)) return false; + else mathIdentity(&itransform); auto halfScale = _halfScale(image->scale); -- 2.7.4 From b3b39593237fb02e80ac8636ed8b4cda7cd3901e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 6 Jan 2022 12:32:16 +0900 Subject: [PATCH 14/16] sw_engine raster: fix a regression bug. Picture example were broken by 90fa26b7bb75cb938290170882e65da8d9fc5d9e the correct condition must be like this change. Change-Id: Ib3119a832118221c115cbc6ba9cf5f96ab7625b4 --- src/lib/sw_engine/tvgSwRaster.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index c51fda9..6b5e00a 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -500,8 +500,9 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const { Matrix itransform; - if (transform && !mathInverse(transform, &itransform)) return false; - else mathIdentity(&itransform); + if (transform) { + if (!mathInverse(transform, &itransform)) return false; + } else mathIdentity(&itransform); auto halfScale = _halfScale(image->scale); @@ -847,8 +848,9 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat { Matrix itransform; - if (transform && !mathInverse(transform, &itransform)) return false; - else mathIdentity(&itransform); + if (transform) { + if (!mathInverse(transform, &itransform)) return false; + } else mathIdentity(&itransform); auto halfScale = _halfScale(image->scale); -- 2.7.4 From c53a728d699b8474e5d93775d2686c435891ac35 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 6 Jan 2022 12:40:52 +0900 Subject: [PATCH 15/16] svg_loader: ++robustness prevent a crash with an exceptional handling. @Issue: https://github.com/Samsung/thorvg/issues/1131 Change-Id: If45a5ae201165ba97f087cdeb8c6e35d97adca72 --- src/loaders/svg/tvgSvgLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 24aba19..5a9eae2 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -540,7 +540,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** } } } - } else if (len >= 3 && !strncmp(str, "url", 3)) { + } else if (ref && len >= 3 && !strncmp(str, "url", 3)) { *ref = _idFromUrl((const char*)(str + 3)); } else { //Handle named color -- 2.7.4 From bb04761a9af81f986b83bdfba022881574bf3a11 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 2 Jan 2022 02:23:11 +0100 Subject: [PATCH 16/16] svg_loader: mask-type attribute introduced In an svg file the mask-type attribute can be specified. It takes one of the two values: Luminosity or Alpha. After the LumaMask is introduced into TVG, this attribute can be properly read by the svg loader. Change-Id: I8de1c94aed6bc0e7b31c1026b57f3336f29c2e30 --- src/loaders/svg/tvgSvgLoader.cpp | 18 ++++++++++++++++++ src/loaders/svg/tvgSvgLoaderCommon.h | 10 +++++++++- src/loaders/svg/tvgSvgSceneBuilder.cpp | 9 +++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 5a9eae2..aef549e 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -199,6 +199,14 @@ static int _toOpacity(const char* str) } +static SvgMaskType _toMaskType(const char* str) +{ + if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha; + + return SvgMaskType::Luminance; +} + + #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \ static Type _to##Name1(const char* str) \ { \ @@ -921,6 +929,12 @@ static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con } +static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->node.mask.type = _toMaskType(value); +} + + static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { //TODO : The display attribute can have various values as well as "none". @@ -958,6 +972,7 @@ static constexpr struct STYLE_DEF(transform, Transform, SvgStyleFlags::Transform), STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath), STYLE_DEF(mask, Mask, SvgStyleFlags::Mask), + STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType), STYLE_DEF(display, Display, SvgStyleFlags::Display) }; @@ -1062,6 +1077,8 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value) node->id = _copyId(value); } else if (!strcmp(key, "maskContentUnits")) { if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false; + } else if (!strcmp(key, "mask-type")) { + mask->type = _toMaskType(value); } else { return _parseStyleAttr(loader, key, value, false); } @@ -1172,6 +1189,7 @@ static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUS if (!loader->svgParse->node) return nullptr; loader->svgParse->node->node.mask.userSpace = true; + loader->svgParse->node->node.mask.type = SvgMaskType::Luminance; simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 585e4e1..bf94beb 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -111,7 +111,8 @@ enum class SvgStyleFlags Transform = 0x800, ClipPath = 0x1000, Mask = 0x2000, - Display = 0x4000 + MaskType = 0x4000, + Display = 0x8000 }; enum class SvgStopStyleFlags @@ -127,6 +128,12 @@ enum class SvgFillRule OddEven = 1 }; +enum class SvgMaskType +{ + Luminance = 0, + Alpha +}; + //Length type to recalculate %, pt, pc, mm, cm etc enum class SvgParserLengthType { @@ -222,6 +229,7 @@ struct SvgClipNode struct SvgMaskNode { + SvgMaskType type; bool userSpace; }; diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 8701fe3..b6b581a 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -275,7 +275,7 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox Composition can be applied recursively if its children nodes have composition target to this one. */ if (node->style->mask.applying) { TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?"); - } else { + } else { auto compNode = node->style->mask.node; if (compNode && compNode->child.count > 0) { node->style->mask.applying = true; @@ -283,7 +283,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true); if (comp) { if (node->transform) comp->transform(*node->transform); - paint->composite(move(comp), CompositeMethod::AlphaMask); + + if (compNode->node.mask.type == SvgMaskType::Luminance) { + paint->composite(move(comp), CompositeMethod::LumaMask); + } else { + paint->composite(move(comp), CompositeMethod::AlphaMask); + } } node->style->mask.applying = false; -- 2.7.4