renderer/sw_engine: support the focal property in the radial gradient.

@Issue: https://github.com/thorvg/thorvg/issues/1558
This commit is contained in:
Mira Grudzinska 2023-09-04 16:14:33 +09:00 committed by Hermet Park
parent 7430589586
commit ce3c11838c
8 changed files with 369 additions and 306 deletions

View file

@ -29,11 +29,9 @@ source_file = [
'tvgFill.cpp',
'tvgGlCanvas.cpp',
'tvgInitializer.cpp',
'tvgLinearGradient.cpp',
'tvgLoader.cpp',
'tvgPaint.cpp',
'tvgPicture.cpp',
'tvgRadialGradient.cpp',
'tvgRender.cpp',
'tvgSaver.cpp',
'tvgScene.cpp',

View file

@ -139,10 +139,11 @@ struct SwFill
};
struct SwRadial {
float a11, a12, shiftX;
float a21, a22, shiftY;
float detSecDeriv;
float a;
float a11, a12, a13;
float a21, a22, a23;
float fx, fy, fr;
float dx, dy, dr;
float invA, a;
};
union {

View file

@ -22,7 +22,7 @@
#include "tvgMath.h"
#include "tvgSwCommon.h"
#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
@ -32,6 +32,35 @@
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
/*
* quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
* A = a // fill->radial.a
* B = 2 * (dr * fr + rx * dx + ry * dy)
* C = fr^2 - rx^2 - ry^2
* Derivatives are computed with respect to dx.
* This procedure aims to optimize and eliminate the need to calculate all values from the beginning
* for consecutive x values with a constant y. The Taylor series expansions are computed as long as
* its terms are non-zero.
*/
static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
{
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
auto rr = rx * rx + ry * ry;
auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
}
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
{
@ -146,14 +175,36 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
{
float radius, cx, cy;
if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
if (radius < FLT_EPSILON) return true;
auto cx = P(radial)->cx;
auto cy = P(radial)->cy;
auto r = P(radial)->r;
auto fx = P(radial)->fx;
auto fy = P(radial)->fy;
auto fr = P(radial)->fr;
float invR = 1.0f / radius;
fill->radial.shiftX = -cx;
fill->radial.shiftY = -cy;
fill->radial.a = radius;
if (r < FLT_EPSILON) return true;
fill->radial.dr = r - fr;
fill->radial.dx = cx - fx;
fill->radial.dy = cy - fy;
fill->radial.fr = fr;
fill->radial.fx = fx;
fill->radial.fy = fy;
fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
//This condition fulfills the SVG 1.1 std:
//the focal point, if outside the end circle, is moved to be on the end circle
//See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
if (fill->radial.a < 0) {
auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
fill->radial.fx = cx + r * (fx - cx) / dist;
fill->radial.fy = cy + r * (fy - cy) / dist;
fill->radial.dx = cx - fill->radial.fx;
fill->radial.dy = cy - fill->radial.fy;
fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
}
if (fill->radial.a > FLT_EPSILON) fill->radial.invA = 1.0f / fill->radial.a;
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
@ -169,22 +220,17 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
fill->radial.a11 = invTransform.e11 * invR;
fill->radial.a12 = invTransform.e12 * invR;
fill->radial.shiftX += invTransform.e13;
fill->radial.a21 = invTransform.e21 * invR;
fill->radial.a22 = invTransform.e22 * invR;
fill->radial.shiftY += invTransform.e23;
fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
fill->radial.a11 = invTransform.e11;
fill->radial.a12 = invTransform.e12;
fill->radial.a13 = invTransform.e13;
fill->radial.a21 = invTransform.e21;
fill->radial.a22 = invTransform.e22;
fill->radial.a23 = invTransform.e23;
} else {
fill->radial.a11 = fill->radial.a22 = invR;
fill->radial.a12 = fill->radial.a21 = 0.0f;
fill->radial.detSecDeriv = 2.0f * invR * invR;
fill->radial.a11 = fill->radial.a22 = 1.0f;
fill->radial.a12 = fill->radial.a13 = 0.0f;
fill->radial.a21 = fill->radial.a23 = 0.0f;
}
fill->radial.shiftX *= invR;
fill->radial.shiftY *= invR;
return true;
}
@ -235,26 +281,45 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
//edge case
if (fill->radial.a < FLT_EPSILON) {
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
if (opacity == 255) {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp));
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
if (opacity == 255) {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
*dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
rx += radial->a11;
ry += radial->a21;
}
} else {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
*dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
rx += radial->a11;
ry += radial->a21;
}
}
} else {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp)));
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
float b, deltaB, det, deltaDet, deltaDeltaDet;
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
if (opacity == 255) {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
} else {
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
}
}
}
@ -262,89 +327,132 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
if (fill->radial.a < FLT_EPSILON) {
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
for (uint32_t i = 0; i < len; ++i, ++dst) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
*dst = op(_pixel(fill, x0), *dst, a);
rx += radial->a11;
ry += radial->a21;
}
} else {
float b, deltaB, det, deltaDet, deltaDeltaDet;
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
*dst = op(_pixel(fill, sqrtf(det)), *dst, a);
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
for (uint32_t i = 0; i < len; ++i, ++dst) {
*dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
}
}
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
if (fill->radial.a < FLT_EPSILON) {
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
auto src = MULTIPLY(a, A(_pixel(fill, x0)));
*dst = maskOp(src, *dst, ~src);
rx += radial->a11;
ry += radial->a21;
}
} else {
float b, deltaB, det, deltaDet, deltaDeltaDet;
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det))));
*dst = maskOp(src, *dst, ~src);
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
*dst = maskOp(src, *dst, ~src);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
}
}
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
if (fill->radial.a < FLT_EPSILON) {
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
rx += radial->a11;
ry += radial->a21;
}
} else {
float b, deltaB, det, deltaDet, deltaDeltaDet;
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
deltaDet += deltaDeltaDet;
b += deltaB;
}
}
}
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
if (fill->radial.a < FLT_EPSILON) {
auto radial = &fill->radial;
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
if (a == 255) {
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
*dst = op2(tmp, *dst, 255);
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
if (a == 255) {
for (uint32_t i = 0; i < len; ++i, ++dst) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
auto tmp = op(_pixel(fill, x0), *dst, 255);
*dst = op2(tmp, *dst, 255);
rx += radial->a11;
ry += radial->a21;
}
} else {
for (uint32_t i = 0; i < len; ++i, ++dst) {
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
auto tmp = op(_pixel(fill, x0), *dst, 255);
auto tmp2 = op2(tmp, *dst, 255);
*dst = INTERPOLATE(tmp2, *dst, a);
rx += radial->a11;
ry += radial->a21;
}
}
} else {
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
auto tmp2 = op2(tmp, *dst, 255);
*dst = INTERPOLATE(tmp2, *dst, a);
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
float b, deltaB, det, deltaDet, deltaDeltaDet;
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
if (a == 255) {
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
*dst = op2(tmp, *dst, 255);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
} else {
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
auto tmp2 = op2(tmp, *dst, 255);
*dst = INTERPOLATE(tmp2, *dst, a);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
}
}
}

View file

@ -1595,8 +1595,6 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region,
static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
if (fill->radial.a < FLT_EPSILON) return false;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill);
else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill);
@ -1610,7 +1608,6 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
}
/************************************************************************/
/* Rle Gradient */
/************************************************************************/
@ -1741,7 +1738,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
if (!rle || fill->radial.a < FLT_EPSILON) return false;
if (!rle) return false;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);

View file

@ -26,6 +26,50 @@
/* Internal Class Implementation */
/************************************************************************/
Fill* RadialGradient::Impl::duplicate()
{
auto ret = RadialGradient::gen();
if (!ret) return nullptr;
ret->pImpl->cx = cx;
ret->pImpl->cy = cy;
ret->pImpl->r = r;
ret->pImpl->fx = fx;
ret->pImpl->fy = fy;
ret->pImpl->fr = fr;
return ret.release();
}
Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
{
if (r < 0 || fr < 0) return Result::InvalidArguments;
this->cx = cx;
this->cy = cy;
this->r = r;
this->fx = fx;
this->fy = fy;
this->fr = fr;
return Result::Success;
};
Fill* LinearGradient::Impl::duplicate()
{
auto ret = LinearGradient::gen();
if (!ret) return nullptr;
ret->pImpl->x1 = x1;
ret->pImpl->y1 = y1;
ret->pImpl->x2 = x2;
ret->pImpl->y2 = y2;
return ret.release();
};
/************************************************************************/
/* External Class Implementation */
@ -110,7 +154,97 @@ Fill* Fill::duplicate() const noexcept
return pImpl->duplicate();
}
uint32_t Fill::identifier() const noexcept
{
return pImpl->id;
}
RadialGradient::RadialGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
}
RadialGradient::~RadialGradient()
{
delete(pImpl);
}
Result RadialGradient::radial(float cx, float cy, float r) noexcept
{
return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
}
Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
{
if (cx) *cx = pImpl->cx;
if (cy) *cy = pImpl->cy;
if (r) *r = pImpl->r;
return Result::Success;
}
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
{
return unique_ptr<RadialGradient>(new RadialGradient);
}
uint32_t RadialGradient::identifier() noexcept
{
return TVG_CLASS_ID_RADIAL;
}
LinearGradient::LinearGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
}
LinearGradient::~LinearGradient()
{
delete(pImpl);
}
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
{
pImpl->x1 = x1;
pImpl->y1 = y1;
pImpl->x2 = x2;
pImpl->y2 = y2;
return Result::Success;
}
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{
if (x1) *x1 = pImpl->x1;
if (x2) *x2 = pImpl->x2;
if (y1) *y1 = pImpl->y1;
if (y2) *y2 = pImpl->y2;
return Result::Success;
}
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
{
return unique_ptr<LinearGradient>(new LinearGradient);
}
uint32_t LinearGradient::identifier() noexcept
{
return TVG_CLASS_ID_LINEAR;
}

View file

@ -86,4 +86,27 @@ struct Fill::Impl
}
};
struct RadialGradient::Impl
{
float cx = 0.0f, cy = 0.0f;
float fx = 0.0f, fy = 0.0f;
float r = 0.0f, fr = 0.0f;
Fill* duplicate();
Result radial(float cx, float cy, float r, float fx, float fy, float fr);
};
struct LinearGradient::Impl
{
float x1 = 0.0f;
float y1 = 0.0f;
float x2 = 0.0f;
float y2 = 0.0f;
Fill* duplicate();
};
#endif //_TVG_FILL_H_

View file

@ -1,100 +0,0 @@
/*
* 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
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <float.h>
#include <math.h>
#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct LinearGradient::Impl
{
float x1 = 0;
float y1 = 0;
float x2 = 0;
float y2 = 0;
Fill* duplicate()
{
auto ret = LinearGradient::gen();
if (!ret) return nullptr;
ret->pImpl->x1 = x1;
ret->pImpl->y1 = y1;
ret->pImpl->x2 = x2;
ret->pImpl->y2 = y2;
return ret.release();
}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
LinearGradient::LinearGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
}
LinearGradient::~LinearGradient()
{
delete(pImpl);
}
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
{
pImpl->x1 = x1;
pImpl->y1 = y1;
pImpl->x2 = x2;
pImpl->y2 = y2;
return Result::Success;
}
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{
if (x1) *x1 = pImpl->x1;
if (x2) *x2 = pImpl->x2;
if (y1) *y1 = pImpl->y1;
if (y2) *y2 = pImpl->y2;
return Result::Success;
}
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
{
return unique_ptr<LinearGradient>(new LinearGradient);
}
uint32_t LinearGradient::identifier() noexcept
{
return TVG_CLASS_ID_LINEAR;
}

View file

@ -1,98 +0,0 @@
/*
* 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
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <float.h>
#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct RadialGradient::Impl
{
float cx = 0;
float cy = 0;
float radius = 0;
Fill* duplicate()
{
auto ret = RadialGradient::gen();
if (!ret) return nullptr;
ret->pImpl->cx = cx;
ret->pImpl->cy = cy;
ret->pImpl->radius = radius;
return ret.release();
}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
RadialGradient::RadialGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
}
RadialGradient::~RadialGradient()
{
delete(pImpl);
}
Result RadialGradient::radial(float cx, float cy, float radius) noexcept
{
if (radius < 0) return Result::InvalidArguments;
pImpl->cx = cx;
pImpl->cy = cy;
pImpl->radius = radius;
return Result::Success;
}
Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
{
if (cx) *cx = pImpl->cx;
if (cy) *cy = pImpl->cy;
if (radius) *radius = pImpl->radius;
return Result::Success;
}
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
{
return unique_ptr<RadialGradient>(new RadialGradient);
}
uint32_t RadialGradient::identifier() noexcept
{
return TVG_CLASS_ID_RADIAL;
}