Updated linear and radial patterns

This commit is contained in:
David Reveman 2004-04-29 20:26:56 +00:00
parent c9c882d2b2
commit f516564f98
8 changed files with 309 additions and 249 deletions

View file

@ -1,3 +1,28 @@
2004-04-30 David Reveman <c99drn@cs.umu.se>
* src/cairoint.h: Radial patterns only store radius per circle.
Only inverse CTM to _cairo_pattern_transform.
* src/cairo_pattern.c (cairo_pattern_create_radial): Only one radius
per circle now.
(_cairo_pattern_transform): A matrix multiplication with inverse CTM
is all that's needed here.
(_cairo_pattern_calc_color_at_pixel): Fixed extend type reflect.
(_cairo_image_data_set_linear): New linear gradient code.
Transformation of linear gradient is now handled correctly.
(_cairo_image_data_set_radial): New radial gradient code. Inner circle
is now used for creating radial gradients. Transformation of radial
gradient is now handled correctly.
(_cairo_pattern_get_image): Fixed handling of pattern offset.
* src/cairo_gstate.c (_cairo_gstate_create_pattern): All pattern types
are transformed using inverse CTM.
* src/cairo_gl_surface.c (_cairo_gl_surface_create_pattern): Updated
to use glitz's new linear and radial gradients.
* configure.in: Require glitz 0.1.1.
2004-04-28 David Reveman <c99drn@cs.umu.se>
* src/cairo_gl_surface.c: Added CAIRO_GL_MULTITEXTURE_SUPPORT and

View file

@ -141,7 +141,7 @@ AC_ARG_ENABLE(gl,
[use_gl=$enableval], [use_gl=yes])
if test "x$use_gl" = "xyes"; then
PKG_CHECK_MODULES(GL, glitz >= 0.1.0, [
PKG_CHECK_MODULES(GL, glitz >= 0.1.1, [
GL_REQUIRES=glitz
use_gl=yes], [use_gl="no (requires glitz http://freedesktop.org/software/glitz)"])
fi

View file

@ -1278,7 +1278,7 @@ _cairo_gstate_create_pattern (cairo_gstate_t *gstate,
}
_cairo_pattern_set_alpha (pattern, gstate->alpha);
_cairo_pattern_transform (pattern, &gstate->ctm, &gstate->ctm_inverse);
_cairo_pattern_transform (pattern, &gstate->ctm_inverse);
_cairo_pattern_set_source_offset (pattern,
gstate->pattern_offset.x,

View file

@ -175,12 +175,10 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
pattern->type = CAIRO_PATTERN_RADIAL;
pattern->u.radial.center0.x = cx0;
pattern->u.radial.center0.y = cy0;
pattern->u.radial.radius0.dx = fabs (radius0);
pattern->u.radial.radius0.dy = fabs (radius0);
pattern->u.radial.radius0 = fabs (radius0);
pattern->u.radial.center1.x = cx1;
pattern->u.radial.center1.y = cy1;
pattern->u.radial.radius1.dx = fabs (radius1);
pattern->u.radial.radius1.dy = fabs (radius1);
pattern->u.radial.radius1 = fabs (radius1);
return pattern;
}
@ -353,45 +351,9 @@ _cairo_pattern_set_source_offset (cairo_pattern_t *pattern,
void
_cairo_pattern_transform (cairo_pattern_t *pattern,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse)
{
cairo_matrix_t matrix;
switch (pattern->type) {
case CAIRO_PATTERN_SURFACE:
/* hmm, maybe we should instead multiply with the inverse of the
pattern matrix here? */
cairo_matrix_multiply (&pattern->matrix, ctm_inverse,
&pattern->matrix);
break;
case CAIRO_PATTERN_LINEAR:
cairo_matrix_multiply (&matrix, &pattern->matrix, ctm);
cairo_matrix_transform_point (&matrix,
&pattern->u.linear.point0.x,
&pattern->u.linear.point0.y);
cairo_matrix_transform_point (&matrix,
&pattern->u.linear.point1.x,
&pattern->u.linear.point1.y);
break;
case CAIRO_PATTERN_RADIAL:
cairo_matrix_multiply (&matrix, &pattern->matrix, ctm);
cairo_matrix_transform_point (&matrix,
&pattern->u.radial.center0.x,
&pattern->u.radial.center0.y);
cairo_matrix_transform_distance (&matrix,
&pattern->u.radial.radius0.dx,
&pattern->u.radial.radius0.dy);
cairo_matrix_transform_point (&matrix,
&pattern->u.radial.center1.x,
&pattern->u.radial.center1.y);
cairo_matrix_transform_distance (&matrix,
&pattern->u.radial.radius1.dx,
&pattern->u.radial.radius1.dy);
break;
case CAIRO_PATTERN_SOLID:
break;
}
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
}
void
@ -518,12 +480,7 @@ _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
factor -= factor & 0xffff0000;
break;
case CAIRO_EXTEND_REFLECT:
if (factor < 0) {
if ((factor >> 16) % 2)
factor -= factor & 0xffff0000;
else
factor = 65536 - (factor - (factor & 0xffff0000));
} else if (factor > 65536) {
if (factor < 0 || factor > 65536) {
if ((factor >> 16) % 2)
factor = 65536 - (factor - (factor & 0xffff0000));
else
@ -562,90 +519,156 @@ static void
_cairo_image_data_set_linear (cairo_pattern_t *pattern,
double offset_x,
double offset_y,
char *data,
int *pixels,
int width,
int height)
{
int x, y;
cairo_point_double_t point0, point1, angle;
double a, length, start;
cairo_point_double_t point0, point1;
double px, py, ex, ey;
double a, b, c, d, tx, ty;
double length, start, angle, fx, fy, factor;
cairo_shader_op_t op;
double factor;
_cairo_pattern_shader_init (pattern, &op);
point0.x = pattern->u.linear.point0.x - offset_x;
point0.y = pattern->u.linear.point0.y - offset_y;
point1.x = pattern->u.linear.point1.x - offset_x;
point1.y = pattern->u.linear.point1.y - offset_y;
point0.x = pattern->u.linear.point0.x;
point0.y = pattern->u.linear.point0.y;
point1.x = pattern->u.linear.point1.x;
point1.y = pattern->u.linear.point1.y;
cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty);
length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) +
(point1.y - point0.y) * (point1.y - point0.y));
length = (length) ? 1.0 / length : CAIRO_MAXSHORT;
a = -atan2 (point1.y - point0.y, point1.x - point0.x);
angle.x = cos (a);
angle.y = -sin (a);
start = angle.x * point0.x;
start += angle.y * point0.y;
angle = -atan2 (point1.y - point0.y, point1.x - point0.x);
fx = cos (angle);
fy = -sin (angle);
start = fx * point0.x;
start += fy * point0.y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
px = x + offset_x;
py = y + offset_y;
/* transform fragment */
ex = a * px + c * py + tx;
ey = b * px + d * py + ty;
factor = ((angle.x * (double) x) +
(angle.y * (double) y) - start) * length;
factor = ((fx * ex + fy * ey) - start) * length;
_cairo_pattern_calc_color_at_pixel (&op,
factor * 65536,
(int *)
&data[y * width * 4 + x * 4]);
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
}
/* TODO: Inner circle is currently ignored. */
static void
_cairo_image_data_set_radial (cairo_pattern_t *pattern,
double offset_x,
double offset_y,
char *data,
int *pixels,
int width,
int height)
{
int x, y;
cairo_point_double_t center1, pos;
cairo_distance_double_t length;
double factor, min_length;
int x, y, aligned_circles;
cairo_point_double_t c0, c1;
double px, py, ex, ey;
double a, b, c, d, tx, ty;
double r0, r1, c0_e_x, c0_e_y, c0_e, c1_e_x, c1_e_y, c1_e,
c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2,
denumerator, fraction, factor;
cairo_shader_op_t op;
_cairo_pattern_shader_init (pattern, &op);
center1.x = pattern->u.radial.center1.x - offset_x;
center1.y = pattern->u.radial.center1.y - offset_y;
c0.x = pattern->u.radial.center0.x;
c0.y = pattern->u.radial.center0.y;
r0 = pattern->u.radial.radius0;
c1.x = pattern->u.radial.center1.x;
c1.y = pattern->u.radial.center1.y;
r1 = pattern->u.radial.radius1;
min_length = (pattern->u.radial.radius1.dx < pattern->u.radial.radius1.dy)?
pattern->u.radial.radius1.dx : pattern->u.radial.radius1.dy;
length.dx = min_length / pattern->u.radial.radius1.dx;
length.dy = min_length / pattern->u.radial.radius1.dy;
min_length = (min_length)? 1.0 / min_length: CAIRO_MAXSHORT;
if (c0.x != c1.x || c0.y != c1.y) {
aligned_circles = 0;
c0_c1_x = c1.x - c0.x;
c0_c1_y = c1.y - c0.y;
c0_c1 = sqrt (c0_c1_x * c0_c1_x + c0_c1_y * c0_c1_y);
r1_2 = r1 * r1;
} else {
aligned_circles = 1;
r1 = 1.0 / (r1 - r0);
}
cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
pos.x = x - center1.x;
pos.y = y - center1.y;
px = x + offset_x;
py = y + offset_y;
/* transform fragment */
ex = a * px + c * py + tx;
ey = b * px + d * py + ty;
pos.x *= length.dx;
pos.y *= length.dy;
if (aligned_circles) {
ex = ex - c1.x;
ey = ey - c1.y;
factor = sqrt (pos.x * pos.x + pos.y * pos.y) * min_length;
factor = (sqrt (ex * ex + ey * ey) - r0) * r1;
} else {
/* Here we need to calulate distance c0 -> x; the distance from
the inner circle center c0, through point (ex, ey) to
point x where it crosses the outer circle. The gradient offset
can then be calculated within the distance of the inner and
outer circles.
y y_x (ex, ey)
c0 -------------------+---------- x
\ | __--
\ | __--
r0 \ | c1_y __--
\ | __-- r1
\ | __--
c1 --
*/
_cairo_pattern_calc_color_at_pixel (&op,
factor * 65536,
(int *)
&data[y * width * 4 + x * 4]);
c0_e_x = ex - c0.x;
c0_e_y = ey - c0.y;
c0_e = sqrt (c0_e_x * c0_e_x + c0_e_y * c0_e_y);
c1_e_x = ex - c1.x;
c1_e_y = ey - c1.y;
c1_e = sqrt (c1_e_x * c1_e_x + c1_e_y * c1_e_y);
denumerator = -2.0 * c0_e * c0_c1;
if (denumerator != 0.0) {
fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) /
denumerator;
if (fraction > 1.0)
fraction = 1.0;
else if (fraction < -1.0)
fraction = -1.0;
angle_c0 = acos (fraction);
c0_y = cos (angle_c0) * c0_c1;
c1_y = sin (angle_c0) * c0_c1;
y_x = sqrt (r1_2 - c1_y * c1_y);
c0_x = y_x + c0_y;
factor = (c0_e - r0) / (c0_x - r0);
} else
factor = -r0;
}
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
}
@ -670,14 +693,16 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
if (pattern->type == CAIRO_PATTERN_RADIAL)
_cairo_image_data_set_radial (pattern,
pattern->source_offset.x + x,
pattern->source_offset.y + y,
data, width, height);
x - pattern->source_offset.x,
y - pattern->source_offset.y,
(int *) data,
width, height);
else
_cairo_image_data_set_linear (pattern,
pattern->source_offset.x + x,
pattern->source_offset.y + y,
data, width, height);
x - pattern->source_offset.x,
y - pattern->source_offset.y,
(int *) data,
width, height);
_cairo_pattern_set_source_offset (pattern, x, y);
@ -715,3 +740,4 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
return (cairo_image_surface_t *) surface;
}

View file

@ -590,7 +590,7 @@ _cairo_gl_create_color_range (cairo_pattern_t *pattern,
{
unsigned int i, bytes = size * 4;
cairo_shader_op_t op;
_cairo_pattern_shader_init (pattern, &op);
for (i = 0; i < bytes; i += 4)
@ -607,10 +607,6 @@ _cairo_gl_surface_create_pattern (void *abstract_surface,
cairo_gl_surface_t *surface = abstract_surface;
glitz_surface_t *programmatic = NULL;
cairo_gl_surface_t *gl_surface;
double bbox_x = box->p1.x >> 16;
double bbox_y = box->p1.y >> 16;
double x = bbox_x + pattern->source_offset.x;
double y = bbox_y + pattern->source_offset.y;
switch (pattern->type) {
case CAIRO_PATTERN_SOLID: {
@ -622,8 +618,7 @@ _cairo_gl_surface_create_pattern (void *abstract_surface,
color.alpha = pattern->color.alpha_short;
programmatic = glitz_surface_create_solid (&color);
}
break;
} break;
case CAIRO_PATTERN_LINEAR:
case CAIRO_PATTERN_RADIAL: {
unsigned int color_range_size;
@ -642,26 +637,17 @@ _cairo_gl_surface_create_pattern (void *abstract_surface,
if (pattern->type == CAIRO_PATTERN_LINEAR) {
double dx, dy;
dx = (pattern->u.linear.point1.x - x) -
(pattern->u.linear.point0.x - x);
dy = (pattern->u.linear.point1.y - y) -
(pattern->u.linear.point0.y - y);
dx = pattern->u.linear.point1.x - pattern->u.linear.point0.x;
dy = pattern->u.linear.point1.y - pattern->u.linear.point0.y;
color_range_size = sqrt (dx * dx + dy * dy);
} else {
/* glitz doesn't support inner circle yet. */
if (pattern->u.radial.center0.x !=
pattern->u.radial.center1.x
|| pattern->u.radial.center0.y !=
pattern->u.radial.center1.y
|| pattern->u.radial.radius0.dx
|| pattern->u.radial.radius0.dy)
if (pattern->u.radial.center0.x != pattern->u.radial.center1.x ||
pattern->u.radial.center0.y != pattern->u.radial.center1.y)
return CAIRO_INT_STATUS_UNSUPPORTED;
color_range_size = sqrt (pattern->u.radial.radius1.dx *
pattern->u.radial.radius1.dx +
pattern->u.radial.radius1.dy *
pattern->u.radial.radius1.dy);
color_range_size = pattern->u.radial.radius1;
}
if ((!CAIRO_GL_TEXTURE_NPOT_SUPPORT (surface)))
@ -672,8 +658,8 @@ _cairo_gl_surface_create_pattern (void *abstract_surface,
return CAIRO_STATUS_NO_MEMORY;
_cairo_gl_create_color_range (pattern,
glitz_color_range_get_data
(color_range), color_range_size);
glitz_color_range_get_data (color_range),
color_range_size);
switch (pattern->extend) {
case CAIRO_EXTEND_REPEAT:
@ -687,60 +673,58 @@ _cairo_gl_surface_create_pattern (void *abstract_surface,
break;
}
glitz_color_range_set_filter (color_range, GLITZ_FILTER_BILINEAR);
if (pattern->type == CAIRO_PATTERN_LINEAR) {
glitz_point_fixed_t start;
glitz_point_fixed_t stop;
start.x =
_cairo_fixed_from_double (pattern->u.linear.point0.x - x);
start.y =
_cairo_fixed_from_double (pattern->u.linear.point0.y - y);
stop.x = _cairo_fixed_from_double (pattern->u.linear.point1.x - x);
stop.y = _cairo_fixed_from_double (pattern->u.linear.point1.y - y);
start.x = _cairo_fixed_from_double (pattern->u.linear.point0.x);
start.y = _cairo_fixed_from_double (pattern->u.linear.point0.y);
stop.x = _cairo_fixed_from_double (pattern->u.linear.point1.x);
stop.y = _cairo_fixed_from_double (pattern->u.linear.point1.y);
programmatic =
glitz_surface_create_linear (&start, &stop, color_range);
} else {
glitz_point_fixed_t center;
glitz_distance_fixed_t radius;
center.x =
_cairo_fixed_from_double (pattern->u.radial.center1.x - x);
center.y =
_cairo_fixed_from_double (pattern->u.radial.center1.y - y);
radius.dx =
_cairo_fixed_from_double (pattern->u.radial.radius1.dx);
radius.dy =
_cairo_fixed_from_double (pattern->u.radial.radius1.dy);
programmatic =
glitz_surface_create_radial (&center, &radius, color_range);
center.x = _cairo_fixed_from_double (pattern->u.radial.center1.x);
center.y = _cairo_fixed_from_double (pattern->u.radial.center1.y);
programmatic = glitz_surface_create_radial
(&center,
_cairo_fixed_from_double (pattern->u.radial.radius0),
_cairo_fixed_from_double (pattern->u.radial.radius1),
color_range);
}
glitz_color_range_destroy (color_range);
}
break;
default:
} break;
case CAIRO_PATTERN_SURFACE:
return CAIRO_INT_STATUS_UNSUPPORTED;
break;
}
if (!programmatic)
return CAIRO_STATUS_NO_MEMORY;
gl_surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create (programmatic, 1);
_cairo_gl_surface_create (programmatic, 1);
if (!gl_surface) {
glitz_surface_destroy (programmatic);
return CAIRO_STATUS_NO_MEMORY;
}
if (pattern->type == CAIRO_PATTERN_LINEAR ||
pattern->type == CAIRO_PATTERN_RADIAL)
cairo_surface_set_matrix (gl_surface, &pattern->matrix);
_cairo_pattern_init_copy (&gl_surface->pattern, pattern);
gl_surface->pattern_box = *box;
pattern->source = &gl_surface->base;
_cairo_pattern_set_source_offset (pattern, bbox_x, bbox_y);
return CAIRO_STATUS_SUCCESS;
}

View file

@ -1278,7 +1278,7 @@ _cairo_gstate_create_pattern (cairo_gstate_t *gstate,
}
_cairo_pattern_set_alpha (pattern, gstate->alpha);
_cairo_pattern_transform (pattern, &gstate->ctm, &gstate->ctm_inverse);
_cairo_pattern_transform (pattern, &gstate->ctm_inverse);
_cairo_pattern_set_source_offset (pattern,
gstate->pattern_offset.x,

View file

@ -175,12 +175,10 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
pattern->type = CAIRO_PATTERN_RADIAL;
pattern->u.radial.center0.x = cx0;
pattern->u.radial.center0.y = cy0;
pattern->u.radial.radius0.dx = fabs (radius0);
pattern->u.radial.radius0.dy = fabs (radius0);
pattern->u.radial.radius0 = fabs (radius0);
pattern->u.radial.center1.x = cx1;
pattern->u.radial.center1.y = cy1;
pattern->u.radial.radius1.dx = fabs (radius1);
pattern->u.radial.radius1.dy = fabs (radius1);
pattern->u.radial.radius1 = fabs (radius1);
return pattern;
}
@ -353,45 +351,9 @@ _cairo_pattern_set_source_offset (cairo_pattern_t *pattern,
void
_cairo_pattern_transform (cairo_pattern_t *pattern,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse)
{
cairo_matrix_t matrix;
switch (pattern->type) {
case CAIRO_PATTERN_SURFACE:
/* hmm, maybe we should instead multiply with the inverse of the
pattern matrix here? */
cairo_matrix_multiply (&pattern->matrix, ctm_inverse,
&pattern->matrix);
break;
case CAIRO_PATTERN_LINEAR:
cairo_matrix_multiply (&matrix, &pattern->matrix, ctm);
cairo_matrix_transform_point (&matrix,
&pattern->u.linear.point0.x,
&pattern->u.linear.point0.y);
cairo_matrix_transform_point (&matrix,
&pattern->u.linear.point1.x,
&pattern->u.linear.point1.y);
break;
case CAIRO_PATTERN_RADIAL:
cairo_matrix_multiply (&matrix, &pattern->matrix, ctm);
cairo_matrix_transform_point (&matrix,
&pattern->u.radial.center0.x,
&pattern->u.radial.center0.y);
cairo_matrix_transform_distance (&matrix,
&pattern->u.radial.radius0.dx,
&pattern->u.radial.radius0.dy);
cairo_matrix_transform_point (&matrix,
&pattern->u.radial.center1.x,
&pattern->u.radial.center1.y);
cairo_matrix_transform_distance (&matrix,
&pattern->u.radial.radius1.dx,
&pattern->u.radial.radius1.dy);
break;
case CAIRO_PATTERN_SOLID:
break;
}
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
}
void
@ -518,12 +480,7 @@ _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
factor -= factor & 0xffff0000;
break;
case CAIRO_EXTEND_REFLECT:
if (factor < 0) {
if ((factor >> 16) % 2)
factor -= factor & 0xffff0000;
else
factor = 65536 - (factor - (factor & 0xffff0000));
} else if (factor > 65536) {
if (factor < 0 || factor > 65536) {
if ((factor >> 16) % 2)
factor = 65536 - (factor - (factor & 0xffff0000));
else
@ -562,90 +519,156 @@ static void
_cairo_image_data_set_linear (cairo_pattern_t *pattern,
double offset_x,
double offset_y,
char *data,
int *pixels,
int width,
int height)
{
int x, y;
cairo_point_double_t point0, point1, angle;
double a, length, start;
cairo_point_double_t point0, point1;
double px, py, ex, ey;
double a, b, c, d, tx, ty;
double length, start, angle, fx, fy, factor;
cairo_shader_op_t op;
double factor;
_cairo_pattern_shader_init (pattern, &op);
point0.x = pattern->u.linear.point0.x - offset_x;
point0.y = pattern->u.linear.point0.y - offset_y;
point1.x = pattern->u.linear.point1.x - offset_x;
point1.y = pattern->u.linear.point1.y - offset_y;
point0.x = pattern->u.linear.point0.x;
point0.y = pattern->u.linear.point0.y;
point1.x = pattern->u.linear.point1.x;
point1.y = pattern->u.linear.point1.y;
cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty);
length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) +
(point1.y - point0.y) * (point1.y - point0.y));
length = (length) ? 1.0 / length : CAIRO_MAXSHORT;
a = -atan2 (point1.y - point0.y, point1.x - point0.x);
angle.x = cos (a);
angle.y = -sin (a);
start = angle.x * point0.x;
start += angle.y * point0.y;
angle = -atan2 (point1.y - point0.y, point1.x - point0.x);
fx = cos (angle);
fy = -sin (angle);
start = fx * point0.x;
start += fy * point0.y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
px = x + offset_x;
py = y + offset_y;
/* transform fragment */
ex = a * px + c * py + tx;
ey = b * px + d * py + ty;
factor = ((angle.x * (double) x) +
(angle.y * (double) y) - start) * length;
factor = ((fx * ex + fy * ey) - start) * length;
_cairo_pattern_calc_color_at_pixel (&op,
factor * 65536,
(int *)
&data[y * width * 4 + x * 4]);
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
}
/* TODO: Inner circle is currently ignored. */
static void
_cairo_image_data_set_radial (cairo_pattern_t *pattern,
double offset_x,
double offset_y,
char *data,
int *pixels,
int width,
int height)
{
int x, y;
cairo_point_double_t center1, pos;
cairo_distance_double_t length;
double factor, min_length;
int x, y, aligned_circles;
cairo_point_double_t c0, c1;
double px, py, ex, ey;
double a, b, c, d, tx, ty;
double r0, r1, c0_e_x, c0_e_y, c0_e, c1_e_x, c1_e_y, c1_e,
c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2,
denumerator, fraction, factor;
cairo_shader_op_t op;
_cairo_pattern_shader_init (pattern, &op);
center1.x = pattern->u.radial.center1.x - offset_x;
center1.y = pattern->u.radial.center1.y - offset_y;
c0.x = pattern->u.radial.center0.x;
c0.y = pattern->u.radial.center0.y;
r0 = pattern->u.radial.radius0;
c1.x = pattern->u.radial.center1.x;
c1.y = pattern->u.radial.center1.y;
r1 = pattern->u.radial.radius1;
min_length = (pattern->u.radial.radius1.dx < pattern->u.radial.radius1.dy)?
pattern->u.radial.radius1.dx : pattern->u.radial.radius1.dy;
length.dx = min_length / pattern->u.radial.radius1.dx;
length.dy = min_length / pattern->u.radial.radius1.dy;
min_length = (min_length)? 1.0 / min_length: CAIRO_MAXSHORT;
if (c0.x != c1.x || c0.y != c1.y) {
aligned_circles = 0;
c0_c1_x = c1.x - c0.x;
c0_c1_y = c1.y - c0.y;
c0_c1 = sqrt (c0_c1_x * c0_c1_x + c0_c1_y * c0_c1_y);
r1_2 = r1 * r1;
} else {
aligned_circles = 1;
r1 = 1.0 / (r1 - r0);
}
cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
pos.x = x - center1.x;
pos.y = y - center1.y;
px = x + offset_x;
py = y + offset_y;
/* transform fragment */
ex = a * px + c * py + tx;
ey = b * px + d * py + ty;
pos.x *= length.dx;
pos.y *= length.dy;
if (aligned_circles) {
ex = ex - c1.x;
ey = ey - c1.y;
factor = sqrt (pos.x * pos.x + pos.y * pos.y) * min_length;
factor = (sqrt (ex * ex + ey * ey) - r0) * r1;
} else {
/* Here we need to calulate distance c0 -> x; the distance from
the inner circle center c0, through point (ex, ey) to
point x where it crosses the outer circle. The gradient offset
can then be calculated within the distance of the inner and
outer circles.
y y_x (ex, ey)
c0 -------------------+---------- x
\ | __--
\ | __--
r0 \ | c1_y __--
\ | __-- r1
\ | __--
c1 --
*/
_cairo_pattern_calc_color_at_pixel (&op,
factor * 65536,
(int *)
&data[y * width * 4 + x * 4]);
c0_e_x = ex - c0.x;
c0_e_y = ey - c0.y;
c0_e = sqrt (c0_e_x * c0_e_x + c0_e_y * c0_e_y);
c1_e_x = ex - c1.x;
c1_e_y = ey - c1.y;
c1_e = sqrt (c1_e_x * c1_e_x + c1_e_y * c1_e_y);
denumerator = -2.0 * c0_e * c0_c1;
if (denumerator != 0.0) {
fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) /
denumerator;
if (fraction > 1.0)
fraction = 1.0;
else if (fraction < -1.0)
fraction = -1.0;
angle_c0 = acos (fraction);
c0_y = cos (angle_c0) * c0_c1;
c1_y = sin (angle_c0) * c0_c1;
y_x = sqrt (r1_2 - c1_y * c1_y);
c0_x = y_x + c0_y;
factor = (c0_e - r0) / (c0_x - r0);
} else
factor = -r0;
}
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
}
@ -670,14 +693,16 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
if (pattern->type == CAIRO_PATTERN_RADIAL)
_cairo_image_data_set_radial (pattern,
pattern->source_offset.x + x,
pattern->source_offset.y + y,
data, width, height);
x - pattern->source_offset.x,
y - pattern->source_offset.y,
(int *) data,
width, height);
else
_cairo_image_data_set_linear (pattern,
pattern->source_offset.x + x,
pattern->source_offset.y + y,
data, width, height);
x - pattern->source_offset.x,
y - pattern->source_offset.y,
(int *) data,
width, height);
_cairo_pattern_set_source_offset (pattern, x, y);
@ -715,3 +740,4 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
return (cairo_image_surface_t *) surface;
}

View file

@ -508,8 +508,8 @@ struct cairo_pattern {
struct {
cairo_point_double_t center0;
cairo_point_double_t center1;
cairo_distance_double_t radius0;
cairo_distance_double_t radius1;
double radius0;
double radius1;
} radial;
} u;
};
@ -1355,8 +1355,7 @@ _cairo_pattern_set_source_offset (cairo_pattern_t *pattern,
extern void __internal_linkage
_cairo_pattern_transform (cairo_pattern_t *pattern,
cairo_matrix_t *matrix,
cairo_matrix_t *matrix_inverse);
cairo_matrix_t *ctm_inverse);
extern void __internal_linkage
_cairo_pattern_prepare_surface (cairo_pattern_t *pattern);