From d3c640ded06cb48dc01bec5dd3302617a29946be Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 4 Feb 2022 18:59:00 +0100 Subject: [PATCH 01/16] svg_loader: the 'use' node properly transformed The translation of the use node shouldn't overwrite its transformation. Change-Id: Ibc6761404bc98bd5a82ac1c2d76bf664a98e5d91 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 9abcc6a..ed9576b 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -540,7 +540,7 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con string imagePath = href; if (strncmp(href, "/", 1)) { auto last = svgPath.find_last_of("/"); - imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1 )) + imagePath; + imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath; } if (picture->load(imagePath) != Result::Success) return nullptr; } @@ -561,12 +561,17 @@ 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) { auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite); + if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { - scene->translate(node->node.use.x, node->node.use.y); + Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; + Matrix transform = scene->transform(); + m = mathMultiply(&transform, &m); + scene->transform(m); } if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) { //TODO: handle width/height properties } + return scene; } -- 2.7.4 From 8917e3cfdafceefc76ec76112e2597ffbaea0623 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 7 Feb 2022 02:39:48 +0100 Subject: [PATCH 02/16] svg_loader: proper image transformation One of the image's attributes can be a transformation matrix. Now it's applied. Change-Id: I7056aa0a5deff519bf60e5fd99b4786c2a6cb8bf --- src/loaders/svg/tvgSvgLoader.cpp | 2 ++ src/loaders/svg/tvgSvgSceneBuilder.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 39ee166..adc8de8 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1715,6 +1715,8 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value) _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); } else { return _parseStyleAttr(loader, key, value); } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index ed9576b..737d99b 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -546,12 +546,14 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } float w, h; + Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { auto sx = node->node.image.w / w; auto sy = node->node.image.h / h; - Matrix m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; - picture->transform(m); + m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; } + if (node->transform) m = mathMultiply(node->transform, &m); + picture->transform(m); _applyComposition(picture.get(), node, vBox, svgPath); return picture; -- 2.7.4 From cb813767473f619f779b9307fa235a5452cc6802 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 9 Feb 2022 19:39:08 +0100 Subject: [PATCH 03/16] svg_loader: preventing invalid log msg Change-Id: I3f93189028debb1c9449bc085d2849c3156c035d --- src/loaders/svg/tvgSvgLoader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index adc8de8..f597942 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2295,8 +2295,8 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); - } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) { - grad->userSpace = true; + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2485,8 +2485,8 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); - } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) { - grad->userSpace = true; + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { -- 2.7.4 From 41106dd0a3ffb4878452b402f1998e59f4f1af27 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 9 Feb 2022 17:50:57 -0800 Subject: [PATCH 04/16] infra: Disable unit test in window CI Symbol file generation for compiling test code in CI fails. It's not caused by recent patches. We don't test until we know the exact cause. However, loader test is added to check the safety of window build. Error Log [45/68] Generating symbol file src/thorvg-0.dll.p/thorvg-0.dll.symbols FAILED: src/thorvg-0.dll.p/thorvg-0.dll.symbols "C:\hostedtoolcache\windows\Python\3.7.9\x64\Scripts\meson" "--internal" "symbolextractor" "D:\a\thorvg\thorvg\build" src/thorvg-0.dll "src\thorvg.lib" src/thorvg-0.dll.p/thorvg-0.dll.symbols Change-Id: I03401e691d7a21b59ab2f1a2e40ddbdf4062be08 --- .github/workflows/build_win.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index f1b38b0..be9f23c 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -28,7 +28,7 @@ jobs: name: result path: build/src\thorvg* - unit_test: + build_loaders: runs-on: windows-latest steps: - uses: actions/checkout@v2 @@ -39,7 +39,7 @@ jobs: - name: Build run: | - meson --backend=ninja build -Dloaders="svg, tvg, png, jpg" -Dsavers="tvg" -Dbindings="capi" -Dtests=true + meson --backend=ninja build -Dloaders="svg, tvg, png, jpg" -Dsavers="tvg" -Dbindings="capi" where link - ninja -C build test + ninja -C build install -- 2.7.4 From 176f8f4261400c76c110c10c4384b18e4d6075f5 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 9 Feb 2022 19:25:00 +0100 Subject: [PATCH 05/16] svg_loader: fixing memory leak The css style node was improperly freed. Change-Id: I362ea9d30d974a65ddc45e167ef755b260722d56 --- src/loaders/svg/tvgSvgLoader.cpp | 2 ++ src/loaders/svg/tvgSvgLoaderCommon.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index f597942..05d67f7 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2632,6 +2632,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); if (!strcmp(tagName, "style")) { loader->cssStyle = node; + loader->doc->node.doc.style = node; loader->style = true; } } @@ -2929,6 +2930,7 @@ static void _freeNode(SvgNode* node) } case SvgNodeType::Doc: { _freeNode(node->node.doc.defs); + _freeNode(node->node.doc.style); break; } case SvgNodeType::Defs: { diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 364ec15..d9e9828 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -153,6 +153,7 @@ struct SvgDocNode float vw; float vh; SvgNode* defs; + SvgNode* style; bool preserveAspect; }; @@ -405,7 +406,7 @@ struct SvgNodeIdPair struct SvgLoaderData { - Array stack = {nullptr, 0, 0}; + Array stack = {nullptr, 0, 0}; SvgNode* doc = nullptr; SvgNode* def = nullptr; SvgNode* cssStyle = nullptr; -- 2.7.4 From 5705cbfc44594c63e0d8b6de874c5db88b89d612 Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 2 Mar 2023 10:31:00 +0900 Subject: [PATCH 06/16] Bump up 0.7.9 Change-Id: Iaacfb1959ac48a334562dea752c14af8c530e434 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 2f5fab7..b7226fb 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.7.8 +Version: 0.7.9 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4 From be6b1495c1e85771ead45f7458129fe2b6b2afbd Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 14 Feb 2022 14:38:03 +0900 Subject: [PATCH 07/16] examples: adds a new sample svg Change-Id: I541117f38dee2fbf11d3fc9a7fd5c255d0e17ed5 --- ...932619-bd3d6921-72df-4f09-856b-f9743ae32a14.svg | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/examples/images/152932619-bd3d6921-72df-4f09-856b-f9743ae32a14.svg diff --git a/src/examples/images/152932619-bd3d6921-72df-4f09-856b-f9743ae32a14.svg b/src/examples/images/152932619-bd3d6921-72df-4f09-856b-f9743ae32a14.svg new file mode 100644 index 0000000..17f444f --- /dev/null +++ b/src/examples/images/152932619-bd3d6921-72df-4f09-856b-f9743ae32a14.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.7.4 From 9cbe0aad58fe802cb97ec985d886f410091dc0cf Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 16 Feb 2022 12:22:53 +0900 Subject: [PATCH 08/16] jpeg_loader: resolve the asan bug report. fix the invalid negative shift operations. @Issue: https://github.com/Samsung/thorvg/issues/1172 Change-Id: I36aac7ecc1869464a5be9cbabf69cd3120d894a2 --- src/loaders/jpg/tvgJpgd.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/loaders/jpg/tvgJpgd.cpp b/src/loaders/jpg/tvgJpgd.cpp index b80516a..dacd45e 100644 --- a/src/loaders/jpg/tvgJpgd.cpp +++ b/src/loaders/jpg/tvgJpgd.cpp @@ -382,8 +382,8 @@ struct Row const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - const int tmp0 = (ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS; - const int tmp1 = (ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS; + const int tmp0 = static_cast(ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS; + const int tmp1 = static_cast(ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS; const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; @@ -461,8 +461,8 @@ struct Col const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - const int tmp0 = (ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS; - const int tmp1 = (ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS; + const int tmp0 = static_cast(ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS; + const int tmp1 = static_cast(ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS; const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; @@ -2557,7 +2557,7 @@ void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int s = JPGD_HUFF_EXTEND(r, s); } pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]); - p[0] = static_cast(s << pD->m_successive_low); + p[0] = static_cast(static_cast(s) << pD->m_successive_low); } @@ -2588,7 +2588,7 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR); r = pD->get_bits_no_markers(s); s = JPGD_HUFF_EXTEND(r, s); - p[g_ZAG[k]] = static_cast(s << pD->m_successive_low); + p[g_ZAG[k]] = static_cast(static_cast(s) << pD->m_successive_low); } else { if (r == 15) { if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR); @@ -2607,7 +2607,7 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in { int s, k, r; int p1 = 1 << pD->m_successive_low; - int m1 = (-1) << pD->m_successive_low; + int m1 = static_cast(-1) << pD->m_successive_low; jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); JPGD_ASSERT(pD->m_spectral_end <= 63); -- 2.7.4 From af7fe3339dc30ee0ff06537fe378dfdc5c4d7fdb Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 1 Feb 2022 20:55:15 +0100 Subject: [PATCH 09/16] svg_loader: symbol tag implemented The 'symbol' tag introduced. It can be used to define graphical template objects which can be instantiated by a 'use' tag. Change-Id: I48c000156e764c039a9d27e5a205a226909d2edf --- src/loaders/svg/tvgSvgLoader.cpp | 72 +++++++++++++++++++++++++++++----- src/loaders/svg/tvgSvgLoaderCommon.h | 12 +++++- src/loaders/svg/tvgSvgSceneBuilder.cpp | 65 +++++++++++++++++++++++++++--- src/loaders/svg/tvgXmlParser.cpp | 1 + 4 files changed, 134 insertions(+), 16 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 05d67f7..a16b2b2 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -61,6 +61,7 @@ #include "tvgSvgSceneBuilder.h" #include "tvgSvgUtil.h" #include "tvgSvgCssStyle.h" +#include "tvgMath.h" /************************************************************************/ /* Internal Class Implementation */ @@ -801,13 +802,11 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) if (!strcmp(value, "none")) doc->preserveAspect = false; } else if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); - } #ifdef THORVG_LOG_ENABLED - else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) { + } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) { TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value); - } #endif - else { + } else { return _parseStyleAttr(loader, key, value, false); } return true; @@ -1137,6 +1136,36 @@ static bool _attrParseCssStyleNode(void* data, const char* key, const char* valu } +static bool _attrParseSymbolNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgSymbolNode* symbol = &(node->node.symbol); + + if (!strcmp(key, "viewBox")) { + if (_parseNumber(&value, &symbol->vx)) { + if (_parseNumber(&value, &symbol->vy)) { + if (_parseNumber(&value, &symbol->vw)) { + _parseNumber(&value, &symbol->vh); + } + } + } + } else if (!strcmp(key, "width")) { + symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + } else if (!strcmp(key, "height")) { + symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); + } else if (!strcmp(key, "preserveAspectRatio")) { + if (!strcmp(value, "none")) symbol->preserveAspect = false; + } else if (!strcmp(key, "overflow")) { + if (!strcmp(value, "visible")) symbol->overflowVisible = true; + } else { + return _attrParseGNode(data, key, value); + } + + return true; +} + + static SvgNode* _createNode(SvgNode* parent, SvgNodeType type) { SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode)); @@ -1268,6 +1297,21 @@ static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, cons if (!loader->svgParse->node) return nullptr; func(buf, bufLength, _attrParseCssStyleNode, loader); + + return loader->svgParse->node; +} + + +static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol); + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->display = false; + loader->svgParse->node->node.symbol.preserveAspect = true; + + func(buf, bufLength, _attrParseSymbolNode, loader); + return loader->svgParse->node; } @@ -2044,6 +2088,9 @@ static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) auto nodeFrom = _findChildById(defs, nodeIdPair.id); if (!nodeFrom) nodeFrom = _findChildById(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; + } free(nodeIdPair.id); } } @@ -2085,6 +2132,7 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) nodeFrom = _findChildById(defs, id); if (nodeFrom) { _cloneNode(nodeFrom, node, 0); + if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; free(id); } else { //some svg export software include element at the end of the file @@ -2092,6 +2140,10 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) //after the whole file is parsed _postpone(loader->cloneNodes, node, id); } +#ifdef THORVG_LOG_ENABLED + } else if ((!strcmp(key, "width") || !strcmp(key, "height")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) { + TVGLOG("SVG", "Unsupported attributes used [Elements type: Use][Attribute: %s][Value: %s]", key, value); +#endif } else { return _attrParseGNode(data, key, value); } @@ -2140,7 +2192,8 @@ static constexpr struct {"svg", sizeof("svg"), _createSvgNode}, {"mask", sizeof("mask"), _createMaskNode}, {"clipPath", sizeof("clipPath"), _createClipPathNode}, - {"style", sizeof("style"), _createCssStyleNode} + {"style", sizeof("style"), _createCssStyleNode}, + {"symbol", sizeof("symbol"), _createSymbolNode} }; @@ -2571,7 +2624,8 @@ static constexpr struct {"defs", sizeof("defs")}, {"mask", sizeof("mask")}, {"clipPath", sizeof("clipPath")}, - {"style", sizeof("style")} + {"style", sizeof("style")}, + {"symbol", sizeof("symbol")} }; @@ -2755,7 +2809,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node) #ifdef THORVG_LOG_ENABLED auto type = simpleXmlNodeTypeToString(node->type); - if (!node->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type); + if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type); if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type); if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type); @@ -3049,11 +3103,11 @@ void SvgLoader::run(unsigned tid) if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle); if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle); + if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); + _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); - if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); - if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index d9e9828..1bb5405 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -52,6 +52,7 @@ enum class SvgNodeType ClipPath, Mask, CssStyle, + Symbol, Unknown }; @@ -166,12 +167,20 @@ struct SvgDefsNode Array gradients; }; +struct SvgSymbolNode +{ + float w, h; + float vx, vy, vw, vh; + bool preserveAspect; + bool overflowVisible; +}; + struct SvgUseNode { float x, y, w, h; + SvgNode* symbol; }; - struct SvgEllipseNode { float cx; @@ -375,6 +384,7 @@ struct SvgNode SvgMaskNode mask; SvgClipNode clip; SvgCssStyleNode cssStyle; + SvgSymbolNode symbol; } node; bool display; ~SvgNode(); diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 737d99b..2646c48 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -73,7 +73,7 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, static inline bool _isGroupType(SvgNodeType type) { - if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath) return true; + if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol) return true; return false; } @@ -562,19 +562,72 @@ 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) { + unique_ptr finalScene; auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite); + // mUseTransform = mUseTransform * mTranslate + Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) mUseTransform = *node->transform; if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { - Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; - Matrix transform = scene->transform(); - m = mathMultiply(&transform, &m); - scene->transform(m); + Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; + mUseTransform = mathMultiply(&mUseTransform, &mTranslate); } + + if (node->node.use.symbol && !node->node.use.symbol->node.symbol.overflowVisible) { + auto symbol = node->node.use.symbol->node.symbol; + + Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if ((!mathEqual(symbol.w, symbol.vw) || !mathEqual(symbol.h, symbol.vh)) && symbol.vw > 0 && symbol.vh > 0) { + auto sx = symbol.w / symbol.vw; + auto sy = symbol.h / symbol.vh; + if (symbol.preserveAspect) { + if (sx < sy) sy = sx; + else sx = sy; + } + + auto tvx = symbol.vx * sx; + auto tvy = symbol.vy * sy; + auto tvw = symbol.vw * sx; + auto tvh = symbol.vh * sy; + if (tvw > tvh) tvy -= (symbol.h - tvh) * 0.5f; + else tvx -= (symbol.w - tvw) * 0.5f; + + mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { + mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; + } + + // mSceneTransform = mUseTransform * mViewBox + Matrix mSceneTransform = mathMultiply(&mUseTransform, &mViewBox); + scene->transform(mSceneTransform); + + auto viewBoxClip = Shape::gen(); + viewBoxClip->appendRect(0, 0, symbol.w, symbol.h, 0, 0); + // mClipTransform = mUseTransform * mSymbolTransform + Matrix mClipTransform = mUseTransform; + if (node->node.use.symbol->transform) { + mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform); + } + viewBoxClip->transform(mClipTransform); + + auto compositeLayer = Scene::gen(); + compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(move(scene)); + + auto root = Scene::gen(); + root->push(move(compositeLayer)); + + finalScene = move(root); + } else if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { + scene->transform(mUseTransform); + finalScene = move(scene); + } + if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) { //TODO: handle width/height properties } - return scene; + return finalScene; } diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index aa93870..a12689c 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -272,6 +272,7 @@ const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type) "Video", "ClipPath", "Mask", + "Symbol", "Unknown", }; return TYPE_NAMES[(int) type]; -- 2.7.4 From 78801e6f52b970f4cbbf3cc660d140df262f5fae Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 16 Feb 2022 01:45:26 +0100 Subject: [PATCH 10/16] svg_loader: symbol++ - The initial value of the overflow attribute was missing - overflow="visible" was missing scaling Change-Id: I25b68a37ba154878a6c966b6c38e0214040bbc35 --- src/loaders/svg/tvgSvgLoader.cpp | 1 + src/loaders/svg/tvgSvgSceneBuilder.cpp | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index a16b2b2..1ec9732 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1309,6 +1309,7 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const loader->svgParse->node->display = false; loader->svgParse->node->node.symbol.preserveAspect = true; + loader->svgParse->node->node.symbol.overflowVisible = false; func(buf, bufLength, _attrParseSymbolNode, loader); diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 2646c48..591355a 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -573,7 +573,7 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c mUseTransform = mathMultiply(&mUseTransform, &mTranslate); } - if (node->node.use.symbol && !node->node.use.symbol->node.symbol.overflowVisible) { + if (node->node.use.symbol) { auto symbol = node->node.use.symbol->node.symbol; Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -601,23 +601,27 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c Matrix mSceneTransform = mathMultiply(&mUseTransform, &mViewBox); scene->transform(mSceneTransform); - auto viewBoxClip = Shape::gen(); - viewBoxClip->appendRect(0, 0, symbol.w, symbol.h, 0, 0); - // mClipTransform = mUseTransform * mSymbolTransform - Matrix mClipTransform = mUseTransform; - if (node->node.use.symbol->transform) { - mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform); - } - viewBoxClip->transform(mClipTransform); + if (node->node.use.symbol->node.symbol.overflowVisible) { + finalScene = move(scene); + } else { + auto viewBoxClip = Shape::gen(); + viewBoxClip->appendRect(0, 0, symbol.w, symbol.h, 0, 0); + // mClipTransform = mUseTransform * mSymbolTransform + Matrix mClipTransform = mUseTransform; + if (node->node.use.symbol->transform) { + mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform); + } + viewBoxClip->transform(mClipTransform); - auto compositeLayer = Scene::gen(); - compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath); - compositeLayer->push(move(scene)); + auto compositeLayer = Scene::gen(); + compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(move(scene)); - auto root = Scene::gen(); - root->push(move(compositeLayer)); + auto root = Scene::gen(); + root->push(move(compositeLayer)); - finalScene = move(root); + finalScene = move(root); + } } else if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { scene->transform(mUseTransform); finalScene = move(scene); -- 2.7.4 From 854b0264038133eb48ce9514040189d2884feb94 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 17 Feb 2022 01:47:17 +0100 Subject: [PATCH 11/16] svg_loader: fixing symbol transformation For a Symbol node, the transformation based on the 'viewBox', 'width' and 'height' attributes has to be applied before the transformation based on the 'transformation' attribute. Change-Id: I72fe990e5c95f0f73aec6c77124d4017e8227a65 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 591355a..a02cc32 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -591,14 +591,17 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c auto tvh = symbol.vh * sy; if (tvw > tvh) tvy -= (symbol.h - tvh) * 0.5f; else tvx -= (symbol.w - tvw) * 0.5f; - mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; } - // mSceneTransform = mUseTransform * mViewBox - Matrix mSceneTransform = mathMultiply(&mUseTransform, &mViewBox); + // mSceneTransform = mUseTransform * mSymbolTransform * mViewBox + Matrix mSceneTransform = mViewBox; + if (node->node.use.symbol->transform) { + mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox); + } + mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform); scene->transform(mSceneTransform); if (node->node.use.symbol->node.symbol.overflowVisible) { @@ -606,6 +609,7 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c } else { auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, symbol.w, symbol.h, 0, 0); + // mClipTransform = mUseTransform * mSymbolTransform Matrix mClipTransform = mUseTransform; if (node->node.use.symbol->transform) { @@ -639,7 +643,8 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, { if (_isGroupType(node->type) || mask) { auto scene = Scene::gen(); - if (!mask && node->transform) scene->transform(*node->transform); + // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() + if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform); if (node->display && node->style->opacity != 0) { auto child = node->child.data; -- 2.7.4 From ae7cb5492efed969b967817bc19588a7a7dd8aa1 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 21 Feb 2022 11:28:02 +0900 Subject: [PATCH 12/16] svg_loader: handle the exception properly. viewBox doesn't expect the missing attributes, it won't have any default values. So we can decide the fault when the values are missed. Change-Id: I99a1e97d6d5bd569cf53d1eca2d4d4905a69e08b --- src/loaders/svg/tvgSvgLoader.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 1ec9732..37711d4 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1143,13 +1143,8 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value) SvgSymbolNode* symbol = &(node->node.symbol); if (!strcmp(key, "viewBox")) { - if (_parseNumber(&value, &symbol->vx)) { - if (_parseNumber(&value, &symbol->vy)) { - if (_parseNumber(&value, &symbol->vw)) { - _parseNumber(&value, &symbol->vh); - } - } - } + if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false; + if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false; } else if (!strcmp(key, "width")) { symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); } else if (!strcmp(key, "height")) { -- 2.7.4 From e99ad1ea8d287e60cc8c51cf4e346f4a53985c7d Mon Sep 17 00:00:00 2001 From: mgrudzinska Date: Wed, 23 Feb 2022 21:22:55 +0100 Subject: [PATCH 13/16] svg_loader: preserveAspectRatio fix The default value should be xMidYMid, now it is. Change-Id: Idb347ed847f4ac19f687f4dfc7c7507a8ca0abbe --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index a02cc32..dc26af8 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -705,17 +705,13 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo auto tvy = vy * scale; auto tvw = vw * scale; auto tvh = vh * scale; - if (vw > vh) tvy -= (h - tvh) * 0.5f; - else tvx -= (w - tvw) * 0.5f; + tvx -= (w - tvw) * 0.5f; + tvy -= (h - tvh) * 0.5f; docNode->translate(-tvx, -tvy); } else { //Align auto tvx = vx * sx; auto tvy = vy * sy; - auto tvw = vw * sx; - auto tvh = vh * sy; - if (tvw > tvh) tvy -= (h - tvh) * 0.5f; - else tvx -= (w - tvw) * 0.5f; Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; docNode->transform(m); } -- 2.7.4 From ac4d6dd006c83c05cf3807b49608bb9178fc9ecd Mon Sep 17 00:00:00 2001 From: mgrudzinska Date: Wed, 23 Feb 2022 21:34:15 +0100 Subject: [PATCH 14/16] svg_loader: symbol preserveAspectRatio attribute fixed The symbol node was properly scaled only for 'preserveAspectRatio=none'. Now it works also for the default value of this attribute (xMidYMid). Change-Id: I8f77eac6cd28eb598622ec56e645d6cbea671944 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index dc26af8..250887c 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -589,8 +589,8 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c auto tvy = symbol.vy * sy; auto tvw = symbol.vw * sx; auto tvh = symbol.vh * sy; - if (tvw > tvh) tvy -= (symbol.h - tvh) * 0.5f; - else tvx -= (symbol.w - tvw) * 0.5f; + tvy -= (symbol.h - tvh) * 0.5f; + tvx -= (symbol.w - tvw) * 0.5f; mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; -- 2.7.4 From d8d0097c024b9ff4c372562ae0aadb0093edf967 Mon Sep 17 00:00:00 2001 From: mgrudzinska Date: Wed, 23 Feb 2022 22:53:41 +0100 Subject: [PATCH 15/16] svg_loader: use node fixed By mistake the use node was improperly transformed and/or displayed for a reference node other than a symbol node. Change-Id: I2b6a4bae1a3027e1726725a7a70773c58928b7a8 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 250887c..6fbaee9 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -626,8 +626,8 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c finalScene = move(root); } - } else if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { - scene->transform(mUseTransform); + } else { + if (!mathIdentity((const Matrix*)(&mUseTransform))) scene->transform(mUseTransform); finalScene = move(scene); } -- 2.7.4 From e3cdde7d9e5564e73a76f1451ddaf33ad77fc23b Mon Sep 17 00:00:00 2001 From: mgrudzinska Date: Sun, 27 Feb 2022 02:24:18 +0100 Subject: [PATCH 16/16] common: viewport values improperly rounded For a very specific scaling factors shapes were to much clipped because of wrong rounding of the viewport. Change-Id: Icfd42938c3754c91381565e0b114ca7168f33726 --- src/lib/tvgPaint.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 10dc5fd..d0e908d 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -79,8 +79,8 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, viewport.x = static_cast(v1.x); viewport.y = static_cast(v1.y); - viewport.w = static_cast(v2.x - v1.x + 0.5f); - viewport.h = static_cast(v2.y - v1.y + 0.5f); + viewport.w = static_cast(ceil(v2.x - viewport.x)); + viewport.h = static_cast(ceil(v2.y - viewport.y)); if (viewport.w < 0) viewport.w = 0; if (viewport.h < 0) viewport.h = 0; @@ -404,4 +404,4 @@ uint8_t Paint::opacity() const noexcept uint32_t Paint::identifier() const noexcept { return pImpl->id; -} \ No newline at end of file +} -- 2.7.4