SvgLoader: Support to linear, radial gradient 68/238168/1
authorJunsuChoi <jsuya.choi@samsung.com>
Thu, 9 Jul 2020 04:23:41 +0000 (13:23 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 9 Jul 2020 04:36:46 +0000 (13:36 +0900)
Change-Id: Ida3f6ccca5f0d6ed1922b7ce99d2d2f3203f5ba9

src/loaders/svg_loader/tvgSvgLoader.cpp
src/loaders/svg_loader/tvgSvgLoaderCommon.h
src/loaders/svg_loader/tvgSvgSceneBuilder.cpp
test/svgs/lineargrad1.svg [new file with mode: 0644]
test/svgs/radialgrad1.svg [new file with mode: 0644]

index f4ed3be..f8f7c4e 100644 (file)
@@ -1365,11 +1365,18 @@ static SvgNode* _findChildById(SvgNode* node, const char* id)
 }
 
 
-static vector<SvgGradientStop*> _cloneGradStops(vector<SvgGradientStop*> src)
+static void _cloneGradStops(vector<Fill::ColorStop*> *dst, vector<Fill::ColorStop*> src)
 {
-    vector<SvgGradientStop*> dst;
-    copy(src.begin(), src.end(), dst.begin());
-    return dst;
+    for(vector<Fill::ColorStop*>::iterator itrStop = src.begin(); itrStop != src.end(); itrStop++) {
+         Fill::ColorStop *stop = (Fill::ColorStop *)malloc(sizeof(Fill::ColorStop));
+         stop->r = (*itrStop)->r;
+         stop->g = (*itrStop)->g;
+         stop->b = (*itrStop)->b;
+         stop->a = (*itrStop)->a;
+         stop->offset = (*itrStop)->offset;
+         dst->push_back(stop);
+    }
+
 }
 
 
@@ -1381,8 +1388,8 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
 
     grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
     grad->type = from->type;
-    grad->id = _copyId(from->id->c_str());
-    grad->ref = _copyId(from->ref->c_str());
+    grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
+    grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
     grad->spread = from->spread;
     grad->usePercentage = from->usePercentage;
     grad->userSpace = from->userSpace;
@@ -1390,7 +1397,6 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
         grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
         memcpy(grad->transform, from->transform, sizeof(Matrix));
     }
-    grad->stops = _cloneGradStops(from->stops);
     if (grad->type == SvgGradientType::Linear) {
         grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
         memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
@@ -1399,6 +1405,7 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
         memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
     }
 
+    _cloneGradStops(&(grad->stops), from->stops);
     return grad;
 }
 
@@ -1562,14 +1569,14 @@ FIND_FACTORY(Group, groupTags);
 FIND_FACTORY(Graphics, graphicsTags);
 
 
-SvgGradientSpread _parseSpreadValue(const char* value)
+FillSpread _parseSpreadValue(const char* value)
 {
-    SvgGradientSpread spread = SvgGradientSpread::Pad;
+    FillSpread spread = FillSpread::Pad;
 
     if (!strcmp(value, "reflect")) {
-        spread = SvgGradientSpread::Reflect;
+        spread = FillSpread::Reflect;
     } else if (!strcmp(value, "repeat")) {
-        spread = SvgGradientSpread::Repeat;
+        spread = FillSpread::Repeat;
     }
 
     return spread;
@@ -1730,7 +1737,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
 static bool _attrParseStops(void* data, const char* key, const char* value)
 {
     SvgLoaderData* loader = (SvgLoaderData*)data;
-    SvgGradientStop* stop = loader->svgParse->gradStop;
+    Fill::ColorStop* stop = loader->svgParse->gradStop;
 
     if (!strcmp(key, "offset")) {
         stop->offset = _toOffset(value);
@@ -2008,7 +2015,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
         }
         loader->latestGradient = gradient;
     } else if (!strcmp(tagName, "stop")) {
-        SvgGradientStop* stop = (SvgGradientStop*)calloc(1, sizeof(SvgGradientStop));
+        Fill::ColorStop* stop = (Fill::ColorStop*)calloc(1, sizeof(Fill::ColorStop));
         loader->svgParse->gradStop = stop;
         /* default value for opacity */
         stop->a = 255;
@@ -2067,7 +2074,7 @@ static void _styleInherit(SvgStyleProperty* child, SvgStyleProperty* parent)
         child->fill.paint.b = parent->fill.paint.b;
         child->fill.paint.none = parent->fill.paint.none;
         child->fill.paint.curColor = parent->fill.paint.curColor;
-        child->fill.paint.url = parent->fill.paint.url ? _copyId(parent->fill.paint.url->c_str()) : nullptr;
+        if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url->c_str());
     }
     if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
         child->fill.opacity = parent->fill.opacity;
@@ -2109,12 +2116,12 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
 }
 
 
-static SvgStyleGradient* _gradientDup(vector<SvgStyleGradient*> gradList, const char* id)
+static SvgStyleGradient* _gradientDup(vector<SvgStyleGradient*> gradList, string* id)
 {
     SvgStyleGradient* result = nullptr;
 
     for (vector<SvgStyleGradient*>::iterator itrGrad = gradList.begin(); itrGrad != gradList.end(); itrGrad++) {
-        if ((*itrGrad)->id->compare(string(id))) {
+        if (!((*itrGrad)->id->compare(*id))) {
             result = _cloneGradient(*itrGrad);
             break;
         }
@@ -2122,9 +2129,9 @@ static SvgStyleGradient* _gradientDup(vector<SvgStyleGradient*> gradList, const
 
     if (result && result->ref) {
         for (vector<SvgStyleGradient*>::iterator itrGrad = gradList.begin(); itrGrad != gradList.end(); itrGrad++) {
-            if ((*itrGrad)->id->compare(*result->ref)) {
+            if (!((*itrGrad)->id->compare(*result->ref))) {
                 if (!result->stops.empty()) {
-                    result->stops = _cloneGradStops((*itrGrad)->stops);
+                    _cloneGradStops(&(result->stops), (*itrGrad)->stops);
                 }
                 //TODO: Properly inherit other property
                 break;
@@ -2138,15 +2145,15 @@ static SvgStyleGradient* _gradientDup(vector<SvgStyleGradient*> gradList, const
 
 static void _updateGradient(SvgNode* node, vector<SvgStyleGradient*> gradList)
 {
-    if (node->child.empty()) {
+    if (!node->child.empty()) {
         for (vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
             _updateGradient(*itrChild, gradList);
         }
     } else {
         if (node->style->fill.paint.url) {
-            node->style->fill.paint.gradient = _gradientDup(gradList, node->style->fill.paint.url->c_str());
+            node->style->fill.paint.gradient = _gradientDup(gradList, node->style->fill.paint.url);
         } else if (node->style->stroke.paint.url) {
-            node->style->stroke.paint.gradient = _gradientDup(gradList, node->style->stroke.paint.url->c_str());
+            //node->style->stroke.paint.gradient = _gradientDup(gradList, node->style->stroke.paint.url);
         }
     }
 }
@@ -2161,7 +2168,7 @@ static void _freeGradientStyle(SvgStyleGradient* grad)
     free(grad->linear);
     if (grad->transform) free(grad->transform);
 
-    for(vector<SvgGradientStop*>::iterator itrStop = grad->stops.begin(); itrStop != grad->stops.end(); itrStop++) {
+    for(vector<Fill::ColorStop*>::iterator itrStop = grad->stops.begin(); itrStop != grad->stops.end(); itrStop++) {
         free(*itrStop);
     }
     free(grad);
@@ -2284,7 +2291,9 @@ bool SvgLoader::read()
             else {
                 if (!loader->loaderData.gradients.empty()) {
                     vector<SvgStyleGradient*> gradientList;
-                    std::copy(loader->loaderData.gradients.begin(), loader->loaderData.gradients.end(), gradientList.begin());
+                    for(vector<SvgStyleGradient*>::iterator itrGrad = loader->loaderData.gradients.begin(); itrGrad != loader->loaderData.gradients.end(); itrGrad++) {
+                         gradientList.push_back(*itrGrad);
+                    }
                     _updateGradient(loader->loaderData.doc, gradientList);
                     gradientList.clear();
                 }
index b5ae7bb..2f1af74 100644 (file)
@@ -97,14 +97,6 @@ enum class SvgStyleType
     CompOp
 };
 
-enum class SvgGradientSpread
-{
-    Pad = 0,
-    Reflect,
-    Repeat,
-    Last
-};
-
 enum class SvgFillRule
 {
     Winding = 0,
@@ -221,7 +213,7 @@ struct SvgGradientStop
 struct SvgPaint
 {
     SvgStyleGradient* gradient;
-    stringurl;
+    string *url;
     uint8_t r;
     uint8_t g;
     uint8_t b;
@@ -238,13 +230,13 @@ struct SvgDash
 struct _SvgStyleGradient
 {
     SvgGradientType type;
-    string* id;
-    string* ref;
-    SvgGradientSpread spread;
-    vector<SvgGradientStop *> stops;
+    string *id;
+    string *ref;
+    FillSpread spread;
     SvgRadialGradient* radial;
     SvgLinearGradient* linear;
     Matrix* transform;
+    vector<Fill::ColorStop *> stops;
     bool userSpace;
     bool usePercentage;
 };
@@ -286,8 +278,8 @@ struct _SvgNode
     SvgNodeType type;
     SvgNode* parent;
     vector<SvgNode*> child;
-    stringid;
-    SvgStylePropertystyle;
+    string *id;
+    SvgStyleProperty *style;
     Matrix* transform;
     union {
         SvgGNode g;
@@ -309,7 +301,7 @@ struct SvgParser
 {
     SvgNode* node;
     SvgStyleGradient* styleGrad;
-    SvgGradientStop* gradStop;
+    Fill::ColorStop* gradStop;
     struct
     {
         int x, y;
@@ -334,4 +326,4 @@ struct SvgLoaderData
     bool result = false;
 };
 
-#endif
\ No newline at end of file
+#endif
index 69e4a0a..d7fcb82 100644 (file)
@@ -40,7 +40,188 @@ static void _getTransformationData(Matrix* m, float* tx, float* ty, float* s, fl
 }
 
 
-unique_ptr<tvg::Shape> _applyProperty(SvgNode* node, unique_ptr<tvg::Shape> vg)
+unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
+{
+    Fill::ColorStop* stops;
+    int stopCount = 0;
+    float fillOpacity = 255.0f;
+    float gx, gy, gw, gh;
+
+    auto fillGrad = LinearGradient::gen();
+
+    if (g->usePercentage) {
+        g->linear->x1 = g->linear->x1 * rw + rx;
+        g->linear->y1 = g->linear->y1 * rh + ry;
+        g->linear->x2 = g->linear->x2 * rw + rx;
+        g->linear->y2 = g->linear->y2 * rh + ry;
+    }
+
+    //In case of objectBoundingBox it need proper scaling
+    if (!g->userSpace) {
+        float scaleX = 1.0, scaleReversedX = 1.0;
+        float scaleY = 1.0, scaleReversedY = 1.0;
+
+        //Check the smallest size, find the scale value
+        if (rh > rw) {
+            scaleY = ((float)rw) / rh;
+            scaleReversedY = ((float)rh) / rw;
+        } else {
+            scaleX = ((float)rh) / rw;
+            scaleReversedX = ((float)rw) / rh;
+        }
+
+        vg->bounds(&gx, &gy, &gw, &gh);
+
+        float cy = ((float)gh) * 0.5 + gy;
+        float cy_scaled = (((float)gh) * 0.5) * scaleReversedY;
+        float cx = ((float)gw) * 0.5 + gx;
+        float cx_scaled = (((float)gw) * 0.5) * scaleReversedX;
+
+        //= T(gx, gy) x S(scaleX, scaleY) x T(cx_scaled - cx, cy_scaled - cy) x (radial->x, radial->y)
+        g->linear->x1 = g->linear->x1 * scaleX + scaleX * (cx_scaled - cx) + gx;
+        g->linear->y1 = g->linear->y1 * scaleY + scaleY * (cy_scaled - cy) + gy;
+        g->linear->x2 = g->linear->x2 * scaleX + scaleX * (cx_scaled - cx) + gx;
+        g->linear->y2 = g->linear->y2 * scaleY + scaleY * (cy_scaled - cy) + gy;
+    }
+
+    if (g->transform) {
+         float cy = ((float) rh) * 0.5 + ry;
+         float cx = ((float) rw) * 0.5 + rx;
+
+         //Calc start point
+         //= T(x - cx, y - cy) x g->transform x T(cx, cy)
+         g->linear->x1 = cx * (g->transform->e11 + g->transform->e31 * (g->linear->x1 - cx)) +
+                         cx * (g->transform->e12 + g->transform->e32 * (g->linear->x1 - cx)) +
+                         cx * (g->transform->e13 + g->transform->e33 * (g->linear->x1 - cx));
+
+         g->linear->y1 = cy * (g->transform->e21 + g->transform->e31 * (g->linear->y1 - cy)) +
+                         cy * (g->transform->e22 + g->transform->e32 * (g->linear->y1 - cy)) +
+                         cy * (g->transform->e23 + g->transform->e33 * (g->linear->y1 - cy));
+
+         //Calc end point
+         g->linear->x2 = cx * (g->transform->e11 + g->transform->e31 * (g->linear->x2 - cx)) +
+                         cx * (g->transform->e12 + g->transform->e32 * (g->linear->x2 - cx)) +
+                         cx * (g->transform->e13 + g->transform->e33 * (g->linear->x2 - cx));
+
+         g->linear->y2 = cy * (g->transform->e21 + g->transform->e31 * (g->linear->y2 - cy)) +
+                         cy * (g->transform->e22 + g->transform->e32 * (g->linear->y2 - cy)) +
+                         cy * (g->transform->e23 + g->transform->e33 * (g->linear->y2 - cy));
+    }
+
+    fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
+    fillGrad->spread(g->spread);
+
+    //Update the stops
+    stopCount = g->stops.size();
+    if (stopCount > 0) {
+        float opacity;
+        float fopacity = fillOpacity / 255.0f; //fill opacity if any exists.
+        int i = 0;
+        stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
+        for (vector<Fill::ColorStop*>::iterator itrStop = g->stops.begin(); itrStop != g->stops.end(); itrStop++) {
+            //Use premultiplied color
+            opacity = ((float)(*itrStop)->a / 255) * fopacity;
+            stops[i].r = ((*itrStop)->r * opacity);
+            stops[i].g = ((*itrStop)->g * opacity);
+            stops[i].b = ((*itrStop)->b * opacity);
+            stops[i].a = ((*itrStop)->a * fopacity);
+            stops[i].offset = (*itrStop)->offset;
+            i++;
+        }
+        fillGrad->colorStops(stops, stopCount);
+        free(stops);
+    }
+    return move(fillGrad);
+}
+
+
+unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
+{
+    Fill::ColorStop *stops;
+    int stopCount = 0;
+    float gx, gy, gw, gh;
+    int radius;
+    float fillOpacity = 255.0f;
+
+    auto fillGrad = RadialGradient::gen();
+
+    radius = sqrt(pow(rw, 2) + pow(rh, 2)) / sqrt(2.0);
+    if (!g->userSpace) {
+         //That is according to Units in here
+         //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+        int min = (rh > rw) ? rw : rh;
+        radius = sqrt(pow(min, 2) + pow(min, 2)) / sqrt(2.0);
+    }
+
+    if (g->usePercentage) {
+        g->radial->cx = g->radial->cx * rw + rx;
+        g->radial->cy = g->radial->cy * rh + ry;
+        g->radial->r = g->radial->r * radius;
+        g->radial->fx = g->radial->fx * rw + rx;
+        g->radial->fy = g->radial->fy * rh + ry;
+    }
+
+    //In case of objectBoundingBox it need proper scaling
+    if (!g->userSpace) {
+        float scaleX = 1.0, scaleReversedX = 1.0;
+        float scaleY = 1.0, scaleReversedY = 1.0;
+
+        //Check the smallest size, find the scale value
+        if (rh > rw) {
+            scaleY = ((float)rw) / rh;
+            scaleReversedY = ((float)rh) / rw;
+        } else {
+            scaleX = ((float)rh) / rw;
+            scaleReversedX = ((float)rw) / rh;
+        }
+
+        vg->bounds(&gx, &gy, &gw, &gh);
+
+        float cy = ((float)gh) * 0.5 + gy;
+        float cy_scaled = (((float)gh) * 0.5) * scaleReversedY;
+        float cx = ((float)gw) * 0.5 + gx;
+        float cx_scaled = (((float)gw) * 0.5) * scaleReversedX;
+
+         //= T(gx, gy) x S(scaleX, scaleY) x T(cx_scaled - cx, cy_scaled - cy) x (radial->x, radial->y)
+        g->radial->cx = g->radial->cx * scaleX + scaleX * (cx_scaled - cx) + gx;
+        g->radial->cy = g->radial->cy * scaleY + scaleY * (cy_scaled - cy) + gy;
+    }
+
+    //TODO: Radial gradient transformation is not yet supported.
+    //if (g->transform) {}
+
+    //TODO: Tvg is not support to focal
+    //if (g->radial->fx != 0 && g->radial->fy != 0) {
+    //    fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
+    //}
+    fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+    fillGrad->spread(g->spread);
+
+    //Update the stops
+    stopCount = g->stops.size();
+    if (stopCount > 0) {
+        float opacity;
+        float fopacity = fillOpacity / 255.0f; //fill opacity if any exists.
+        int i = 0;
+        stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
+        for (vector<Fill::ColorStop*>::iterator itrStop = g->stops.begin(); itrStop != g->stops.end(); itrStop++) {
+            //Use premultiplied color
+            opacity = ((float)(*itrStop)->a / 255) * fopacity;
+            stops[i].r = ((*itrStop)->r * opacity);
+            stops[i].g = ((*itrStop)->g * opacity);
+            stops[i].b = ((*itrStop)->b * opacity);
+            stops[i].a = ((*itrStop)->a * fopacity);
+            stops[i].offset = (*itrStop)->offset;
+            i++;
+        }
+        fillGrad->colorStops(stops, stopCount);
+        free(stops);
+    }
+    return move(fillGrad);
+}
+
+
+unique_ptr<Shape> _applyProperty(SvgNode* node, unique_ptr<Shape> vg, float vx, float vy, float vw, float vh)
 {
     SvgStyleProperty* style = node->style;
 
@@ -59,7 +240,15 @@ unique_ptr<tvg::Shape> _applyProperty(SvgNode* node, unique_ptr<tvg::Shape> vg)
     if (style->fill.paint.none) {
         //Do nothing
     } else if (style->fill.paint.gradient) {
-        //TODO: Support gradient style
+         if (!style->fill.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh);
+
+        if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
+             auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg.get(), vx, vy, vw, vh);
+             vg->fill(move(linear));
+        } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
+             auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg.get(), vx, vy, vw, vh);
+             vg->fill(move(radial));
+        }
     } else if (style->fill.paint.curColor) {
         //Apply the current style color
         float fa = ((float)style->fill.opacity / 255.0);
@@ -113,9 +302,9 @@ unique_ptr<tvg::Shape> _applyProperty(SvgNode* node, unique_ptr<tvg::Shape> vg)
 }
 
 
-unique_ptr<tvg::Shape> _shapeBuildHelper(SvgNode* node)
+unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
 {
-    auto shape = tvg::Shape::gen();
+    auto shape = Shape::gen();
     switch (node->type) {
         case SvgNodeType::Path: {
             if (node->node.path.path) {
@@ -162,15 +351,15 @@ unique_ptr<tvg::Shape> _shapeBuildHelper(SvgNode* node)
             break;
         }
     }
-    shape = move(_applyProperty(node, move(shape)));
+    shape = move(_applyProperty(node, move(shape), vx, vy, vw, vh));
     return shape;
 }
 
 
-unique_ptr<tvg::Scene> _sceneBuildHelper(SvgNode* node)
+unique_ptr<Scene> _sceneBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
 {
     if (node->type == SvgNodeType::Doc || node->type == SvgNodeType::G) {
-        auto scene = tvg::Scene::gen();
+        auto scene = Scene::gen();
         if (node->transform) {
             float tx = 0, ty = 0, s = 0, z = 0;
             _getTransformationData(node->transform, &tx, &ty, &s, &z);
@@ -180,8 +369,8 @@ unique_ptr<tvg::Scene> _sceneBuildHelper(SvgNode* node)
         }
         for (vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
             SvgNode* child = *itrChild;
-            if (child->type == SvgNodeType::Doc || child->type == SvgNodeType::G) scene->push(_sceneBuildHelper(*itrChild));
-            else scene->push(_shapeBuildHelper(*itrChild));
+            if (child->type == SvgNodeType::Doc || child->type == SvgNodeType::G) scene->push(_sceneBuildHelper(*itrChild, vx, vy, vw, vh));
+            else scene->push(_shapeBuildHelper(*itrChild, vx, vy, vw, vh));
         }
         return move(scene);
     }
@@ -209,7 +398,7 @@ unique_ptr<Scene> SvgSceneBuilder::build(SvgNode* node)
     viewBox.h = node->node.doc.vh;
     preserveAspect = node->node.doc.preserveAspect;
     staticViewBox = true;
-    return _sceneBuildHelper(node);
+    return _sceneBuildHelper(node, viewBox.x, viewBox.y, viewBox.w, viewBox.h);
 }
 
 
diff --git a/test/svgs/lineargrad1.svg b/test/svgs/lineargrad1.svg
new file mode 100644 (file)
index 0000000..343d161
--- /dev/null
@@ -0,0 +1,20 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient1"
+       x1="0"
+       y1="0"
+       x2="0.2"
+       y2="0.2"
+       spreadMethod="reflect">
+      <stop
+         style="stop-color:#ff0000;stop-opacity:1;"
+         offset="0"/>
+      <stop
+         style="stop-color:#0000ff;stop-opacity:1;"
+         offset="1"/>
+    </linearGradient>
+  </defs>
+  <rect x="0" y="0" width="100" height="100" fill="url(#linearGradient1)"/>
+</svg>
diff --git a/test/svgs/radialgrad1.svg b/test/svgs/radialgrad1.svg
new file mode 100644 (file)
index 0000000..5cb6da8
--- /dev/null
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
+  <defs
+     id="defs4">
+    <radialGradient
+       id="radialGradient1"
+       r="0.2"
+       cx="0.3"
+       cy="0.3"
+       spreadMethod="reflect">
+      <stop
+         style="stop-color:#ff0000;stop-opacity:1;"
+         offset="0"/>
+      <stop
+         style="stop-color:#0000ff;stop-opacity:1;"
+         offset="1"/>
+    </radialGradient>
+  </defs>
+  <rect x="0" y="0" width="100" height="100" fill="url(#radialGradient1)"/>
+</svg>