summaryrefslogtreecommitdiff
path: root/src/cairo-pattern.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-pattern.c')
-rw-r--r--src/cairo-pattern.c439
1 files changed, 237 insertions, 202 deletions
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 6464d23f9..b81b1bd01 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -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 = radius0;
- pattern->u.radial.radius0.dy = radius0;
+ pattern->u.radial.radius0 = fabs (radius0);
pattern->u.radial.center1.x = cx1;
pattern->u.radial.center1.y = cy1;
- pattern->u.radial.radius1.dx = radius1;
- pattern->u.radial.radius1.dy = radius1;
+ pattern->u.radial.radius1 = fabs (radius1);
return pattern;
}
@@ -229,6 +227,7 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern,
double alpha)
{
cairo_color_stop_t *stop;
+ int i;
_cairo_restrict_value (&offset, 0.0, 1.0);
_cairo_restrict_value (&red, 0.0, 1.0);
@@ -246,7 +245,7 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern,
stop = &pattern->stops[pattern->n_stops - 1];
- stop->offset = offset;
+ stop->offset = _cairo_fixed_from_double (offset);
stop->id = pattern->n_stops;
_cairo_color_init (&stop->color);
_cairo_color_set_rgb (&stop->color, red, green, blue);
@@ -260,6 +259,13 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern,
qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t),
_cairo_pattern_stop_compare);
+ for (i = 0; i < pattern->n_stops - 1; i++) {
+ pattern->stops[i + 1].scale =
+ pattern->stops[i + 1].offset - pattern->stops[i].offset;
+ if (pattern->stops[i + 1].scale == 65536)
+ pattern->stops[i + 1].scale = 0;
+ }
+
return CAIRO_STATUS_SUCCESS;
}
@@ -336,54 +342,18 @@ _cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha)
}
void
-_cairo_pattern_add_source_offset (cairo_pattern_t *pattern,
+_cairo_pattern_set_source_offset (cairo_pattern_t *pattern,
double x, double y)
{
- pattern->source_offset.x += x;
- pattern->source_offset.y += y;
+ pattern->source_offset.x = x;
+ pattern->source_offset.y = y;
}
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
@@ -419,135 +389,127 @@ _cairo_pattern_prepare_surface (cairo_pattern_t *pattern)
cairo_surface_set_filter (pattern->source, pattern->filter);
}
-typedef void (*cairo_shader_function_t) (unsigned char *color0,
- unsigned char *color1,
- double factor,
- unsigned char *result_color);
-
#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \
- ((unsigned char) ((factor < 0.5)? c1: c2))
+ ((factor < 32768)? c1: c2)
static void
_cairo_pattern_shader_nearest (unsigned char *color0,
unsigned char *color1,
- double factor,
- unsigned char *result_color)
+ cairo_fixed_t factor,
+ int *pixel)
{
- result_color[0] = INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor);
- result_color[1] = INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor);
- result_color[2] = INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor);
- result_color[3] = INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor);
+ *pixel =
+ ((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) |
+ (INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor) << 16) |
+ (INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor) << 8) |
+ (INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor) << 0));
}
#undef INTERPOLATE_COLOR_NEAREST
#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \
- ((unsigned char) ((c2 * factor) + (c1 * (1.0 - factor))))
+ (((c2 * factor) + (c1 * (65536 - factor))) / 65536)
static void
_cairo_pattern_shader_linear (unsigned char *color0,
unsigned char *color1,
- double factor,
- unsigned char *result_color)
+ cairo_fixed_t factor,
+ int *pixel)
{
- result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor);
- result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor);
- result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor);
- result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor);
+ *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
+ (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
+ (INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) |
+ (INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0));
}
+#define E_MINUS_ONE 1.7182818284590452354
+
static void
_cairo_pattern_shader_gaussian (unsigned char *color0,
unsigned char *color1,
- double factor,
- unsigned char *result_color)
+ cairo_fixed_t factor,
+ int *pixel)
{
- factor = (exp (factor * factor) - 1.0) / (M_E - 1.0);
+ double f = ((double) factor) / 65536.0;
- result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor);
- result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor);
- result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor);
- result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor);
+ factor = (cairo_fixed_t) (((exp (f * f) - 1.0) / E_MINUS_ONE) * 65536);
+
+ *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
+ (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
+ (INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) |
+ (INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0));
}
#undef INTERPOLATE_COLOR_LINEAR
void
-_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern,
- double factor,
- int *pixel)
+_cairo_pattern_shader_init (cairo_pattern_t *pattern,
+ cairo_shader_op_t *op)
{
- int p, colorstop;
- double factorscale;
- unsigned char result_color[4];
- cairo_shader_function_t shader_function;
-
+ op->stops = pattern->stops;
+ op->n_stops = pattern->n_stops - 1;
+ op->min_offset = pattern->stops[0].offset;
+ op->max_offset = pattern->stops[op->n_stops].offset;
+ op->extend = pattern->extend;
+
switch (pattern->filter) {
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
- shader_function = _cairo_pattern_shader_nearest;
+ op->shader_function = _cairo_pattern_shader_nearest;
break;
case CAIRO_FILTER_GAUSSIAN:
- shader_function = _cairo_pattern_shader_gaussian;
+ op->shader_function = _cairo_pattern_shader_gaussian;
break;
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
- shader_function = _cairo_pattern_shader_linear;
+ op->shader_function = _cairo_pattern_shader_linear;
break;
}
+}
+
+void
+_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
+ cairo_fixed_t factor,
+ int *pixel)
+{
+ int i;
- if (factor > 1.0 || factor < 0.0) {
- switch (pattern->extend) {
- case CAIRO_EXTEND_REPEAT:
- factor -= floor (factor);
- break;
- case CAIRO_EXTEND_REFLECT:
- if (factor >= 0.0) {
- if (((int) factor) % 2)
- factor = 1.0 - (factor - floor (factor));
- else
- factor -= floor (factor);
- } else {
- if (((int) factor) % 2)
- factor -= floor (factor);
- else
- factor = 1.0 - (factor - floor (factor));
- }
- break;
- case CAIRO_EXTEND_NONE:
- break;
+ switch (op->extend) {
+ case CAIRO_EXTEND_REPEAT:
+ factor -= factor & 0xffff0000;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ if (factor < 0 || factor > 65536) {
+ if ((factor >> 16) % 2)
+ factor = 65536 - (factor - (factor & 0xffff0000));
+ else
+ factor -= factor & 0xffff0000;
}
+ break;
+ case CAIRO_EXTEND_NONE:
+ break;
}
-
- if (factor < pattern->stops[0].offset)
- factor = pattern->stops[0].offset;
- if (factor > pattern->stops[pattern->n_stops - 1].offset)
- factor = pattern->stops[pattern->n_stops - 1].offset;
-
- for (colorstop = 0; colorstop < pattern->n_stops - 1; colorstop++) {
- if (factor <= pattern->stops[colorstop + 1].offset) {
- factorscale = fabs (pattern->stops[colorstop].offset -
- pattern->stops[colorstop + 1].offset);
-
- /* abrubt change, difference between two offsets == 0.0 */
- if (factorscale == 0)
- break;
-
- factor -= pattern->stops[colorstop].offset;
+ if (factor < op->min_offset)
+ factor = op->min_offset;
+ else if (factor > op->max_offset)
+ factor = op->max_offset;
+ for (i = 0; i < op->n_stops; i++) {
+ if (factor <= op->stops[i + 1].offset) {
+
/* take offset as new 0 of coordinate system */
- factor /= factorscale;
-
- shader_function (pattern->stops[colorstop].color_char,
- pattern->stops[colorstop + 1].color_char,
- factor, result_color);
-
- p = ((result_color[3] << 24) |
- (result_color[0] << 16) |
- (result_color[1] << 8) | (result_color[2] << 0));
- *pixel = p;
+ factor -= op->stops[i].offset;
+
+ /* difference between two offsets == 0, abrubt change */
+ if (op->stops[i + 1].scale)
+ factor = ((cairo_fixed_48_16_t) factor << 16) /
+ op->stops[i + 1].scale;
+
+ op->shader_function (op->stops[i].color_char,
+ op->stops[i + 1].color_char,
+ factor, pixel);
break;
}
}
@@ -557,92 +519,164 @@ 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, end;
- double factor;
+ 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;
+
+ _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 : INT_MAX;
-
- a = -atan2 (point1.y - point0.y, point1.x - point0.x);
- angle.x = cos (a);
- angle.y = -sin (a);
+ length = (length) ? 1.0 / length : CAIRO_MAXSHORT;
- start = angle.x * point0.x;
- start += angle.y * point0.y;
-
- end = angle.x * point1.x;
- end += angle.y * point1.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++) {
-
- factor = angle.x * (double) x;
- factor += angle.y * (double) y;
-
- factor = factor - start;
- factor *= length;
-
- _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *)
- &data[y * width * 4 + x * 4]);
+ px = x + offset_x;
+ py = y + offset_y;
+
+ /* transform fragment */
+ ex = a * px + c * py + tx;
+ ey = b * px + d * py + ty;
+
+ factor = ((fx * ex + fy * ey) - start) * length;
+
+ _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;
- double min_length;
-
- center1.x = pattern->u.radial.center1.x - offset_x;
- center1.y = pattern->u.radial.center1.y - offset_y;
-
- min_length =
- fabs ((pattern->u.radial.radius1.dx < pattern->u.radial.radius1.dy) ?
- pattern->u.radial.radius1.dx : pattern->u.radial.radius1.dy);
-
- /* ugly */
- if (min_length == 0.0)
- min_length = 0.000001;
-
- length.dx = min_length / pattern->u.radial.radius1.dx;
- length.dy = min_length / pattern->u.radial.radius1.dy;
+ 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);
+
+ 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;
+
+ 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);
+ }
- min_length = 1.0 / min_length;
+ 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;
-
- pos.x *= length.dx;
- pos.y *= length.dy;
-
- factor = sqrt (pos.x * pos.x + pos.y * pos.y) * min_length;
+ px = x + offset_x;
+ py = y + offset_y;
+
+ /* transform fragment */
+ ex = a * px + c * py + tx;
+ ey = b * px + d * py + ty;
+
+ if (aligned_circles) {
+ ex = ex - c1.x;
+ ey = ey - c1.y;
+
+ factor = (sqrt (ex * ex + ey * ey) - r0) * r1;
+ } else {
+ /*
+ y (ex, ey)
+ c0 -------------------+---------- x
+ \ | __--
+ \ | __--
+ \ | __--
+ \ | __-- r1
+ \ | __--
+ c1 --
+
+ We need to calulate distance c0->x; the distance from
+ the inner circle center c0, through fragment position
+ (ex, ey) to point x where it crosses the outer circle.
+
+ From points c0, c1 and (ex, ey) we get angle C0. With
+ angle C0 we calculate distance c1->y and c0->y and by
+ knowing c1->y and r1, we also know y->x. Adding y->x to
+ c0->y gives us c0->x. The gradient offset can then be
+ calculated as:
+
+ offset = (c0->e - r0) / (c0->x - r0)
+
+ */
+
+ 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 (pattern, factor, (int *)
- &data[y * width * 4 + x * 4]);
+ _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
}
@@ -656,35 +690,35 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
case CAIRO_PATTERN_LINEAR:
case CAIRO_PATTERN_RADIAL: {
char *data;
- int width = ceil (_cairo_fixed_to_double (box->p2.x)) -
- floor (_cairo_fixed_to_double (box->p1.x));
- int height = ceil (_cairo_fixed_to_double (box->p2.y)) -
- floor (_cairo_fixed_to_double (box->p1.y));
+ double x = box->p1.x >> 16;
+ double y = box->p1.y >> 16;
+ int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16);
+ int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16);
data = malloc (width * height * 4);
if (!data)
return NULL;
-
- _cairo_pattern_add_source_offset (pattern,
- floor (_cairo_fixed_to_double (box->p1.x)),
- floor (_cairo_fixed_to_double (box->p1.y)));
-
+
if (pattern->type == CAIRO_PATTERN_RADIAL)
_cairo_image_data_set_radial (pattern,
- pattern->source_offset.x,
- pattern->source_offset.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,
- pattern->source_offset.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);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
width, height,
width * 4);
-
+
if (surface)
_cairo_image_surface_assume_ownership_of_data (
(cairo_image_surface_t *) surface);
@@ -714,3 +748,4 @@ _cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
return (cairo_image_surface_t *) surface;
}
+