svg_loader: the preserveAspectRatio attrib handled 35/280035/3 accepted/tizen/unified/20221102.020531
authorMira Grudzinska <m.grudzinska@samsung.com>
Mon, 22 Aug 2022 13:53:37 +0000 (15:53 +0200)
committerMira Grudzinska <m.grudzinska@samsung.com>
Thu, 8 Sep 2022 09:53:49 +0000 (11:53 +0200)
The preserveAspectRatio attribute handled according
to the svg standard.

Change-Id: Ie7292ba47341994cf703b3634016c267292a22d2

src/lib/tvgLoadModule.h
src/loaders/svg/tvgSvgLoader.cpp
src/loaders/svg/tvgSvgLoader.h
src/loaders/svg/tvgSvgLoaderCommon.h
src/loaders/svg/tvgSvgSceneBuilder.cpp
src/loaders/svg/tvgSvgSceneBuilder.h

index 0c34ecb..a496b09 100644 (file)
@@ -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() {}
 
index def8ae1..050611f 100644 (file)
@@ -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 <svg/>");
         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;
 }
 
index 468f058..8c5b233 100644 (file)
@@ -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<Scene> root;
index cceef91..0a6f019 100644 (file)
@@ -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
index 8701fe3..243fe7b 100644 (file)
@@ -595,11 +595,85 @@ static unique_ptr<Scene> _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<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath)
+unique_ptr<Scene> 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<Scene> 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);
     }
index 4232aca..20b6b2b 100644 (file)
@@ -25,6 +25,6 @@
 
 #include "tvgCommon.h"
 
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath);
+unique_ptr<Scene> 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_