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.c1228
1 files changed, 901 insertions, 327 deletions
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 6cb981458..283c36dbd 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -21,58 +21,108 @@
* 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>
+ * Author: David Reveman <davidr@novell.com>
*/
#include "cairoint.h"
+typedef void (*cairo_shader_function_t) (unsigned char *color0,
+ unsigned char *color1,
+ cairo_fixed_t factor,
+ uint32_t *pixel);
+
+typedef struct _cairo_shader_color_stop {
+ cairo_fixed_t offset;
+ cairo_fixed_48_16_t scale;
+ int id;
+ unsigned char color_char[4];
+} cairo_shader_color_stop_t;
+
+typedef struct _cairo_shader_op {
+ cairo_shader_color_stop_t *stops;
+ int n_stops;
+ cairo_extend_t extend;
+ cairo_shader_function_t shader_function;
+} cairo_shader_op_t;
+
#define MULTIPLY_COLORCOMP(c1, c2) \
((unsigned char) \
((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff))
-void
-_cairo_pattern_init (cairo_pattern_t *pattern)
+static void
+_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
{
+ pattern->type = type;
pattern->ref_count = 1;
-
- pattern->extend = CAIRO_EXTEND_DEFAULT;
- pattern->filter = CAIRO_FILTER_DEFAULT;
-
- _cairo_color_init (&pattern->color);
+ pattern->extend = CAIRO_EXTEND_DEFAULT;
+ pattern->filter = CAIRO_FILTER_DEFAULT;
+ pattern->alpha = 1.0;
_cairo_matrix_init (&pattern->matrix);
+}
- pattern->stops = NULL;
- pattern->n_stops = 0;
+static cairo_status_t
+_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
+ cairo_gradient_pattern_t *other)
+{
+ if (other->base.type == CAIRO_PATTERN_LINEAR)
+ {
+ cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
+ cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
+
+ *dst = *src;
+ }
+ else
+ {
+ cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
+ cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
+
+ *dst = *src;
+ }
- pattern->type = CAIRO_PATTERN_SOLID;
+ if (other->n_stops)
+ {
+ pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t));
+ if (!pattern->stops)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ memcpy (pattern->stops, other->stops,
+ other->n_stops * sizeof (cairo_color_stop_t));
+ }
- pattern->source = NULL;
- pattern->source_offset.x = 0.0;
- pattern->source_offset.y = 0.0;
+ return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other)
{
- *pattern = *other;
-
- pattern->ref_count = 1;
+ switch (other->type) {
+ case CAIRO_PATTERN_SOLID: {
+ cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
+ cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
- 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);
+ *dst = *src;
+ } break;
+ case CAIRO_PATTERN_SURFACE: {
+ cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
+ cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
+
+ *dst = *src;
+ cairo_surface_reference (dst->surface);
+ } break;
+ case CAIRO_PATTERN_LINEAR:
+ case CAIRO_PATTERN_RADIAL: {
+ cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
+ cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
+ cairo_status_t status;
+
+ status = _cairo_gradient_pattern_init_copy (dst, src);
+ if (status)
+ return status;
+ } break;
}
-
- if (pattern->source)
- cairo_surface_reference (other->source);
-
- if (pattern->type == CAIRO_PATTERN_SURFACE)
- cairo_surface_reference (other->u.surface.surface);
+
+ pattern->ref_count = 1;
return CAIRO_STATUS_SUCCESS;
}
@@ -80,110 +130,145 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other)
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);
+ switch (pattern->type) {
+ case CAIRO_PATTERN_SOLID:
+ break;
+ case CAIRO_PATTERN_SURFACE: {
+ cairo_surface_pattern_t *fini = (cairo_surface_pattern_t *) pattern;
+
+ cairo_surface_destroy (fini->surface);
+ } break;
+ case CAIRO_PATTERN_LINEAR:
+ case CAIRO_PATTERN_RADIAL: {
+ cairo_gradient_pattern_t *fini = (cairo_gradient_pattern_t *) pattern;
+
+ if (fini->n_stops)
+ free (fini->stops);
+ } break;
}
+}
+
+void
+_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
+ double red,
+ double green,
+ double blue)
+{
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID);
- if (pattern->source)
- cairo_surface_destroy (pattern->source);
+ pattern->red = red;
+ pattern->green = green;
+ pattern->blue = blue;
}
void
-_cairo_pattern_init_solid (cairo_pattern_t *pattern,
- double red, double green, double blue)
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+ cairo_surface_t *surface)
{
- _cairo_pattern_init (pattern);
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE);
+
+ pattern->surface = surface;
+ cairo_surface_reference (surface);
+}
- pattern->type = CAIRO_PATTERN_SOLID;
- _cairo_color_set_rgb (&pattern->color, red, green, blue);
+static void
+_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
+ cairo_pattern_type_t type)
+{
+ _cairo_pattern_init (&pattern->base, type);
+
+ pattern->stops = 0;
+ pattern->n_stops = 0;
+}
+
+void
+_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
+ double x0, double y0, double x1, double y1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_LINEAR);
+
+ pattern->point0.x = x0;
+ pattern->point0.y = y0;
+ pattern->point1.x = x1;
+ pattern->point1.y = y1;
+}
+
+void
+_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
+ double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_RADIAL);
+
+ pattern->center0.x = cx0;
+ pattern->center0.y = cy0;
+ pattern->radius0 = fabs (radius0);
+ pattern->center1.x = cx1;
+ pattern->center1.y = cy1;
+ pattern->radius1 = fabs (radius1);
}
cairo_pattern_t *
_cairo_pattern_create_solid (double red, double green, double blue)
{
- cairo_pattern_t *pattern;
+ cairo_solid_pattern_t *pattern;
- pattern = malloc (sizeof (cairo_pattern_t));
+ pattern = malloc (sizeof (cairo_solid_pattern_t));
if (pattern == NULL)
return NULL;
_cairo_pattern_init_solid (pattern, red, green, blue);
- return pattern;
+ return &pattern->base;
}
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
{
- cairo_pattern_t *pattern;
+ cairo_surface_pattern_t *pattern;
- pattern = malloc (sizeof (cairo_pattern_t));
+ pattern = malloc (sizeof (cairo_surface_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);
+ _cairo_pattern_init_for_surface (pattern, surface);
+
+ /* this will go away when we completely remove the surface attributes */
+ if (surface->repeat)
+ pattern->base.extend = CAIRO_EXTEND_REPEAT;
+ else
+ pattern->base.extend = CAIRO_EXTEND_DEFAULT;
- return pattern;
+ return &pattern->base;
}
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
{
- cairo_pattern_t *pattern;
+ cairo_linear_pattern_t *pattern;
- pattern = malloc (sizeof (cairo_pattern_t));
+ pattern = malloc (sizeof (cairo_linear_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;
+ _cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
- return pattern;
+ return &pattern->base.base;
}
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
{
- cairo_pattern_t *pattern;
+ cairo_radial_pattern_t *pattern;
- pattern = malloc (sizeof (cairo_pattern_t));
+ pattern = malloc (sizeof (cairo_radial_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 = fabs (radius0);
- pattern->u.radial.center1.x = cx1;
- pattern->u.radial.center1.y = cy1;
- pattern->u.radial.radius1 = fabs (radius1);
+ _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
- return pattern;
+ return &pattern->base.base;
}
void
@@ -209,37 +294,19 @@ cairo_pattern_destroy (cairo_pattern_t *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)
+static cairo_status_t
+_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue,
+ 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);
- _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);
+ pattern->n_stops * sizeof (cairo_color_stop_t));
if (pattern->stops == NULL) {
pattern->n_stops = 0;
@@ -249,41 +316,51 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern,
stop = &pattern->stops[pattern->n_stops - 1];
stop->offset = _cairo_fixed_from_double (offset);
- stop->id = pattern->n_stops;
- stop->color_char[0] = red * 0xff;
- stop->color_char[1] = green * 0xff;
- stop->color_char[2] = blue * 0xff;
- stop->color_char[3] = alpha * 0xff;
+ _cairo_color_init (&stop->color);
+ _cairo_color_set_rgb (&stop->color, red, green, blue);
+ _cairo_color_set_alpha (&stop->color, alpha);
- /* sort stops in ascending order */
- 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;
+}
+
+cairo_status_t
+cairo_pattern_add_color_stop (cairo_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ if (pattern->type != CAIRO_PATTERN_LINEAR &&
+ pattern->type != CAIRO_PATTERN_RADIAL)
+ {
+ /* XXX: CAIRO_STATUS_INVALID_PATTERN? */
+ return CAIRO_STATUS_SUCCESS;
}
- return CAIRO_STATUS_SUCCESS;
+ _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);
+ _cairo_restrict_value (&alpha, 0.0, 1.0);
+
+ return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
+ offset,
+ red, green, blue,
+ alpha);
}
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;
+ return cairo_matrix_copy (&pattern->matrix, matrix);
}
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;
+ return cairo_matrix_copy (matrix, &pattern->matrix);
}
cairo_status_t
@@ -316,9 +393,20 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern)
cairo_status_t
_cairo_pattern_get_rgb (cairo_pattern_t *pattern,
- double *red, double *green, double *blue)
+ double *red,
+ double *green,
+ double *blue)
{
- _cairo_color_get_rgb (&pattern->color, red, green, blue);
+
+ if (pattern->type == CAIRO_PATTERN_SOLID)
+ {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+
+ *red = solid->red;
+ *green = solid->green;
+ *blue = solid->blue;
+ } else
+ *red = *green = *blue = 1.0;
return CAIRO_STATUS_SUCCESS;
}
@@ -326,63 +414,16 @@ _cairo_pattern_get_rgb (cairo_pattern_t *pattern,
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++)
- pattern->stops[i].color_char[3] =
- MULTIPLY_COLORCOMP (pattern->stops[i].color_char[3], alpha * 0xff);
-}
-
-void
-_cairo_pattern_set_source_offset (cairo_pattern_t *pattern,
- double x, double y)
-{
- pattern->source_offset.x = x;
- pattern->source_offset.y = y;
+ pattern->alpha = alpha;
}
void
_cairo_pattern_transform (cairo_pattern_t *pattern,
- cairo_matrix_t *ctm_inverse)
+ cairo_matrix_t *ctm_inverse)
{
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
}
-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);
-}
-
#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \
((factor < 32768)? c1: c2)
@@ -390,7 +431,7 @@ static void
_cairo_pattern_shader_nearest (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
- int *pixel)
+ uint32_t *pixel)
{
*pixel =
((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) |
@@ -408,7 +449,7 @@ static void
_cairo_pattern_shader_linear (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
- int *pixel)
+ uint32_t *pixel)
{
*pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
(INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
@@ -422,7 +463,7 @@ static void
_cairo_pattern_shader_gaussian (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
- int *pixel)
+ uint32_t *pixel)
{
double f = ((double) factor) / 65536.0;
@@ -436,17 +477,59 @@ _cairo_pattern_shader_gaussian (unsigned char *color0,
#undef INTERPOLATE_COLOR_LINEAR
-void
-_cairo_pattern_shader_init (cairo_pattern_t *pattern,
- cairo_shader_op_t *op)
-{
- 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) {
+static int
+_cairo_shader_color_stop_compare (const void *elem1, const void *elem2)
+{
+ cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1;
+ cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2;
+
+ return
+ (s1->offset == s2->offset) ?
+ /* equal offsets, sort on id */
+ ((s1->id < s2->id) ? -1 : 1) :
+ /* sort on offset */
+ ((s1->offset < s2->offset) ? -1 : 1);
+}
+
+static cairo_status_t
+_cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern,
+ cairo_shader_op_t *op)
+{
+ int i;
+
+ op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t));
+ if (!op->stops)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ for (i = 0; i < pattern->n_stops; i++)
+ {
+ op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff;
+ op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff;
+ op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff;
+ op->stops[i].color_char[3] = pattern->stops[i].color.alpha *
+ pattern->base.alpha * 0xff;
+ op->stops[i].offset = pattern->stops[i].offset;
+ op->stops[i].id = i;
+ }
+
+ /* sort stops in ascending order */
+ qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t),
+ _cairo_shader_color_stop_compare);
+
+ for (i = 0; i < pattern->n_stops - 1; i++)
+ {
+ op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset;
+ if (op->stops[i + 1].scale == 65536)
+ op->stops[i + 1].scale = 0;
+ }
+
+ op->n_stops = pattern->n_stops;
+ op->extend = pattern->base.extend;
+
+ /* XXX: this is wrong, the filter should not be used for selecting
+ color stop interpolation function. function should always be 'linear'
+ and filter should be used for computing pixels. */
+ switch (pattern->base.filter) {
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
op->shader_function = _cairo_pattern_shader_nearest;
@@ -460,15 +543,56 @@ _cairo_pattern_shader_init (cairo_pattern_t *pattern,
op->shader_function = _cairo_pattern_shader_linear;
break;
}
+
+ return CAIRO_STATUS_SUCCESS;
}
-void
-_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
- cairo_fixed_t factor,
- int *pixel)
+static void
+_cairo_pattern_shader_fini (cairo_shader_op_t *op)
+{
+ if (op->stops)
+ free (op->stops);
+}
+
+/* Find two color stops bounding the given offset. If the given offset
+ * is before the first or after the last stop offset, the nearest
+ * offset is returned twice.
+ */
+static void
+_cairo_shader_op_find_color_stops (cairo_shader_op_t *op,
+ cairo_fixed_t offset,
+ cairo_shader_color_stop_t *stops[2])
{
int i;
-
+
+ /* Before first stop. */
+ if (offset <= op->stops[0].offset) {
+ stops[0] = &op->stops[0];
+ stops[1] = &op->stops[0];
+ return;
+ }
+
+ /* Between two stops. */
+ for (i = 0; i < op->n_stops - 1; i++) {
+ if (offset <= op->stops[i + 1].offset) {
+ stops[0] = &op->stops[i];
+ stops[1] = &op->stops[i + 1];
+ return;
+ }
+ }
+
+ /* After last stop. */
+ stops[0] = &op->stops[op->n_stops - 1];
+ stops[1] = &op->stops[op->n_stops - 1];
+}
+
+static void
+_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
+ cairo_fixed_t factor,
+ uint32_t *pixel)
+{
+ cairo_shader_color_stop_t *stops[2];
+
switch (op->extend) {
case CAIRO_EXTEND_REPEAT:
factor -= factor & 0xffff0000;
@@ -485,96 +609,158 @@ _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
break;
}
- 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 -= 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;
+ _cairo_shader_op_find_color_stops (op, factor, stops);
+
+ /* take offset as new 0 of coordinate system */
+ factor -= stops[0]->offset;
- op->shader_function (op->stops[i].color_char,
- op->stops[i + 1].color_char,
- factor, pixel);
+ /* difference between two offsets == 0, abrubt change */
+ if (stops[1]->scale)
+ factor = ((cairo_fixed_48_16_t) factor << 16) /
+ stops[1]->scale;
+
+ op->shader_function (stops[0]->color_char,
+ stops[1]->color_char,
+ factor, pixel);
- /* multiply alpha */
- if (((unsigned char) (*pixel >> 24)) != 0xff) {
- *pixel = (*pixel & 0xff000000) |
- (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) |
- (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) |
- (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0);
- }
- break;
- }
+ /* multiply alpha */
+ if (((unsigned char) (*pixel >> 24)) != 0xff) {
+ *pixel = (*pixel & 0xff000000) |
+ (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) |
+ (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) |
+ (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0);
}
}
-static void
-_cairo_image_data_set_linear (cairo_pattern_t *pattern,
- double offset_x,
- double offset_y,
- int *pixels,
- int width,
- int height)
+static cairo_status_t
+_cairo_image_data_set_linear (cairo_linear_pattern_t *pattern,
+ double offset_x,
+ double offset_y,
+ uint32_t *pixels,
+ int width,
+ int height)
{
int x, y;
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;
+ double scale, start, dx, dy, factor;
cairo_shader_op_t op;
-
- _cairo_pattern_shader_init (pattern, &op);
-
- 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;
-
- angle = -atan2 (point1.y - point0.y, point1.x - point0.x);
- fx = cos (angle);
- fy = -sin (angle);
-
- start = fx * point0.x;
- start += fy * point0.y;
+ cairo_status_t status;
+
+ status = _cairo_pattern_shader_init (&pattern->base, &op);
+ if (status)
+ return status;
+
+ /* We compute the position in the linear gradient for
+ * a point q as:
+ *
+ * [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2
+ *
+ * The computation is done in pattern space. The
+ * calculation could be heavily optimized by using the
+ * fact that 'factor' increases linearly in both
+ * directions.
+ */
+ point0.x = pattern->point0.x;
+ point0.y = pattern->point0.y;
+ point1.x = pattern->point1.x;
+ point1.y = pattern->point1.y;
+
+ cairo_matrix_get_affine (&pattern->base.base.matrix,
+ &a, &b, &c, &d, &tx, &ty);
+
+ dx = point1.x - point0.x;
+ dy = point1.y - point0.y;
+ scale = dx * dx + dy * dy;
+ scale = (scale) ? 1.0 / scale : 1.0;
+
+ start = dx * point0.x + dy * point0.y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
- px = x + offset_x;
- py = y + offset_y;
+ double qx_device = x + offset_x;
+ double qy_device = y + offset_y;
- /* transform fragment */
- ex = a * px + c * py + tx;
- ey = b * px + d * py + ty;
+ /* transform fragment into pattern space */
+ double qx = a * qx_device + c * qy_device + tx;
+ double qy = b * qx_device + d * qy_device + ty;
- factor = ((fx * ex + fy * ey) - start) * length;
+ factor = ((dx * qx + dy * qy) - start) * scale;
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
+
+ _cairo_pattern_shader_fini (&op);
+
+ return CAIRO_STATUS_SUCCESS;
}
static void
-_cairo_image_data_set_radial (cairo_pattern_t *pattern,
- double offset_x,
- double offset_y,
- int *pixels,
- int width,
- int height)
+_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
+ double offset_x,
+ double offset_y,
+ int width,
+ int height,
+ cairo_bool_t *is_horizontal,
+ cairo_bool_t *is_vertical)
+{
+ cairo_point_double_t point0, point1;
+ double a, b, c, d, tx, ty;
+ double scale, start, dx, dy;
+ cairo_fixed_t factors[3];
+ int i;
+
+ /* To classidy a pattern as horizontal or vertical, we first
+ * compute the (fixed point) factors at the corners of the
+ * pattern. We actually only need 3/4 corners, so we skip the
+ * fourth.
+ */
+ point0.x = pattern->point0.x;
+ point0.y = pattern->point0.y;
+ point1.x = pattern->point1.x;
+ point1.y = pattern->point1.y;
+
+ cairo_matrix_get_affine (&pattern->base.base.matrix,
+ &a, &b, &c, &d, &tx, &ty);
+
+ dx = point1.x - point0.x;
+ dy = point1.y - point0.y;
+ scale = dx * dx + dy * dy;
+ scale = (scale) ? 1.0 / scale : 1.0;
+
+ start = dx * point0.x + dy * point0.y;
+
+ for (i = 0; i < 3; i++) {
+ double qx_device = (i % 2) * (width - 1) + offset_x;
+ double qy_device = (i / 2) * (height - 1) + offset_y;
+
+ /* transform fragment into pattern space */
+ double qx = a * qx_device + c * qy_device + tx;
+ double qy = b * qx_device + d * qy_device + ty;
+
+ factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
+ }
+
+ /* We consider a pattern to be vertical if the fixed point factor
+ * at the two upper corners is the same. We could accept a small
+ * change, but determining what change is acceptable would require
+ * sorting the stops in the pattern and looking at the differences.
+ *
+ * Horizontal works the same way with the two left corners.
+ */
+
+ *is_vertical = factors[1] == factors[0];
+ *is_horizontal = factors[2] == factors[0];
+}
+
+static cairo_status_t
+_cairo_image_data_set_radial (cairo_radial_pattern_t *pattern,
+ double offset_x,
+ double offset_y,
+ uint32_t *pixels,
+ int width,
+ int height)
{
int x, y, aligned_circles;
cairo_point_double_t c0, c1;
@@ -584,15 +770,18 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern,
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_status_t status;
- _cairo_pattern_shader_init (pattern, &op);
+ status = _cairo_pattern_shader_init (&pattern->base, &op);
+ if (status)
+ return status;
- 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;
+ c0.x = pattern->center0.x;
+ c0.y = pattern->center0.y;
+ r0 = pattern->radius0;
+ c1.x = pattern->center1.x;
+ c1.y = pattern->center1.y;
+ r1 = pattern->radius1;
if (c0.x != c1.x || c0.y != c1.y) {
aligned_circles = 0;
@@ -606,7 +795,8 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern,
r1_2 = c0_c1 = 0.0; /* shut up compiler */
}
- cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty);
+ cairo_matrix_get_affine (&pattern->base.base.matrix,
+ &a, &b, &c, &d, &tx, &ty);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
@@ -682,70 +872,454 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern,
_cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
}
}
+
+ _cairo_pattern_shader_fini (&op);
+
+ return CAIRO_STATUS_SUCCESS;
}
-cairo_image_surface_t *
-_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box)
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attr)
{
- cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ uint32_t *data;
+ cairo_bool_t repeat = FALSE;
+
+ if (pattern->base.type == CAIRO_PATTERN_LINEAR) {
+ cairo_bool_t is_horizontal;
+ cairo_bool_t is_vertical;
+
+ _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
+ x, y, width, height,
+ &is_horizontal, &is_vertical);
+ if (is_horizontal) {
+ height = 1;
+ repeat = TRUE;
+ }
+ if (is_vertical) {
+ width = 1;
+ repeat = TRUE;
+ }
+ }
+
+ data = malloc (width * height * 4);
+ if (!data)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ if (pattern->base.type == CAIRO_PATTERN_LINEAR)
+ {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
+
+ status = _cairo_image_data_set_linear (linear, x, y, data,
+ width, height);
+ }
+ else
+ {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+
+ status = _cairo_image_data_set_radial (radial, x, y, data,
+ width, height);
+ }
- switch (pattern->type) {
- case CAIRO_PATTERN_LINEAR:
- case CAIRO_PATTERN_RADIAL: {
- char *data;
- 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);
+ if (status) {
+ free (data);
+ return status;
+ }
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data ((char *) data,
+ CAIRO_FORMAT_ARGB32,
+ width, height,
+ width * 4);
+
+ if (image == NULL) {
+ free (data);
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ _cairo_image_surface_assume_ownership_of_data (image);
+
+ status = _cairo_surface_clone_similar (dst, &image->base, out);
+
+ cairo_surface_destroy (&image->base);
+
+ attr->x_offset = -x;
+ attr->y_offset = -y;
+ cairo_matrix_set_identity (&attr->matrix);
+ attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
+ attr->filter = CAIRO_FILTER_NEAREST;
+ attr->acquired = FALSE;
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attribs)
+{
+ cairo_color_t color;
+
+ _cairo_color_init (&color);
+ _cairo_color_set_rgb (&color, pattern->red, pattern->green, pattern->blue);
+ _cairo_color_set_alpha (&color, pattern->base.alpha);
+
+ *out = _cairo_surface_create_similar_solid (dst,
+ CAIRO_FORMAT_ARGB32,
+ 1, 1,
+ &color);
+
+ if (*out == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ attribs->x_offset = attribs->y_offset = 0;
+ cairo_matrix_set_identity (&attribs->matrix);
+ attribs->extend = CAIRO_EXTEND_REPEAT;
+ attribs->filter = CAIRO_FILTER_NEAREST;
+ attribs->acquired = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/**
+ * _cairo_pattern_is_opaque
+ *
+ * Convenience function to determine whether a pattern has an opaque
+ * alpha value. This is done by testing whether the pattern's alpha
+ * value when converted to a byte is 255, so if a backend actually
+ * supported deep alpha channels this function might not do the right
+ * thing.
+ *
+ * Note that for a gradient or surface pattern, the overall resulting
+ * alpha for the pattern can be non-opaque even this function returns
+ * %TRUE, since the resulting alpha is the multiplication of the
+ * alpha of the gradient or surface with the pattern's alpha. In
+ * the future, alpha will be moved from the base pattern to the
+ * solid pattern subtype, at which point this function should
+ * probably be renamed to _cairo_pattern_is_opaque_solid()
+ *
+ * Return value: %TRUE if the pattern is opaque
+ **/
+cairo_bool_t
+_cairo_pattern_is_opaque (cairo_pattern_t *pattern)
+{
+ return (pattern->alpha >= ((double)0xff00 / (double)0xffff));
+}
+
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attr)
+{
+ cairo_int_status_t status;
+
+ attr->acquired = FALSE;
+
+ /* handle pattern opacity */
+ if (!_cairo_pattern_is_opaque (&pattern->base))
+ {
+ cairo_surface_pattern_t tmp;
+ cairo_color_t color;
+
+ _cairo_color_init (&color);
+ _cairo_color_set_alpha (&color, pattern->base.alpha);
+
+ *out = _cairo_surface_create_similar_solid (dst,
+ CAIRO_FORMAT_ARGB32,
+ width, height,
+ &color);
+ if (*out == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ status = _cairo_pattern_init_copy (&tmp.base, &pattern->base);
+ if (CAIRO_OK (status))
+ {
+ tmp.base.alpha = 1.0;
+ status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
+ &tmp.base,
+ NULL,
+ *out,
+ x, y, 0, 0, 0, 0,
+ width, height);
+
+ _cairo_pattern_fini (&tmp.base);
+ }
+
+ if (status) {
+ cairo_surface_destroy (*out);
+ return status;
+ }
- data = malloc (width * height * 4);
- if (!data)
- return NULL;
+ attr->x_offset = -x;
+ attr->y_offset = -y;
+ attr->extend = CAIRO_EXTEND_NONE;
+ attr->filter = CAIRO_FILTER_NEAREST;
+
+ cairo_matrix_set_identity (&attr->matrix);
+ }
+ else
+ {
+ int tx, ty;
+
+ if (_cairo_surface_is_image (dst))
+ {
+ cairo_image_surface_t *image;
+
+ status = _cairo_surface_acquire_source_image (pattern->surface,
+ &image,
+ &attr->extra);
+ if (CAIRO_OK (status))
+ *out = &image->base;
+
+ attr->acquired = TRUE;
+ }
+ else
+ status = _cairo_surface_clone_similar (dst, pattern->surface, out);
- if (pattern->type == CAIRO_PATTERN_RADIAL)
- _cairo_image_data_set_radial (pattern, x, y, (int *) data,
- width, height);
+ attr->extend = pattern->base.extend;
+ attr->filter = pattern->base.filter;
+ if (_cairo_matrix_is_integer_translation (&pattern->base.matrix,
+ &tx, &ty))
+ {
+ cairo_matrix_set_identity (&attr->matrix);
+ attr->x_offset = tx;
+ attr->y_offset = ty;
+ }
else
- _cairo_image_data_set_linear (pattern, x, y, (int *) data,
- width, height);
+ {
+ attr->matrix = pattern->base.matrix;
+ attr->x_offset = attr->y_offset = 0;
+ }
+ }
+
+ return status;
+}
- _cairo_pattern_set_source_offset (pattern, x, y);
+/**
+ * _cairo_pattern_acquire_surface:
+ * @pattern: a #cairo_pattern_t
+ * @dst: destination surface
+ * @x: X coordinate in source corresponding to left side of destination area
+ * @y: Y coordinate in source corresponding to top side of destination area
+ * @width: width of destination area
+ * @height: height of destination area
+ * @surface_out: location to store a pointer to a surface
+ * @attributes: surface attributes that destination backend should apply to
+ * the returned surface
+ *
+ * A convenience function to obtain a surface to use as the source for
+ * drawing on @dst.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out.
+ **/
+cairo_int_status_t
+_cairo_pattern_acquire_surface (cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **surface_out,
+ cairo_surface_attributes_t *attributes)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_SOLID: {
+ cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern;
+
+ return _cairo_pattern_acquire_surface_for_solid (src, dst,
+ x, y, width, height,
+ surface_out,
+ attributes);
+ } break;
+ case CAIRO_PATTERN_LINEAR:
+ case CAIRO_PATTERN_RADIAL: {
+ cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
+
+ /* fast path for gradients with less than 2 color stops */
+ if (src->n_stops < 2)
+ {
+ cairo_solid_pattern_t solid;
+
+ if (src->n_stops)
+ {
+ _cairo_pattern_init_solid (&solid,
+ src->stops->color.red,
+ src->stops->color.green,
+ src->stops->color.blue);
+ _cairo_pattern_set_alpha (&solid.base,
+ src->stops->color.alpha);
+ }
+ else
+ {
+ _cairo_pattern_init_solid (&solid, 0.0, 0.0, 0.0);
+ _cairo_pattern_set_alpha (&solid.base, 0.0);
+ }
- surface = cairo_image_surface_create_for_data (data,
- CAIRO_FORMAT_ARGB32,
- width, height,
- width * 4);
+ return _cairo_pattern_acquire_surface_for_solid (&solid, dst,
+ x, y,
+ width, height,
+ surface_out,
+ attributes);
+ }
+ else
+ return _cairo_pattern_acquire_surface_for_gradient (src, dst,
+ x, y,
+ width, height,
+ surface_out,
+ attributes);
+ } break;
+ case CAIRO_PATTERN_SURFACE: {
+ cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern;
- if (surface)
- _cairo_image_surface_assume_ownership_of_data (
- (cairo_image_surface_t *) surface);
+ return _cairo_pattern_acquire_surface_for_surface (src, dst,
+ x, y, width, height,
+ surface_out,
+ attributes);
+ } break;
}
- 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);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/**
+ * _cairo_pattern_release_surface:
+ * @pattern: a #cairo_pattern_t
+ * @info: pointer to #cairo_surface_attributes_t filled in by
+ * _cairo_pattern_acquire_surface
+ *
+ * Releases resources obtained by _cairo_pattern_acquire_surface.
+ **/
+void
+_cairo_pattern_release_surface (cairo_surface_t *dst,
+ cairo_surface_t *surface,
+ cairo_surface_attributes_t *attributes)
+{
+ if (attributes->acquired)
+ _cairo_surface_release_source_image (dst,
+ (cairo_image_surface_t *) surface,
+ attributes->extra);
+ else
+ cairo_surface_destroy (surface);
+}
+
+cairo_int_status_t
+_cairo_pattern_acquire_surfaces (cairo_pattern_t *src,
+ cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **src_out,
+ cairo_surface_t **mask_out,
+ cairo_surface_attributes_t *src_attributes,
+ cairo_surface_attributes_t *mask_attributes)
+{
+ cairo_int_status_t status;
+
+ cairo_pattern_union_t tmp;
+ cairo_bool_t src_opaque, mask_opaque;
+ double src_alpha, mask_alpha;
+
+ src_opaque = _cairo_pattern_is_opaque (src);
+ mask_opaque = !mask || _cairo_pattern_is_opaque (mask);
+
+ /* For surface patterns, we move any translucency from src->alpha
+ * to mask->alpha so we can use the source unchanged. Otherwise we
+ * move the translucency from mask->alpha to src->alpha so that
+ * we can drop the mask if possible.
+ */
+ if (src->type == CAIRO_PATTERN_SURFACE)
+ {
+ if (mask) {
+ mask_opaque = mask_opaque && src_opaque;
+ mask_alpha = mask->alpha * src->alpha;
+ } else {
+ mask_opaque = src_opaque;
+ mask_alpha = src->alpha;
}
- break;
- case CAIRO_PATTERN_SURFACE: {
- cairo_image_surface_t *image;
+
+ src_alpha = 1.0;
+ src_opaque = TRUE;
+ }
+ else
+ {
+ if (mask)
+ {
+ src_opaque = mask_opaque && src_opaque;
+ src_alpha = mask->alpha * src->alpha;
+ /* FIXME: This needs changing when we support RENDER
+ * style 4-channel masks.
+ */
+ if (mask->type == CAIRO_PATTERN_SOLID)
+ mask = NULL;
+ } else
+ src_alpha = src->alpha;
+
+ mask_alpha = 1.0;
+ mask_opaque = TRUE;
+ }
- image = _cairo_surface_get_image (pattern->u.surface.surface);
- if (image)
- surface = &image->base;
+ _cairo_pattern_init_copy (&tmp.base, src);
+ _cairo_pattern_set_alpha (&tmp.base, src_alpha);
+
+ status = _cairo_pattern_acquire_surface (&tmp.base, dst,
+ src_x, src_y,
+ width, height,
+ src_out, src_attributes);
+
+ _cairo_pattern_fini (&tmp.base);
+
+ if (status)
+ return status;
+
+ if (mask || !mask_opaque)
+ {
+ if (mask)
+ _cairo_pattern_init_copy (&tmp.base, mask);
else
- surface = NULL;
+ _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0);
+
+ _cairo_pattern_set_alpha (&tmp.base, mask_alpha);
+
+ status = _cairo_pattern_acquire_surface (&tmp.base, dst,
+ mask_x, mask_y,
+ width, height,
+ mask_out, mask_attributes);
+ _cairo_pattern_fini (&tmp.base);
+
+ if (status)
+ {
+ _cairo_pattern_release_surface (dst, *src_out, src_attributes);
+ return status;
+ }
}
- break;
- default:
- surface = NULL;
- break;
+ else
+ {
+ *mask_out = NULL;
}
-
- return (cairo_image_surface_t *) surface;
+
+ return CAIRO_STATUS_SUCCESS;
}
-