From 598c28a597bc65c4acaee65815c5c62e31ba5b86 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 9 Apr 2023 23:23:20 +0200 Subject: [PATCH] svg_loader: gradient inheritance implemented Till now only the grad stops were inherited. Now all grad attribs are. Inheritance from different grad type is not supported. @Issue: https://github.com/thorvg/thorvg/issues/1209 --- src/loaders/svg/tvgSvgLoader.cpp | 275 ++++++++++++++++++++++++--- src/loaders/svg/tvgSvgLoaderCommon.h | 17 ++ 2 files changed, 270 insertions(+), 22 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 43f927d7..4d9aa9d6 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1891,6 +1891,7 @@ static constexpr struct {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)} }; + static void _cloneNode(SvgNode* from, SvgNode* parent, int depth); static bool _attrParseUseNode(void* data, const char* key, const char* value) { @@ -2086,13 +2087,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 = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Cx); +} + + +static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->cy = from->radial->cy; + to->radial->isCyPercentage = from->radial->isCyPercentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Cy); +} + + +static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->fx = from->radial->fx; + to->radial->isFxPercentage = from->radial->isFxPercentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Fx); +} + + +static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->fy = from->radial->fy; + to->radial->isFyPercentage = from->radial->isFyPercentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Fy); +} + + +static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->r = from->radial->r; + to->radial->isRPercentage = from->radial->isRPercentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)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 \ } @@ -2101,13 +2188,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) }; @@ -2121,6 +2211,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 = (SvgGradientFlags)((int)grad->flags | (int)radialTags[i].flag); return true; } } @@ -2130,11 +2221,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 = (SvgGradientFlags)((int)grad->flags | (int)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 = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::GradientUnits); } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2150,6 +2243,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)); @@ -2277,13 +2371,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 = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::X1); +} + + +static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->x2 = from->linear->x2; + to->linear->isX2Percentage = from->linear->isX2Percentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::X2); +} + + +static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y1 = from->linear->y1; + to->linear->isY1Percentage = from->linear->isY1Percentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Y1); +} + + +static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y2 = from->linear->y2; + to->linear->isY2Percentage = from->linear->isY2Percentage; + to->flags = (SvgGradientFlags)((int)to->flags | (int)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 \ } @@ -2292,12 +2455,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) }; @@ -2311,6 +2477,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 = (SvgGradientFlags)((int)grad->flags | (int)linear_tags[i].flag); return true; } } @@ -2320,11 +2487,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 = (SvgGradientFlags)((int)grad->flags | (int)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 = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::GradientUnits); } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2340,6 +2509,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)); @@ -2407,6 +2577,67 @@ static void _cloneGradStops(Array& dst, const Arrayflags & (int)SvgGradientFlags::SpreadMethod) && + ((int)from->flags & (int)SvgGradientFlags::SpreadMethod)) { + to->spread = from->spread; + to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::SpreadMethod); + } + bool gradUnitSet = (int)to->flags & (int)SvgGradientFlags::GradientUnits; + if (!((int)to->flags & (int)SvgGradientFlags::GradientUnits) && + ((int)from->flags & (int)SvgGradientFlags::GradientUnits)) { + to->userSpace = from->userSpace; + to->flags = (SvgGradientFlags)((int)to->flags | (int)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 = (int)to->flags & (int)linear_tags[i].flag; + if (!((int)to->flags & (int)linear_tags[i].flag) && ((int)from->flags & (int)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 = (int)to->flags & (int)radialTags[i].flag; + if (!((int)to->flags & (int)radialTags[i].flag) && ((int)from->flags & (int)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; @@ -2419,6 +2650,7 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from) 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)); @@ -2948,7 +3180,7 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle) } -static SvgStyleGradient* _gradientDup(Array* gradients, const char* id) +static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array* gradients, const char* id) { SvgStyleGradient* result = nullptr; @@ -2966,8 +3198,7 @@ static SvgStyleGradient* _gradientDup(Array* 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; @@ -2978,12 +3209,12 @@ static SvgStyleGradient* _gradientDup(Array* gradients, const } -static void _updateGradient(SvgNode* node, Array* gradients) +static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array* 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) { @@ -2991,14 +3222,14 @@ static void _updateGradient(SvgNode* node, Array* gradients) node->style->fill.paint.gradient->clear(); free(node->style->fill.paint.gradient); } - node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url); + node->style->fill.paint.gradient = _gradientDup(loader, 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); } - node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url); + node->style->stroke.paint.gradient = _gradientDup(loader, gradients, node->style->stroke.paint.url); } } } @@ -3205,8 +3436,8 @@ 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); + if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); + if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); _updateStyle(loaderData.doc, nullptr); } diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index ebcbf018..3bcc031b 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -125,6 +125,22 @@ enum class SvgStopStyleFlags StopColor = 0x02 }; +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 +}; + enum class SvgFillRule { Winding = 0, @@ -349,6 +365,7 @@ struct SvgStyleGradient SvgLinearGradient* linear; Matrix* transform; Array stops; + SvgGradientFlags flags; bool userSpace; void clear()