From 3556554a3552ca3ce404b5fd05d341de1ddb7e9d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 23 Dec 2021 10:40:09 +0900 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 From 0a891ea82bf9dd8d8d8b6aa777413c0b095be27e Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 8 Jan 2022 00:31:32 +0100 Subject: [PATCH 12/16] svg_loader: typo fixed _svgLoaderParerXmlClose -> _svgLoaderParserXmlClose Change-Id: I24a3fc61e52c74507dc6dd62ddda63f97ed3d05f --- src/loaders/svg/tvgSvgLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index aef549e..252a901 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2380,7 +2380,7 @@ static constexpr struct }; -static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content) +static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content) { content = _skipSpace(content, nullptr); @@ -2488,7 +2488,7 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content break; } case SimpleXMLType::Close: { - _svgLoaderParerXmlClose(loader, content); + _svgLoaderParserXmlClose(loader, content); break; } case SimpleXMLType::Data: -- 2.7.4 From 038dcb0883a6d9553029571046a7abbb1f761554 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 8 Jan 2022 02:17:44 +0100 Subject: [PATCH 13/16] svg_loader: LumaMask used only for masks other than white For the performance reasons, the LumaMask is used only when it's necessary - when the mask color is other than white. Otherwise the AlphaMask is enough. Change-Id: Ia97fb753854435e2e58eeb1ead40dbb3a5ead7cc --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index b6b581a..2c0d4e4 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -67,7 +67,7 @@ struct Box static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath); -static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask); +static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite = nullptr); static inline bool _isGroupType(SvgNodeType type) @@ -280,11 +280,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox if (compNode && compNode->child.count > 0) { node->style->mask.applying = true; - auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true); + bool isMaskWhite = true; + auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, &isMaskWhite); if (comp) { if (node->transform) comp->transform(*node->transform); - if (compNode->node.mask.type == SvgMaskType::Luminance) { + if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) { paint->composite(move(comp), CompositeMethod::LumaMask); } else { paint->composite(move(comp), CompositeMethod::AlphaMask); @@ -556,9 +557,9 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } -static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath) +static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite) { - auto scene = _sceneBuildHelper(node, vBox, svgPath, false); + auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite); if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { scene->translate(node->node.use.x, node->node.use.y); } @@ -569,7 +570,7 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c } -static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask) +static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite) { if (_isGroupType(node->type) || mask) { auto scene = Scene::gen(); @@ -580,15 +581,22 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, for (uint32_t i = 0; i < node->child.count; ++i, ++child) { if (_isGroupType((*child)->type)) { if ((*child)->type == SvgNodeType::Use) - scene->push(_useBuildHelper(*child, vBox, svgPath)); + scene->push(_useBuildHelper(*child, vBox, svgPath, isMaskWhite)); else - scene->push(_sceneBuildHelper(*child, vBox, svgPath, false)); + scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, isMaskWhite)); } else if ((*child)->type == SvgNodeType::Image) { auto image = _imageBuildHelper(*child, vBox, svgPath); if (image) scene->push(move(image)); } else if ((*child)->type != SvgNodeType::Mask) { auto shape = _shapeBuildHelper(*child, vBox, svgPath); - if (shape) scene->push(move(shape)); + if (shape) { + if (isMaskWhite) { + uint8_t r, g, b; + shape->fillColor(&r, &g, &b, nullptr); + if (shape->fill() || r < 255 || g < 255 || b < 255) *isMaskWhite = false; + } + scene->push(move(shape)); + } } } _applyComposition(scene.get(), node, vBox, svgPath); -- 2.7.4 From a370e322662de0e46bf679966a3297b4030f14fd Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 8 Jan 2022 23:47:38 +0100 Subject: [PATCH 14/16] svg_loader: preventing memory leak A memory leak occured when the 'id' attribute was given multiple times for a given gradient element. Fixed. Change-Id: I386565d016c23172dc6b2628ef0f0c158a461417 --- src/loaders/svg/tvgSvgLoader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 252a901..42f85a8 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2098,6 +2098,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char } if (!strcmp(key, "id")) { + if (grad->id && value) free(grad->id); grad->id = _copyId(value); } else if (!strcmp(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); @@ -2286,6 +2287,7 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char } if (!strcmp(key, "id")) { + if (grad->id && value) free(grad->id); grad->id = _copyId(value); } else if (!strcmp(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); -- 2.7.4 From 9a22c40a83de1de2e020cd649a15795029f41c00 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 7 Jan 2022 22:54:46 +0100 Subject: [PATCH 15/16] svg_loader: fix grad update The grad update should be handled after the postponed nodes are cloned. Change-Id: I7391d416a81a9cb465ea9a01f5360c126abc53a2 --- src/loaders/svg/tvgSvgLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 42f85a8..70f5edc 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2857,14 +2857,14 @@ void SvgLoader::run(unsigned tid) if (loaderData.doc) { _updateStyle(loaderData.doc, nullptr); auto defs = loaderData.doc->node.doc.defs; - if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); - - if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes); + + if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); + if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); } -- 2.7.4 From 4d5f800d4df2a53f2da27924b756f7512e91f292 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 8 Jan 2022 23:55:55 +0100 Subject: [PATCH 16/16] svg_loader: removing repeated lines The clip-path and the mask attributes are both parsed in _attrParseGNode function, which is called in th _attrParseUseNode. Change-Id: Ibef6256e25d63d52571949ead21241e3e4346bef --- src/loaders/svg/tvgSvgLoader.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 70f5edc..e40be9a 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1900,10 +1900,6 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) //after the whole file is parsed _postponeCloneNode(loader, node, id); } - } else if (!strcmp(key, "clip-path")) { - _handleClipPathAttr(loader, node, value); - } else if (!strcmp(key, "mask")) { - _handleMaskAttr(loader, node, value); } else { return _attrParseGNode(data, key, value); } -- 2.7.4