svg_loader: gradient transformation properly applied

The final gradient transformation depends on the coordinate system.
It can not be applied during an svg loading. The transformation matrix
has to be passed via api for further gradient processing.
This commit is contained in:
Mira Grudzinska 2021-10-23 14:27:04 +02:00 committed by Hermet Park
parent 5ca50a28a2
commit 4db3087c45

View file

@ -69,29 +69,44 @@ static inline bool _isGroupType(SvgNodeType type)
}
static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf)
{
gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13;
gradTransf->e12 *= mBBox->e11;
gradTransf->e11 *= mBBox->e11;
gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23;
gradTransf->e22 *= mBBox->e22;
gradTransf->e21 *= mBBox->e22;
}
static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, float rx, float ry, float rw, float rh, int opacity)
{
Fill::ColorStop* stops;
int stopCount = 0;
auto fillGrad = LinearGradient::gen();
g->linear->x1 = g->linear->x1 * rw + rx;
g->linear->y1 = g->linear->y1 * rh + ry;
g->linear->x2 = g->linear->x2 * rw + rx;
g->linear->y2 = g->linear->y2 * rh + ry;
bool isTransform = (g->transform ? true : false);
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (isTransform) finalTransform = *g->transform;
if (g->transform) {
//Calc start point
auto x = g->linear->x1;
g->linear->x1 = x * g->transform->e11 + g->linear->y1 * g->transform->e12 + g->transform->e13;
g->linear->y1 = x * g->transform->e21 + g->linear->y1 * g->transform->e22 + g->transform->e23;
//Calc end point
x = g->linear->x2;
g->linear->x2 = x * g->transform->e11 + g->linear->y2 * g->transform->e12 + g->transform->e13;
g->linear->y2 = x * g->transform->e21 + g->linear->y2 * g->transform->e22 + g->transform->e23;
if (g->userSpace) {
g->linear->x1 = g->linear->x1 * rw;
g->linear->y1 = g->linear->y1 * rh;
g->linear->x2 = g->linear->x2 * rw;
g->linear->y2 = g->linear->y2 * rh;
} else {
Matrix m = {rw, 0, rx, 0, rh, ry, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
else {
finalTransform = m;
isTransform = true;
}
}
if (isTransform) fillGrad->transform(finalTransform);
fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
fillGrad->spread(g->spread);
@ -125,32 +140,30 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
{
Fill::ColorStop *stops;
int stopCount = 0;
float radius;
auto fillGrad = RadialGradient::gen();
radius = sqrtf(powf(rw, 2.0f) + powf(rh, 2.0f)) / sqrtf(2.0f);
if (!g->userSpace) {
//That is according to Units in here
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
int min = static_cast<int>((rh > rw) ? rw : rh);
radius = sqrtf(pow(min, 2) + pow(min, 2)) / sqrtf(2.0f);
bool isTransform = (g->transform ? true : false);
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (isTransform) finalTransform = *g->transform;
if (g->userSpace) {
//The radius scalling is done according to the Units section:
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
g->radial->cx = g->radial->cx * rw;
g->radial->cy = g->radial->cy * rh;
g->radial->r = g->radial->r * sqrtf(powf(rw, 2.0f) + powf(rh, 2.0f)) / sqrtf(2.0f);
g->radial->fx = g->radial->fx * rw;
g->radial->fy = g->radial->fy * rh;
} else {
Matrix m = {rw, 0, rx, 0, rh, ry, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
else {
finalTransform = m;
isTransform = true;
}
}
g->radial->cx = g->radial->cx * rw + rx;
g->radial->cy = g->radial->cy * rh + ry;
g->radial->r = g->radial->r * radius;
g->radial->fx = g->radial->fx * rw + rx;
g->radial->fy = g->radial->fy * rh + ry;
//TODO: Radial gradient transformation - all tests possible after rx/ry implementation
if (g->transform) {
auto cx = g->radial->cx * g->transform->e11 + g->radial->cy * g->transform->e12 + g->transform->e13;
g->radial->cy = g->radial->cx * g->transform->e21 + g->radial->cy * g->transform->e22 + g->transform->e23;
g->radial->cx = cx;
auto sx = sqrtf(powf(g->transform->e11, 2.0f) + powf(g->transform->e21, 2.0f));
g->radial->r *= sx;
}
if (isTransform) fillGrad->transform(finalTransform);
//TODO: Tvg is not support to focal
//if (g->radial->fx != 0 && g->radial->fy != 0) {