From 87beeec2192721f7b76000e248f0735f0ce2cf34 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 28 Aug 2022 23:45:29 +0200 Subject: [PATCH 01/16] svg_loader: fill and stroke paiint url were copied twice The url were copied in the _copyAttr and in the _styleCopy functions. Change-Id: Ia68f3f4be8c68e4be517d1376e0cf68e1d468bcc --- src/loaders/svg/tvgSvgLoader.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 00c0911..3b3183d 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1992,8 +1992,6 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) //Copy style attribute _styleCopy(to->style, from->style); to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags); - if (from->style->fill.paint.url) to->style->fill.paint.url = strdup(from->style->fill.paint.url); - if (from->style->stroke.paint.url) to->style->stroke.paint.url = strdup(from->style->stroke.paint.url); if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url); if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url); -- 2.7.4 From 7a1bc9daa4e82a3b60a78c8f5fbad5c65bf86954 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 29 Aug 2022 13:32:06 +0900 Subject: [PATCH 02/16] svg_loader: If there is already set color url, it will be deleted. When setting the url for color, if there is an already set url, it will be overwritten after deletion. This prevents memory leaks. asan result) Direct leak of 2 byte(s) in 1 object(s) allocated from: #0 0x7ff1d547bc68 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10bc68) #1 0x7ff1d4e435b4 in _idFromUrl ../src/loaders/svg/tvgSvgLoader.cpp:327 #2 0x7ff1d4e44a8e in _toColor ../src/loaders/svg/tvgSvgLoader.cpp:558 #3 0x7ff1d4e42ee9 in _parseStyleAttr ../src/loaders/svg/tvgSvgLoader.cpp:1033 #4 0x7ff1d4e7f092 in simpleXmlParseAttributes(char const*, unsigned int, bool (*)(void*, char const*, char const*), void const*) ../src/loaders/svg/tvgXmlParser.cpp:361 #5 0x7ff1d4e597d3 in _createPathNode ../src/loaders/svg/tvgSvgLoader.cpp:1363 #6 0x7ff1d4e61359 in _svgLoaderParserXmlOpen ../src/loaders/svg/tvgSvgLoader.cpp:2723 #7 0x7ff1d4e61c49 in _svgLoaderParser ../src/loaders/svg/tvgSvgLoader.cpp:2801 #8 0x7ff1d4e7f3f6 in simpleXmlParse(char const*, unsigned int, bool, bool (*)(void*, SimpleXMLType, char const*, unsigned int), void const*) ../src/loaders/svg/tvgXmlParser.cpp:429 #9 0x7ff1d4e639a1 in SvgLoader::run(unsigned int) ../src/loaders/svg/tvgSvgLoader.cpp:3121 #10 0x7ff1d4dc8b75 in tvg::Task::operator()(unsigned int) ../src/lib/tvgTaskScheduler.h:68 #11 0x7ff1d4dc8b75 in tvg::TaskSchedulerImpl::run(unsigned int) ../src/lib/tvgTaskScheduler.cpp:138 #12 0x7ff1d4dc98f7 in tvg::TaskSchedulerImpl::TaskSchedulerImpl(unsigned int)::{lambda()#1}::operator()() const ../src/lib/tvgTaskScheduler.cpp:113 #13 0x7ff1d4dc98f7 in void std::__invoke_impl(std::__invoke_other, tvg::TaskSchedulerImpl::TaskSchedulerImpl(unsigned int)::{lambda()#1}&&) /usr/include/c++/9/bits/invoke.h:60 #14 0x7ff1d4dc98f7 in std::__invoke_result::type std::__invoke(std::__invoke_result&&, (tvg::TaskSchedulerImpl::TaskSchedulerImpl(unsigned int)::{lambda()#1}&&)...) /usr/include/c++/9/bits/invoke.h:95 #15 0x7ff1d4dc98f7 in void std::thread::_Invoker >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/include/c++/9/thread:244 #16 0x7ff1d4dc98f7 in std::thread::_Invoker >::operator()() /usr/include/c++/9/thread:251 #17 0x7ff1d4dc98f7 in std::thread::_State_impl > >::_M_run() /usr/include/c++/9/thread:195 #18 0x7ff1d3a344bf (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xd44bf) example) Change-Id: I14c605e7555dbda19113b9f9027954654e423f4b --- src/loaders/svg/tvgSvgCssStyle.cpp | 10 ++++++++-- src/loaders/svg/tvgSvgLoader.cpp | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp index 8f46b62..cca188e 100644 --- a/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -42,7 +42,10 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; - if (from->fill.paint.url) to->fill.paint.url = strdup(from->fill.paint.url); + if (from->fill.paint.url) { + if (to->fill.paint.url) free(to->fill.paint.url); + to->fill.paint.url = strdup(from->fill.paint.url); + } to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint); to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill); } @@ -61,7 +64,10 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; - if (from->stroke.paint.url) to->stroke.paint.url = strdup(from->stroke.paint.url); + if (from->stroke.paint.url) { + if (to->stroke.paint.url) free(to->stroke.paint.url); + to->stroke.paint.url = strdup(from->stroke.paint.url); + } to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint); to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke); } diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 3b3183d..8b4dc7b 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -554,6 +554,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** } } } else if (ref && len >= 3 && !strncmp(str, "url", 3)) { + if (*ref) free(*ref); *ref = _idFromUrl((const char*)(str + 3)); } else { //Handle named color @@ -1889,7 +1890,10 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren child->fill.paint.color = parent->fill.paint.color; child->fill.paint.none = parent->fill.paint.none; child->fill.paint.curColor = parent->fill.paint.curColor; - if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url); + if (parent->fill.paint.url) { + if (child->fill.paint.url) free(child->fill.paint.url); + child->fill.paint.url = _copyId(parent->fill.paint.url); + } } if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) { child->fill.opacity = parent->fill.opacity; @@ -1902,7 +1906,12 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren child->stroke.paint.color = parent->stroke.paint.color; child->stroke.paint.none = parent->stroke.paint.none; child->stroke.paint.curColor = parent->stroke.paint.curColor; - child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr; + if (parent->stroke.paint.url) { + if (child->stroke.paint.url) free(child->stroke.paint.url); + child->stroke.paint.url = _copyId(parent->stroke.paint.url); + } else { + child->stroke.paint.url = nullptr; + } } if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) { child->stroke.opacity = parent->stroke.opacity; @@ -1942,7 +1951,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; - if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url); + if (from->fill.paint.url) { + if (to->fill.paint.url) free(to->fill.paint.url); + to->fill.paint.url = _copyId(from->fill.paint.url); + } } if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) { to->fill.opacity = from->fill.opacity; @@ -1956,7 +1968,12 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; - to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr; + if (from->stroke.paint.url) { + if (to->stroke.paint.url) free(to->stroke.paint.url); + to->stroke.paint.url = _copyId(from->stroke.paint.url); + } else { + to->stroke.paint.url = nullptr; + } } if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) { to->stroke.opacity = from->stroke.opacity; -- 2.7.4 From d817777870410167510db10c769afe8f3d06babf Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 30 Aug 2022 10:54:20 +0900 Subject: [PATCH 03/16] svg_loader: Remove unnecessary code Change-Id: Ia3e0d35ed48b3c04c2408ca24d48bf43c32cc54c --- 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 8b4dc7b..357d19d 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1909,8 +1909,6 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren if (parent->stroke.paint.url) { if (child->stroke.paint.url) free(child->stroke.paint.url); child->stroke.paint.url = _copyId(parent->stroke.paint.url); - } else { - child->stroke.paint.url = nullptr; } } if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) { @@ -1971,8 +1969,6 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) if (from->stroke.paint.url) { if (to->stroke.paint.url) free(to->stroke.paint.url); to->stroke.paint.url = _copyId(from->stroke.paint.url); - } else { - to->stroke.paint.url = nullptr; } } if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) { -- 2.7.4 From c13f371a4eb466a9b0957ed3fb3e6296383a2f6a Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 31 Aug 2022 00:21:59 +0200 Subject: [PATCH 04/16] svg_loader: prevent mem leaks A necessary check added before strdup function is called Change-Id: Ibc1cbfc29ea342d5ca31390b306943f7d5f6e25d --- src/loaders/svg/tvgSvgCssStyle.cpp | 10 ++++++++-- src/loaders/svg/tvgSvgLoader.cpp | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp index cca188e..478ba5d 100644 --- a/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -128,8 +128,14 @@ void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) //Copy style attribute _copyStyle(to->style, from->style); - if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url); - if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url); + if (from->style->clipPath.url) { + if (to->style->clipPath.url) free(to->style->clipPath.url); + to->style->clipPath.url = strdup(from->style->clipPath.url); + } + if (from->style->mask.url) { + if (to->style->mask.url) free(to->style->mask.url); + to->style->mask.url = strdup(from->style->mask.url); + } } diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 357d19d..c619fa9 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1332,6 +1332,7 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value) SvgPathNode* path = &(node->node.path); if (!strcmp(key, "d")) { + if (path->path) free(path->path); //Temporary: need to copy path->path = _copyId(value); } else if (!strcmp(key, "style")) { @@ -2005,8 +2006,14 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) //Copy style attribute _styleCopy(to->style, from->style); to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags); - if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url); - if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url); + if (from->style->clipPath.url) { + if (to->style->clipPath.url) free(to->style->clipPath.url); + to->style->clipPath.url = strdup(from->style->clipPath.url); + } + if (from->style->mask.url) { + if (to->style->mask.url) free(to->style->mask.url); + to->style->mask.url = strdup(from->style->mask.url); + } //Copy node attribute switch (from->type) { @@ -2042,7 +2049,10 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) break; } case SvgNodeType::Path: { - if (from->node.path.path) to->node.path.path = strdup(from->node.path.path); + if (from->node.path.path) { + if (to->node.path.path) free(to->node.path.path); + to->node.path.path = strdup(from->node.path.path); + } break; } case SvgNodeType::Polygon: { @@ -2064,7 +2074,10 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) to->node.image.y = from->node.image.y; to->node.image.w = from->node.image.w; to->node.image.h = from->node.image.h; - if (from->node.image.href) to->node.image.href = strdup(from->node.image.href); + if (from->node.image.href) { + if (to->node.image.href) free(to->node.image.href); + to->node.image.href = strdup(from->node.image.href); + } break; } default: { -- 2.7.4 From 4fc79d4bbc31959ad3b2b2acf97ae04465e5a2c1 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 31 Aug 2022 18:07:40 +0900 Subject: [PATCH 05/16] svg_loader: No skip luma mask when composition node is image Improved to skip Luma Mask when conditions are the same as AlphaMask for optimization in e409bb29. If the composition node is an image, it is not skipped because it is not known for sure whether to skip it. Change-Id: I82d1c5ab0ad4bf9624913a574556ea7ae7709a7e --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index a898473..ae0e498 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -659,7 +659,10 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, 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)); + if (image) { + scene->push(move(image)); + if (isMaskWhite) *isMaskWhite = false; + } } else if ((*child)->type != SvgNodeType::Mask) { auto shape = _shapeBuildHelper(*child, vBox, svgPath); if (shape) { -- 2.7.4 From d035143ce5858aef0ea06f6df9c7f800df818d9b Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 1 Sep 2022 20:53:39 +0200 Subject: [PATCH 06/16] svg_loader: prevent stack-overflow for nested nodes Change-Id: Ib479c8af995f2b7576503dbd182defd4a1747d9d --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index ae0e498..b4695a6 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -68,7 +68,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, bool* isMaskWhite = nullptr); +static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); static inline bool _isGroupType(SvgNodeType type) @@ -282,7 +282,7 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox node->style->mask.applying = true; bool isMaskWhite = true; - auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, &isMaskWhite); + auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite); if (comp) { if (node->transform) comp->transform(*node->transform); @@ -560,10 +560,10 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } -static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite) +static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) { unique_ptr finalScene; - auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite); + auto scene = _sceneBuildHelper(node, vBox, svgPath, false, depth + 1, isMaskWhite); // mUseTransform = mUseTransform * mTranslate Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -642,8 +642,15 @@ 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, bool* isMaskWhite) +static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite) { + /* Exception handling: Prevent invalid SVG data input. + The size is the arbitrary value, we need an experimental size. */ + if (depth > 2192) { + TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); + return nullptr; + } + if (_isGroupType(node->type) || mask) { auto scene = Scene::gen(); // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() @@ -654,9 +661,9 @@ 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, isMaskWhite)); + scene->push(_useBuildHelper(*child, vBox, svgPath, depth + 1, isMaskWhite)); else - scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, isMaskWhite)); + scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, depth + 1, isMaskWhite)); } else if ((*child)->type == SvgNodeType::Image) { auto image = _imageBuildHelper(*child, vBox, svgPath); if (image) { @@ -696,7 +703,7 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo if (!node || (node->type != SvgNodeType::Doc)) return nullptr; Box vBox = {vx, vy, vw, vh}; - auto docNode = _sceneBuildHelper(node, vBox, svgPath, false); + auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0); if (!mathEqual(w, vw) || !mathEqual(h, vh)) { auto sx = w / vw; -- 2.7.4 From 8cb427a2c0ab6272ec6e375a54aa88554f17d696 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 1 Sep 2022 01:15:59 +0200 Subject: [PATCH 07/16] svg_loader: deeper search for postponed nodes Till now the proper node was searched only among children, now all the nodes are checked. Change-Id: I46b9fa1de2de3cc5c53e327f0e6bb6b6dd415fc9 --- src/loaders/svg/tvgSvgLoader.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index c619fa9..225ebe8 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1803,19 +1803,10 @@ static SvgNode* _getDefsNode(SvgNode* node) } -static SvgNode* _findChildById(const SvgNode* node, const char* id) +static SvgNode* _findNodeById(SvgNode *node, const char* id) { if (!node) return nullptr; - auto child = node->child.data; - for (uint32_t i = 0; i < node->child.count; ++i, ++child) { - if (((*child)->id) && !strcmp((*child)->id, id)) return (*child); - } - return nullptr; -} - -static SvgNode* _findNodeById(SvgNode *node, const char* id) -{ SvgNode* result = nullptr; if (node->id && !strcmp(node->id, id)) return node; @@ -1829,6 +1820,7 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id) return result; } + static void _cloneGradStops(Array& dst, const Array& src) { for (uint32_t i = 0; i < src.count; ++i) { @@ -2117,8 +2109,8 @@ static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) for (uint32_t i = 0; i < cloneNodes->count; ++i) { auto nodeIdPair = cloneNodes->data[i]; auto defs = _getDefsNode(nodeIdPair.node); - auto nodeFrom = _findChildById(defs, nodeIdPair.id); - if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id); + auto nodeFrom = _findNodeById(defs, nodeIdPair.id); + if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); _cloneNode(nodeFrom, nodeIdPair.node, 0); if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { nodeIdPair.node->node.use.symbol = nodeFrom; @@ -2165,7 +2157,7 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { id = _idFromHref(value); defs = _getDefsNode(node); - nodeFrom = _findChildById(defs, id); + nodeFrom = _findNodeById(defs, id); if (nodeFrom) { _cloneNode(nodeFrom, node, 0); if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; -- 2.7.4 From 795978177ab26c6ced98fd493f13e99a27c36397 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska <67589014+mgrudzinska@users.noreply.github.com> Date: Fri, 2 Sep 2022 04:59:49 +0200 Subject: [PATCH 08/16] svg_loader: preserveAspectRatio attrib handled according to the svg standard (#1249) * svg_loader: preserveAspectRatio attrib handled according to the svg standard * svg_loader: symbol fixed The correct width/height values used in the _useBuildHelper function. Bug was propageted while the preserveAspectRatio attrib was handled, now fixed. * svg_loader: refactoring code regarding the preserveAspectRatio attrib To avoid copy/paste a new function is introduced to handle the proper scaling. * svg_loader: initialize the svg loader members The 'align' and 'meetOrSlice' svg loader members were not initialized. * svg_loader: resize function forces any transformation The resize function is called after the svg image is read and scaled taking into account the proper preserveAspectRatio value. While resizing the preserveAspectRatio isn't checked any more and the image can be freely transformed. Change-Id: I5ec14b7676063f60c1fc3b12262cc06f85f64e97 --- src/lib/tvgLoadModule.h | 1 - src/loaders/svg/tvgSvgLoader.cpp | 86 +++++++++++++++--------- src/loaders/svg/tvgSvgLoader.h | 3 + src/loaders/svg/tvgSvgLoaderCommon.h | 26 +++++++- src/loaders/svg/tvgSvgSceneBuilder.cpp | 118 ++++++++++++++++++++++----------- src/loaders/svg/tvgSvgSceneBuilder.h | 2 +- 6 files changed, 165 insertions(+), 71 deletions(-) diff --git a/src/lib/tvgLoadModule.h b/src/lib/tvgLoadModule.h index bfcc165..0049831 100644 --- a/src/lib/tvgLoadModule.h +++ b/src/lib/tvgLoadModule.h @@ -36,7 +36,6 @@ public: float vw = 0; float vh = 0; float w = 0, h = 0; //default image size - bool preserveAspect = true; //keep aspect ratio by default. virtual ~LoadModule() {} diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 225ebe8..9a88853 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -115,9 +115,54 @@ static bool _parseNumber(const char** content, float* number) if ((*content) == end) return false; //Skip comma if any *content = _skipComma(end); + + return true; +} + + +static constexpr struct +{ + AspectRatioAlign align; + const char* tag; +} alignTags[] = { + { AspectRatioAlign::XMinYMin, "xMinYMin" }, + { AspectRatioAlign::XMidYMin, "xMidYMin" }, + { AspectRatioAlign::XMaxYMin, "xMaxYMin" }, + { AspectRatioAlign::XMinYMid, "xMinYMid" }, + { AspectRatioAlign::XMidYMid, "xMidYMid" }, + { AspectRatioAlign::XMaxYMid, "xMaxYMid" }, + { AspectRatioAlign::XMinYMax, "xMinYMax" }, + { AspectRatioAlign::XMidYMax, "xMidYMax" }, + { AspectRatioAlign::XMaxYMax, "xMaxYMax" }, +}; + + +static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) +{ + if (!strcmp(*content, "none")) { + *align = AspectRatioAlign::None; + return true; + } + + for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { + if (!strncmp(*content, alignTags[i].tag, 8)) { + *align = alignTags[i].align; + *content += 8; + *content = _skipSpace(*content, nullptr); + break; + } + } + + if (!strcmp(*content, "meet")) { + *meetOrSlice = AspectRatioMeetOrSlice::Meet; + } else if (!strcmp(*content, "slice")) { + *meetOrSlice = AspectRatioMeetOrSlice::Slice; + } + return true; } + /** * According to https://www.w3.org/TR/SVG/coords.html#Units */ @@ -803,7 +848,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) } loader->svgParse->global.x = (int)doc->vx; } else if (!strcmp(key, "preserveAspectRatio")) { - if (!strcmp(value, "none")) doc->preserveAspect = false; + _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); } else if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); #ifdef THORVG_LOG_ENABLED @@ -1157,7 +1202,7 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value) symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); symbol->hasHeight = true; } else if (!strcmp(key, "preserveAspectRatio")) { - if (!strcmp(value, "none")) symbol->preserveAspect = false; + _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice); } else if (!strcmp(key, "overflow")) { if (!strcmp(value, "visible")) symbol->overflowVisible = true; } else { @@ -1249,7 +1294,8 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha loader->svgParse->global.w = 0; loader->svgParse->global.h = 0; - doc->preserveAspect = true; + doc->align = AspectRatioAlign::XMidYMid; + doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; func(buf, bufLength, _attrParseSvgNode, loader); if (loader->svgParse->global.w == 0) { @@ -1310,7 +1356,8 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const if (!loader->svgParse->node) return nullptr; loader->svgParse->node->display = false; - loader->svgParse->node->node.symbol.preserveAspect = true; + loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid; + loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet; loader->svgParse->node->node.symbol.overflowVisible = false; loader->svgParse->node->node.symbol.hasViewBox = false; @@ -3147,7 +3194,7 @@ void SvgLoader::run(unsigned tid) _updateStyle(loaderData.doc, nullptr); } - root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); + root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath); } @@ -3180,7 +3227,8 @@ bool SvgLoader::header() if (vh < FLT_EPSILON) vh = h; } - preserveAspect = loaderData.doc->node.doc.preserveAspect; + align = loaderData.doc->node.doc.align; + meetOrSlice = loaderData.doc->node.doc.meetOrSlice; } else { TVGLOG("SVG", "No SVG File. There is no "); return false; @@ -3235,31 +3283,9 @@ bool SvgLoader::resize(Paint* paint, float w, float h) auto sx = w / this->w; auto sy = h / this->h; + Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1}; + paint->transform(m); - if (preserveAspect) { - //Scale - auto scale = sx < sy ? sx : sy; - paint->scale(scale); - //Align - auto tx = 0.0f; - auto ty = 0.0f; - auto tw = this->w * scale; - auto th = this->h * scale; - if (tw > th) ty -= (h - th) * 0.5f; - else tx -= (w - tw) * 0.5f; - paint->translate(-tx, -ty); - } else { - //Align - auto tx = 0.0f; - auto ty = 0.0f; - auto tw = this->w * sx; - auto th = this->h * sy; - if (tw > th) ty -= (h - th) * 0.5f; - else tx -= (w - tw) * 0.5f; - - Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1}; - paint->transform(m); - } return true; } diff --git a/src/loaders/svg/tvgSvgLoader.h b/src/loaders/svg/tvgSvgLoader.h index 093fb67..f224d1a 100644 --- a/src/loaders/svg/tvgSvgLoader.h +++ b/src/loaders/svg/tvgSvgLoader.h @@ -50,6 +50,9 @@ public: unique_ptr paint() override; private: + AspectRatioAlign align = AspectRatioAlign::XMidYMid; + AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; + bool header(); void clear(); void run(unsigned tid) override; diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index dc9ed55..c657c0e 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -145,6 +145,26 @@ enum class SvgParserLengthType Other }; +enum class AspectRatioAlign +{ + None, + XMinYMin, + XMidYMin, + XMaxYMin, + XMinYMid, + XMidYMid, + XMaxYMid, + XMinYMax, + XMidYMax, + XMaxYMax +}; + +enum class AspectRatioMeetOrSlice +{ + Meet, + Slice +}; + struct SvgDocNode { float w; @@ -155,7 +175,8 @@ struct SvgDocNode float vh; SvgNode* defs; SvgNode* style; - bool preserveAspect; + AspectRatioAlign align; + AspectRatioMeetOrSlice meetOrSlice; }; struct SvgGNode @@ -171,7 +192,8 @@ struct SvgSymbolNode { float w, h; float vx, vy, vw, vh; - bool preserveAspect; + AspectRatioAlign align; + AspectRatioMeetOrSlice meetOrSlice; bool overflowVisible; bool hasViewBox; bool hasWidth; diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index b4695a6..4cb4ffd 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -560,6 +560,80 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } +static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box) +{ + auto sx = width / box.w; + auto sy = height / box.h; + auto tvx = box.x * sx; + auto tvy = box.y * sy; + + if (align == AspectRatioAlign::None) + return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + + //Scale + if (meetOrSlice == AspectRatioMeetOrSlice::Meet) { + if (sx < sy) sy = sx; + else sx = sy; + } else { + if (sx < sy) sx = sy; + else sy = sx; + } + + //Align + tvx = box.x * sx; + tvy = box.y * sy; + auto tvw = box.w * sx; + auto tvh = box.h * sy; + + switch (align) { + case AspectRatioAlign::XMinYMin: { + break; + } + case AspectRatioAlign::XMidYMin: { + tvx -= (width - tvw) * 0.5f; + break; + } + case AspectRatioAlign::XMaxYMin: { + tvx -= width - tvw; + break; + } + case AspectRatioAlign::XMinYMid: { + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMidYMid: { + tvx -= (width - tvw) * 0.5f; + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMaxYMid: { + tvx -= width - tvw; + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMinYMax: { + tvy -= height - tvh; + break; + } + case AspectRatioAlign::XMidYMax: { + tvx -= (width - tvw) * 0.5f; + tvy -= height - tvh; + break; + } + case AspectRatioAlign::XMaxYMax: { + tvx -= width - tvw; + tvy -= height - tvh; + break; + } + default: { + break; + } + } + + return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; +} + + static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) { unique_ptr finalScene; @@ -585,20 +659,8 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) { - auto sx = width / vw; - auto sy = height / vh; - if (symbol.preserveAspect) { - if (sx < sy) sy = sx; - else sx = sy; - } - - auto tvx = symbol.vx * sx; - auto tvy = symbol.vy * sy; - auto tvw = vw * sx; - auto tvh = vh * sy; - tvy -= (symbol.h - tvh) * 0.5f; - tvx -= (symbol.w - tvw) * 0.5f; - mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + Box box = {symbol.vx, symbol.vy, vw, vh}; + mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box); } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; } @@ -698,36 +760,18 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, /* External Class Implementation */ /************************************************************************/ -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath) +unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath) { + //TODO: aspect ratio is valid only if viewBox was set + if (!node || (node->type != SvgNodeType::Doc)) return nullptr; Box vBox = {vx, vy, vw, vh}; auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0); if (!mathEqual(w, vw) || !mathEqual(h, vh)) { - auto sx = w / vw; - auto sy = h / vh; - - if (preserveAspect) { - //Scale - auto scale = sx < sy ? sx : sy; - docNode->scale(scale); - //Align - auto tvx = vx * scale; - auto tvy = vy * scale; - auto tvw = vw * scale; - auto tvh = vh * scale; - tvx -= (w - tvw) * 0.5f; - tvy -= (h - tvh) * 0.5f; - docNode->translate(-tvx, -tvy); - } else { - //Align - auto tvx = vx * sx; - auto tvy = vy * sy; - Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; - docNode->transform(m); - } + Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); + docNode->transform(m); } else if (!mathZero(vx) || !mathZero(vy)) { docNode->translate(-vx, -vy); } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.h b/src/loaders/svg/tvgSvgSceneBuilder.h index cecbbf0..311f3c8 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/src/loaders/svg/tvgSvgSceneBuilder.h @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath); +unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath); #endif //_TVG_SVG_SCENE_BUILDER_H_ -- 2.7.4 From 4498ba42f4aa02b382efc5b36c663169eae8d366 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 2 Sep 2022 12:02:39 +0900 Subject: [PATCH 09/16] svg loader: remove unused logic. Please keep it simple and easy. Don't leave a chance compiler warn it. Change-Id: I48e7928131172ff50c223fb450e57fb18d237437 --- src/loaders/svg/tvgSvgLoader.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 9a88853..0c69eb8 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -137,11 +137,11 @@ static constexpr struct }; -static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) +static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) { if (!strcmp(*content, "none")) { *align = AspectRatioAlign::None; - return true; + return; } for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { @@ -158,8 +158,6 @@ static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp } else if (!strcmp(*content, "slice")) { *meetOrSlice = AspectRatioMeetOrSlice::Slice; } - - return true; } -- 2.7.4 From f9e9ee44c17db8aa6b2c0aa34e0bb55d4219b4fd Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 29 Sep 2022 11:11:34 +0900 Subject: [PATCH 10/16] SwRaster: Prevent memory issue If vv goes out of buffer, it can cause potential memory problems. Therefore, an `if condition` is added so that it does not exceed the height of the image. This is temporary fix. Test) meson -Db_sanitize=address,undefined -Dexamples=true . build $ ./build/src/examples/Texmap ==6298==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f37fd6c2180 at pc 0x7f380d884599 bp 0x7fffb02b2db0 sp 0x7fffb02b2da0 READ of size 4 at 0x7f37fd6c2180 thread T0 #0 0x7f380d884598 in _rasterPolygonImageSegment ../src/lib/sw_engine/tvgSwRasterTexmapInternal.h:107 #1 0x7f380d887694 in _rasterPolygonImage ../src/lib/sw_engine/tvgSwRasterTexmap.h:271 #2 0x7f380d88a598 in _rasterTexmapPolygonMesh ../src/lib/sw_engine/tvgSwRasterTexmap.h:639 #3 0x7f380d89828d in _transformedRGBAImageMesh ../src/lib/sw_engine/tvgSwRaster.cpp:701 #4 0x7f380d89828d in rasterImageMesh(SwSurface*, SwImage*, tvg::Polygon const*, unsigned int, tvg::Matrix const*, SwBBox const&, unsigned int) ../src/lib/sw_engine/tvgSwRaster.cpp:1585 #5 0x7f380d89bf16 in tvg::SwRenderer::renderImageMesh(void*) ../src/lib/sw_engine/tvgSwRenderer.cpp:369 #6 0x7f380d830e7e in tvg::Paint::Impl::render(tvg::RenderMethod&) ../src/lib/tvgPaint.cpp:178 #7 0x7f380d823bd2 in tvg::Canvas::Impl::draw() (/home/junsu/dev/os/thorvg/build/src/examples/../libthorvg.so.0+0x265bd2) #8 0x7f380d821e79 in tvg::Canvas::draw() ../src/lib/tvgCanvas.cpp:60 #9 0x557d07b2d5b6 in drawSwView(void*, _Eo_Opaque*) ../src/examples/Texmap.cpp:103 #10 0x7f380cb7d2aa in evas_process_dirty_pixels ../src/lib/evas/canvas/evas_object_image.c:1894 #11 0x7f380cb7d2aa in _evas_image_pixels_get ../src/lib/evas/canvas/evas_object_image.c:2318 #12 0x7f380cb7d85e in _evas_image_render ../src/lib/evas/canvas/evas_object_image.c:2468 #13 0x7f380cb7ea0c in evas_object_image_render ../src/lib/evas/canvas/evas_object_image.c:2271 #14 0x7f380cbe51db in evas_render_mapped ../src/lib/evas/canvas/evas_render.c:2290 #15 0x7f380cbe6f15 in evas_render_updates_internal_loop ../src/lib/evas/canvas/evas_render.c:3158 #16 0x7f380cbe9912 in evas_render_updates_internal ../src/lib/evas/canvas/evas_render.c:3631 #17 0x7f380cbeb31c in _evas_canvas_render_async ../src/lib/evas/canvas/evas_render.c:4094 #18 0x7f380cb6634a in evas_canvas_render_async ../src/lib/evas/canvas/evas_canvas_eo.c:168 #19 0x7f380cb6fce5 in evas_render_async ../src/lib/evas/canvas/evas_canvas_eo.legacy.c:179 #20 0x7f37f3a81555 in _ecore_evas_x_render ../src/modules/ecore_evas/engines/x/ecore_evas_x.c:761 #21 0x7f3809677e42 in _ecore_evas_idle_enter ../src/lib/ecore_evas/ecore_evas.c:295 #22 0x7f38098a89a4 in _ecore_call_task_cb ../src/lib/ecore/ecore_private.h:456 #23 0x7f38098a89a4 in _ecore_factorized_idle_process ../src/lib/ecore/ecore_idler.c:35 #24 0x7f3809b29081 in _event_callback_call ../src/lib/eo/eo_base_class.c:2114 #25 0x7f3809b29081 in _efl_object_event_callback_call ../src/lib/eo/eo_base_class.c:2186 #26 0x7f3809b22d22 in efl_event_callback_call ../src/lib/eo/eo_base_class.c:2189 #27 0x7f38098aae44 in _ecore_main_loop_iterate_internal ../src/lib/ecore/ecore_main.c:2466 #28 0x7f38098ab689 in _ecore_main_loop_begin ../src/lib/ecore/ecore_main.c:1231 #29 0x7f38098b05e0 in _efl_loop_begin ../src/lib/ecore/efl_loop.c:57 #30 0x7f38098af77c in efl_loop_begin src/lib/ecore/efl_loop.eo.c:28 #31 0x7f38098ab755 in ecore_main_loop_begin ../src/lib/ecore/ecore_main.c:1316 #32 0x7f380d14ba1f in elm_run ../src/lib/elementary/elm_main.c:1359 #33 0x557d07b2d298 in main ../src/examples/Texmap.cpp:176 #34 0x7f380b533c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86) #35 0x557d07b2d3c9 in _start (/home/junsu/dev/os/thorvg/build/src/examples/Texmap+0xb3c9) Change-Id: I732019b5ac8aa01d200c1036e56d5a7143544c30 --- src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index 5053629..ab29e8f 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -104,6 +104,12 @@ ab = (int)(255 * (1 - modff(v, &iptr))); iru = uu + 1; irv = vv + 1; + + //FIXME: If vv goes out of buffer, it can cause potential memory problems. + //Therefore, an `if condition` is added so that it does not exceed the height of the image. + //This is temporary fix. + if (vv >= sh) continue; + px = *(sbuf + (vv * sw) + uu); /* horizontal interpolate */ -- 2.7.4 From 7ac2eb24e7298549a90acc25816e80120efd24ec Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 4 Oct 2022 10:08:52 +0900 Subject: [PATCH 11/16] SwRaster: Remove comment Change-Id: If45195fcba8dda3d2e08a3884428e365ec6f6f9f --- src/lib/sw_engine/tvgSwRasterTexmapInternal.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index ab29e8f..b578f87 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -105,9 +105,6 @@ iru = uu + 1; irv = vv + 1; - //FIXME: If vv goes out of buffer, it can cause potential memory problems. - //Therefore, an `if condition` is added so that it does not exceed the height of the image. - //This is temporary fix. if (vv >= sh) continue; px = *(sbuf + (vv * sw) + uu); -- 2.7.4 From 7c2e5c1373627a403c03a60f2b5d175027ba4ef6 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 20 Nov 2022 20:46:50 +0100 Subject: [PATCH 12/16] [svg2png] size limitation while converting the file In case the svg file size is too large, a heap overflow occurred when conversting to png. To prevent this a size limitation has been added - the resolution of the resulting png file cannot be higher than 8k (7680 x 4320). Change-Id: Iba8cf11a3afc47a0594b2ff07862fc8ee410002f --- src/bin/svg2png/svg2png.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/bin/svg2png/svg2png.cpp b/src/bin/svg2png/svg2png.cpp index 71bc1c0..89b3583 100644 --- a/src/bin/svg2png/svg2png.cpp +++ b/src/bin/svg2png/svg2png.cpp @@ -37,6 +37,10 @@ #include #endif +#define WIDTH_8K 7680 +#define HEIGHT_8K 4320 +#define SIZE_8K 33177600 //WIDTH_8K x HEIGHT_8K + using namespace std; struct PngBuilder @@ -94,6 +98,19 @@ public: picture->size(&fw, &fh); w = static_cast(fw); h = static_cast(fh); + + if (w * h > SIZE_8K) { + float scale = fw / fh; + if (scale > 1) { + w = WIDTH_8K; + h = static_cast(w / scale); + } else { + h = HEIGHT_8K; + w = static_cast(h * scale); + } + cout << "Warning: The SVG width and/or height values exceed the 8k resolution. " + "To avoid the heap overflow, the conversion to the PNG file made in " << WIDTH_8K << " x " << HEIGHT_8K << " resolution." << endl; + } } else { picture->size(w, h); } @@ -272,7 +289,7 @@ private: private: int help() { - cout << "Usage:\n svg2png [SVG files] [-r resolution] [-b bgColor]\n\nFlags:\n -r set the output image resolution.\n -b set the output image background color.\n\nExamples:\n $ svg2png input.svg\n $ svg2png input.svg -r 200x200\n $ svg2png input.svg -r 200x200 -b ff00ff\n $ svg2png input1.svg input2.svg -r 200x200 -b ff00ff\n $ svg2png . -r 200x200\n\n"; + cout << "Usage:\n svg2png [SVG files] [-r resolution] [-b bgColor]\n\nFlags:\n -r set the output image resolution.\n -b set the output image background color.\n\nExamples:\n $ svg2png input.svg\n $ svg2png input.svg -r 200x200\n $ svg2png input.svg -r 200x200 -b ff00ff\n $ svg2png input1.svg input2.svg -r 200x200 -b ff00ff\n $ svg2png . -r 200x200\n\nNote:\n In the case, where the width and height in the SVG file determine the size of the image in resolution higher than 8k (7680 x 4320), limiting the resolution to this value is enforced.\n\n"; return 1; } -- 2.7.4 From 98b89b125239229ce8caabda952683086cdbdc02 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 18 Nov 2022 00:09:35 -0800 Subject: [PATCH 13/16] common Accessor: Add access api that with data parameter It supports data parameters that can pass user data to the callback function. Change-Id: I7b5ea2a2861a741bf7da205c495e8c192c7aeb8c std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint, void* data), void* data) noexcept; --- inc/thorvg.h | 13 +++++++++++++ src/lib/tvgAccessor.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/inc/thorvg.h b/inc/thorvg.h index 0ab99e5..666af3c 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1615,6 +1615,19 @@ public: std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint)) noexcept; /** + * @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. + * @param[in] data Data will be passed to callback function. + * + * @return Return the given @p picture instance. + * + * @note The bitmap based picture might not have the scene-tree. + */ + std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint, void* data), void* data) noexcept; + + /** * @brief Creates a new Accessor object. * * @return A new Accessor object. diff --git a/src/lib/tvgAccessor.cpp b/src/lib/tvgAccessor.cpp index 092c8b0..eb63bdf 100644 --- a/src/lib/tvgAccessor.cpp +++ b/src/lib/tvgAccessor.cpp @@ -42,6 +42,24 @@ static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), Iterat } +static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint, void* data), IteratorAccessor& itrAccessor, void* data) +{ + while (auto child = it->next()) { + //Access the child + if (!func(child, data)) return false; + + //Access the children of the child + if (auto it2 = itrAccessor.iterator(child)) { + if (!accessChildren(it2, func, itrAccessor, data)) { + delete(it2); + return false; + } + delete(it2); + } + } + return true; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -66,6 +84,26 @@ unique_ptr Accessor::access(unique_ptr picture, bool(*func)(co } +unique_ptr Accessor::access(unique_ptr picture, bool(*func)(const Paint* paint, void* data), void* data) noexcept +{ + auto p = picture.get(); + if (!p || !func) return picture; + + //Use the Preorder Tree-Search + + //Root + if (!func(p, data)) return picture; + + //Children + IteratorAccessor itrAccessor; + if (auto it = itrAccessor.iterator(p)) { + accessChildren(it, func, itrAccessor, data); + delete(it); + } + return picture; +} + + Accessor::~Accessor() { -- 2.7.4 From e3ebd3184668675572fd4eed19d84acf22d90df6 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 23 Nov 2022 20:46:44 -0800 Subject: [PATCH 14/16] common Accessor: Add access API using std::function Change-Id: I50eb9443137f40d329105fea24739a549dd0ea41 --- inc/thorvg.h | 10 ++++++---- src/examples/Accessor.cpp | 2 +- src/lib/tvgAccessor.cpp | 31 ++++++------------------------- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 666af3c..8bc7f18 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -14,6 +14,7 @@ #ifndef _THORVG_H_ #define _THORVG_H_ +#include #include #include @@ -1603,7 +1604,7 @@ public: ~Accessor(); /** - * @brief Access the Picture scene stree nodes. + * @brief Access the Picture scene tree 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. @@ -1615,17 +1616,18 @@ public: std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint)) noexcept; /** - * @brief Access the Picture scene stree nodes. + * @brief Set the access function for traversing the Picture scene tree 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. - * @param[in] data Data will be passed to callback function. * * @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, void* data), void* data) noexcept; + std::unique_ptr set(std::unique_ptr picture, std::function func) noexcept; /** * @brief Creates a new Accessor object. diff --git a/src/examples/Accessor.cpp b/src/examples/Accessor.cpp index 89dcbf4..433b3f9 100644 --- a/src/examples/Accessor.cpp +++ b/src/examples/Accessor.cpp @@ -55,7 +55,7 @@ void tvgDrawCmds(tvg::Canvas* canvas) return true; }; - picture = accessor->access(move(picture), f); + picture = accessor->set(move(picture), f); canvas->push(move(picture)); } diff --git a/src/lib/tvgAccessor.cpp b/src/lib/tvgAccessor.cpp index eb63bdf..971f2dd 100644 --- a/src/lib/tvgAccessor.cpp +++ b/src/lib/tvgAccessor.cpp @@ -23,7 +23,7 @@ /* Internal Class Implementation */ /************************************************************************/ -static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor) +static bool accessChildren(Iterator* it, IteratorAccessor& itrAccessor, function func) { while (auto child = it->next()) { //Access the child @@ -31,26 +31,7 @@ static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), Iterat //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; -} - - -static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint, void* data), IteratorAccessor& itrAccessor, void* data) -{ - while (auto child = it->next()) { - //Access the child - if (!func(child, data)) return false; - - //Access the children of the child - if (auto it2 = itrAccessor.iterator(child)) { - if (!accessChildren(it2, func, itrAccessor, data)) { + if (!accessChildren(it2, itrAccessor, func)) { delete(it2); return false; } @@ -77,14 +58,14 @@ unique_ptr Accessor::access(unique_ptr picture, bool(*func)(co //Children IteratorAccessor itrAccessor; if (auto it = itrAccessor.iterator(p)) { - accessChildren(it, func, itrAccessor); + accessChildren(it, itrAccessor, func); delete(it); } return picture; } -unique_ptr Accessor::access(unique_ptr picture, bool(*func)(const Paint* paint, void* data), void* data) noexcept +unique_ptr Accessor::set(unique_ptr picture, function func) noexcept { auto p = picture.get(); if (!p || !func) return picture; @@ -92,12 +73,12 @@ unique_ptr Accessor::access(unique_ptr picture, bool(*func)(co //Use the Preorder Tree-Search //Root - if (!func(p, data)) return picture; + if (!func(p)) return picture; //Children IteratorAccessor itrAccessor; if (auto it = itrAccessor.iterator(p)) { - accessChildren(it, func, itrAccessor, data); + accessChildren(it, itrAccessor, func); delete(it); } return picture; -- 2.7.4 From 7590c58d47baf577935eeaf35d100f46a63316a4 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 26 Nov 2022 13:41:36 +0900 Subject: [PATCH 15/16] Update README.md added godot practice. Change-Id: I5caa77cfb7377123cf3cb26a61690b4b1285cb9c --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 730bf4c..9700ec0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ ThorVG has the threading mechanism so that it tries to acquire the next scenes w - [Practices](#practices) - [Tizen](#tizen) - [Rive](#rive) + - [Godot](#godot) - [Examples](#examples) - [Documentation](#documentation) - [Tools](#tools) @@ -250,6 +251,16 @@ that supports fancy, user-interactive vector animations. For more details see [R [Back to contents](#contents)

+### Godot +ThorVG has been integrated into the [Godot](https://www.godotengine.org) project for use of neat and slick icons in Godot editors. Godot is a completely free and open-source modern game engine, it provides a huge set of common tools, so you can just focus on making your game without reinventing the wheel. + +

+ +

+ +[Back to contents](#contents) +
+
## Examples There are various examples available in `thorvg/src/examples` to help you understand ThorVG APIs. -- 2.7.4 From 1fc5ee15cb92fbc17106ebc7cbe26c3234e4a4bc Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 26 Nov 2022 18:47:55 +0900 Subject: [PATCH 16/16] common Accessor: removed deprecated api. the old version of Accessor::access() is redundant, we replaced it with set(). It's still under the beta, we can remove it immediately. Note that we've planned to promote them official APIs in v0.9. Newly changed, tagging beta again...w Change-Id: I1afa8424c81daabe214f5dd704678aa35da610ba --- inc/thorvg.h | 16 ++++------------ src/lib/tvgAccessor.cpp | 20 -------------------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 8bc7f18..1429839 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1597,6 +1597,8 @@ public: * 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 { @@ -1604,18 +1606,6 @@ public: ~Accessor(); /** - * @brief Access the Picture scene tree 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. - */ - std::unique_ptr access(std::unique_ptr picture, bool(*func)(const Paint* paint)) noexcept; - - /** * @brief Set the access function for traversing the Picture scene tree nodes. * * @param[in] picture The picture node to traverse the internal scene-tree. @@ -1633,6 +1623,8 @@ public: * @brief Creates a new Accessor object. * * @return A new Accessor object. + * + * @BETA_API */ static std::unique_ptr gen() noexcept; diff --git a/src/lib/tvgAccessor.cpp b/src/lib/tvgAccessor.cpp index 971f2dd..b69c766 100644 --- a/src/lib/tvgAccessor.cpp +++ b/src/lib/tvgAccessor.cpp @@ -45,26 +45,6 @@ static bool accessChildren(Iterator* it, IteratorAccessor& itrAccessor, function /* 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, itrAccessor, func); - delete(it); - } - return picture; -} - - unique_ptr Accessor::set(unique_ptr picture, function func) noexcept { auto p = picture.get(); -- 2.7.4