summaryrefslogtreecommitdiff
path: root/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp')
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp1120
1 files changed, 732 insertions, 388 deletions
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index bc350a0eb8..f9f08cdb81 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -137,11 +137,11 @@ static constexpr struct
};
-static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
+static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
{
if (!strcmp(*content, "none")) {
*align = AspectRatioAlign::None;
- return true;
+ return;
}
for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
@@ -158,8 +158,6 @@ static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
} else if (!strcmp(*content, "slice")) {
*meetOrSlice = AspectRatioMeetOrSlice::Slice;
}
-
- return true;
}
@@ -254,6 +252,36 @@ static SvgMaskType _toMaskType(const char* str)
}
+//The default rendering order: fill, stroke, markers
+//If any is omitted, will be rendered in its default order after the specified ones.
+static bool _toPaintOrder(const char* str)
+{
+ uint8_t position = 1;
+ uint8_t strokePosition = 0;
+ uint8_t fillPosition = 0;
+
+ while (*str != '\0') {
+ str = _skipSpace(str, nullptr);
+ if (!strncmp(str, "fill", 4)) {
+ fillPosition = position++;
+ str += 4;
+ } else if (!strncmp(str, "stroke", 6)) {
+ strokePosition = position++;
+ str += 6;
+ } else if (!strncmp(str, "markers", 7)) {
+ str += 7;
+ } else {
+ return _toPaintOrder("fill stroke");
+ }
+ }
+
+ if (fillPosition == 0) fillPosition = position++;
+ if (strokePosition == 0) strokePosition = position++;
+
+ return fillPosition < strokePosition;
+}
+
+
#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
static Type _to##Name1(const char* str) \
{ \
@@ -833,20 +861,40 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
if (!strcmp(key, "width")) {
doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
+ }
} else if (!strcmp(key, "height")) {
doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
+ }
} else if (!strcmp(key, "viewBox")) {
if (_parseNumber(&value, &doc->vx)) {
if (_parseNumber(&value, &doc->vy)) {
if (_parseNumber(&value, &doc->vw)) {
- _parseNumber(&value, &doc->vh);
- loader->svgParse->global.h = doc->vh;
+ if (_parseNumber(&value, &doc->vh)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
+ loader->svgParse->global.h = doc->vh;
+ }
+ loader->svgParse->global.w = doc->vw;
}
- loader->svgParse->global.w = doc->vw;
+ loader->svgParse->global.y = doc->vy;
}
- loader->svgParse->global.y = doc->vy;
+ loader->svgParse->global.x = doc->vx;
+ }
+ if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
+ doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
+ TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
+ }
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
+ loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
}
- loader->svgParse->global.x = doc->vx;
} else if (!strcmp(key, "preserveAspectRatio")) {
_parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (!strcmp(key, "style")) {
@@ -890,7 +938,7 @@ static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, co
static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
+ style->fill.flags = (style->fill.flags | SvgFillFlags::Paint);
_handlePaintAttr(&style->fill.paint, value);
}
@@ -898,47 +946,47 @@ static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con
static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
+ style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint);
_handlePaintAttr(&style->stroke.paint, value);
}
static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity);
node->style->stroke.opacity = _toOpacity(value);
}
static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash);
_parseDashArray(loader, value, &node->style->stroke.dash);
}
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
}
static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap);
node->style->stroke.cap = _toLineCap(value);
}
static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join);
node->style->stroke.join = _toLineJoin(value);
}
static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule);
node->style->fill.fillRule = _toFillRule(value);
}
@@ -951,7 +999,7 @@ static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity);
node->style->fill.opacity = _toOpacity(value);
}
@@ -1001,6 +1049,13 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
}
+static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder);
+ node->style->paintOrder = _toPaintOrder(value);
+}
+
+
static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
auto cssClass = &node->style->cssClass;
@@ -1051,7 +1106,8 @@ static constexpr struct
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
- STYLE_DEF(display, Display, SvgStyleFlags::Display)
+ STYLE_DEF(display, Display, SvgStyleFlags::Display),
+ STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
};
@@ -1071,8 +1127,8 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
if (style) {
styleTags[i].tagHandler(loader, node, value);
- node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
- } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
+ node->style->flags = (node->style->flags | styleTags[i].flag);
+ } else if (!(node->style->flags & styleTags[i].flag)) {
styleTags[i].tagHandler(loader, node, value);
}
return true;
@@ -1253,6 +1309,8 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
node->style->stroke.join = StrokeJoin::Miter;
node->style->stroke.scale = 1.0;
+ node->style->paintOrder = _toPaintOrder("fill stroke");
+
//Default display is true("inline").
node->display = true;
@@ -1291,22 +1349,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
if (!loader->svgParse->node) return nullptr;
SvgDocNode* doc = &(loader->svgParse->node->node.doc);
- loader->svgParse->global.w = 0;
- loader->svgParse->global.h = 0;
+ loader->svgParse->global.w = 1.0f;
+ loader->svgParse->global.h = 1.0f;
doc->align = AspectRatioAlign::XMidYMid;
doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
+ doc->viewFlag = SvgViewFlag::None;
func(buf, bufLength, _attrParseSvgNode, loader);
- if (loader->svgParse->global.w == 0) {
- if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
- else loader->svgParse->global.w = doc->w;
- }
- if (loader->svgParse->global.h == 0) {
- if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
- else loader->svgParse->global.h = doc->h;
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ if (doc->viewFlag & SvgViewFlag::Width) {
+ loader->svgParse->global.w = doc->w;
+ }
+ if (doc->viewFlag & SvgViewFlag::Height) {
+ loader->svgParse->global.h = doc->h;
+ }
}
-
return loader->svgParse->node;
}
@@ -1868,309 +1926,6 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id)
}
-static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
-{
- for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src.data[i]);
- }
-}
-
-
-static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
-{
- if (!from) return nullptr;
-
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- if (!grad) return nullptr;
-
- grad->type = from->type;
- grad->id = from->id ? _copyId(from->id) : nullptr;
- grad->ref = from->ref ? _copyId(from->ref) : nullptr;
- grad->spread = from->spread;
- grad->userSpace = from->userSpace;
-
- if (from->transform) {
- grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
- if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
- }
-
- if (grad->type == SvgGradientType::Linear) {
- grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
- if (!grad->linear) goto error_grad_alloc;
- memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
- } else if (grad->type == SvgGradientType::Radial) {
- grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
- if (!grad->radial) goto error_grad_alloc;
- memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
- }
-
- _cloneGradStops(grad->stops, from->stops);
-
- return grad;
-
-error_grad_alloc:
- if (grad) {
- grad->clear();
- free(grad);
- }
- return nullptr;
-}
-
-
-static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
-{
- if (parent == nullptr) return;
- //Inherit the property of parent if not present in child.
- if (!child->curColorSet) {
- child->color = parent->color;
- child->curColorSet = parent->curColorSet;
- }
- //Fill
- if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
- child->fill.paint.color = parent->fill.paint.color;
- child->fill.paint.none = parent->fill.paint.none;
- child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) {
- if (child->fill.paint.url) free(child->fill.paint.url);
- child->fill.paint.url = _copyId(parent->fill.paint.url);
- }
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
- child->fill.opacity = parent->fill.opacity;
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
- child->fill.fillRule = parent->fill.fillRule;
- }
- //Stroke
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- child->stroke.paint.color = parent->stroke.paint.color;
- child->stroke.paint.none = parent->stroke.paint.none;
- child->stroke.paint.curColor = parent->stroke.paint.curColor;
- if (parent->stroke.paint.url) {
- if (child->stroke.paint.url) free(child->stroke.paint.url);
- child->stroke.paint.url = _copyId(parent->stroke.paint.url);
- } else {
- child->stroke.paint.url = nullptr;
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- child->stroke.opacity = parent->stroke.opacity;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
- child->stroke.width = parent->stroke.width;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (parent->stroke.dash.array.count > 0) {
- child->stroke.dash.array.clear();
- child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
- for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
- }
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- child->stroke.cap = parent->stroke.cap;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
- child->stroke.join = parent->stroke.join;
- }
-}
-
-
-static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
-{
- if (from == nullptr) return;
- //Copy the properties of 'from' only if they were explicitly set (not the default ones).
- if (from->curColorSet) {
- to->color = from->color;
- to->curColorSet = true;
- }
- //Fill
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
- if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
- to->fill.paint.color = from->fill.paint.color;
- to->fill.paint.none = from->fill.paint.none;
- to->fill.paint.curColor = from->fill.paint.curColor;
- if (from->fill.paint.url) {
- if (to->fill.paint.url) free(to->fill.paint.url);
- to->fill.paint.url = _copyId(from->fill.paint.url);
- }
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
- to->fill.opacity = from->fill.opacity;
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
- to->fill.fillRule = from->fill.fillRule;
- }
- //Stroke
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- to->stroke.paint.color = from->stroke.paint.color;
- to->stroke.paint.none = from->stroke.paint.none;
- to->stroke.paint.curColor = from->stroke.paint.curColor;
- if (from->stroke.paint.url) {
- if (to->stroke.paint.url) free(to->stroke.paint.url);
- to->stroke.paint.url = _copyId(from->stroke.paint.url);
- } else {
- to->stroke.paint.url = nullptr;
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- to->stroke.opacity = from->stroke.opacity;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
- to->stroke.width = from->stroke.width;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (from->stroke.dash.array.count > 0) {
- to->stroke.dash.array.clear();
- to->stroke.dash.array.reserve(from->stroke.dash.array.count);
- for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
- }
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- to->stroke.cap = from->stroke.cap;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
- to->stroke.join = from->stroke.join;
- }
-}
-
-
-static void _copyAttr(SvgNode* to, const SvgNode* from)
-{
- //Copy matrix attribute
- if (from->transform) {
- to->transform = (Matrix*)malloc(sizeof(Matrix));
- if (to->transform) *to->transform = *from->transform;
- }
- //Copy style attribute
- _styleCopy(to->style, from->style);
- to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
- if (from->style->clipPath.url) {
- if (to->style->clipPath.url) free(to->style->clipPath.url);
- to->style->clipPath.url = strdup(from->style->clipPath.url);
- }
- if (from->style->mask.url) {
- if (to->style->mask.url) free(to->style->mask.url);
- to->style->mask.url = strdup(from->style->mask.url);
- }
-
- //Copy node attribute
- switch (from->type) {
- case SvgNodeType::Circle: {
- to->node.circle.cx = from->node.circle.cx;
- to->node.circle.cy = from->node.circle.cy;
- to->node.circle.r = from->node.circle.r;
- break;
- }
- case SvgNodeType::Ellipse: {
- to->node.ellipse.cx = from->node.ellipse.cx;
- to->node.ellipse.cy = from->node.ellipse.cy;
- to->node.ellipse.rx = from->node.ellipse.rx;
- to->node.ellipse.ry = from->node.ellipse.ry;
- break;
- }
- case SvgNodeType::Rect: {
- to->node.rect.x = from->node.rect.x;
- to->node.rect.y = from->node.rect.y;
- to->node.rect.w = from->node.rect.w;
- to->node.rect.h = from->node.rect.h;
- to->node.rect.rx = from->node.rect.rx;
- to->node.rect.ry = from->node.rect.ry;
- to->node.rect.hasRx = from->node.rect.hasRx;
- to->node.rect.hasRy = from->node.rect.hasRy;
- break;
- }
- case SvgNodeType::Line: {
- to->node.line.x1 = from->node.line.x1;
- to->node.line.y1 = from->node.line.y1;
- to->node.line.x2 = from->node.line.x2;
- to->node.line.y2 = from->node.line.y2;
- break;
- }
- case SvgNodeType::Path: {
- if (from->node.path.path) {
- if (to->node.path.path) free(to->node.path.path);
- to->node.path.path = strdup(from->node.path.path);
- }
- break;
- }
- case SvgNodeType::Polygon: {
- if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
- to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
- memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Polyline: {
- if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
- to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
- memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Image: {
- to->node.image.x = from->node.image.x;
- to->node.image.y = from->node.image.y;
- to->node.image.w = from->node.image.w;
- to->node.image.h = from->node.image.h;
- if (from->node.image.href) {
- if (to->node.image.href) free(to->node.image.href);
- to->node.image.href = strdup(from->node.image.href);
- }
- break;
- }
- default: {
- break;
- }
- }
-}
-
-
-static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
-{
- /* Exception handling: Prevent invalid SVG data input.
- The size is the arbitrary value, we need an experimental size. */
- if (depth == 8192) {
- TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
- return;
- }
-
- SvgNode* newNode;
- if (!from || !parent || from == parent) return;
-
- newNode = _createNode(parent, from->type);
- if (!newNode) return;
-
- _styleInherit(newNode->style, parent->style);
- _copyAttr(newNode, from);
-
- auto child = from->child.data;
- for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
- _cloneNode(*child, newNode, depth + 1);
- }
-}
-
-
-static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
-{
- for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = cloneNodes->data[i];
- auto defs = _getDefsNode(nodeIdPair.node);
- auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
- if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
- _cloneNode(nodeFrom, nodeIdPair.node, 0);
- if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
- nodeIdPair.node->node.use.symbol = nodeFrom;
- }
- free(nodeIdPair.id);
- }
-}
-
-
static constexpr struct
{
const char* tag;
@@ -2185,6 +1940,7 @@ static constexpr struct
};
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth);
static bool _attrParseUseNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2379,13 +2135,99 @@ static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial,
}
+static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCxPercentage) {
+ if (userSpace) radial->cx /= loader->svgParse->global.w;
+ else radial->cx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCyPercentage) {
+ if (userSpace) radial->cy /= loader->svgParse->global.h;
+ else radial->cy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFxPercentage) {
+ if (userSpace) radial->fx /= loader->svgParse->global.w;
+ else radial->fx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFyPercentage) {
+ if (userSpace) radial->fy /= loader->svgParse->global.h;
+ else radial->fy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isRPercentage) {
+ if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ }
+}
+
+
+static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cx = from->radial->cx;
+ to->radial->isCxPercentage = from->radial->isCxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cx);
+}
+
+
+static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cy = from->radial->cy;
+ to->radial->isCyPercentage = from->radial->isCyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cy);
+}
+
+
+static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fx = from->radial->fx;
+ to->radial->isFxPercentage = from->radial->isFxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fx);
+}
+
+
+static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fy = from->radial->fy;
+ to->radial->isFyPercentage = from->radial->isFyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fy);
+}
+
+
+static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->r = from->radial->r;
+ to->radial->isRPercentage = from->radial->isRPercentage;
+ to->flags = (to->flags | SvgGradientFlags::R);
+}
+
+
typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
+typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
-#define RADIAL_DEF(Name, Name1) \
+#define RADIAL_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
+#Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \
}
@@ -2394,13 +2236,16 @@ static constexpr struct
const char* tag;
int sz;
radialMethod tagHandler;
+ radialInheritMethod tagInheritHandler;
radialMethodRecalc tagRecalc;
+ radialMethodRecalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} radialTags[] = {
- RADIAL_DEF(cx, Cx),
- RADIAL_DEF(cy, Cy),
- RADIAL_DEF(fx, Fx),
- RADIAL_DEF(fy, Fy),
- RADIAL_DEF(r, R)
+ RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx),
+ RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
+ RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
+ RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
+ RADIAL_DEF(r, R, SvgGradientFlags::R)
};
@@ -2414,6 +2259,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
radialTags[i].tagHandler(loader, radial, value);
+ grad->flags = (grad->flags | radialTags[i].flag);
return true;
}
}
@@ -2423,11 +2269,13 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2443,6 +2291,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Radial;
grad->userSpace = false;
grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
@@ -2485,10 +2334,10 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
if (!strcmp(key, "stop-opacity")) {
stop->a = _toOpacity(value);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
} else if (!strcmp(key, "stop-color")) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
} else {
return false;
}
@@ -2505,11 +2354,11 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
if (!strcmp(key, "offset")) {
stop->offset = _toOffset(value);
} else if (!strcmp(key, "stop-opacity")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) {
stop->a = _toOpacity(value);
}
} else if (!strcmp(key, "stop-color")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
}
} else if (!strcmp(key, "style")) {
@@ -2570,13 +2419,82 @@ static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear
}
+static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX1Percentage) {
+ if (userSpace) linear->x1 /= loader->svgParse->global.w;
+ else linear->x1 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX2Percentage) {
+ if (userSpace) linear->x2 /= loader->svgParse->global.w;
+ else linear->x2 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY1Percentage) {
+ if (userSpace) linear->y1 /= loader->svgParse->global.h;
+ else linear->y1 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY2Percentage) {
+ if (userSpace) linear->y2 /= loader->svgParse->global.h;
+ else linear->y2 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x1 = from->linear->x1;
+ to->linear->isX1Percentage = from->linear->isX1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X1);
+}
+
+
+static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x2 = from->linear->x2;
+ to->linear->isX2Percentage = from->linear->isX2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X2);
+}
+
+
+static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y1 = from->linear->y1;
+ to->linear->isY1Percentage = from->linear->isY1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y1);
+}
+
+
+static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y2 = from->linear->y2;
+ to->linear->isY2Percentage = from->linear->isY2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y2);
+}
+
+
typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
+typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
-#define LINEAR_DEF(Name, Name1) \
+#define LINEAR_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
+#Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \
}
@@ -2585,12 +2503,15 @@ static constexpr struct
const char* tag;
int sz;
Linear_Method tagHandler;
+ Linear_Inherit_Method tagInheritHandler;
Linear_Method_Recalc tagRecalc;
+ Linear_Method_Recalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} linear_tags[] = {
- LINEAR_DEF(x1, X1),
- LINEAR_DEF(y1, Y1),
- LINEAR_DEF(x2, X2),
- LINEAR_DEF(y2, Y2)
+ LINEAR_DEF(x1, X1, SvgGradientFlags::X1),
+ LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1),
+ LINEAR_DEF(x2, X2, SvgGradientFlags::X2),
+ LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2)
};
@@ -2604,6 +2525,7 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
linear_tags[i].tagHandler(loader, linear, value);
+ grad->flags = (grad->flags | linear_tags[i].flag);
return true;
}
}
@@ -2613,11 +2535,13 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2633,6 +2557,7 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Linear;
grad->userSpace = false;
grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
@@ -2692,6 +2617,371 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
}
+static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
+{
+ for (uint32_t i = 0; i < src.count; ++i) {
+ dst.push(src.data[i]);
+ }
+}
+
+
+static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ if (!to || !from) return;
+
+ if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) {
+ to->spread = from->spread;
+ to->flags = (to->flags | SvgGradientFlags::SpreadMethod);
+ }
+ bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits);
+ if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) {
+ to->userSpace = from->userSpace;
+ to->flags = (to->flags | SvgGradientFlags::GradientUnits);
+ }
+
+ if (!to->transform && from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) {
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ bool coordSet = to->flags & linear_tags[i].flag;
+ if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) {
+ linear_tags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ linear_tags[i].tagRecalc(loader, to->linear, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace);
+ }
+ }
+ } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) {
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ bool coordSet = (to->flags & radialTags[i].flag);
+ if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) {
+ radialTags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ radialTags[i].tagRecalc(loader, to->radial, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace);
+ }
+ }
+ }
+
+ if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops);
+}
+
+
+static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
+{
+ if (!from) return nullptr;
+
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ if (!grad) return nullptr;
+
+ grad->type = from->type;
+ grad->id = from->id ? _copyId(from->id) : nullptr;
+ grad->ref = from->ref ? _copyId(from->ref) : nullptr;
+ grad->spread = from->spread;
+ grad->userSpace = from->userSpace;
+ grad->flags = from->flags;
+
+ if (from->transform) {
+ grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
+ if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (grad->type == SvgGradientType::Linear) {
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) goto error_grad_alloc;
+ memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
+ } else if (grad->type == SvgGradientType::Radial) {
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) goto error_grad_alloc;
+ memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
+ }
+
+ _cloneGradStops(grad->stops, from->stops);
+
+ return grad;
+
+ error_grad_alloc:
+ if (grad) {
+ grad->clear();
+ free(grad);
+ }
+ return nullptr;
+}
+
+
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ if (!(child->flags & SvgStyleFlags::PaintOrder)) {
+ child->paintOrder = parent->paintOrder;
+ }
+ //Fill
+ if (!(child->fill.flags & SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) {
+ if (child->fill.paint.url) free(child->fill.paint.url);
+ child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ }
+ if (!(child->fill.flags & SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!(child->fill.flags & SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!(child->stroke.flags & SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ if (parent->stroke.paint.url) {
+ if (child->stroke.paint.url) free(child->stroke.paint.url);
+ child->stroke.paint.url = _copyId(parent->stroke.paint.url);
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ if (from->flags & SvgStyleFlags::PaintOrder) {
+ to->paintOrder = from->paintOrder;
+ }
+ //Fill
+ to->fill.flags = (to->fill.flags | from->fill.flags);
+ if (from->fill.flags & SvgFillFlags::Paint) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) {
+ if (to->fill.paint.url) free(to->fill.paint.url);
+ to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ }
+ if (from->fill.flags & SvgFillFlags::Opacity) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (from->fill.flags & SvgFillFlags::FillRule) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (to->stroke.flags | from->stroke.flags);
+ if (from->stroke.flags & SvgStrokeFlags::Paint) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ if (from->stroke.paint.url) {
+ if (to->stroke.paint.url) free(to->stroke.paint.url);
+ to->stroke.paint.url = _copyId(from->stroke.paint.url);
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Opacity) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Width) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Dash) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Cap) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Join) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
+static void _copyAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) *to->transform = *from->transform;
+ }
+ //Copy style attribute
+ _styleCopy(to->style, from->style);
+ to->style->flags = (to->style->flags | from->style->flags);
+ if (from->style->clipPath.url) {
+ if (to->style->clipPath.url) free(to->style->clipPath.url);
+ to->style->clipPath.url = strdup(from->style->clipPath.url);
+ }
+ if (from->style->mask.url) {
+ if (to->style->mask.url) free(to->style->mask.url);
+ to->style->mask.url = strdup(from->style->mask.url);
+ }
+
+ //Copy node attribute
+ switch (from->type) {
+ case SvgNodeType::Circle: {
+ to->node.circle.cx = from->node.circle.cx;
+ to->node.circle.cy = from->node.circle.cy;
+ to->node.circle.r = from->node.circle.r;
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ to->node.ellipse.cx = from->node.ellipse.cx;
+ to->node.ellipse.cy = from->node.ellipse.cy;
+ to->node.ellipse.rx = from->node.ellipse.rx;
+ to->node.ellipse.ry = from->node.ellipse.ry;
+ break;
+ }
+ case SvgNodeType::Rect: {
+ to->node.rect.x = from->node.rect.x;
+ to->node.rect.y = from->node.rect.y;
+ to->node.rect.w = from->node.rect.w;
+ to->node.rect.h = from->node.rect.h;
+ to->node.rect.rx = from->node.rect.rx;
+ to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.hasRx = from->node.rect.hasRx;
+ to->node.rect.hasRy = from->node.rect.hasRy;
+ break;
+ }
+ case SvgNodeType::Line: {
+ to->node.line.x1 = from->node.line.x1;
+ to->node.line.y1 = from->node.line.y1;
+ to->node.line.x2 = from->node.line.x2;
+ to->node.line.y2 = from->node.line.y2;
+ break;
+ }
+ case SvgNodeType::Path: {
+ if (from->node.path.path) {
+ if (to->node.path.path) free(to->node.path.path);
+ to->node.path.path = strdup(from->node.path.path);
+ }
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Image: {
+ to->node.image.x = from->node.image.x;
+ to->node.image.y = from->node.image.y;
+ to->node.image.w = from->node.image.w;
+ to->node.image.h = from->node.image.h;
+ if (from->node.image.href) {
+ if (to->node.image.href) free(to->node.image.href);
+ to->node.image.href = strdup(from->node.image.href);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
+{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
+ SvgNode* newNode;
+ if (!from || !parent || from == parent) return;
+
+ newNode = _createNode(parent, from->type);
+ if (!newNode) return;
+
+ _styleInherit(newNode->style, parent->style);
+ _copyAttr(newNode, from);
+
+ auto child = from->child.data;
+ for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
+ _cloneNode(*child, newNode, depth + 1);
+ }
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
+ for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+ auto nodeIdPair = cloneNodes->data[i];
+ auto defs = _getDefsNode(nodeIdPair.node);
+ auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
+ _cloneNode(nodeFrom, nodeIdPair.node, 0);
+ if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
+ nodeIdPair.node->node.use.symbol = nodeFrom;
+ }
+ free(nodeIdPair.id);
+ }
+}
+
+
static constexpr struct
{
const char* tag;
@@ -2738,6 +3028,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
//Parse the empty tag
attrs = content;
while ((attrs != nullptr) && *attrs != '>') attrs++;
+ if (empty) attrs--;
}
if (attrs) {
@@ -2942,7 +3233,7 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
}
-static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const char* id)
+static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
{
SvgStyleGradient* result = nullptr;
@@ -2960,8 +3251,7 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
gradList = gradients->data;
for (uint32_t i = 0; i < gradients->count; ++i) {
if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) {
- if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops);
- //TODO: Properly inherit other property
+ _inheritGradient(loader, result, *gradList);
break;
}
++gradList;
@@ -2972,27 +3262,33 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
}
-static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
+static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
{
if (node->child.count > 0) {
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateGradient(*child, gradients);
+ _updateGradient(loader, *child, gradients);
}
} else {
if (node->style->fill.paint.url) {
- if (node->style->fill.paint.gradient) {
- node->style->fill.paint.gradient->clear();
- free(node->style->fill.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
+ if (newGrad) {
+ if (node->style->fill.paint.gradient) {
+ node->style->fill.paint.gradient->clear();
+ free(node->style->fill.paint.gradient);
+ }
+ node->style->fill.paint.gradient = newGrad;
}
- node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
}
if (node->style->stroke.paint.url) {
- if (node->style->stroke.paint.gradient) {
- node->style->stroke.paint.gradient->clear();
- free(node->style->stroke.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
+ if (newGrad) {
+ if (node->style->stroke.paint.gradient) {
+ node->style->stroke.paint.gradient->clear();
+ free(node->style->stroke.paint.gradient);
+ }
+ node->style->stroke.paint.gradient = newGrad;
}
- node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
}
}
}
@@ -3181,7 +3477,8 @@ SvgLoader::~SvgLoader()
void SvgLoader::run(unsigned tid)
{
//According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
- if (renderingDisabled) {
+ if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) {
+ TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
root = Scene::gen();
return;
}
@@ -3199,12 +3496,12 @@ void SvgLoader::run(unsigned tid)
_updateComposite(loaderData.doc, loaderData.doc);
if (defs) _updateComposite(loaderData.doc, defs);
- if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
- if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
-
_updateStyle(loaderData.doc, nullptr);
+
+ if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
}
- root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath);
+ root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
}
@@ -3217,34 +3514,78 @@ bool SvgLoader::header()
if (!loaderData.svgParse) return false;
loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+ viewFlag = SvgViewFlag::None;
simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
- //Return the brief resource info such as viewbox:
- vx = loaderData.doc->node.doc.vx;
- vy = loaderData.doc->node.doc.vy;
- w = vw = loaderData.doc->node.doc.vw;
- h = vh = loaderData.doc->node.doc.vh;
-
- //Override size
- if (loaderData.doc->node.doc.w > 0) {
+ viewFlag = loaderData.doc->node.doc.viewFlag;
+ align = loaderData.doc->node.doc.align;
+ meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
+
+ if (viewFlag & SvgViewFlag::Viewbox) {
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
+
+ if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
+ else {
+ w = loaderData.doc->node.doc.vw;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w *= loaderData.doc->node.doc.w;
+ viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Width);
+ }
+ if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
+ else {
+ h = loaderData.doc->node.doc.vh;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h *= loaderData.doc->node.doc.h;
+ viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Height);
+ }
+ //In case no viewbox and width/height data is provided the completion of loading
+ //has to be forced, in order to establish this data based on the whole picture.
+ } else {
+ //Before loading, set default viewbox & size if they are empty
+ vx = vy = 0.0f;
+ if (viewFlag & SvgViewFlag::Width) {
+ vw = w = loaderData.doc->node.doc.w;
+ } else {
+ vw = 1.0f;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w = loaderData.doc->node.doc.w;
+ } else w = 1.0f;
+ }
+
+ if (viewFlag & SvgViewFlag::Height) {
+ vh = h = loaderData.doc->node.doc.h;
+ } else {
+ vh = 1.0f;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h = loaderData.doc->node.doc.h;
+ } else h = 1.0f;
+ }
+
+ run(0);
+
+ //Override viewbox & size again after svg loading.
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
w = loaderData.doc->node.doc.w;
- if (vw < FLT_EPSILON) vw = w;
- }
- if (loaderData.doc->node.doc.h > 0) {
h = loaderData.doc->node.doc.h;
- if (vh < FLT_EPSILON) vh = h;
}
- align = loaderData.doc->node.doc.align;
- meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
- } else {
- TVGLOG("SVG", "No SVG File. There is no <svg/>");
- return false;
+ return true;
}
- return true;
+ TVGLOG("SVG", "No SVG File. There is no <svg/>");
+ return false;
}
@@ -3304,6 +3645,9 @@ bool SvgLoader::read()
{
if (!content || size == 0) return false;
+ //the loading has been already completed in header()
+ if (root) return true;
+
TaskScheduler::request(this);
return true;