From 2111930048843b2373907f4e9813f97a3b2d6206 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 22 Aug 2022 15:53:37 +0200 Subject: [PATCH] svg_loader: the preserveAspectRatio attrib handled The preserveAspectRatio attribute handled according to the svg standard. Change-Id: Ie7292ba47341994cf703b3634016c267292a22d2 --- src/lib/tvgLoadModule.h | 1 - src/loaders/svg/tvgSvgLoader.cpp | 80 ++++++++++++++++--------- src/loaders/svg/tvgSvgLoader.h | 2 + src/loaders/svg/tvgSvgLoaderCommon.h | 23 +++++++- src/loaders/svg/tvgSvgSceneBuilder.cpp | 104 ++++++++++++++++++++++++--------- src/loaders/svg/tvgSvgSceneBuilder.h | 2 +- 6 files changed, 154 insertions(+), 58 deletions(-) diff --git a/src/lib/tvgLoadModule.h b/src/lib/tvgLoadModule.h index 0c34ecb..a496b09 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 def8ae1..050611f 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -116,6 +116,49 @@ static bool _parseNumber(const char** content, float* number) 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 void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) +{ + if (!strcmp(*content, "none")) { + *align = AspectRatioAlign::None; + return; + } + + 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; +} + + /** * According to https://www.w3.org/TR/SVG/coords.html#Units */ @@ -783,7 +826,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, _parseStyleAttr, loader); } @@ -1150,7 +1193,9 @@ 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; + simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader); if (loader->svgParse->global.w == 0) { @@ -2846,7 +2891,7 @@ void SvgLoader::run(unsigned tid) if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes); } - 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); } @@ -2879,7 +2924,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; @@ -2934,31 +2980,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 468f058..8c5b233 100644 --- a/src/loaders/svg/tvgSvgLoader.h +++ b/src/loaders/svg/tvgSvgLoader.h @@ -32,6 +32,8 @@ public: string svgPath = ""; const char* content = nullptr; uint32_t size = 0; + AspectRatioAlign align = AspectRatioAlign::XMidYMid; + AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; SvgLoaderData loaderData; unique_ptr root; diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index cceef91..0a6f019 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -136,6 +136,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; @@ -145,7 +165,8 @@ struct SvgDocNode float vw; float vh; SvgNode* defs; - bool preserveAspect; + AspectRatioAlign align; + AspectRatioMeetOrSlice meetOrSlice; }; struct SvgGNode diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 8701fe3..243fe7b 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -595,11 +595,85 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, } +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}; +} + + /************************************************************************/ /* 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) { if (!node || (node->type != SvgNodeType::Doc)) return nullptr; @@ -607,32 +681,8 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo auto docNode = _sceneBuildHelper(node, vBox, svgPath, false); 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; - if (vw > vh) tvy -= (h - tvh) * 0.5f; - else tvx -= (w - tvw) * 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); - } + 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 4232aca..20b6b2b 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