From f6444436557600ebecd36e1fd7a2acf1270bf74a Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 9 Jul 2020 13:23:41 +0900 Subject: [PATCH] SvgLoader: Support to linear, radial gradient Change-Id: Ida3f6ccca5f0d6ed1922b7ce99d2d2f3203f5ba9 --- src/loaders/svg_loader/tvgSvgLoader.cpp | 55 +++-- src/loaders/svg_loader/tvgSvgLoaderCommon.h | 26 +-- src/loaders/svg_loader/tvgSvgSceneBuilder.cpp | 209 +++++++++++++++++- test/svgs/lineargrad1.svg | 20 ++ test/svgs/radialgrad1.svg | 19 ++ 5 files changed, 279 insertions(+), 50 deletions(-) create mode 100644 test/svgs/lineargrad1.svg create mode 100644 test/svgs/radialgrad1.svg diff --git a/src/loaders/svg_loader/tvgSvgLoader.cpp b/src/loaders/svg_loader/tvgSvgLoader.cpp index f4ed3be9..f8f7c4ea 100644 --- a/src/loaders/svg_loader/tvgSvgLoader.cpp +++ b/src/loaders/svg_loader/tvgSvgLoader.cpp @@ -1365,11 +1365,18 @@ static SvgNode* _findChildById(SvgNode* node, const char* id) } -static vector _cloneGradStops(vector src) +static void _cloneGradStops(vector *dst, vector src) { - vector dst; - copy(src.begin(), src.end(), dst.begin()); - return dst; + for(vector::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 gradList, const char* id) +static SvgStyleGradient* _gradientDup(vector gradList, string* id) { SvgStyleGradient* result = nullptr; for (vector::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 gradList, const if (result && result->ref) { for (vector::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 gradList, const static void _updateGradient(SvgNode* node, vector gradList) { - if (node->child.empty()) { + if (!node->child.empty()) { for (vector::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::iterator itrStop = grad->stops.begin(); itrStop != grad->stops.end(); itrStop++) { + for(vector::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 gradientList; - std::copy(loader->loaderData.gradients.begin(), loader->loaderData.gradients.end(), gradientList.begin()); + for(vector::iterator itrGrad = loader->loaderData.gradients.begin(); itrGrad != loader->loaderData.gradients.end(); itrGrad++) { + gradientList.push_back(*itrGrad); + } _updateGradient(loader->loaderData.doc, gradientList); gradientList.clear(); } diff --git a/src/loaders/svg_loader/tvgSvgLoaderCommon.h b/src/loaders/svg_loader/tvgSvgLoaderCommon.h index b5ae7bb2..2f1af74f 100644 --- a/src/loaders/svg_loader/tvgSvgLoaderCommon.h +++ b/src/loaders/svg_loader/tvgSvgLoaderCommon.h @@ -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; - string* url; + 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 stops; + string *id; + string *ref; + FillSpread spread; SvgRadialGradient* radial; SvgLinearGradient* linear; Matrix* transform; + vector stops; bool userSpace; bool usePercentage; }; @@ -286,8 +278,8 @@ struct _SvgNode SvgNodeType type; SvgNode* parent; vector child; - string* id; - SvgStyleProperty* style; + 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 diff --git a/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp b/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp index 69e4a0a2..d7fcb82f 100644 --- a/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp @@ -40,7 +40,188 @@ static void _getTransformationData(Matrix* m, float* tx, float* ty, float* s, fl } -unique_ptr _applyProperty(SvgNode* node, unique_ptr vg) +unique_ptr _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::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 _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::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 _applyProperty(SvgNode* node, unique_ptr vg, float vx, float vy, float vw, float vh) { SvgStyleProperty* style = node->style; @@ -59,7 +240,15 @@ unique_ptr _applyProperty(SvgNode* node, unique_ptr 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 _applyProperty(SvgNode* node, unique_ptr vg) } -unique_ptr _shapeBuildHelper(SvgNode* node) +unique_ptr _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 _shapeBuildHelper(SvgNode* node) break; } } - shape = move(_applyProperty(node, move(shape))); + shape = move(_applyProperty(node, move(shape), vx, vy, vw, vh)); return shape; } -unique_ptr _sceneBuildHelper(SvgNode* node) +unique_ptr _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 _sceneBuildHelper(SvgNode* node) } for (vector::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 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 index 00000000..343d161f --- /dev/null +++ b/test/svgs/lineargrad1.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/test/svgs/radialgrad1.svg b/test/svgs/radialgrad1.svg new file mode 100644 index 00000000..5cb6da8b --- /dev/null +++ b/test/svgs/radialgrad1.svg @@ -0,0 +1,19 @@ + + + + + + + + +