diff options
Diffstat (limited to 'src/cairo-pattern.c')
-rw-r--r-- | src/cairo-pattern.c | 439 |
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; } + |