SvgLoader: Implement ClipPath style
authorJunsuChoi <jsuya.choi@samsung.com>
Thu, 24 Sep 2020 02:52:26 +0000 (11:52 +0900)
committerHermet Park <chuneon.park@samsung.com>
Tue, 13 Oct 2020 04:54:32 +0000 (13:54 +0900)
Supports case of using style attribute for defined <clipPath>.
In SVG, <clipPath> can be used as a "clipPath" attribute or a style "clip-path".
This patch only supports "clip-path" of style is declared.
The remaining features will be added later.

[Example SVG case]

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
 <defs>
      <clipPath id="clipPath">
          <rect x="15" y="15" width="40" height="40" fill="#F00" />
          <circle cx="20" cy="20" r="10" fill="#F00" />
      </clipPath>
  </defs>

  <circle cx="25" cy="25" r="20"
          style="fill: #0000ff; clip-path: url(#clipPath); " />
</svg>

Change-Id: Ie9b1000a52070ca86f67ce794413c3640784a079

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

index 3495fca..0624a1b 100644 (file)
@@ -850,6 +850,14 @@ static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node
     node->transform = _parseTransformationMatrix(value);
 }
 
+static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+    SvgStyleProperty* style = node->style;
+    style->comp.flags = (SvgCompositeFlags)((int)style->comp.flags | (int)SvgCompositeFlags::ClipPath);
+
+    int len = strlen(value);
+    if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
+}
 
 static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
 {
@@ -889,6 +897,7 @@ static constexpr struct
     STYLE_DEF(stroke-opacity, StrokeOpacity),
     STYLE_DEF(stroke-dasharray, StrokeDashArray),
     STYLE_DEF(transform, Transform),
+    STYLE_DEF(clip-path, ClipPath),
     STYLE_DEF(display, Display)
 };
 
@@ -938,6 +947,26 @@ static bool _attrParseGNode(void* data, const char* key, const char* value)
 }
 
 
+/* parse clipPath node
+ * https://www.w3.org/TR/SVG/struct.html#Groups
+ */
+static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
+{
+    SvgLoaderData* loader = (SvgLoaderData*)data;
+    SvgNode* node = loader->svgParse->node;
+
+    if (!strcmp(key, "style")) {
+        return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+    } else if (!strcmp(key, "transform")) {
+        node->transform = _parseTransformationMatrix(value);
+    } else if (!strcmp(key, "id")) {
+        node->id = _copyId(value);
+    } else {
+        return _parseStyleAttr(loader, key, value);
+    }
+    return true;
+}
+
 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
 {
     SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
@@ -1019,10 +1048,12 @@ static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, const ch
 
 static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
 {
-    loader->svgParse->node = _createNode(parent, SvgNodeType::Unknown);
+    loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
 
     loader->svgParse->node->display = false;
 
+    simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
+
     return loader->svgParse->node;
 }
 
@@ -1397,6 +1428,20 @@ static SvgNode* _findChildById(SvgNode* node, const char* id)
     return nullptr;
 }
 
+static SvgNode* _findNodeById(SvgNode *node, string* id)
+{
+    SvgNode* result = nullptr;
+    if ((node->id != nullptr) && !node->id->compare(*id)) return node;
+
+    if (node->child.cnt > 0) {
+        auto child = node->child.list;
+        for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
+            result = _findNodeById(*child, id);
+            if (result) break;
+        }
+    }
+    return result;
+}
 
 static void _cloneGradStops(SvgVector<Fill::ColorStop*>* dst, SvgVector<Fill::ColorStop*>* src)
 {
@@ -2200,6 +2245,20 @@ static void _updateGradient(SvgNode* node, SvgVector<SvgStyleGradient*>* gradide
     }
 }
 
+static void _updateComposite(SvgNode* node, SvgNode* root)
+{
+    if (node->style->comp.url && !node->style->comp.node) {
+        SvgNode *findResult = _findNodeById(root, node->style->comp.url);
+        if (findResult) node->style->comp.node = findResult;
+    }
+    if (node->child.cnt > 0) {
+        auto child = node->child.list;
+        for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
+            _updateComposite(*child, root);
+        }
+    }
+}
+
 static void _freeGradientStyle(SvgStyleGradient* grad)
 {
     if (!grad) return;
@@ -2362,6 +2421,9 @@ void SvgLoader::run()
         if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
 
         if (loaderData.gradients.cnt > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
+
+        _updateComposite(loaderData.doc, loaderData.doc);
+        if (defs) _updateComposite(loaderData.doc, defs);
     }
     root = builder.build(loaderData.doc);
 };
index 3cbe22d..b91bd08 100644 (file)
@@ -45,6 +45,7 @@ enum class SvgNodeType
     Tspan,
     Use,
     Video,
+    ClipPath,
     //Custome_command,   //Not support
     Unknown
 };
@@ -60,12 +61,18 @@ enum class SvgLengthType
     In,
 };
 
+enum class SvgCompositeFlags
+{
+    ClipPath = 0x01,
+};
+
 enum class SvgFillFlags
 {
-    Paint = 0x1,
-    Opacity = 0x2,
-    Gradient = 0x4,
-    FillRule = 0x8
+    Paint = 0x01,
+    Opacity = 0x02,
+    Gradient = 0x04,
+    FillRule = 0x08,
+    ClipPath = 0x16
 };
 
 enum class SvgStrokeFlags
@@ -244,6 +251,13 @@ struct SvgGradientStop
     uint8_t a;
 };
 
+struct SvgComposite
+{
+    SvgCompositeFlags flags;
+    string *url;
+    SvgNode* node;
+};
+
 struct SvgPaint
 {
     SvgStyleGradient* gradient;
@@ -300,6 +314,7 @@ struct SvgStyleProperty
 {
     SvgStyleFill fill;
     SvgStyleStroke stroke;
+    SvgComposite comp;
     int opacity;
     uint8_t r;
     uint8_t g;
index f3922cb..6c20164 100644 (file)
 #include "tvgSvgSceneBuilder.h"
 #include "tvgSvgPath.h"
 
+void _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh);
+
+bool _isGroupType(SvgNodeType type)
+{
+    if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::ClipPath) return true;
+    return false;
+}
+
 unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
 {
     Fill::ColorStop* stops;
@@ -185,6 +193,14 @@ unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, Sha
     return fillGrad;
 }
 
+void _appendChildShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh)
+{
+    _appendShape(node, shape, vx, vy, vw, vh);
+    if (node->child.cnt > 0) {
+        auto child = node->child.list;
+        for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) _appendChildShape(*child, shape, vx, vy, vw, vh);
+    }
+}
 
 void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, float vh)
 {
@@ -252,12 +268,31 @@ void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, floa
         vg->strokeColor(&r, &g, &b, &a);
         vg->stroke(r, g, b, (a * style->opacity) / 255.0f);
     }
-}
 
+    //Apply composite node
+    if (style->comp.node) {
+        //Composite ClipPath
+        if (((int)style->comp.flags & (int)SvgCompositeFlags::ClipPath)) {
+            auto compNode = style->comp.node;
+            if (compNode->child.cnt > 0) {
+                auto comp = Shape::gen();
+                auto child = compNode->child.list;
+                for (uint32_t i = 0; i < compNode->child.cnt; ++i, ++child) _appendChildShape(*child, comp.get(), vx, vy, vw, vh);
+                vg->composite(move(comp), CompositeMethod::ClipPath);
+            }
+        }
+    }
+}
 
 unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
 {
     auto shape = Shape::gen();
+    _appendShape(node, shape.get(), vx, vy, vw, vh);
+    return shape;
+}
+
+void _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh)
+{
     switch (node->type) {
         case SvgNodeType::Path: {
             if (node->node.path.path) {
@@ -304,26 +339,40 @@ unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw,
             break;
         }
     }
-    _applyProperty(node, shape.get(), vx, vy, vw, vh);
-    return shape;
-}
 
+    _applyProperty(node, shape, vx, vy, vw, vh);
+}
 
 unique_ptr<Scene> _sceneBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh, int parentOpacity)
 {
-    if (node->type == SvgNodeType::Doc || node->type == SvgNodeType::G) {
+    if (_isGroupType(node->type)) {
         auto scene = Scene::gen();
         if (node->transform) scene->transform(*node->transform);
         node->style->opacity = (node->style->opacity * parentOpacity) / 255.0f;
+
         if (node->display) {
             auto child = node->child.list;
             for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
-                if ((*child)->type == SvgNodeType::Doc || (*child)->type == SvgNodeType::G) {
+                if (_isGroupType((*child)->type)) {
                     scene->push(_sceneBuildHelper(*child, vx, vy, vw, vh, node->style->opacity));
                 } else {
                     (*child)->style->opacity = ((*child)->style->opacity * node->style->opacity) / 255.0f;
                     scene->push(_shapeBuildHelper(*child, vx, vy, vw, vh));
                 }
+
+            //Apply composite node
+            if (node->style->comp.node) {
+                 //Composite ClipPath
+                if (((int)node->style->comp.flags & (int)SvgCompositeFlags::ClipPath)) {
+                    auto compNode = node->style->comp.node;
+                    if (compNode->child.cnt > 0) {
+                        auto comp = Shape::gen();
+                        auto child = compNode->child.list;
+                        for (uint32_t i = 0; i < compNode->child.cnt; ++i, ++child) _appendChildShape(*child, comp.get(), vx, vy, vw, vh);
+                        scene->composite(move(comp), CompositeMethod::ClipPath);
+                    }
+                }
+            }
             }
         }
         return scene;