diff options
Diffstat (limited to 'thirdparty/thorvg/src/loaders/svg')
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp | 75 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h | 2 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp | 1120 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h | 5 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h | 110 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp | 11 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h | 2 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp | 61 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h | 4 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp | 6 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h | 2 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp | 2 | ||||
-rw-r--r-- | thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h | 2 |
13 files changed, 934 insertions, 468 deletions
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp index 478ba5d3d1..694e6d1ebf 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2022 - 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 @@ -32,13 +32,13 @@ static void _copyStyle(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 && !((int)to->flags & (int)SvgStyleFlags::Color)) { + if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) { to->color = from->color; to->curColorSet = true; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color); + to->flags = (to->flags | SvgStyleFlags::Color); } //Fill - if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) { + if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; @@ -46,21 +46,21 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->fill.paint.url) free(to->fill.paint.url); to->fill.paint.url = strdup(from->fill.paint.url); } - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill); + to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Fill); } - if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) { + if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity); + to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::FillOpacity); } - if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) { + if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) { to->fill.fillRule = from->fill.fillRule; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule); + to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); + to->flags = (to->flags | SvgStyleFlags::FillRule); } //Stroke - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) { + if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; @@ -68,45 +68,45 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->stroke.paint.url) free(to->stroke.paint.url); to->stroke.paint.url = strdup(from->stroke.paint.url); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Stroke); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) { + if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) { + if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) { to->stroke.width = from->stroke.width; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); + to->flags = (to->flags | SvgStyleFlags::StrokeWidth); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) { + if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) { 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]); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); + to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); } } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) { + if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) { to->stroke.cap = from->stroke.cap; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Cap); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); + to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) { + if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) { to->stroke.join = from->stroke.join; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); + to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); } //Opacity //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' - if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) { + if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) { to->opacity = from->opacity; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::Opacity); } } @@ -118,11 +118,11 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute - if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) { + if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) { to->transform = (Matrix*)malloc(sizeof(Matrix)); if (to->transform) { *to->transform = *from->transform; - to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform); + to->style->flags = (to->style->flags | SvgStyleFlags::Transform); } } //Copy style attribute @@ -155,12 +155,12 @@ SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType t SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) { - if (!style) return nullptr; + if (!style || !title) return nullptr; auto child = style->child.data; for (uint32_t i = 0; i < style->child.count; ++i, ++child) { if ((*child)->type == SvgNodeType::CssStyle) { - if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + if ((*child)->id && !strcmp((*child)->id, title)) return (*child); } } return nullptr; @@ -175,9 +175,6 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style) if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) { cssCopyStyleAttr(*child, cssNode); } - if (auto cssNode = cssFindStyleNode(style, nullptr)) { - cssCopyStyleAttr(*child, cssNode); - } cssUpdateStyle(*child, style); } } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h index 66477c1a32..228c5996da 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2022 - 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 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; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h index c6fdde55af..5c74184ec8 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h @@ -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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SVG_LOADER_H_ #define _TVG_SVG_LOADER_H_ @@ -50,9 +51,9 @@ public: unique_ptr<Paint> paint() override; private: + SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; - bool renderingDisabled = false; bool header(); void clear(); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index 3588cabf0b..dec9fadebe 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -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 @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #ifndef _TVG_SVG_LOADER_COMMON_H_ #define _TVG_SVG_LOADER_COMMON_H_ @@ -79,6 +80,16 @@ enum class SvgFillFlags ClipPath = 0x16 }; +constexpr bool operator &(SvgFillFlags a, SvgFillFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b) +{ + return SvgFillFlags(int(a) | int(b)); +} + enum class SvgStrokeFlags { Paint = 0x1, @@ -91,6 +102,17 @@ enum class SvgStrokeFlags Dash = 0x80, }; +constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return SvgStrokeFlags(int(a) | int(b)); +} + + enum class SvgGradientType { Linear, @@ -114,9 +136,20 @@ enum class SvgStyleFlags ClipPath = 0x1000, Mask = 0x2000, MaskType = 0x4000, - Display = 0x8000 + Display = 0x8000, + PaintOrder = 0x10000 }; +constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b) +{ + return SvgStyleFlags(int(a) | int(b)); +} + enum class SvgStopStyleFlags { StopDefault = 0x0, @@ -124,6 +157,42 @@ enum class SvgStopStyleFlags StopColor = 0x02 }; +constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return SvgStopStyleFlags(int(a) | int(b)); +} + +enum class SvgGradientFlags +{ + None = 0x0, + GradientUnits = 0x1, + SpreadMethod = 0x2, + X1 = 0x4, + X2 = 0x8, + Y1 = 0x10, + Y2 = 0x20, + Cx = 0x40, + Cy = 0x80, + R = 0x100, + Fx = 0x200, + Fy = 0x400 +}; + +constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b) +{ + return SvgGradientFlags(int(a) | int(b)); +} + enum class SvgFillRule { Winding = 0, @@ -145,6 +214,31 @@ enum class SvgParserLengthType Other }; +enum class SvgViewFlag +{ + None = 0x0, + Width = 0x01, //viewPort width + Height = 0x02, //viewPort height + Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set + WidthInPercent = 0x08, + HeightInPercent = 0x10 +}; + +constexpr bool operator &(SvgViewFlag a, SvgViewFlag b) +{ + return static_cast<int>(a) & static_cast<int>(b); +} + +constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) | int(b)); +} + +constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) ^ int(b)); +} + enum class AspectRatioAlign { None, @@ -167,12 +261,13 @@ enum class AspectRatioMeetOrSlice struct SvgDocNode { - float w; - float h; + float w; //unit: point or in percentage see: SvgViewFlag + float h; //unit: point or in percentage see: SvgViewFlag float vx; float vy; float vw; float vh; + SvgViewFlag viewFlag; SvgNode* defs; SvgNode* style; AspectRatioAlign align; @@ -339,6 +434,7 @@ struct SvgStyleGradient SvgLinearGradient* linear; Matrix* transform; Array<Fill::ColorStop> stops; + SvgGradientFlags flags; bool userSpace; void clear() @@ -384,6 +480,7 @@ struct SvgStyleProperty SvgColor color; bool curColorSet; char* cssClass; + bool paintOrder; //true if default (fill, stroke), false otherwise SvgStyleFlags flags; }; @@ -456,6 +553,11 @@ struct SvgLoaderData bool style = false; }; +struct Box +{ + float x, y, w, h; +}; + /* * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017 * diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp index a09a2797d0..e044931b51 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.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 @@ -52,7 +52,6 @@ #include <cstring> #include <math.h> -#include <clocale> #include <ctype.h> #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" @@ -545,11 +544,6 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point char cmd = 0; bool isQuadratic = false; char* path = (char*)svgPath; - char* curLocale; - - curLocale = setlocale(LC_NUMERIC, NULL); - if (curLocale) curLocale = strdup(curLocale); - setlocale(LC_NUMERIC, "POSIX"); while ((path[0] != '\0')) { path = _nextCommand(path, &cmd, numberArray, &numberCount); @@ -557,8 +551,5 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break; } - setlocale(LC_NUMERIC, curLocale); - if (curLocale) free(curLocale); - return true; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h index 8f5f9035dc..4199088dc1 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h @@ -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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp index 254ee2d008..c5e5df893e 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.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 @@ -61,12 +61,6 @@ /* Internal Class Implementation */ /************************************************************************/ -struct Box -{ - float x, y, w, h; -}; - - static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath); static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); @@ -255,7 +249,6 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox node->style->clipPath.applying = true; auto comp = Shape::gen(); - if (node->transform) comp->transform(*node->transform); auto child = compNode->child.data; auto valid = false; //Composite only when valid shapes are existed @@ -264,6 +257,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true; } + if (node->transform) { + auto m = comp->transform(); + m = mathMultiply(node->transform, &m); + comp->transform(m); + } + if (valid) paint->composite(move(comp), CompositeMethod::ClipPath); node->style->clipPath.applying = false; @@ -331,6 +330,8 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri //Apply the fill rule vg->fill((tvg::FillRule)style->fill.fillRule); + //Rendering order + vg->order(!style->paintOrder); //Apply node opacity if (style->opacity < 255) vg->opacity(style->opacity); @@ -444,9 +445,11 @@ enum class imageMimeTypeEncoding base64 = 0x1, utf8 = 0x2 }; + constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b)); } + constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return (static_cast<int>(a) & static_cast<int>(b)); } @@ -755,24 +758,45 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, } +static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) +{ + bool validWidth = (viewFlag & SvgViewFlag::Width); + bool validHeight = (viewFlag & SvgViewFlag::Height); + + float x, y; + scene->bounds(&x, &y, &vBox.w, &vBox.h, false); + if (!validWidth && !validHeight) { + vBox.x = x; + vBox.y = y; + } else { + if (validWidth) vBox.w = w; + if (validHeight) vBox.h = h; + } + + //the size would have 1x1 or percentage values. + if (!validWidth) w *= vBox.w; + if (!validHeight) h *= vBox.h; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath) +unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) { //TODO: aspect ratio is valid only if viewBox was set - if (!node || (node->type != SvgNodeType::Doc)) return nullptr; + if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr; - Box vBox = {vx, vy, vw, vh}; - auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0); + auto docNode = _sceneBuildHelper(loaderData.doc, vBox, svgPath, false, 0); - if (!mathEqual(w, vw) || !mathEqual(h, vh)) { + if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); + + if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) { Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); docNode->transform(m); - } else if (!mathZero(vx) || !mathZero(vy)) { - docNode->translate(-vx, -vy); + } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) { + docNode->translate(-vBox.x, -vBox.y); } auto viewBoxClip = Shape::gen(); @@ -786,5 +810,12 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo auto root = Scene::gen(); root->push(move(compositeLayer)); + loaderData.doc->node.doc.vx = vBox.x; + loaderData.doc->node.doc.vy = vBox.y; + loaderData.doc->node.doc.vw = vBox.w; + loaderData.doc->node.doc.vh = vBox.h; + loaderData.doc->node.doc.w = w; + loaderData.doc->node.doc.h = h; + return root; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h index 311f3c80e6..f6a60f850d 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h @@ -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 @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath); +unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); #endif //_TVG_SVG_SCENE_BUILDER_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp index 7fb108bc21..12d20c9731 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.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 @@ -94,8 +94,8 @@ float svgUtilStrtof(const char *nPtr, char **endPtr) if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; else goto error; - if (tolower(*(iter + 3)) == 'i') { - if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5; + if (tolower(*(iter)) == 'i') { + if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5; else goto error; } if (endPtr) *endPtr = (char *)(iter); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h index b5e6e1bdb2..6f94367aeb 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h @@ -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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp index 0e2c3fa141..77467e071b 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.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 diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h index e2761ca8da..7333bb09fb 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h @@ -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 |