diff options
-rw-r--r-- | src/cairo-pattern.c | 716 | ||||
-rw-r--r-- | src/cairo_pattern.c | 716 |
2 files changed, 1432 insertions, 0 deletions
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c new file mode 100644 index 00000000..dcdb1566 --- /dev/null +++ b/src/cairo-pattern.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2002 University of Southern California + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <c99drn@cs.umu.se> + */ + +#include "cairoint.h" + +void +_cairo_pattern_init (cairo_pattern_t *pattern) +{ + pattern->ref_count = 1; + + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + + _cairo_color_init (&pattern->color); + + _cairo_matrix_init (&pattern->matrix); + + pattern->stops = NULL; + pattern->n_stops = 0; + + pattern->type = CAIRO_PATTERN_SOLID; + + pattern->source = NULL; + pattern->source_offset.x = 0.0; + pattern->source_offset.y = 0.0; +} + +cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) +{ + *pattern = *other; + + pattern->ref_count = 1; + + if (pattern->n_stops) { + pattern->stops = + malloc (sizeof (cairo_color_stop_t) * pattern->n_stops); + if (pattern->stops == NULL) + return CAIRO_STATUS_NO_MEMORY; + memcpy (pattern->stops, other->stops, + sizeof (cairo_color_stop_t) * other->n_stops); + } + + if (pattern->source) + cairo_surface_reference (other->source); + + if (pattern->type == CAIRO_PATTERN_SURFACE) + cairo_surface_reference (other->u.surface.surface); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_fini (cairo_pattern_t *pattern) +{ + if (pattern->n_stops) + free (pattern->stops); + + if (pattern->type == CAIRO_PATTERN_SURFACE) { + /* show_surface require us to restore surface matrix, repeat + attribute, filter type */ + if (pattern->source) { + cairo_surface_set_matrix (pattern->source, + &pattern->u.surface.save_matrix); + cairo_surface_set_repeat (pattern->source, + pattern->u.surface.save_repeat); + cairo_surface_set_filter (pattern->source, + pattern->u.surface.save_filter); + } + cairo_surface_destroy (pattern->u.surface.surface); + } + + if (pattern->source) + cairo_surface_destroy (pattern->source); +} + +void +_cairo_pattern_init_solid (cairo_pattern_t *pattern, + double red, double green, double blue) +{ + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SOLID; + _cairo_color_set_rgb (&pattern->color, red, green, blue); +} + +cairo_pattern_t * +_cairo_pattern_create_solid (double red, double green, double blue) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init_solid (pattern, red, green, blue); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SURFACE; + pattern->u.surface.surface = surface; + cairo_surface_reference (surface); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_LINEAR; + pattern->u.linear.point0.x = x0; + pattern->u.linear.point0.y = y0; + pattern->u.linear.point1.x = x1; + pattern->u.linear.point1.y = y1; + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + 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.center1.x = cx1; + pattern->u.radial.center1.y = cy1; + pattern->u.radial.radius1.dx = radius1; + pattern->u.radial.radius1.dy = radius1; + + return pattern; +} + +void +cairo_pattern_reference (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count++; +} + +void +cairo_pattern_destroy (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count--; + if (pattern->ref_count) + return; + + _cairo_pattern_fini (pattern); + free (pattern); +} + +static int +_cairo_pattern_stop_compare (const void *elem1, const void *elem2) +{ + return + (((cairo_color_stop_t *) elem1)->offset == + ((cairo_color_stop_t *) elem2)->offset) ? + /* equal offsets, sort on id */ + ((((cairo_color_stop_t *) elem1)->id < + ((cairo_color_stop_t *) elem2)->id) ? -1 : 1) : + /* sort on offset */ + ((((cairo_color_stop_t *) elem1)->offset < + ((cairo_color_stop_t *) elem2)->offset) ? -1 : 1); +} + +cairo_status_t +cairo_pattern_add_color_stop (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue, + double alpha) +{ + cairo_color_stop_t *stop; + + _cairo_restrict_value (&offset, 0.0, 1.0); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + + pattern->n_stops++; + pattern->stops = realloc (pattern->stops, + sizeof (cairo_color_stop_t) * pattern->n_stops); + if (pattern->stops == NULL) { + pattern->n_stops = 0; + + return CAIRO_STATUS_NO_MEMORY; + } + + stop = &pattern->stops[pattern->n_stops - 1]; + + stop->offset = offset; + stop->id = pattern->n_stops; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + + /* sort stops in ascending order */ + qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), + _cairo_pattern_stop_compare); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +{ + cairo_matrix_copy (&pattern->matrix, matrix); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +{ + cairo_matrix_copy (matrix, &pattern->matrix); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) +{ + pattern->filter = filter; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern) +{ + return pattern->filter; +} + +cairo_status_t +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) +{ + pattern->extend = extend; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern) +{ + return pattern->extend; +} + +cairo_status_t +_cairo_pattern_get_rgb (cairo_pattern_t *pattern, + double *red, double *green, double *blue) +{ + _cairo_color_get_rgb (&pattern->color, red, green, blue); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) +{ + int i; + + _cairo_color_set_alpha (&pattern->color, alpha); + + for (i = 0; i < pattern->n_stops; i++) { + cairo_color_stop_t *stop = &pattern->stops[i]; + + _cairo_color_set_alpha (&stop->color, stop->color.alpha * alpha); + + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + } +} + +void +_cairo_pattern_add_source_offset (cairo_pattern_t *pattern, + double x, double 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; + } +} + +void +_cairo_pattern_prepare_surface (cairo_pattern_t *pattern) +{ + cairo_matrix_t device_to_source; + cairo_matrix_t user_to_source; + + /* should the surface matrix interface be remove from the API? + for now we multiple the surface matrix with the pattern matrix */ + cairo_surface_get_matrix (pattern->u.surface.surface, &user_to_source); + cairo_matrix_multiply (&device_to_source, &pattern->matrix, + &user_to_source); + cairo_surface_set_matrix (pattern->source, &device_to_source); + + /* storing original surface matrix in pattern */ + pattern->u.surface.save_matrix = user_to_source; + + /* storing original surface repeat mode in pattern */ + pattern->u.surface.save_repeat = pattern->source->repeat; + + /* what do we do with extend types pad and reflect? */ + if (pattern->extend == CAIRO_EXTEND_REPEAT + || pattern->source->repeat == 1) + cairo_surface_set_repeat (pattern->source, 1); + else + cairo_surface_set_repeat (pattern->source, 0); + + /* storing original surface filter in pattern */ + pattern->u.surface.save_filter = + cairo_surface_get_filter (pattern->source); + + 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)) + +static void +_cairo_pattern_shader_nearest (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + 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); +} + +#undef INTERPOLATE_COLOR_NEAREST + +#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \ + ((unsigned char) ((c2 * factor) + (c1 * (1.0 - factor)))) + +static void +_cairo_pattern_shader_linear (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + 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); +} + +static void +_cairo_pattern_shader_gaussian (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + factor = (exp (factor * factor) - 1.0) / (M_E - 1.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); +} + +#undef INTERPOLATE_COLOR_LINEAR + +void +_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern, + double factor, + int *pixel) +{ + int p, colorstop; + double factorscale; + unsigned char result_color[4]; + cairo_shader_function_t shader_function; + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + shader_function = _cairo_pattern_shader_nearest; + break; + case CAIRO_FILTER_GAUSSIAN: + 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; + break; + } + + 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; + } + } + + 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; + + /* 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; + break; + } + } +} + +static void +_cairo_image_data_set_linear (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t point0, point1, angle; + double a, length, start, end; + double factor; + + 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; + + 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); + + start = angle.x * point0.x; + start += angle.y * point0.y; + + end = angle.x * point1.x; + end += angle.y * point1.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]); + } + } +} + +/* 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 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; + + min_length = 1.0 / min_length; + + 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; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +cairo_image_surface_t * +_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +{ + cairo_surface_t *surface; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + char *data; + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + + data = malloc (width * height * 4); + if (!data) + return NULL; + + _cairo_pattern_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _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); + else + _cairo_image_data_set_linear (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + + 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); + } + break; + case CAIRO_PATTERN_SOLID: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + if (surface) { + _cairo_surface_fill_rectangle (surface, + CAIRO_OPERATOR_SRC, + &pattern->color, 0, 0, 1, 1); + cairo_surface_set_repeat (surface, 1); + } + break; + case CAIRO_PATTERN_SURFACE: { + cairo_image_surface_t *image; + + image = _cairo_surface_get_image (pattern->u.surface.surface); + if (image) + surface = &image->base; + else + surface = NULL; + + } + break; + } + + return (cairo_image_surface_t *) surface; +} diff --git a/src/cairo_pattern.c b/src/cairo_pattern.c new file mode 100644 index 00000000..dcdb1566 --- /dev/null +++ b/src/cairo_pattern.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2002 University of Southern California + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <c99drn@cs.umu.se> + */ + +#include "cairoint.h" + +void +_cairo_pattern_init (cairo_pattern_t *pattern) +{ + pattern->ref_count = 1; + + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + + _cairo_color_init (&pattern->color); + + _cairo_matrix_init (&pattern->matrix); + + pattern->stops = NULL; + pattern->n_stops = 0; + + pattern->type = CAIRO_PATTERN_SOLID; + + pattern->source = NULL; + pattern->source_offset.x = 0.0; + pattern->source_offset.y = 0.0; +} + +cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) +{ + *pattern = *other; + + pattern->ref_count = 1; + + if (pattern->n_stops) { + pattern->stops = + malloc (sizeof (cairo_color_stop_t) * pattern->n_stops); + if (pattern->stops == NULL) + return CAIRO_STATUS_NO_MEMORY; + memcpy (pattern->stops, other->stops, + sizeof (cairo_color_stop_t) * other->n_stops); + } + + if (pattern->source) + cairo_surface_reference (other->source); + + if (pattern->type == CAIRO_PATTERN_SURFACE) + cairo_surface_reference (other->u.surface.surface); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_fini (cairo_pattern_t *pattern) +{ + if (pattern->n_stops) + free (pattern->stops); + + if (pattern->type == CAIRO_PATTERN_SURFACE) { + /* show_surface require us to restore surface matrix, repeat + attribute, filter type */ + if (pattern->source) { + cairo_surface_set_matrix (pattern->source, + &pattern->u.surface.save_matrix); + cairo_surface_set_repeat (pattern->source, + pattern->u.surface.save_repeat); + cairo_surface_set_filter (pattern->source, + pattern->u.surface.save_filter); + } + cairo_surface_destroy (pattern->u.surface.surface); + } + + if (pattern->source) + cairo_surface_destroy (pattern->source); +} + +void +_cairo_pattern_init_solid (cairo_pattern_t *pattern, + double red, double green, double blue) +{ + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SOLID; + _cairo_color_set_rgb (&pattern->color, red, green, blue); +} + +cairo_pattern_t * +_cairo_pattern_create_solid (double red, double green, double blue) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init_solid (pattern, red, green, blue); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SURFACE; + pattern->u.surface.surface = surface; + cairo_surface_reference (surface); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_LINEAR; + pattern->u.linear.point0.x = x0; + pattern->u.linear.point0.y = y0; + pattern->u.linear.point1.x = x1; + pattern->u.linear.point1.y = y1; + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init (pattern); + + 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.center1.x = cx1; + pattern->u.radial.center1.y = cy1; + pattern->u.radial.radius1.dx = radius1; + pattern->u.radial.radius1.dy = radius1; + + return pattern; +} + +void +cairo_pattern_reference (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count++; +} + +void +cairo_pattern_destroy (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count--; + if (pattern->ref_count) + return; + + _cairo_pattern_fini (pattern); + free (pattern); +} + +static int +_cairo_pattern_stop_compare (const void *elem1, const void *elem2) +{ + return + (((cairo_color_stop_t *) elem1)->offset == + ((cairo_color_stop_t *) elem2)->offset) ? + /* equal offsets, sort on id */ + ((((cairo_color_stop_t *) elem1)->id < + ((cairo_color_stop_t *) elem2)->id) ? -1 : 1) : + /* sort on offset */ + ((((cairo_color_stop_t *) elem1)->offset < + ((cairo_color_stop_t *) elem2)->offset) ? -1 : 1); +} + +cairo_status_t +cairo_pattern_add_color_stop (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue, + double alpha) +{ + cairo_color_stop_t *stop; + + _cairo_restrict_value (&offset, 0.0, 1.0); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + + pattern->n_stops++; + pattern->stops = realloc (pattern->stops, + sizeof (cairo_color_stop_t) * pattern->n_stops); + if (pattern->stops == NULL) { + pattern->n_stops = 0; + + return CAIRO_STATUS_NO_MEMORY; + } + + stop = &pattern->stops[pattern->n_stops - 1]; + + stop->offset = offset; + stop->id = pattern->n_stops; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + + /* sort stops in ascending order */ + qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), + _cairo_pattern_stop_compare); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +{ + cairo_matrix_copy (&pattern->matrix, matrix); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +{ + cairo_matrix_copy (matrix, &pattern->matrix); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) +{ + pattern->filter = filter; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern) +{ + return pattern->filter; +} + +cairo_status_t +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) +{ + pattern->extend = extend; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern) +{ + return pattern->extend; +} + +cairo_status_t +_cairo_pattern_get_rgb (cairo_pattern_t *pattern, + double *red, double *green, double *blue) +{ + _cairo_color_get_rgb (&pattern->color, red, green, blue); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) +{ + int i; + + _cairo_color_set_alpha (&pattern->color, alpha); + + for (i = 0; i < pattern->n_stops; i++) { + cairo_color_stop_t *stop = &pattern->stops[i]; + + _cairo_color_set_alpha (&stop->color, stop->color.alpha * alpha); + + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + } +} + +void +_cairo_pattern_add_source_offset (cairo_pattern_t *pattern, + double x, double 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; + } +} + +void +_cairo_pattern_prepare_surface (cairo_pattern_t *pattern) +{ + cairo_matrix_t device_to_source; + cairo_matrix_t user_to_source; + + /* should the surface matrix interface be remove from the API? + for now we multiple the surface matrix with the pattern matrix */ + cairo_surface_get_matrix (pattern->u.surface.surface, &user_to_source); + cairo_matrix_multiply (&device_to_source, &pattern->matrix, + &user_to_source); + cairo_surface_set_matrix (pattern->source, &device_to_source); + + /* storing original surface matrix in pattern */ + pattern->u.surface.save_matrix = user_to_source; + + /* storing original surface repeat mode in pattern */ + pattern->u.surface.save_repeat = pattern->source->repeat; + + /* what do we do with extend types pad and reflect? */ + if (pattern->extend == CAIRO_EXTEND_REPEAT + || pattern->source->repeat == 1) + cairo_surface_set_repeat (pattern->source, 1); + else + cairo_surface_set_repeat (pattern->source, 0); + + /* storing original surface filter in pattern */ + pattern->u.surface.save_filter = + cairo_surface_get_filter (pattern->source); + + 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)) + +static void +_cairo_pattern_shader_nearest (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + 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); +} + +#undef INTERPOLATE_COLOR_NEAREST + +#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \ + ((unsigned char) ((c2 * factor) + (c1 * (1.0 - factor)))) + +static void +_cairo_pattern_shader_linear (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + 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); +} + +static void +_cairo_pattern_shader_gaussian (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + factor = (exp (factor * factor) - 1.0) / (M_E - 1.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); +} + +#undef INTERPOLATE_COLOR_LINEAR + +void +_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern, + double factor, + int *pixel) +{ + int p, colorstop; + double factorscale; + unsigned char result_color[4]; + cairo_shader_function_t shader_function; + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + shader_function = _cairo_pattern_shader_nearest; + break; + case CAIRO_FILTER_GAUSSIAN: + 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; + break; + } + + 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; + } + } + + 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; + + /* 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; + break; + } + } +} + +static void +_cairo_image_data_set_linear (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t point0, point1, angle; + double a, length, start, end; + double factor; + + 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; + + 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); + + start = angle.x * point0.x; + start += angle.y * point0.y; + + end = angle.x * point1.x; + end += angle.y * point1.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]); + } + } +} + +/* 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 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; + + min_length = 1.0 / min_length; + + 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; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +cairo_image_surface_t * +_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +{ + cairo_surface_t *surface; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + char *data; + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + + data = malloc (width * height * 4); + if (!data) + return NULL; + + _cairo_pattern_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _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); + else + _cairo_image_data_set_linear (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + + 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); + } + break; + case CAIRO_PATTERN_SOLID: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + if (surface) { + _cairo_surface_fill_rectangle (surface, + CAIRO_OPERATOR_SRC, + &pattern->color, 0, 0, 1, 1); + cairo_surface_set_repeat (surface, 1); + } + break; + case CAIRO_PATTERN_SURFACE: { + cairo_image_surface_t *image; + + image = _cairo_surface_get_image (pattern->u.surface.surface); + if (image) + surface = &image->base; + else + surface = NULL; + + } + break; + } + + return (cairo_image_surface_t *) surface; +} |