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
This commit is contained in:
Mira Grudzinska 2023-04-09 23:23:20 +02:00 committed by Hermet Park
parent 66ef84e7d7
commit 598c28a597
2 changed files with 270 additions and 22 deletions

View file

@ -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<Fill::ColorStop>& dst, const Array<Fill::Color
}
static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
{
if (!to || !from) return;
if (!((int)to->flags & (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<SvgStyleGradient*>* gradients, const char* id)
static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
{
SvgStyleGradient* result = nullptr;
@ -2966,8 +3198,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;
@ -2978,12 +3209,12 @@ 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) {
@ -2991,14 +3222,14 @@ static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* 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);
}

View file

@ -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<Fill::ColorStop> stops;
SvgGradientFlags flags;
bool userSpace;
void clear()