mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-04-29 15:10:47 +02:00
Merge branch 'radial-gradient-fixes' into cairo
This commit is contained in:
commit
df2d42ac7f
12 changed files with 344 additions and 108 deletions
|
|
@ -37,6 +37,7 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef M_PI
|
||||
|
|
@ -2739,12 +2740,13 @@ typedef struct
|
|||
CARD32 right_rb;
|
||||
int32_t left_x;
|
||||
int32_t right_x;
|
||||
int32_t width_x;
|
||||
int32_t stepper;
|
||||
|
||||
pixman_gradient_stop_t *stops;
|
||||
int num_stops;
|
||||
unsigned int spread;
|
||||
|
||||
int need_reset;
|
||||
} GradientWalker;
|
||||
|
||||
static void
|
||||
|
|
@ -2756,13 +2758,14 @@ _gradient_walker_init (GradientWalker *walker,
|
|||
walker->stops = pGradient->gradient.stops;
|
||||
walker->left_x = 0;
|
||||
walker->right_x = 0x10000;
|
||||
walker->width_x = 0; /* will force a reset */
|
||||
walker->stepper = 0;
|
||||
walker->left_ag = 0;
|
||||
walker->left_rb = 0;
|
||||
walker->right_ag = 0;
|
||||
walker->right_rb = 0;
|
||||
walker->spread = spread;
|
||||
|
||||
walker->need_reset = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2894,27 +2897,28 @@ _gradient_walker_reset (GradientWalker *walker,
|
|||
|
||||
walker->left_x = left_x;
|
||||
walker->right_x = right_x;
|
||||
walker->width_x = right_x - left_x;
|
||||
walker->left_ag = ((left_c->alpha >> 8) << 16) | (left_c->green >> 8);
|
||||
walker->left_rb = ((left_c->red & 0xff00) << 8) | (left_c->blue >> 8);
|
||||
walker->right_ag = ((right_c->alpha >> 8) << 16) | (right_c->green >> 8);
|
||||
walker->right_rb = ((right_c->red & 0xff00) << 8) | (right_c->blue >> 8);
|
||||
|
||||
if ( walker->width_x == 0 ||
|
||||
if ( walker->left_x == walker->right_x ||
|
||||
( walker->left_ag == walker->right_ag &&
|
||||
walker->left_rb == walker->right_rb ) )
|
||||
{
|
||||
walker->width_x = 1;
|
||||
walker->stepper = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
walker->stepper = ((1 << 24) + walker->width_x/2)/walker->width_x;
|
||||
int32_t width = right_x - left_x;
|
||||
walker->stepper = ((1 << 24) + width/2)/width;
|
||||
}
|
||||
|
||||
walker->need_reset = FALSE;
|
||||
}
|
||||
|
||||
#define GRADIENT_WALKER_NEED_RESET(w,x) \
|
||||
( (x) < (w)->left_x || (x) - (w)->left_x >= (w)->width_x )
|
||||
( (w)->need_reset || (x) < (w)->left_x || (x) >= (w)->right_x)
|
||||
|
||||
/* the following assumes that GRADIENT_WALKER_NEED_RESET(w,x) is FALSE */
|
||||
static CARD32
|
||||
|
|
@ -2949,8 +2953,6 @@ _gradient_walker_pixel (GradientWalker *walker,
|
|||
return (color | (t1 & 0xff00ff) | (t2 & 0xff00));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *buffer, CARD32 *mask, CARD32 maskBits)
|
||||
{
|
||||
SourcePictPtr pGradient = pict->pSourcePict;
|
||||
|
|
@ -3082,13 +3084,128 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
|
|||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* In the radial gradient problem we are given two circles (c₁,r₁) and
|
||||
* (c₂,r₂) that define the gradient itself. Then, for any point p, we
|
||||
* must compute the value(s) of t within [0.0, 1.0] representing the
|
||||
* circle(s) that would color the point.
|
||||
*
|
||||
* There are potentially two values of t since the point p can be
|
||||
* colored by both sides of the circle, (which happens whenever one
|
||||
* circle is not entirely contained within the other).
|
||||
*
|
||||
* If we solve for a value of t that is outside of [0.0, 1.0] then we
|
||||
* use the extend mode (NONE, REPEAT, REFLECT, or PAD) to map to a
|
||||
* value within [0.0, 1.0].
|
||||
*
|
||||
* Here is an illustration of the problem:
|
||||
*
|
||||
* p₂
|
||||
* p •
|
||||
* • ╲
|
||||
* · ╲r₂
|
||||
* p₁ · ╲
|
||||
* • θ╲
|
||||
* ╲ ╌╌•
|
||||
* ╲r₁ · c₂
|
||||
* θ╲ ·
|
||||
* ╌╌•
|
||||
* c₁
|
||||
*
|
||||
* Given (c₁,r₁), (c₂,r₂) and p, we must find an angle θ such that two
|
||||
* points p₁ and p₂ on the two circles are collinear with p. Then, the
|
||||
* desired value of t is the ratio of the length of p₁p to the length
|
||||
* of p₁p₂.
|
||||
*
|
||||
* So, we have six unknown values: (p₁x, p₁y), (p₂x, p₂y), θ and t.
|
||||
* We can also write six equations that constrain the problem:
|
||||
*
|
||||
* Point p₁ is a distance r₁ from c₁ at an angle of θ:
|
||||
*
|
||||
* 1. p₁x = c₁x + r₁·cos θ
|
||||
* 2. p₁y = c₁y + r₁·sin θ
|
||||
*
|
||||
* Point p₂ is a distance r₂ from c₂ at an angle of θ:
|
||||
*
|
||||
* 3. p₂x = c₂x + r2·cos θ
|
||||
* 4. p₂y = c₂y + r2·sin θ
|
||||
*
|
||||
* Point p lies at a fraction t along the line segment p₁p₂:
|
||||
*
|
||||
* 5. px = t·p₂x + (1-t)·p₁x
|
||||
* 6. py = t·p₂y + (1-t)·p₁y
|
||||
*
|
||||
* To solve, first subtitute 1-4 into 5 and 6:
|
||||
*
|
||||
* px = t·(c₂x + r₂·cos θ) + (1-t)·(c₁x + r₁·cos θ)
|
||||
* py = t·(c₂y + r₂·sin θ) + (1-t)·(c₁y + r₁·sin θ)
|
||||
*
|
||||
* Then solve each for cos θ and sin θ expressed as a function of t:
|
||||
*
|
||||
* cos θ = (-(c₂x - c₁x)·t + (px - c₁x)) / ((r₂-r₁)·t + r₁)
|
||||
* sin θ = (-(c₂y - c₁y)·t + (py - c₁y)) / ((r₂-r₁)·t + r₁)
|
||||
*
|
||||
* To simplify this a bit, we define new variables for several of the
|
||||
* common terms as shown below:
|
||||
*
|
||||
* p₂
|
||||
* p •
|
||||
* • ╲
|
||||
* · ┆ ╲r₂
|
||||
* p₁ · ┆ ╲
|
||||
* • pdy┆ ╲
|
||||
* ╲ ┆ •c₂
|
||||
* ╲r₁ ┆ · ┆
|
||||
* ╲ ·┆ ┆cdy
|
||||
* •╌╌╌╌┴╌╌╌╌╌╌╌┘
|
||||
* c₁ pdx cdx
|
||||
*
|
||||
* cdx = (c₂x - c₁x)
|
||||
* cdy = (c₂y - c₁y)
|
||||
* dr = r₂-r₁
|
||||
* pdx = px - c₁x
|
||||
* pdy = py - c₁y
|
||||
*
|
||||
* Note that cdx, cdy, and dr do not depend on point p at all, so can
|
||||
* be pre-computed for the entire gradient. The simplifed equations
|
||||
* are now:
|
||||
*
|
||||
* cos θ = (-cdx·t + pdx) / (dr·t + r₁)
|
||||
* sin θ = (-cdy·t + pdy) / (dr·t + r₁)
|
||||
*
|
||||
* Finally, to get a single function of t and eliminate the last
|
||||
* unknown θ, we use the identity sin²θ + cos²θ = 1. First, square
|
||||
* each equation, (we knew a quadratic was coming since it must be
|
||||
* possible to obtain two solutions in some cases):
|
||||
*
|
||||
* cos²θ = (cdx²t² - 2·cdx·pdx·t + pdx²) / (dr²·t² + 2·r₁·dr·t + r₁²)
|
||||
* sin²θ = (cdy²t² - 2·cdy·pdy·t + pdy²) / (dr²·t² + 2·r₁·dr·t + r₁²)
|
||||
*
|
||||
* Then add both together, set the result equal to 1, and express as a
|
||||
* standard quadratic equation in t of the form At² + Bt + C = 0
|
||||
*
|
||||
* (cdx² + cdy² - dr²)·t² - 2·(cdx·pdx + cdy·pdy + r₁·dr)·t + (pdx² + pdy² - r₁²) = 0
|
||||
*
|
||||
* In other words:
|
||||
*
|
||||
* A = cdx² + cdy² - dr²
|
||||
* B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
|
||||
* C = pdx² + pdy² - r₁²
|
||||
*
|
||||
* And again, notice that A does not depend on p, so can be
|
||||
* precomputed. From here we just use the quadratic formula to solve
|
||||
* for t:
|
||||
*
|
||||
* t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
|
||||
*/
|
||||
/* radial or conical */
|
||||
Bool projective = FALSE;
|
||||
double cx = 1.;
|
||||
double cy = 0.;
|
||||
double cz = 0.;
|
||||
double rx = x;
|
||||
double ry = y;
|
||||
double rx = x + 0.5;
|
||||
double ry = y + 0.5;
|
||||
double rz = 1.;
|
||||
|
||||
if (pict->transform) {
|
||||
|
|
@ -3110,23 +3227,36 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
|
|||
}
|
||||
|
||||
if (pGradient->type == SourcePictTypeRadial) {
|
||||
pixman_radial_gradient_image_t *radial;
|
||||
radial = &pGradient->radial;
|
||||
if (!projective) {
|
||||
rx -= pGradient->radial.fx;
|
||||
ry -= pGradient->radial.fy;
|
||||
|
||||
while (buffer < end) {
|
||||
double b, c, det, s;
|
||||
|
||||
if (!mask || *mask++ & maskBits)
|
||||
{
|
||||
xFixed_48_16 t;
|
||||
double pdx, pdy;
|
||||
double B, C;
|
||||
double det;
|
||||
double c1x = xFixedToDouble (radial->c1.x);
|
||||
double c1y = xFixedToDouble (radial->c1.y);
|
||||
double r1 = xFixedToDouble (radial->c1.radius);
|
||||
xFixed_48_16 t;
|
||||
|
||||
b = 2*(rx*pGradient->radial.dx + ry*pGradient->radial.dy);
|
||||
c = -(rx*rx + ry*ry);
|
||||
det = (b * b) - (4 * pGradient->radial.a * c);
|
||||
s = (-b + sqrt(det))/(2. * pGradient->radial.a);
|
||||
pdx = rx - c1x;
|
||||
pdy = ry - c1y;
|
||||
|
||||
t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536);
|
||||
B = -2 * ( pdx * radial->cdx
|
||||
+ pdy * radial->cdy
|
||||
+ r1 * radial->dr);
|
||||
C = (pdx * pdx + pdy * pdy - r1 * r1);
|
||||
|
||||
det = (B * B) - (4 * radial->A * C);
|
||||
if (det < 0.0)
|
||||
det = 0.0;
|
||||
|
||||
if (radial->A < 0)
|
||||
t = (xFixed_48_16) ((- B - sqrt(det)) / (2.0 * radial->A) * 65536);
|
||||
else
|
||||
t = (xFixed_48_16) ((- B + sqrt(det)) / (2.0 * radial->A) * 65536);
|
||||
|
||||
*buffer = _gradient_walker_pixel (&walker, t);
|
||||
}
|
||||
|
|
@ -3135,35 +3265,12 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
|
|||
ry += cy;
|
||||
}
|
||||
} else {
|
||||
while (buffer < end) {
|
||||
double x, y;
|
||||
double b, c, det, s;
|
||||
|
||||
if (!mask || *mask++ & maskBits)
|
||||
{
|
||||
xFixed_48_16 t;
|
||||
|
||||
if (rz != 0) {
|
||||
x = rx/rz;
|
||||
y = ry/rz;
|
||||
} else {
|
||||
x = y = 0.;
|
||||
}
|
||||
x -= pGradient->radial.fx;
|
||||
y -= pGradient->radial.fy;
|
||||
b = 2*(x*pGradient->radial.dx + y*pGradient->radial.dy);
|
||||
c = -(x*x + y*y);
|
||||
det = (b * b) - (4 * pGradient->radial.a * c);
|
||||
s = (-b + sqrt(det))/(2. * pGradient->radial.a);
|
||||
t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536);
|
||||
|
||||
*buffer = _gradient_walker_pixel (&walker, t);
|
||||
}
|
||||
++buffer;
|
||||
rx += cx;
|
||||
ry += cy;
|
||||
rz += cz;
|
||||
}
|
||||
/* In cairo, we don't have projective transformed
|
||||
* radial gradients---so I'm not going to bother
|
||||
* implementing something untested and broken
|
||||
* here. Instead, someone trying to get this code into
|
||||
* shape for use in the X server can fix this here. */
|
||||
assert (0);
|
||||
}
|
||||
} else /* SourcePictTypeConical */ {
|
||||
double a = pGradient->conical.angle/(180.*65536);
|
||||
|
|
|
|||
|
|
@ -247,7 +247,6 @@ pixman_image_create_radial_gradient (const pixman_radial_gradient_t *gradient,
|
|||
{
|
||||
pixman_radial_gradient_image_t *radial;
|
||||
pixman_image_t *image;
|
||||
double x;
|
||||
|
||||
if (n_stops < 2)
|
||||
return NULL;
|
||||
|
|
@ -270,19 +269,14 @@ pixman_image_create_radial_gradient (const pixman_radial_gradient_t *gradient,
|
|||
memcpy (radial->stops, stops, sizeof (pixman_gradient_stop_t) * n_stops);
|
||||
|
||||
radial->type = SourcePictTypeRadial;
|
||||
x = (double) gradient->inner.radius / (double) gradient->outer.radius;
|
||||
radial->dx = (gradient->outer.x - gradient->inner.x);
|
||||
radial->dy = (gradient->outer.y - gradient->inner.y);
|
||||
radial->fx = (gradient->inner.x) - x * radial->dx;
|
||||
radial->fy = (gradient->inner.y) - x * radial->dy;
|
||||
radial->m = 1. / (1 + x);
|
||||
radial->b = -x * radial->m;
|
||||
radial->dx /= 65536.;
|
||||
radial->dy /= 65536.;
|
||||
radial->fx /= 65536.;
|
||||
radial->fy /= 65536.;
|
||||
x = gradient->outer.radius / 65536.;
|
||||
radial->a = x * x - radial->dx * radial->dx - radial->dy * radial->dy;
|
||||
radial->c1 = gradient->c1;
|
||||
radial->c2 = gradient->c2;
|
||||
radial->cdx = xFixedToDouble (gradient->c2.x - gradient->c1.x);
|
||||
radial->cdy = xFixedToDouble (gradient->c2.y - gradient->c1.y);
|
||||
radial->dr = xFixedToDouble (gradient->c2.radius - gradient->c1.radius);
|
||||
radial->A = ( radial->cdx * radial->cdx
|
||||
+ radial->cdy * radial->cdy
|
||||
- radial->dr * radial->dr);
|
||||
|
||||
image->pSourcePict = (pixman_source_image_t *) radial;
|
||||
|
||||
|
|
|
|||
|
|
@ -101,13 +101,12 @@ typedef struct _pixman_radial_gradient_image {
|
|||
int stopRange;
|
||||
uint32_t *colorTable;
|
||||
int colorTableSize;
|
||||
double fx;
|
||||
double fy;
|
||||
double dx;
|
||||
double dy;
|
||||
double a;
|
||||
double m;
|
||||
double b;
|
||||
pixman_circle_t c1;
|
||||
pixman_circle_t c2;
|
||||
double cdx;
|
||||
double cdy;
|
||||
double dr;
|
||||
double A;
|
||||
} pixman_radial_gradient_image_t;
|
||||
|
||||
typedef struct _pixman_conical_gradient_image {
|
||||
|
|
|
|||
|
|
@ -1026,6 +1026,7 @@ typedef xFixed_16_16 xFixed;
|
|||
#define IntToxFixed(i) ((xFixed) (i) << XFIXED_BITS)
|
||||
#define xFixedE ((xFixed) 1)
|
||||
#define xFixed1 (IntToxFixed(1))
|
||||
#define xFixedToDouble(f) (double) ((f) / (double) xFixed1)
|
||||
#define xFixed1MinusE (xFixed1 - xFixedE)
|
||||
#define xFixedFrac(f) ((f) & xFixed1MinusE)
|
||||
#define xFixedFloor(f) ((f) & ~xFixed1MinusE)
|
||||
|
|
|
|||
|
|
@ -364,8 +364,8 @@ typedef struct pixman_linear_gradient {
|
|||
} pixman_linear_gradient_t;
|
||||
|
||||
typedef struct pixman_radial_gradient {
|
||||
pixman_circle_t inner;
|
||||
pixman_circle_t outer;
|
||||
pixman_circle_t c1;
|
||||
pixman_circle_t c2;
|
||||
} pixman_radial_gradient_t;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
|||
|
|
@ -244,12 +244,12 @@ _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
|
|||
{
|
||||
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
|
||||
|
||||
pattern->gradient.inner.x = _cairo_fixed_from_double (cx0);
|
||||
pattern->gradient.inner.y = _cairo_fixed_from_double (cy0);
|
||||
pattern->gradient.inner.radius = _cairo_fixed_from_double (fabs (radius0));
|
||||
pattern->gradient.outer.x = _cairo_fixed_from_double (cx1);
|
||||
pattern->gradient.outer.y = _cairo_fixed_from_double (cy1);
|
||||
pattern->gradient.outer.radius = _cairo_fixed_from_double (fabs (radius1));
|
||||
pattern->gradient.c1.x = _cairo_fixed_from_double (cx0);
|
||||
pattern->gradient.c1.y = _cairo_fixed_from_double (cy0);
|
||||
pattern->gradient.c1.radius = _cairo_fixed_from_double (fabs (radius0));
|
||||
pattern->gradient.c2.x = _cairo_fixed_from_double (cx1);
|
||||
pattern->gradient.c2.y = _cairo_fixed_from_double (cy1);
|
||||
pattern->gradient.c2.radius = _cairo_fixed_from_double (fabs (radius1));
|
||||
}
|
||||
|
||||
cairo_pattern_t *
|
||||
|
|
@ -1855,12 +1855,12 @@ cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
|
|||
/**
|
||||
* cairo_pattern_get_radial_circles
|
||||
* @pattern: a #cairo_pattern_t
|
||||
* @x0: return value for the x coordinate of the center of the first (inner) circle, or %NULL
|
||||
* @y0: return value for the y coordinate of the center of the first (inner) circle, or %NULL
|
||||
* @r0: return value for the radius of the first (inner) circle, or %NULL
|
||||
* @x1: return value for the x coordinate of the center of the second (outer) circle, or %NULL
|
||||
* @y1: return value for the y coordinate of the center of the second (outer) circle, or %NULL
|
||||
* @r1: return value for the radius of the second (outer) circle, or %NULL
|
||||
* @x0: return value for the x coordinate of the center of the first circle, or %NULL
|
||||
* @y0: return value for the y coordinate of the center of the first circle, or %NULL
|
||||
* @r0: return value for the radius of the first circle, or %NULL
|
||||
* @x1: return value for the x coordinate of the center of the second circle, or %NULL
|
||||
* @y1: return value for the y coordinate of the center of the second circle, or %NULL
|
||||
* @r1: return value for the radius of the second circle, or %NULL
|
||||
*
|
||||
* Gets the gradient endpoint circles for a radial gradient, each
|
||||
* specified as a center coordinate and a radius.
|
||||
|
|
@ -1882,17 +1882,17 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
|
|||
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
|
||||
|
||||
if (x0)
|
||||
*x0 = _cairo_fixed_to_double (radial->gradient.inner.x);
|
||||
*x0 = _cairo_fixed_to_double (radial->gradient.c1.x);
|
||||
if (y0)
|
||||
*y0 = _cairo_fixed_to_double (radial->gradient.inner.y);
|
||||
*y0 = _cairo_fixed_to_double (radial->gradient.c1.y);
|
||||
if (r0)
|
||||
*r0 = _cairo_fixed_to_double (radial->gradient.inner.radius);
|
||||
*r0 = _cairo_fixed_to_double (radial->gradient.c1.radius);
|
||||
if (x1)
|
||||
*x1 = _cairo_fixed_to_double (radial->gradient.outer.x);
|
||||
*x1 = _cairo_fixed_to_double (radial->gradient.c2.x);
|
||||
if (y1)
|
||||
*y1 = _cairo_fixed_to_double (radial->gradient.outer.y);
|
||||
*y1 = _cairo_fixed_to_double (radial->gradient.c2.y);
|
||||
if (r1)
|
||||
*r1 = _cairo_fixed_to_double (radial->gradient.outer.radius);
|
||||
*r1 = _cairo_fixed_to_double (radial->gradient.c2.radius);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1263,13 +1263,13 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *patte
|
|||
p2u = pattern->base.base.matrix;
|
||||
cairo_matrix_invert (&p2u);
|
||||
|
||||
x0 = _cairo_fixed_to_double (pattern->gradient.inner.x);
|
||||
y0 = _cairo_fixed_to_double (pattern->gradient.inner.y);
|
||||
r0 = _cairo_fixed_to_double (pattern->gradient.inner.radius);
|
||||
x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
|
||||
y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
|
||||
r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
|
||||
cairo_matrix_transform_point (&p2u, &x0, &y0);
|
||||
x1 = _cairo_fixed_to_double (pattern->gradient.outer.x);
|
||||
y1 = _cairo_fixed_to_double (pattern->gradient.outer.y);
|
||||
r1 = _cairo_fixed_to_double (pattern->gradient.outer.radius);
|
||||
x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
|
||||
y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
|
||||
r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
|
||||
cairo_matrix_transform_point (&p2u, &x1, &y1);
|
||||
|
||||
/* FIXME: This is surely crack, but how should you scale a radius
|
||||
|
|
|
|||
|
|
@ -1280,20 +1280,23 @@ emit_radial_pattern (cairo_svg_surface_t *surface,
|
|||
double x0, y0, x1, y1, r0, r1;
|
||||
double fx, fy;
|
||||
|
||||
x0 = _cairo_fixed_to_double (pattern->gradient.inner.x);
|
||||
y0 = _cairo_fixed_to_double (pattern->gradient.inner.y);
|
||||
r0 = _cairo_fixed_to_double (pattern->gradient.inner.radius);
|
||||
x1 = _cairo_fixed_to_double (pattern->gradient.outer.x);
|
||||
y1 = _cairo_fixed_to_double (pattern->gradient.outer.y);
|
||||
r1 = _cairo_fixed_to_double (pattern->gradient.outer.radius);
|
||||
x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
|
||||
y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
|
||||
r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
|
||||
x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
|
||||
y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
|
||||
r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
|
||||
|
||||
/* SVG doesn't have a start radius, so computing now SVG focal coordinates
|
||||
* and emulating start radius by translating color stops.
|
||||
* FIXME: We also need to emulate cairo behaviour inside start circle when
|
||||
* extend != CAIRO_EXTEND_NONE.
|
||||
* FIXME: Handle radius1 <= radius0 */
|
||||
fx = (r1 * x0 - r0 * x1) / (r1 - r0);
|
||||
fy = (r1 * y0 - r0 * y1) / (r1 - r0);
|
||||
if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
|
||||
fx = x0;
|
||||
fy = y0;
|
||||
} else {
|
||||
fx = (r1 * x0 - r0 * x1) / (r1 - r0);
|
||||
fy = (r1 * y0 - r0 * y1) / (r1 - r0);
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (document->xml_node_defs,
|
||||
"<radialGradient id=\"radial%d\" "
|
||||
|
|
@ -1309,7 +1312,27 @@ emit_radial_pattern (cairo_svg_surface_t *surface,
|
|||
cairo_matrix_invert (&p2u);
|
||||
emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
|
||||
|
||||
/* To support cairo's EXTEND_NONE, (for which SVG has no similar
|
||||
* notion), we add transparent color stops on either end of the
|
||||
* user-provided stops. */
|
||||
if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
|
||||
_cairo_output_stream_printf (document->xml_node_defs,
|
||||
"<stop offset=\"0\" style=\""
|
||||
"stop-color: rgb(0%%,0%%,0%%); "
|
||||
"stop-opacity: 0;\"/>\n");
|
||||
if (r0 != 0.0)
|
||||
_cairo_output_stream_printf (document->xml_node_defs,
|
||||
"<stop offset=\"%f\" style=\""
|
||||
"stop-color: rgb(0%%,0%%,0%%); "
|
||||
"stop-opacity: 0;\"/>\n",
|
||||
r0 / r1);
|
||||
}
|
||||
emit_pattern_stops (document->xml_node_defs, &pattern->base, r0 / r1);
|
||||
if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
|
||||
_cairo_output_stream_printf (document->xml_node_defs,
|
||||
"<stop offset=\"1.0\" style=\""
|
||||
"stop-color: rgb(0%%,0%%,0%%); "
|
||||
"stop-opacity: 0;\"/>\n");
|
||||
|
||||
_cairo_output_stream_printf (document->xml_node_defs,
|
||||
"</radialGradient>\n");
|
||||
|
|
|
|||
1
test/.gitignore
vendored
1
test/.gitignore
vendored
|
|
@ -97,6 +97,7 @@ png-flatten
|
|||
ps-features
|
||||
ps-features.ps
|
||||
push-group
|
||||
radial-gradient
|
||||
random-intersections
|
||||
svg2png
|
||||
svg-clip
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ paint-with-alpha \
|
|||
pattern-get-type \
|
||||
pattern-getters \
|
||||
pixman-rotate \
|
||||
radial-gradient \
|
||||
random-intersections \
|
||||
rectangle-rounding-error \
|
||||
rectilinear-stroke \
|
||||
|
|
|
|||
BIN
test/radial-gradient-ref.png
Normal file
BIN
test/radial-gradient-ref.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
110
test/radial-gradient.c
Normal file
110
test/radial-gradient.c
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright © 2005, 2007 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: Carl D. Worth <cworth@cworth.org>
|
||||
*/
|
||||
|
||||
#include "cairo-test.h"
|
||||
|
||||
static cairo_test_draw_function_t draw;
|
||||
|
||||
#define NUM_GRADIENTS 4
|
||||
#define NUM_EXTEND 4
|
||||
#define SIZE 60
|
||||
#define WIDTH (SIZE * NUM_GRADIENTS)
|
||||
#define HEIGHT (SIZE * NUM_EXTEND)
|
||||
|
||||
cairo_test_t test = {
|
||||
"radial-gradient",
|
||||
"Simple test of radial gradients",
|
||||
WIDTH, HEIGHT,
|
||||
draw
|
||||
};
|
||||
|
||||
static void
|
||||
draw_gradient (cairo_t *cr,
|
||||
int x,
|
||||
int y,
|
||||
int size,
|
||||
double offset,
|
||||
double inner_radius,
|
||||
cairo_extend_t extend)
|
||||
{
|
||||
cairo_pattern_t *pattern;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
pattern = cairo_pattern_create_radial (x + size/2.0 + offset,
|
||||
y + size/2.0 + offset, inner_radius,
|
||||
x + size/2.0,
|
||||
y + size/2.0, size/3.0);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 0.0,
|
||||
1.0, 0.0, 0.0, 1.0);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, sqrt (1.0 / 2.0),
|
||||
0.0, 1.0, 0.0, 0.0);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 1.0,
|
||||
0.0, 0.0, 1.0, 0.5);
|
||||
cairo_pattern_set_extend (pattern, extend);
|
||||
|
||||
cairo_rectangle (cr, x, y, size, size);
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_pattern_destroy (pattern);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static cairo_test_status_t
|
||||
draw (cairo_t *cr, int width, int height)
|
||||
{
|
||||
int i, j;
|
||||
double inner_radius, offset;
|
||||
cairo_extend_t extend[NUM_EXTEND] = {
|
||||
CAIRO_EXTEND_NONE,
|
||||
CAIRO_EXTEND_REPEAT,
|
||||
CAIRO_EXTEND_REFLECT,
|
||||
CAIRO_EXTEND_PAD
|
||||
};
|
||||
|
||||
cairo_test_paint_checkered (cr);
|
||||
|
||||
for (j = 0; j < NUM_EXTEND; j++) {
|
||||
for (i = 0; i < NUM_GRADIENTS; i++) {
|
||||
offset = i % 2 ? SIZE / 12.0 : 0.0;
|
||||
inner_radius = i >= NUM_EXTEND / 2 ? SIZE / 6.0 : 0.0;
|
||||
draw_gradient (cr, i * SIZE, j * SIZE, SIZE,
|
||||
offset, inner_radius, extend[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return CAIRO_TEST_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return cairo_test (&test);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue