From d035143ce5858aef0ea06f6df9c7f800df818d9b Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 1 Sep 2022 20:53:39 +0200 Subject: [PATCH 01/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 02/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 03/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 04/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 13498752a43d407fb268df153d702546aa46cf21 Mon Sep 17 00:00:00 2001 From: jykeon Date: Tue, 14 Mar 2023 17:05:15 +0900 Subject: [PATCH 05/16] Bump up 0.8.5 Change-Id: I8be041ac09cf457cc85d976ac2093b1886b45497 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 191e285..a82c42b 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.8.4 +Version: 0.8.5 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 From 2bdef360a7f3835b0fb30740c2ef3b82006ef537 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 7 Dec 2022 16:20:47 +0900 Subject: [PATCH 13/16] saver/loader tvg: support picture mesh properties. this mesh properites newly introduced in v0.8 (see: 3dd65dfed00849f0bd9d0bb0ade177fa961cd7a5) tvg saver/loader should implement mesh support to properly capture/replay the scene snapshot. @Issue: https://github.com/Samsung/thorvg/issues/1242 Change-Id: Id48e50be8093c2f13d3f3b4e1112151974597928 --- src/lib/tvgBinaryDesc.h | 1 + src/loaders/tvg/tvgTvgBinInterpreter.cpp | 52 +++++++++++++++++++++----------- src/savers/tvg/tvgTvgSaver.cpp | 14 +++++++++ 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/lib/tvgBinaryDesc.h b/src/lib/tvgBinaryDesc.h index f139def..bab6b79 100644 --- a/src/lib/tvgBinaryDesc.h +++ b/src/lib/tvgBinaryDesc.h @@ -92,5 +92,6 @@ using TvgBinFlag = TvgBinByte; //Picture #define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70 +#define TVG_TAG_PICTURE_MESH (TvgBinTag)0x71 #endif //_TVG_BINARY_DESC_H_ diff --git a/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/src/loaders/tvg/tvgTvgBinInterpreter.cpp index 01a39b6..ffea9d2 100644 --- a/src/loaders/tvg/tvgTvgBinInterpreter.cpp +++ b/src/loaders/tvg/tvgTvgBinInterpreter.cpp @@ -357,27 +357,45 @@ static bool _parsePicture(TvgBinBlock block, Paint* paint) { auto picture = static_cast(paint); - //Case1: Image Picture - if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) { - if (block.length < 2 * SIZE(uint32_t)) return false; + switch (block.type) { + case TVG_TAG_PICTURE_RAW_IMAGE: { + if (block.length < 2 * SIZE(uint32_t)) return false; - auto ptr = block.data; - uint32_t w, h; + auto ptr = block.data; + uint32_t w, h; - READ_UI32(&w, ptr); - ptr += SIZE(uint32_t); - READ_UI32(&h, ptr); - ptr += SIZE(uint32_t); + READ_UI32(&w, ptr); + ptr += SIZE(uint32_t); + READ_UI32(&h, ptr); + ptr += SIZE(uint32_t); - auto size = w * h * SIZE(uint32_t); - if (block.length != 2 * SIZE(uint32_t) + size) return false; + auto size = w * h * SIZE(uint32_t); + if (block.length != 2 * SIZE(uint32_t) + size) return false; - picture->load((uint32_t*) ptr, w, h, true); - return true; - } + picture->load((uint32_t*) ptr, w, h, true); - //Case2: Base Paint Properties - if (_parsePaintProperty(block, picture)) return true; + return true; + } + case TVG_TAG_PICTURE_MESH: { + if (block.length < 1 * SIZE(uint32_t)) return false; + + auto ptr = block.data; + uint32_t meshCnt; + READ_UI32(&meshCnt, ptr); + ptr += SIZE(uint32_t); + + auto size = meshCnt * SIZE(Polygon); + if (block.length != SIZE(uint32_t) + size) return false; + + picture->mesh((Polygon*) ptr, meshCnt); + + return true; + } + //Base Paint Properties + default: { + if (_parsePaintProperty(block, picture)) return true; + } + } //Vector Picture won't be requested since Saver replaces it with the Scene return false; @@ -414,7 +432,7 @@ static Paint* _parsePaint(TvgBinBlock baseBlock) auto ptr = baseBlock.data; - //2. Read Subsquent properties of the current paint. + //2. Read Subsequent properties of the current paint. while (ptr < baseBlock.end) { auto block = _readBlock(ptr); if (block.end > baseBlock.end) return paint; diff --git a/src/savers/tvg/tvgTvgSaver.cpp b/src/savers/tvg/tvgTvgSaver.cpp index 57a21dc..e86c377 100644 --- a/src/savers/tvg/tvgTvgSaver.cpp +++ b/src/savers/tvg/tvgTvgSaver.cpp @@ -609,6 +609,20 @@ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* p cnt += writeData(pixels, imgSize); cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + //mesh: currently only available in bitmap image. + const Polygon* triangles = nullptr; + auto triangleCnt = picture->mesh(&triangles); + if (triangles && triangleCnt > 0) { + TvgBinCounter triangleCntSize = SIZE(triangleCnt); + TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]); + + writeTag(TVG_TAG_PICTURE_MESH); + writeCount(triangleCntSize + trianglesSize); + cnt += writeData(&triangleCnt, triangleCntSize); + cnt += writeData(triangles, trianglesSize); + cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + } + //Bitmap picture needs the transform info. cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM); -- 2.7.4 From 9853b32812194aba6ee2aad1c89eb23a19e5f7ca Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 8 Dec 2022 23:44:04 +0900 Subject: [PATCH 14/16] common picture: +exception handling. invalid arguments won't be allowed. Change-Id: Ia70a3d112e8e8cee36ee1cb05085c95b0402f546 --- src/lib/tvgPicture.cpp | 7 +++++-- src/lib/tvgPictureImpl.h | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 56f447d..b6f4787 100644 --- a/src/lib/tvgPicture.cpp +++ b/src/lib/tvgPicture.cpp @@ -123,8 +123,11 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept Result Picture::mesh(const Polygon* triangles, const uint32_t triangleCnt) noexcept { - if (pImpl->mesh(triangles, triangleCnt)) return Result::Success; - return Result::Unknown; + if (!triangles && triangleCnt > 0) return Result::InvalidArguments; + if (triangles && triangleCnt == 0) return Result::InvalidArguments; + + pImpl->mesh(triangles, triangleCnt); + return Result::Success; } diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 0ad7e03..8bbb745 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -252,7 +252,7 @@ struct Picture::Impl return Result::Success; } - bool mesh(const Polygon* triangles, const uint32_t triangleCnt) + void mesh(const Polygon* triangles, const uint32_t triangleCnt) { if (triangles && triangleCnt > 0) { this->triangleCnt = triangleCnt; @@ -263,7 +263,6 @@ struct Picture::Impl this->triangles = nullptr; this->triangleCnt = 0; } - return true; } Paint* duplicate() -- 2.7.4 From d7504aed3f1331e55cd2a5cf69c7280d9f898a88 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 8 Dec 2022 23:47:57 +0900 Subject: [PATCH 15/16] test picture: add picture mesh api unit test @Issue: https://github.com/Samsung/thorvg/issues/1241 Change-Id: I52234dd9136428a01526301e94241c26507a997d --- test/testPicture.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/testPicture.cpp b/test/testPicture.cpp index 1859e55..09e5f99 100644 --- a/test/testPicture.cpp +++ b/test/testPicture.cpp @@ -106,6 +106,69 @@ TEST_CASE("Load RAW Data", "[tvgPicture]") free(data); } +TEST_CASE("Texture mesh", "[tvgPicture]") +{ + auto picture = Picture::gen(); + REQUIRE(picture); + + string path(TEST_DIR"/rawimage_200x300.raw"); + + ifstream file(path); + 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(); + + REQUIRE(picture->load(data, 200, 300, false) == Result::Success); + + //Composing Meshes + tvg::Polygon triangles[4]; + triangles[0].vertex[0] = {{100, 125}, {0, 0}}; + triangles[0].vertex[1] = {{300, 100}, {0.5, 0}}; + triangles[0].vertex[2] = {{200, 550}, {0, 1}}; + + triangles[1].vertex[0] = {{300, 100}, {0.5, 0}}; + triangles[1].vertex[1] = {{350, 450}, {0.5, 1}}; + triangles[1].vertex[2] = {{200, 550}, {0, 1}}; + + triangles[2].vertex[0] = {{300, 100}, {0.5, 0}}; + triangles[2].vertex[1] = {{500, 200}, {1, 0}}; + triangles[2].vertex[2] = {{350, 450}, {0.5, 1}}; + + triangles[3].vertex[0] = {{500, 200}, {1, 0}}; + triangles[3].vertex[1] = {{450, 450}, {1, 1}}; + triangles[3].vertex[2] = {{350, 450}, {0.5, 1}}; + + //Negative cases + const tvg::Polygon* triangles2 = nullptr; + REQUIRE(picture->mesh(nullptr, 4) == tvg::Result::InvalidArguments); + REQUIRE(picture->mesh(nullptr) == 0); + REQUIRE(picture->mesh(&triangles2) == 0); + REQUIRE(picture->mesh(triangles, 0) == tvg::Result::InvalidArguments); + REQUIRE(picture->mesh(nullptr) == 0); + REQUIRE(picture->mesh(&triangles2) == 0); + + //Positive cases + REQUIRE(picture->mesh(triangles, 4) == tvg::Result::Success); + REQUIRE(picture->mesh(nullptr) == 4); + REQUIRE(picture->mesh(&triangles2) == 4); + + for(int i = 0; i < 4; i++) { + for(int j = 0; j < 3; j++) { + REQUIRE(triangles[i].vertex[j].pt.x == triangles2[i].vertex[j].pt.x); + REQUIRE(triangles[i].vertex[j].pt.y == triangles2[i].vertex[j].pt.y); + REQUIRE(triangles[i].vertex[j].uv.x == triangles2[i].vertex[j].uv.x); + REQUIRE(triangles[i].vertex[j].uv.y == triangles2[i].vertex[j].uv.y); + } + } + + REQUIRE(picture->mesh(nullptr, 0) == tvg::Result::Success); + REQUIRE(picture->mesh(nullptr) == 0); + REQUIRE(picture->mesh(&triangles2) == 0); + + free(data); +} + TEST_CASE("Load PNG file from path", "[tvgPicture]") { auto picture = Picture::gen(); -- 2.7.4 From b83980845aebd2a94c525ea2ab3432f6e4e4b618 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 12 Dec 2022 13:38:43 +0900 Subject: [PATCH 16/16] svg_loader: Change SVG viewbox variable type from int to float The viewbox and size(width, height) defined in SVG can be of type float. This prevents matrix calculation errors caused by this. Change-Id: I85dd502b63dc9a13e969bd459e1f98975c0ef6ed --- src/loaders/svg/tvgSvgLoader.cpp | 20 ++++++++++---------- src/loaders/svg/tvgSvgLoaderCommon.h | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 0c69eb8..9b82ebd 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -178,9 +178,9 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w; else //if other then it's radius { - float max = (float)svgParse->global.w; + float max = svgParse->global.w; if (max < svgParse->global.h) - max = (float)svgParse->global.h; + max = svgParse->global.h; parsedValue = (parsedValue / 100.0) * max; } } @@ -339,7 +339,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das ++end; //Refers to the diagonal length of the viewport. //https://www.w3.org/TR/SVG2/coords.html#Units - parsedValue = (sqrtf(pow(loader->svgParse->global.w, 2) + pow(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); + parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); } (*dash).array.push(parsedValue); str = end; @@ -838,13 +838,13 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) if (_parseNumber(&value, &doc->vy)) { if (_parseNumber(&value, &doc->vw)) { _parseNumber(&value, &doc->vh); - loader->svgParse->global.h = (uint32_t)doc->vh; + loader->svgParse->global.h = doc->vh; } - loader->svgParse->global.w = (uint32_t)doc->vw; + loader->svgParse->global.w = doc->vw; } - loader->svgParse->global.y = (int)doc->vy; + loader->svgParse->global.y = doc->vy; } - loader->svgParse->global.x = (int)doc->vx; + loader->svgParse->global.x = doc->vx; } else if (!strcmp(key, "preserveAspectRatio")) { _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); } else if (!strcmp(key, "style")) { @@ -1298,11 +1298,11 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha if (loader->svgParse->global.w == 0) { if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1; - else loader->svgParse->global.w = (uint32_t)doc->w; + else loader->svgParse->global.w = doc->w; } if (loader->svgParse->global.h == 0) { if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1; - else loader->svgParse->global.h = (uint32_t)doc->h; + else loader->svgParse->global.h = doc->h; } return loader->svgParse->node; @@ -2369,7 +2369,7 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) { // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html - if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0)); + if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); } diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index c657c0e..3588cab 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -425,8 +425,7 @@ struct SvgParser SvgStopStyleFlags flags; struct { - int x, y; - uint32_t w, h; + float x, y, w, h; } global; struct { -- 2.7.4