diff options
Diffstat (limited to 'thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp')
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp | 1120 |
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; |