diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index b50a9941c..f3886c068 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -2,6 +2,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California + * Copyright © 2009,2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -33,19 +34,35 @@ * * Contributor(s): * Carl D. Worth + * Chris Wilson */ #include "cairoint.h" +#include "cairo-boxes-private.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" #include "cairo-region-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" /* Limit on the width / height of an image surface in pixels. This is * mainly determined by coordinates of things sent to pixman at the * moment being in 16.16 format. */ #define MAX_IMAGE_SIZE 32767 +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +static cairo_int_status_t +_cairo_image_surface_fill (void *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); static cairo_bool_t _cairo_image_surface_is_size_valid (int width, int height) @@ -54,7 +71,7 @@ _cairo_image_surface_is_size_valid (int width, int height) 0 <= height && height <= MAX_IMAGE_SIZE; } -static cairo_format_t +cairo_format_t _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) { switch (pixman_format) { @@ -96,69 +113,18 @@ _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) return CAIRO_FORMAT_INVALID; } -static cairo_content_t +cairo_content_t _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) { - switch (pixman_format) { - case PIXMAN_a8r8g8b8: - case PIXMAN_a8b8g8r8: - case PIXMAN_a1r5g5b5: - case PIXMAN_a1b5g5r5: - case PIXMAN_a4r4g4b4: - case PIXMAN_a4b4g4r4: - case PIXMAN_a2r2g2b2: - case PIXMAN_a2b2g2r2: - case PIXMAN_a1r1g1b1: - case PIXMAN_a1b1g1r1: -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9) - case PIXMAN_a2b10g10r10: -#endif -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1) - case PIXMAN_b8g8r8a8: -#endif -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) - case PIXMAN_a2r10g10b10: -#endif - return CAIRO_CONTENT_COLOR_ALPHA; - case PIXMAN_x8r8g8b8: - case PIXMAN_x8b8g8r8: - case PIXMAN_r8g8b8: - case PIXMAN_b8g8r8: - case PIXMAN_r5g6b5: - case PIXMAN_b5g6r5: - case PIXMAN_x1r5g5b5: - case PIXMAN_x1b5g5r5: - case PIXMAN_x4r4g4b4: - case PIXMAN_x4b4g4r4: - case PIXMAN_r3g3b2: - case PIXMAN_b2g3r3: - case PIXMAN_c8: - case PIXMAN_g8: - case PIXMAN_r1g2b1: - case PIXMAN_b1g2r1: - case PIXMAN_c4: - case PIXMAN_g4: - case PIXMAN_g1: - case PIXMAN_yuy2: - case PIXMAN_yv12: -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9) - case PIXMAN_x2b10g10r10: -#endif -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1) - case PIXMAN_b8g8r8x8: -#endif -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16) - case PIXMAN_x2r10g10b10: -#endif - return CAIRO_CONTENT_COLOR; - case PIXMAN_a8: - case PIXMAN_a1: - case PIXMAN_x4a4: - case PIXMAN_a4: - return CAIRO_CONTENT_ALPHA; - } + cairo_content_t content; - return CAIRO_CONTENT_COLOR_ALPHA; + content = 0; + if (PIXMAN_FORMAT_RGB (pixman_format)) + content |= CAIRO_CONTENT_COLOR; + if (PIXMAN_FORMAT_A (pixman_format)) + content |= CAIRO_CONTENT_ALPHA; + + return content; } cairo_surface_t * @@ -169,10 +135,6 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, int width = pixman_image_get_width (pixman_image); int height = pixman_image_get_height (pixman_image); - if (! _cairo_image_surface_is_size_valid (width, height)) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - } - surface = malloc (sizeof (cairo_image_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -186,7 +148,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->pixman_format = pixman_format; surface->format = _cairo_format_from_pixman_format (pixman_format); - surface->data = (unsigned char *) pixman_image_get_data (pixman_image); + surface->data = (uint8_t *) pixman_image_get_data (pixman_image); surface->owns_data = FALSE; surface->transparency = CAIRO_IMAGE_UNKNOWN; @@ -195,8 +157,6 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->stride = pixman_image_get_stride (pixman_image); surface->depth = pixman_image_get_depth (pixman_image); - surface->clip_region = NULL; - return &surface->base; } @@ -305,46 +265,6 @@ _pixman_format_to_masks (pixman_format_code_t format, } } -/* XXX: This function really should be eliminated. We don't really - * want to advertise a cairo image surface that supports any possible - * format. A minimal step would be to replace this function with one - * that accepts a #cairo_internal_format_t rather than mask values. */ -cairo_surface_t * -_cairo_image_surface_create_with_masks (unsigned char *data, - cairo_format_masks_t *masks, - int width, - int height, - int stride) -{ - pixman_format_code_t pixman_format; - - if (! _pixman_format_from_masks (masks, &pixman_format)) { - fprintf (stderr, - "Error: Cairo %s does not yet support the requested image format:\n" - "\tDepth: %d\n" - "\tAlpha mask: 0x%08lx\n" - "\tRed mask: 0x%08lx\n" - "\tGreen mask: 0x%08lx\n" - "\tBlue mask: 0x%08lx\n" -#ifdef PACKAGE_BUGGREPORT - "Please file an enhancement request (quoting the above) at:\n" - PACKAGE_BUGREPORT"\n" -#endif - , - cairo_version_string (), - masks->bpp, masks->alpha_mask, - masks->red_mask, masks->green_mask, masks->blue_mask); - - ASSERT_NOT_REACHED; - } - - return _cairo_image_surface_create_with_pixman_format (data, - pixman_format, - width, - height, - stride); -} - static pixman_format_code_t _cairo_format_to_pixman_format_code (cairo_format_t format) { @@ -442,9 +362,6 @@ _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height) { - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - return cairo_image_surface_create (_cairo_format_from_content (content), width, height); } @@ -554,6 +471,9 @@ cairo_image_surface_create_for_data (unsigned char *data, if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + if (! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + minstride = cairo_format_stride_for_width (format, width); if (stride < 0) { if (stride > -minstride) { @@ -573,21 +493,6 @@ cairo_image_surface_create_for_data (unsigned char *data, } slim_hidden_def (cairo_image_surface_create_for_data); -cairo_surface_t * -_cairo_image_surface_create_for_data_with_content (unsigned char *data, - cairo_content_t content, - int width, - int height, - int stride) -{ - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - return cairo_image_surface_create_for_data (data, - _cairo_format_from_content (content), - width, height, stride); -} - /** * cairo_image_surface_get_data: * @surface: a #cairo_image_surface_t @@ -770,6 +675,9 @@ _cairo_image_surface_create_similar (void *abstract_other, { cairo_image_surface_t *other = abstract_other; + if (! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (content == other->base.content) { return _cairo_image_surface_create_with_pixman_format (NULL, other->pixman_format, @@ -796,8 +704,6 @@ _cairo_image_surface_finish (void *abstract_surface) surface->data = NULL; } - cairo_region_destroy (surface->clip_region); - return CAIRO_STATUS_SUCCESS; } @@ -825,176 +731,6 @@ _cairo_image_surface_release_source_image (void *abstract_surf { } -static cairo_status_t -_cairo_image_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_image_surface_t *surface = abstract_surface; - - image_rect_out->x = 0; - image_rect_out->y = 0; - image_rect_out->width = surface->width; - image_rect_out->height = surface->height; - - *image_out = surface; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_image_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_image_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_image_surface_set_matrix (cairo_image_surface_t *surface, - const cairo_matrix_t *matrix, - double xc, double yc) -{ - pixman_transform_t pixman_transform; - - _cairo_matrix_to_pixman_matrix (matrix, &pixman_transform, xc, yc); - - if (! pixman_image_set_transform (surface->pixman_image, &pixman_transform)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_set_filter (cairo_image_surface_t *surface, - cairo_filter_t filter) -{ - pixman_filter_t pixman_filter; - - switch (filter) { - case CAIRO_FILTER_FAST: - pixman_filter = PIXMAN_FILTER_FAST; - break; - case CAIRO_FILTER_GOOD: - pixman_filter = PIXMAN_FILTER_GOOD; - break; - case CAIRO_FILTER_BEST: - pixman_filter = PIXMAN_FILTER_BEST; - break; - case CAIRO_FILTER_NEAREST: - pixman_filter = PIXMAN_FILTER_NEAREST; - break; - case CAIRO_FILTER_BILINEAR: - pixman_filter = PIXMAN_FILTER_BILINEAR; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - pixman_filter = PIXMAN_FILTER_BEST; - } - - if (! pixman_image_set_filter (surface->pixman_image, - pixman_filter, - NULL, 0)) - { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_set_extend (cairo_image_surface_t *surface, - cairo_extend_t extend) -{ - pixman_repeat_t pixman_repeat; - - switch (extend) { - case CAIRO_EXTEND_NONE: - pixman_repeat = PIXMAN_REPEAT_NONE; - break; - case CAIRO_EXTEND_REPEAT: - pixman_repeat = PIXMAN_REPEAT_NORMAL; - break; - case CAIRO_EXTEND_REFLECT: - pixman_repeat = PIXMAN_REPEAT_REFLECT; - break; - case CAIRO_EXTEND_PAD: - pixman_repeat = PIXMAN_REPEAT_PAD; - break; - } - - pixman_image_set_repeat (surface->pixman_image, pixman_repeat); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_set_component_alpha (cairo_image_surface_t *surface, - cairo_bool_t ca) -{ - pixman_image_set_component_alpha (surface->pixman_image, ca); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_set_attributes (cairo_image_surface_t *surface, - cairo_surface_attributes_t *attributes, - double xc, double yc) -{ - cairo_int_status_t status; - - status = _cairo_image_surface_set_matrix (surface, &attributes->matrix, - xc, yc); - if (unlikely (status)) - return status; - - status = _cairo_image_surface_set_filter (surface, attributes->filter); - if (unlikely (status)) - return status; - - status = _cairo_image_surface_set_extend (surface, attributes->extend); - if (unlikely (status)) - return status; - - status = _cairo_image_surface_set_component_alpha (surface, - attributes->has_component_alpha); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - /* XXX: I think we should fix pixman to match the names/order of the * cairo operators, but that will likely be better done at the same * time the X server is ported to pixman, (which will change a lot of @@ -1077,148 +813,3223 @@ static cairo_status_t _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, cairo_region_t *region) { - if (region == surface->clip_region) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_equal (surface->clip_region, region)) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (! pixman_image_set_clip_region32 (surface->pixman_image, - region ? ®ion->rgn : NULL)) - { + if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_image_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) +static void +_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface) { - cairo_surface_attributes_t src_attr, mask_attr; - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src; - cairo_image_surface_t *mask; - cairo_int_status_t status; + pixman_image_set_clip_region32 (surface->pixman_image, NULL); +} - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; +static double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); - if (unlikely (status)) - return status; +static cairo_bool_t +_nearest_sample (cairo_filter_t filter, double *tx, double *ty) +{ + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { + *tx = _pixman_nearest_sample (*tx); + *ty = _pixman_nearest_sample (*ty); + } else { + if (*tx != floor (*tx) || *ty != floor (*ty)) + return FALSE; + } + return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; +} - status = _cairo_image_surface_set_attributes (src, &src_attr, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto CLEANUP_SURFACES; +#if HAS_ATOMIC_OPS +static pixman_image_t * +_pixman_transparent_image (void) +{ + static pixman_image_t *__pixman_transparent_image; + pixman_image_t *image; - if (mask) { - status = _cairo_image_surface_set_attributes (mask, &mask_attr, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto CLEANUP_SURFACES; + image = __pixman_transparent_image; + if (unlikely (image == NULL)) { + pixman_color_t color; - pixman_image_composite (_pixman_operator (op), - src->pixman_image, - mask->pixman_image, - dst->pixman_image, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0x00; + + image = pixman_image_create_solid_fill (&color); + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, + NULL, image) == NULL) + { + pixman_image_ref (image); + } } else { - pixman_image_composite (_pixman_operator (op), - src->pixman_image, - NULL, - dst->pixman_image, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); + pixman_image_ref (image); } - if (! _cairo_operator_bounded_by_source (op)) { - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr, src->width, src->height, - mask ? &mask_attr : NULL, - mask ? mask->width : 0, - mask ? mask->height : 0, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - } + return image; +} - CLEANUP_SURFACES: - if (mask) - _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); +static pixman_image_t * +_pixman_black_image (void) +{ + static pixman_image_t *__pixman_black_image; + pixman_image_t *image; - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); + image = __pixman_black_image; + if (unlikely (image == NULL)) { + pixman_color_t color; - return status; -} + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0xffff; -static cairo_int_status_t -_cairo_image_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_image_surface_t *surface = abstract_surface; + image = pixman_image_create_solid_fill (&color); - pixman_color_t pixman_color; - pixman_rectangle16_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (pixman_rectangle16_t)]; - pixman_rectangle16_t *pixman_rects = stack_rects; - int i; + if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, + NULL, image) == NULL) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } - cairo_int_status_t status; + return image; +} - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); +static pixman_image_t * +_pixman_white_image (void) +{ + static pixman_image_t *__pixman_white_image; + pixman_image_t *image; - pixman_color.red = color->red_short; - pixman_color.green = color->green_short; - pixman_color.blue = color->blue_short; - pixman_color.alpha = color->alpha_short; + image = __pixman_white_image; + if (unlikely (image == NULL)) { + pixman_color_t color; - status = _cairo_image_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); + color.red = 0xffff; + color.green = 0xffff; + color.blue = 0xffff; + color.alpha = 0xffff; - if (num_rects > ARRAY_LENGTH (stack_rects)) { - pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t)); - if (unlikely (pixman_rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + image = pixman_image_create_solid_fill (&color); + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, + NULL, image) == NULL) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} +#else +static pixman_image_t * +_pixman_white_image (void) +{ + return _pixman_image_for_solid (&_cairo_pattern_white); +} +#endif + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static pixman_image_t * +_pixman_image_for_solid (const cairo_solid_pattern_t *pattern) +{ + static struct { + cairo_color_t color; + pixman_image_t *image; + } cache[16]; + static int n_cached; + pixman_color_t color; + pixman_image_t *image; + int i; + +#if HAS_ATOMIC_OPS + if (pattern->color.alpha_short <= 0x00ff) + return _pixman_transparent_image (); + + if (pattern->color.alpha_short >= 0xff00) { + if (pattern->color.red_short <= 0x00ff && + pattern->color.green_short <= 0x00ff && + pattern->color.blue_short <= 0x00ff) + { + return _pixman_black_image (); + } + + if (pattern->color.red_short >= 0xff00 && + pattern->color.green_short >= 0xff00 && + pattern->color.blue_short >= 0xff00) + { + return _pixman_white_image (); + } + } +#endif + + CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); + for (i = 0; i < n_cached; i++) { + if (_cairo_color_equal (&cache[i].color, &pattern->color)) { + image = pixman_image_ref (cache[i].image); + goto UNLOCK; + } + } + + color.red = pattern->color.red_short; + color.green = pattern->color.green_short; + color.blue = pattern->color.blue_short; + color.alpha = pattern->color.alpha_short; + + image = pixman_image_create_solid_fill (&color); + if (image == NULL) + goto UNLOCK; + + if (n_cached < ARRAY_LENGTH (cache)) { + i = n_cached++; + } else { + i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); + pixman_image_unref (cache[i].image); + } + cache[i].image = pixman_image_ref (image); + cache[i].color = pattern->color; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); + return image; +} + +static pixman_image_t * +_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + pixman_gradient_stop_t pixman_stops_static[2]; + pixman_gradient_stop_t *pixman_stops = pixman_stops_static; + cairo_matrix_t matrix = pattern->base.matrix; + double tx, ty; + unsigned int i; + + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof(pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return NULL; + } + + for (i = 0; i < pattern->n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); + pixman_stops[i].color.red = pattern->stops[i].color.red_short; + pixman_stops[i].color.green = pattern->stops[i].color.green_short; + pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; + pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + } + + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + pixman_point_fixed_t p1, p2; + cairo_fixed_t xdim, ydim; + + xdim = fabs (linear->p2.x - linear->p1.x); + ydim = fabs (linear->p2.y - linear->p1.y); + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * XXX: Consider converting out-of-range co-ordinates and transforms. + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ + if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || + _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) + { + double sf; + + if (xdim > ydim) + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); + else + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); + + p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); + p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); + p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); + p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); + + cairo_matrix_scale (&matrix, sf, sf); + } + else + { + p1.x = _cairo_fixed_to_16_16 (linear->p1.x); + p1.y = _cairo_fixed_to_16_16 (linear->p1.y); + p2.x = _cairo_fixed_to_16_16 (linear->p2.x); + p2.y = _cairo_fixed_to_16_16 (linear->p2.y); + } + + pixman_image = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + pattern->n_stops); + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + pixman_point_fixed_t c1, c2; + pixman_fixed_t r1, r2; + + c1.x = _cairo_fixed_to_16_16 (radial->c1.x); + c1.y = _cairo_fixed_to_16_16 (radial->c1.y); + r1 = _cairo_fixed_to_16_16 (radial->r1); + + c2.x = _cairo_fixed_to_16_16 (radial->c2.x); + c2.y = _cairo_fixed_to_16_16 (radial->c2.y); + r2 = _cairo_fixed_to_16_16 (radial->r2); + + pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2, + pixman_stops, + pattern->n_stops); + } + + if (pixman_stops != pixman_stops_static) + free (pixman_stops); + + tx = pattern->base.matrix.x0; + ty = pattern->base.matrix.y0; + if (! _cairo_matrix_is_translation (&pattern->base.matrix) || + ! _nearest_sample (pattern->base.filter, &tx, &ty)) + { + pixman_transform_t pixman_transform; + + if (tx != 0. || ty != 0.) { + cairo_matrix_t m, inv; + cairo_status_t status; + double x, y; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + inv = pattern->base.matrix; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + + x = floor (inv.x0 / 2); + y = floor (inv.y0 / 2); + tx = -x; + ty = -y; + cairo_matrix_init_translate (&inv, x, y); + cairo_matrix_multiply (&m, &inv, &pattern->base.matrix); + _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + } else { + tx = ty = 0; + _cairo_matrix_to_pixman_matrix (&pattern->base.matrix, + &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + } + + if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { + pixman_image_unref (pixman_image); + return NULL; + } + } + *ix = tx; + *iy = ty; + + { + pixman_repeat_t pixman_repeat; + + switch (pattern->base.extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + return pixman_image; +} + +struct acquire_source_cleanup { + cairo_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +_acquire_source_cleanup (pixman_image_t *pixman_image, + void *closure) +{ + struct acquire_source_cleanup *data = closure; + + _cairo_surface_release_source_image (data->surface, + data->image, + data->image_extra); + free (data); +} + +static pixman_image_t * +_pixman_image_for_surface (const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + cairo_extend_t extend; + double tx, ty; + + tx = pattern->base.matrix.x0; + ty = pattern->base.matrix.y0; + + extend = pattern->base.extend; + + pixman_image = NULL; + if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; + cairo_surface_type_t type; + + if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target; + + type = source->base.backend->type; + if (type == CAIRO_SURFACE_TYPE_IMAGE) { + if (extend != CAIRO_EXTEND_NONE && + extents->x >= 0 && extents->y >= 0 && + extents->x + extents->width <= source->width && + extents->y + extents->height <= source->height) + { + extend = CAIRO_EXTEND_NONE; + } + + /* avoid allocating a 'pattern' image if we can reuse the original */ + if (extend == CAIRO_EXTEND_NONE && + _cairo_matrix_is_translation (&pattern->base.matrix) && + _nearest_sample (pattern->base.filter, &tx, &ty)) + { + *ix = tx; + *iy = ty; + return pixman_image_ref (source->pixman_image); + } + + pixman_image = pixman_image_create_bits (source->pixman_format, + source->width, + source->height, + (uint32_t *) source->data, + source->stride); + if (unlikely (pixman_image == NULL)) + return NULL; + } else if (type == CAIRO_INTERNAL_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub; + cairo_bool_t is_contained = FALSE; + + sub = (cairo_surface_subsurface_t *) source; + source = (cairo_image_surface_t *) sub->target; + + if (extend != CAIRO_EXTEND_NONE && + extents->x >= 0 && extents->y >= 0 && + extents->x + extents->width <= sub->extents.width && + extents->y + extents->height <= sub->extents.height) + { + extend = CAIRO_EXTEND_NONE; + is_contained = TRUE; + } + + if (is_contained && + _cairo_matrix_is_translation (&pattern->base.matrix) && + _nearest_sample (pattern->base.filter, &tx, &ty)) + { + *ix = tx + sub->extents.x; + *iy = ty + sub->extents.y; + return pixman_image_ref (source->pixman_image); + } + + /* Avoid sub-byte offsets, force a copy in that case. */ + if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { + pixman_image = pixman_image_create_bits (source->pixman_format, + sub->extents.width, + sub->extents.height, + (uint32_t *) (source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride), + source->stride); + if (unlikely (pixman_image == NULL)) + return NULL; + } + } + } + + if (pixman_image == NULL) { + struct acquire_source_cleanup *cleanup; + cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; + cairo_status_t status; + + cleanup = malloc (sizeof (*cleanup)); + if (unlikely (cleanup == NULL)) + return NULL; + + cleanup->surface = pattern->surface; + status = _cairo_surface_acquire_source_image (pattern->surface, + &cleanup->image, + &cleanup->image_extra); + if (unlikely (status)) { + free (cleanup); + return NULL; + } + + source = cleanup->image; + if (extend != CAIRO_EXTEND_NONE && + extents->x >= 0 && extents->y >= 0 && + extents->x + extents->width <= source->width && + extents->y + extents->height <= source->height) + { + extend = CAIRO_EXTEND_NONE; + } + + pixman_image = pixman_image_create_bits (source->pixman_format, + source->width, + source->height, + (uint32_t *) source->data, + source->stride); + if (unlikely (pixman_image == NULL)) { + _cairo_surface_release_source_image (pattern->surface, + cleanup->image, + cleanup->image_extra); + free (cleanup); + return NULL; + } + + pixman_image_set_destroy_function (pixman_image, + _acquire_source_cleanup, cleanup); + } + + if (! _cairo_matrix_is_translation (&pattern->base.matrix) || + ! _nearest_sample (pattern->base.filter, &tx, &ty)) + { + pixman_transform_t pixman_transform; + cairo_matrix_t m; + + m = pattern->base.matrix; + if (m.x0 != 0. || m.y0 != 0.) { + cairo_matrix_t inv; + cairo_status_t status; + double x, y; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + inv = m; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + + x = floor (inv.x0 / 2); + y = floor (inv.y0 / 2); + tx = -x; + ty = -y; + cairo_matrix_init_translate (&inv, x, y); + cairo_matrix_multiply (&m, &inv, &m); + } else { + tx = ty = 0; + } + + _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { + pixman_image_unref (pixman_image); + return NULL; + } + } + *ix = tx; + *iy = ty; + + if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) && + tx == pattern->base.matrix.x0 && + ty == pattern->base.matrix.y0) + { + pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); + } + else + { + pixman_filter_t pixman_filter; + + switch (pattern->base.filter) { + case CAIRO_FILTER_FAST: + pixman_filter = PIXMAN_FILTER_FAST; + break; + case CAIRO_FILTER_GOOD: + pixman_filter = PIXMAN_FILTER_GOOD; + break; + case CAIRO_FILTER_BEST: + pixman_filter = PIXMAN_FILTER_BEST; + break; + case CAIRO_FILTER_NEAREST: + pixman_filter = PIXMAN_FILTER_NEAREST; + break; + case CAIRO_FILTER_BILINEAR: + pixman_filter = PIXMAN_FILTER_BILINEAR; + break; + case CAIRO_FILTER_GAUSSIAN: + /* XXX: The GAUSSIAN value has no implementation in cairo + * whatsoever, so it was really a mistake to have it in the + * API. We could fix this by officially deprecating it, or + * else inventing semantics and providing an actual + * implementation for it. */ + default: + pixman_filter = PIXMAN_FILTER_BEST; + } + + pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); + } + + { + pixman_repeat_t pixman_repeat; + + switch (extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + return pixman_image; +} + +static pixman_image_t * +_pixman_image_for_pattern (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *tx, int *ty) +{ + *tx = *ty = 0; + + if (pattern == NULL) + return _pixman_white_image (); + + switch (pattern->type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); + + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_LINEAR: + return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, + extents, tx, ty); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern, + extents, tx, ty); + } +} + +static void +_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst, + const cairo_composite_rectangles_t *rects, + cairo_clip_t *clip) +{ + pixman_image_t *mask = NULL; + int mask_x = 0, mask_y = 0; + + if (clip != NULL) { + cairo_surface_t *clip_surface; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base); + assert (clip_surface->status == CAIRO_STATUS_SUCCESS); + + mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; + mask_x = -clip->path->extents.x; + mask_y = -clip->path->extents.y; + } else { + if (rects->bounded.width == rects->unbounded.width && + rects->bounded.height == rects->unbounded.height) + { + return; + } + } + + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + int x = rects->unbounded.x; + int y = rects->unbounded.y; + int width = rects->unbounded.width; + int height = rects->bounded.y - y; + + if (mask != NULL) { + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } else { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x, y, width, height, + 0); + } + } + + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + int x = rects->unbounded.x; + int y = rects->bounded.y; + int width = rects->bounded.x - rects->unbounded.x; + int height = rects->bounded.height; + + if (mask != NULL) { + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } else { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x, y, width, height, + 0); + } + } + + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + int x = rects->bounded.x + rects->bounded.width; + int y = rects->bounded.y; + int width = rects->unbounded.x + rects->unbounded.width - x; + int height = rects->bounded.height; + + if (mask != NULL) { + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } else { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x, y, width, height, + 0); + } + } + + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + int x = rects->unbounded.x; + int y = rects->bounded.y + rects->bounded.height; + int width = rects->unbounded.width; + int height = rects->unbounded.y + rects->unbounded.height - y; + + if (mask != NULL) { + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } else { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x, y, width, height, + 0); + } + } +} + +static cairo_status_t +_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_region_t *clip_region, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + if (boxes->num_boxes <= 1 && clip_region == NULL) { + _cairo_image_surface_fixup_unbounded (dst, extents, NULL); + return CAIRO_STATUS_SUCCESS; + } + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); + int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); + int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); + int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); + + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + 0); + } + } + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return dst->base.is_clear && + dst->base.content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +/* low level compositor */ +typedef cairo_status_t +(*image_draw_func_t) (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *src, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); + +static pixman_image_t * +_create_composite_mask_pattern (cairo_clip_t *clip, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_region_t *clip_region = NULL; + pixman_image_t *mask; + cairo_status_t status; + cairo_bool_t need_clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + /* The all-clipped state should never propagate this far. */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return NULL; + + /* Is it worth setting the clip region here? */ + if (clip_region != NULL) { + pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y); + pixman_image_set_clip_region32 (mask, &clip_region->rgn); + pixman_region32_translate (&clip_region->rgn, extents->x, extents->y); + } + + status = draw_func (draw_closure, + mask, PIXMAN_a8, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) { + pixman_image_unref (mask); + return NULL; + } + + if (need_clip_surface) { + cairo_surface_t *tmp; + + tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8); + if (unlikely (tmp->status)) { + pixman_image_unref (mask); + return NULL; + } + + pixman_image_ref (mask); + + status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y); + cairo_surface_destroy (tmp); + if (unlikely (status)) { + pixman_image_unref (mask); + return NULL; + } + } + + if (clip_region != NULL) + pixman_image_set_clip_region (mask, NULL); + + return mask; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *mask; + + mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (pattern == NULL) { + if (dst->pixman_format == PIXMAN_a8) { + pixman_image_composite (_pixman_operator (op), + mask, NULL, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + pixman_image_t *src; + + src = _pixman_white_image (); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite (_pixman_operator (op), + src, mask, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + pixman_image_unref (src); + } + } else { + pixman_image_t *src; + int src_x, src_y; + + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + pixman_image_unref (src); + } + + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *tmp; + cairo_surface_t *clip_surface; + cairo_status_t status; + + tmp = pixman_image_create_bits (dst->pixman_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (tmp == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (src == NULL) { + status = (*draw_func) (draw_closure, + tmp, dst->pixman_format, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + } else { + /* Initialize the temporary surface from the destination surface */ + if (! dst->base.is_clear) { + pixman_image_composite (PIXMAN_OP_SRC, + dst->pixman_image, NULL, tmp, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height); + } + + status = (*draw_func) (draw_closure, + tmp, dst->pixman_format, + op, src, + extents->x, extents->y, + extents, NULL); + } + if (unlikely (status)) + goto CLEANUP_SURFACE; + + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, &dst->base); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + if (! dst->base.is_clear) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite (PIXMAN_OP_LERP, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + extents->x, extents->y, + extents->width, extents->height); +#else + /* Punch the clip out of the destination */ + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + NULL, dst->pixman_image, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now add the two results together */ + pixman_image_composite (PIXMAN_OP_ADD, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + extents->x, extents->y, + extents->width, extents->height); +#endif + } else { + pixman_image_composite (PIXMAN_OP_SRC, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + extents->x, extents->y, + extents->width, extents->height); + } + + CLEANUP_SURFACE: + pixman_image_unref (tmp); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *pattern, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *mask, *src; + int src_x, src_y; + + if (pattern == NULL) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = draw_func (draw_closure, + dst->pixman_image, dst->pixman_format, + CAIRO_OPERATOR_SOURCE, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) + return status; + + if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0); + + return status; + } + + /* Create a surface that is mask IN clip */ + mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + if (! dst->base.is_clear) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite (PIXMAN_OP_LERP, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); +#else + /* Compute dest' = dest OUT (mask IN clip) */ + pixman_image_composite (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + pixman_image_composite (PIXMAN_OP_ADD, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); +#endif + } else { + pixman_image_composite (PIXMAN_OP_SRC, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + + pixman_image_unref (src); + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_clip_and_composite (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + image_draw_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + assert (! _cairo_status_is_error (status)); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + if (reduce_alpha_op (dst, op, src)) { + op = CAIRO_OPERATOR_ADD; + src = NULL; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + if (op == CAIRO_OPERATOR_CLEAR) { + src = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + + if (need_clip_surface) { + if (extents->is_bounded) { + status = _clip_and_composite_with_mask (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + status = _clip_and_composite_combine (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } + } else { + status = draw_func (draw_closure, + dst->pixman_image, dst->pixman_format, + op, src, + 0, 0, + &extents->bounded, + clip_region); + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + _cairo_image_surface_fixup_unbounded (dst, extents, + need_clip_surface ? clip : NULL); + } + + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return status; +} + +#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) +#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x <= CAIRO_FIXED_16_16_MIN || + line->p1.x >= CAIRO_FIXED_16_16_MAX || + + line->p2.x <= CAIRO_FIXED_16_16_MIN || + line->p2.x >= CAIRO_FIXED_16_16_MAX || + + line->p1.y <= CAIRO_FIXED_16_16_MIN || + line->p1.y >= CAIRO_FIXED_16_16_MAX || + + line->p2.y <= CAIRO_FIXED_16_16_MIN || + line->p2.y >= CAIRO_FIXED_16_16_MAX; +} + +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + pixman_line_fixed_t *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + + +typedef struct { + cairo_trapezoid_t *traps; + int num_traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +static void +_pixman_image_add_traps (pixman_image_t *image, + int dst_x, int dst_y, + composite_traps_info_t *info) +{ + cairo_trapezoid_t *t = info->traps; + int num_traps = info->num_traps; + while (num_traps--) { + pixman_trapezoid_t trap; + + /* top/bottom will be clamped to surface bounds */ + trap.top = _cairo_fixed_to_16_16 (t->top); + trap.bottom = _cairo_fixed_to_16_16 (t->bottom); + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (_line_exceeds_16_16 (&t->left))) { + _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); + trap.left.p1.y = trap.top; + trap.left.p2.y = trap.bottom; + } else { + trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); + } + + if (unlikely (_line_exceeds_16_16 (&t->right))) { + _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); + trap.right.p1.y = trap.top; + trap.right.p2.y = trap.bottom; + } else { + trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); + } + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + + t++; + } +} + +static cairo_status_t +_composite_traps (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_traps_info_t *info = closure; + pixman_image_t *src, *mask; + pixman_format_code_t format; + int src_x = 0, src_y = 0; + cairo_status_t status; + + /* Special case adding trapezoids onto a mask surface; we want to avoid + * creating an intermediate temporary mask unnecessarily. + * + * We make the assumption here that the portion of the trapezoids + * contained within the surface is bounded by [dst_x,dst_y,width,height]; + * the Cairo core code passes bounds based on the trapezoid extents. + */ + format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; + if (dst_format == format && + (pattern == NULL || + (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern)))) + { + _pixman_image_add_traps (dst, dst_x, dst_y, info); + return CAIRO_STATUS_SUCCESS; + } + + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + mask = pixman_image_create_bits (format, extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_SOURCE; + } + + _pixman_image_add_traps (mask, extents->x, extents->y, info); + pixman_image_composite (_pixman_operator (op), + src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + + status = CAIRO_STATUS_SUCCESS; + CLEANUP_SOURCE: + pixman_image_unref (src); + + return status; +} + +static inline uint32_t +color_to_uint32 (const cairo_color_t *color) +{ + return + (color->alpha_short >> 8 << 24) | + (color->red_short >> 8 << 16) | + (color->green_short & 0xff00) | + (color->blue_short >> 8); +} + +static inline cairo_bool_t +color_to_pixel (const cairo_color_t *color, + pixman_format_code_t format, + uint32_t *pixel) +{ + uint32_t c; + + if (!(format == PIXMAN_a8r8g8b8 || + format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5 || + format == PIXMAN_a8)) + { + return FALSE; + } + + c = color_to_uint32 (color); + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { + c = ((c & 0xff000000) >> 0) | + ((c & 0x00ff0000) >> 16) | + ((c & 0x0000ff00) >> 0) | + ((c & 0x000000ff) << 16); + } + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { + c = ((c & 0xff000000) >> 24) | + ((c & 0x00ff0000) >> 8) | + ((c & 0x0000ff00) << 8) | + ((c & 0x000000ff) << 24); + } + + if (format == PIXMAN_a8) { + c = c >> 24; + } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { + c = ((((c) >> 3) & 0x001f) | + (((c) >> 5) & 0x07e0) | + (((c) >> 8) & 0xf800)); + } + + *pixel = c; + return TRUE; +} + +static inline cairo_bool_t +pattern_to_pixel (const cairo_solid_pattern_t *solid, + cairo_operator_t op, + pixman_format_code_t format, + uint32_t *pixel) +{ + if (op == CAIRO_OPERATOR_CLEAR) { + *pixel = 0; + return TRUE; + } + + if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + if (op == CAIRO_OPERATOR_OVER) { + if (solid->color.alpha_short >= 0xff00) + op = CAIRO_OPERATOR_SOURCE; + } + + if (op != CAIRO_OPERATOR_SOURCE) + return FALSE; + + return color_to_pixel (&solid->color, format, pixel); +} + +typedef struct _fill_span { + cairo_span_renderer_t base; + + uint8_t *mask_data; + pixman_image_t *src, *dst, *mask; +} fill_span_renderer_t; + +static cairo_status_t +_fill_span (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + fill_span_renderer_t *renderer = abstract_renderer; + uint8_t *row; + unsigned i; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + row = renderer->mask_data - spans[0].x; + for (i = 0; i < num_spans - 1; i++) { + /* We implement setting the most common single pixel wide + * span case to avoid the overhead of a memset call. + * Open coding setting longer spans didn't show a + * noticeable improvement over memset. + */ + if (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].x); + } + } + + do { + pixman_image_composite (PIXMAN_OP_OVER, + renderer->src, renderer->mask, renderer->dst, + 0, 0, 0, 0, + spans[0].x, y++, + spans[i].x - spans[0].x, 1); + } while (--height); + + return CAIRO_STATUS_SUCCESS; +} + +/* avoid using region code to re-validate boxes */ +static cairo_status_t +_fill_unaligned_boxes (cairo_image_surface_t *dst, + const cairo_pattern_t *pattern, + uint32_t pixel, + const cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + fill_span_renderer_t renderer; + cairo_rectangular_scan_converter_t converter; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int i; + + /* XXX + * using composite for fill: + * spiral-box-nonalign-evenodd-fill.512 2201957 2.202 + * spiral-box-nonalign-nonzero-fill.512 336726 0.337 + * spiral-box-pixalign-evenodd-fill.512 352256 0.352 + * spiral-box-pixalign-nonzero-fill.512 147056 0.147 + * using fill: + * spiral-box-nonalign-evenodd-fill.512 3174565 3.175 + * spiral-box-nonalign-nonzero-fill.512 182710 0.183 + * spiral-box-pixalign-evenodd-fill.512 353863 0.354 + * spiral-box-pixalign-nonzero-fill.512 147402 0.147 + * + * cairo-perf-trace seems to favour using fill. + */ + + renderer.base.render_rows = _fill_span; + renderer.dst = dst->pixman_image; + + if ((unsigned) extents->bounded.width <= sizeof (buf)) { + renderer.mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, 1, + (uint32_t *) buf, + sizeof (buf)); + } else { + renderer.mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, 1, + NULL, 0); + } + if (unlikely (renderer.mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask); + + renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); + if (unlikely (renderer.src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_MASK; + } + + _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); + + /* first blit any aligned part of the boxes */ + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_ceil (box[i].p1.x); + int y1 = _cairo_fixed_integer_ceil (box[i].p1.y); + int x2 = _cairo_fixed_integer_floor (box[i].p2.x); + int y2 = _cairo_fixed_integer_floor (box[i].p2.y); + + if (x2 > x1 && y2 > y1) { + cairo_box_t b; + + pixman_fill ((uint32_t *) dst->data, + dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + + /* top */ + if (! _cairo_fixed_is_integer (box[i].p1.y)) { + b.p1.x = box[i].p1.x; + b.p1.y = box[i].p1.y; + b.p2.x = box[i].p2.x; + b.p2.y = _cairo_fixed_from_int (y1); + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* left */ + if (! _cairo_fixed_is_integer (box[i].p1.x)) { + b.p1.x = box[i].p1.x; + b.p1.y = box[i].p1.y; + b.p2.x = _cairo_fixed_from_int (x1); + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* right */ + if (! _cairo_fixed_is_integer (box[i].p2.x)) { + b.p1.x = _cairo_fixed_from_int (x2); + b.p1.y = box[i].p1.y; + b.p2.x = box[i].p2.x; + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* bottom */ + if (! _cairo_fixed_is_integer (box[i].p2.y)) { + b.p1.x = box[i].p1.x; + b.p1.y = _cairo_fixed_from_int (y2); + b.p2.x = box[i].p2.x; + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + } else { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + } + } + + status = converter.base.generate (&converter.base, &renderer.base); + + CLEANUP_CONVERTER: + converter.base.destroy (&converter.base); + pixman_image_unref (renderer.src); + CLEANUP_MASK: + pixman_image_unref (renderer.mask); + + return status; +} + +typedef struct _cairo_image_surface_span_renderer { + cairo_span_renderer_t base; + + uint8_t *mask_data; + uint32_t mask_stride; +} cairo_image_surface_span_renderer_t; + +static cairo_status_t +_cairo_image_surface_span (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + uint8_t *row; + unsigned i; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + /* XXX will it be quicker to repeat the sparse memset, + * or perform a simpler memcpy? + * The fairly dense spiral benchmarks suggests that the sparse + * memset is a win there as well. + */ + row = renderer->mask_data + y * renderer->mask_stride; + do { + for (i = 0; i < num_spans - 1; i++) { + if (! spans[i].coverage) + continue; + + /* We implement setting rendering the most common single + * pixel wide span case to avoid the overhead of a memset + * call. Open coding setting longer spans didn't show a + * noticeable improvement over memset. */ + if (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].x); + } + } + row += renderer->mask_stride; + } while (--height); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_composite_unaligned_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + cairo_image_surface_span_renderer_t renderer; + cairo_rectangular_scan_converter_t converter; + pixman_image_t *mask, *src; + cairo_status_t status; + const struct _cairo_boxes_chunk *chunk; + int i, src_x, src_y; + + i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height; + if ((unsigned) i <= sizeof (buf)) { + mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, + extents->bounded.height, + (uint32_t *) buf, + CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8)); + memset (buf, 0, i); + } else { + mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, + extents->bounded.height, + NULL, 0); + } + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + renderer.base.render_rows = _cairo_image_surface_span; + renderer.mask_stride = pixman_image_get_stride (mask); + renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); + renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x; + + _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP; + } + } + + status = converter.base.generate (&converter.base, &renderer.base); + if (unlikely (status)) + goto CLEANUP; + + src = _pixman_image_for_pattern (pattern, &extents->bounded, &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + pixman_image_composite (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + pixman_image_unref (src); + + CLEANUP: + converter.base.destroy (&converter.base); + pixman_image_unref (mask); + + return status; +} + +static cairo_status_t +_composite_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_mask = FALSE; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + uint32_t pixel; + int i; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_mask && + (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) { + if (need_clip_mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, + dst->pixman_format, &pixel)) + { + return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents); + } + else + { + return _composite_unaligned_boxes (dst, op, pattern, boxes, extents); + } + } + } + + status = CAIRO_STATUS_SUCCESS; + if (! need_clip_mask && + pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format, + &pixel)) + { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 == x1 || y2 == y1) + continue; + + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + } + } + } + else + { + pixman_image_t *src = NULL, *mask = NULL; + int src_x, src_y, mask_x = 0, mask_y = 0; + pixman_op_t pixman_op = _pixman_operator (op); + + if (need_clip_mask) { + cairo_surface_t *clip_surface; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + mask_x = -clip->path->extents.x; + mask_y = -clip->path->extents.y; + + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = NULL; + pixman_op = PIXMAN_OP_OUT_REVERSE; + } + + mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; + } + + if (pattern != NULL) { + src = _pixman_image_for_pattern (pattern, &extents->bounded, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + src = mask; + src_x = mask_x; + src_y = mask_y; + mask = NULL; + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 == x1 || y2 == y1) + continue; + + pixman_image_composite (pixman_op, + src, mask, dst->pixman_image, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1, y1, + x2 - x1, y2 - y1); + } + } + + if (pattern != NULL) + pixman_image_unref (src); + + if (! extents->is_bounded) { + status = + _cairo_image_surface_fixup_unbounded_boxes (dst, extents, + clip_region, boxes); + } + } + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_traps_t traps; + cairo_status_t status; + composite_traps_info_t info; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise render via a mask and composite in the usual fashion. */ + status = _cairo_traps_init_boxes (&traps, boxes); + if (unlikely (status)) + return status; + + info.num_traps = traps.num_traps; + info.traps = traps.traps; + info.antialias = antialias; + return _clip_and_composite (dst, op, src, + _composite_traps, &info, + extents, clip); +} + +static cairo_bool_t +_mono_edge_is_vertical (const cairo_line_t *line) +{ + return _cairo_fixed_integer_round (line->p1.x) == _cairo_fixed_integer_round (line->p2.x); +} + +static cairo_bool_t +_traps_are_pixel_aligned (cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + if (! _mono_edge_is_vertical (&traps->traps[i].left) || + ! _mono_edge_is_vertical (&traps->traps[i].right)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } + + return TRUE; +} + +static void +_boxes_for_traps (cairo_boxes_t *boxes, + cairo_traps_t *traps) +{ + int i; + + _cairo_boxes_init (boxes); + + boxes->num_boxes = traps->num_traps; + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.count = traps->num_traps; + boxes->chunks.size = traps->num_traps; + + for (i = 0; i < traps->num_traps; i++) { + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + boxes->chunks.base[i].p1.x = x1; + boxes->chunks.base[i].p1.y = y1; + boxes->chunks.base[i].p2.x = x2; + boxes->chunks.base[i].p2.y = y2; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } +} + +static cairo_status_t +_clip_and_composite_trapezoids (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t info; + cairo_bool_t need_clip_surface = FALSE; + cairo_status_t status; + + if (traps->num_traps == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + cairo_region_t *clip_region; + + status = _cairo_clip_get_region (clip, &clip_region); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (traps->has_intersections) { + if (traps->is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + else if (traps->is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + return status; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) && + (! need_clip_surface || + (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) + { + cairo_boxes_t boxes; + + _boxes_for_traps (&boxes, traps); + return _clip_and_composite_boxes (dst, op, src, + &boxes, antialias, + extents, clip); + } + + /* No fast path, exclude self-intersections and clip trapezoids. */ + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + info.traps = traps->traps;; + info.num_traps = traps->num_traps; + info.antialias = antialias; + return _clip_and_composite (dst, op, src, + _composite_traps, &info, + extents, clip); +} + +static cairo_bool_t +box_is_aligned (const cairo_box_t *box) +{ + return + _cairo_fixed_is_integer (box->p1.x) && + _cairo_fixed_is_integer (box->p1.y) && + _cairo_fixed_is_integer (box->p2.x) && + _cairo_fixed_is_integer (box->p2.y); +} + +static inline cairo_status_t +_clip_to_boxes (cairo_clip_t **clip, + const cairo_composite_rectangles_t *extents, + cairo_box_t **boxes, + int *num_boxes) +{ + cairo_status_t status; + const cairo_rectangle_int_t *rect; + + rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; + + if (*clip == NULL) + goto EXTENTS; + + status = _cairo_clip_rectangle (*clip, rect); + if (unlikely (status)) + return status; + + status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); + switch ((int) status) { + case CAIRO_STATUS_SUCCESS: + if (extents->is_bounded || (*num_boxes == 1 && box_is_aligned (*boxes))) + *clip = NULL; + goto DONE; + + case CAIRO_INT_STATUS_UNSUPPORTED: + goto EXTENTS; + + default: + return status; + } + + EXTENTS: + status = CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&(*boxes)[0], rect); + *num_boxes = 1; + DONE: + return status; +} + +static cairo_clip_path_t * +_clip_get_single_path (cairo_clip_t *clip) +{ + cairo_clip_path_t *iter = clip->path; + cairo_clip_path_t *path = NULL; + + do { + if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) { + if (path != NULL) + return FALSE; + + path = iter; + } + iter = iter->prev; + } while (iter != NULL); + + return path; +} + +/* high level image interface */ + +static cairo_int_status_t +_cairo_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_clip_path_t *clip_path; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + surface->width, + surface->height, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && + extents.is_bounded && + (clip_path = _clip_get_single_path (clip)) != NULL) + { + status = _cairo_image_surface_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } + else + { + cairo_boxes_t boxes; + + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (surface, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_status_t +_composite_mask (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + const cairo_pattern_t *mask_pattern = closure; + pixman_image_t *src, *mask = NULL; + int src_x = 0, src_y = 0; + int mask_x = 0, mask_y = 0; + + if (src_pattern != NULL) { + src = _pixman_image_for_pattern (src_pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + mask = _pixman_image_for_pattern (mask_pattern, extents, &mask_x, &mask_y); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + if (mask_pattern->has_component_alpha) + pixman_image_set_component_alpha (mask, TRUE); + } else { + src = _pixman_image_for_pattern (mask_pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite (_pixman_operator (op), src, mask, dst, + extents->x + src_x, extents->y + src_y, + extents->x + mask_x, extents->y + mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + if (mask != NULL) + pixman_image_unref (mask); + pixman_image_unref (src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_image_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + surface->width, surface->height, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + + have_clip = TRUE; + } + + status = _clip_and_composite (surface, op, source, + _composite_mask, (void *) mask, + &extents, clip); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_polygon_t *polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_spans_info_t; + +//#define USE_BOTOR_SCAN_CONVERTER +static cairo_status_t +_composite_spans (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE]; + composite_spans_info_t *info = closure; + cairo_image_surface_span_renderer_t renderer; +#if USE_BOTOR_SCAN_CONVERTER + cairo_box_t box; + cairo_botor_scan_converter_t converter; +#else + cairo_scan_converter_t *converter; +#endif + pixman_image_t *mask; + cairo_status_t status; + +#if USE_BOTOR_SCAN_CONVERTER + box.p1.x = _cairo_fixed_from_int (extents->x); + box.p1.y = _cairo_fixed_from_int (extents->y); + box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); + box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); + _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); + status = converter.base.add_polygon (&converter.base, info->polygon); +#else + converter = _cairo_tor_scan_converter_create (extents->x, extents->y, + extents->x + extents->width, + extents->y + extents->height, + info->fill_rule); + status = converter->add_polygon (converter, info->polygon); +#endif + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + /* TODO: support rendering to A1 surfaces (or: go add span + * compositing to pixman.) */ + + if (pattern == NULL && dst_format == PIXMAN_a8) { + mask = dst; + dst = NULL; + } else { + int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8); + uint8_t *data = mask_buf; + + if (extents->height * stride <= (int) sizeof (mask_buf)) + memset (data, 0, extents->height * stride); + else + data = NULL, stride = 0; + + mask = pixman_image_create_bits (PIXMAN_a8, + extents->width, + extents->height, + (uint32_t *) data, + stride); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_CONVERTER; + } + } + + renderer.base.render_rows = _cairo_image_surface_span; + renderer.mask_stride = pixman_image_get_stride (mask); + renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); + if (dst != NULL) + renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; + else + renderer.mask_data -= dst_y * renderer.mask_stride + dst_x; + +#if USE_BOTOR_SCAN_CONVERTER + status = converter.base.generate (&converter.base, &renderer.base); +#else + status = converter->generate (converter, &renderer.base); +#endif + if (unlikely (status)) + goto CLEANUP_RENDERER; + + if (dst != NULL) { + pixman_image_t *src; + int src_x, src_y; + + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_RENDERER; + } + + pixman_image_composite (_pixman_operator (op), src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, /* mask.x, mask.y */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + pixman_image_unref (src); + } + + CLEANUP_RENDERER: + if (dst != NULL) + pixman_image_unref (mask); + CLEANUP_CONVERTER: +#if USE_BOTOR_SCAN_CONVERTER + converter.base.destroy (&converter.base); +#else + converter->destroy (converter); +#endif + return status; +} + +static cairo_status_t +_clip_and_composite_polygon (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (polygon->num_edges == 0) { + cairo_traps_t traps; + + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + _cairo_traps_init (&traps); + status = _clip_and_composite_trapezoids (dst, op, src, + &traps, antialias, + extents, clip); + _cairo_traps_fini (&traps); + + return status; + } + + _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) + return CAIRO_STATUS_SUCCESS; + + if (antialias != CAIRO_ANTIALIAS_NONE) { + composite_spans_info_t info; + + info.polygon = polygon; + info.fill_rule = fill_rule; + info.antialias = antialias; + + status = _clip_and_composite (dst, op, src, + _composite_spans, &info, + extents, clip); + } else { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_trapezoids (dst, op, src, + &traps, antialias, + extents, clip); + } + + _cairo_traps_fini (&traps); + } + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + surface->width, + surface->height, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (path->is_rectilinear) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_polygon (surface, op, source, &polygon, + CAIRO_FILL_RULE_WINDING, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + surface->width, + surface->height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (extents.is_bounded && clip != NULL) { + cairo_clip_path_t *clip_path; + + if (((clip_path = _clip_get_single_path (clip)) != NULL) && + _cairo_path_fixed_equal (&clip_path->path, path)) + { + clip = NULL; + } + } + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (_cairo_path_fixed_is_rectilinear_fill (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } else { + cairo_polygon_t polygon; + + assert (! path->is_empty_fill); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_polygon (surface, op, source, &polygon, + fill_rule, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_scaled_font_t *font; + cairo_glyph_t *glyphs; + int num_glyphs; +} composite_glyphs_info_t; + +static cairo_status_t +_composite_glyphs_via_mask (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_font_t *font = info->font; + cairo_glyph_t *glyphs = info->glyphs; + int num_glyphs = info->num_glyphs; + pixman_image_t *mask = NULL; + pixman_image_t *src; + pixman_image_t *white; + pixman_format_code_t mask_format = 0; /* silence gcc */ + cairo_status_t status; + int src_x, src_y; + int i; + + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + white = _pixman_white_image (); + if (unlikely (white == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_scaled_font_freeze_cache (font); + + for (i = 0; i < num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (font, glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + goto CLEANUP; + + glyph_surface = scaled_glyph->surface; + + if (glyph_surface->width == 0 || glyph_surface->height == 0) + continue; + + /* To start, create the mask using the format from the first + * glyph. Later we'll deal with different formats. */ + if (mask == NULL) { + mask_format = glyph_surface->pixman_format; + mask = pixman_image_create_bits (mask_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (PIXMAN_FORMAT_RGB (mask_format)) + pixman_image_set_component_alpha (mask, TRUE); + } + + /* If we have glyphs of different formats, we "upgrade" the mask + * to the wider of the formats. */ + if (glyph_surface->pixman_format != mask_format && + PIXMAN_FORMAT_BPP (mask_format) < + PIXMAN_FORMAT_BPP (glyph_surface->pixman_format)) + { + pixman_image_t *new_mask; + + mask_format = glyph_surface->pixman_format; + new_mask = pixman_image_create_bits (mask_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (new_mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + pixman_image_composite (PIXMAN_OP_SRC, + white, mask, new_mask, + 0, 0, 0, 0, 0, 0, + extents->width, extents->height); + + pixman_image_unref (mask); + mask = new_mask; + if (PIXMAN_FORMAT_RGB (mask_format)) + pixman_image_set_component_alpha (mask, TRUE); + } + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (glyphs[i].y - + glyph_surface->base.device_transform.y0); + if (glyph_surface->pixman_format == mask_format) { + pixman_image_composite (PIXMAN_OP_ADD, + glyph_surface->pixman_image, NULL, mask, + 0, 0, 0, 0, + x - extents->x, y - extents->y, + glyph_surface->width, + glyph_surface->height); + } else { + pixman_image_composite (PIXMAN_OP_ADD, + white, glyph_surface->pixman_image, mask, + 0, 0, 0, 0, + x - extents->x, y - extents->y, + glyph_surface->width, + glyph_surface->height); + } + } + + pixman_image_composite (_pixman_operator (op), + src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + +CLEANUP: + _cairo_scaled_font_thaw_cache (font); + pixman_image_unref (mask); + pixman_image_unref (src); + pixman_image_unref (white); + + return status; +} + +static cairo_status_t +_composite_glyphs (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_glyph_t *glyph_cache[64]; + pixman_op_t pixman_op = _pixman_operator (op); + pixman_image_t *src = NULL; + int src_x, src_y; + cairo_status_t status; + int i; + + if (pattern != NULL) { + src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + src_x -= dst_x; + src_y -= dst_y; + } else { + src = _pixman_white_image (); + src_x = src_y = 0; + } + + memset (glyph_cache, 0, sizeof (glyph_cache)); + status = CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (info->font); + for (i = 0; i < info->num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width && glyph_surface->height) { + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[i].y - + glyph_surface->base.device_transform.y0); + + pixman_image_composite (pixman_op, + src, glyph_surface->pixman_image, dst, + x + src_x, y + src_y, + 0, 0, + x - dst_x, y - dst_y, + glyph_surface->width, + glyph_surface->height); + } + } + _cairo_scaled_font_thaw_cache (info->font); + + pixman_image_unref (src); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *num_remaining) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + composite_glyphs_info_t glyph_info; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_bool_t overlap; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + surface->width, + surface->height, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + + have_clip = TRUE; + } + + glyph_info.font = scaled_font; + glyph_info.glyphs = glyphs; + glyph_info.num_glyphs = num_glyphs; + + status = _clip_and_composite (surface, op, source, + overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs, + &glyph_info, + &extents, clip); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + *num_remaining = 0; + return status; +} + +static cairo_bool_t +_cairo_image_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_image_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_image_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); +} + +/* legacy interface kept for compatibility until surface-fallback is removed */ +static cairo_status_t +_cairo_image_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_image_surface_t *surface = abstract_surface; + + image_rect_out->x = 0; + image_rect_out->y = 0; + image_rect_out->width = surface->width; + image_rect_out->height = surface->height; + + *image_out = surface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ +} + +static cairo_status_t +_cairo_image_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_image_surface_t *surface = abstract_surface; + + if (src->backend == surface->base.backend) { + *clone_offset_x = *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_image_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_image_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + pixman_image_t *src; + int src_offset_x, src_offset_y; + cairo_status_t status; + + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + extents.source.x = src_x; + extents.source.y = src_y; + extents.source.width = width; + extents.source.height = height; + + extents.mask.x = dst_x; + extents.mask.y = dst_y; + extents.mask.width = width; + extents.mask.height = height; + + extents.bounded.x = dst_x; + extents.bounded.y = dst_y; + extents.bounded.width = width; + extents.bounded.height = height; + + if (clip_region != NULL) { + cairo_region_get_extents (clip_region, &extents.unbounded); + } else { + extents.unbounded.x = 0; + extents.unbounded.y = 0; + extents.unbounded.width = dst->width; + extents.unbounded.height = dst->height; + } + + extents.is_bounded = _cairo_operator_bounded_by_either (op); + + src = _pixman_image_for_pattern (src_pattern, &extents.bounded, &src_offset_x, &src_offset_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (mask_pattern != NULL) { + pixman_image_t *mask; + int mask_offset_x, mask_offset_y; + + mask = _pixman_image_for_pattern (mask_pattern, &extents.bounded, &mask_offset_x, &mask_offset_y); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite (_pixman_operator (op), + src, mask, dst->pixman_image, + src_x + src_offset_x, + src_y + src_offset_y, + mask_x + mask_offset_x, + mask_y + mask_offset_y, + dst_x, dst_y, width, height); + + pixman_image_unref (mask); + } else { + pixman_image_composite (_pixman_operator (op), + src, NULL, dst->pixman_image, + src_x + src_offset_x, + src_y + src_offset_y, + 0, 0, + dst_x, dst_y, width, height); + } + + pixman_image_unref (src); + + if (! extents.is_bounded) + _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); + + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_image_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_image_surface_t *surface = abstract_surface; + + pixman_color_t pixman_color; + pixman_rectangle16_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (pixman_rectangle16_t)]; + pixman_rectangle16_t *pixman_rects = stack_rects; + int i; + + cairo_int_status_t status; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pixman_color.red = color->red_short; + pixman_color.green = color->green_short; + pixman_color.blue = color->blue_short; + pixman_color.alpha = color->alpha_short; + + if (num_rects > ARRAY_LENGTH (stack_rects)) { + pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t)); + if (unlikely (pixman_rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_rects; i++) { @@ -1228,7 +4039,6 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, pixman_rects[i].height = rects[i].height; } - /* XXX: pixman_fill_region() should be implemented */ status = CAIRO_STATUS_SUCCESS; if (! pixman_image_fill_rectangles (_pixman_operator (op), surface->pixman_image, @@ -1245,42 +4055,6 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, return status; } -static pixman_format_code_t -_pixman_mask_format_from_antialias (cairo_antialias_t antialias) -{ - if (antialias == CAIRO_ANTIALIAS_NONE) - return PIXMAN_a1; - return PIXMAN_a8; -} - -static void -_pixman_add_trapezoids (pixman_image_t *image, - int dst_x, int dst_y, - const cairo_trapezoid_t *traps, - int num_traps) -{ - while (num_traps--) { - pixman_trapezoid_t trap; - - trap.top = _cairo_fixed_to_16_16 (traps->top); - trap.bottom = _cairo_fixed_to_16_16 (traps->bottom); - - trap.left.p1.x = _cairo_fixed_to_16_16 (traps->left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (traps->left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (traps->left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (traps->left.p2.y); - - trap.right.p1.x = _cairo_fixed_to_16_16 (traps->right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (traps->right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (traps->right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (traps->right.p2.y); - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - - traps++; - } -} - static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t op, const cairo_pattern_t *pattern, @@ -1296,11 +4070,10 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, int num_traps, cairo_region_t *clip_region) { - cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src; - cairo_int_status_t status; - pixman_image_t *mask; + cairo_composite_rectangles_t extents; + composite_traps_info_t info; + cairo_status_t status; if (height == 0 || width == 0) return CAIRO_STATUS_SUCCESS; @@ -1308,101 +4081,66 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - /* Special case adding trapezoids onto a mask surface; we want to avoid - * creating an intermediate temporary mask unnecessarily. - * - * We make the assumption here that the portion of the trapezoids - * contained within the surface is bounded by [dst_x,dst_y,width,height]; - * the Cairo core code passes bounds based on the trapezoid extents. - * - * Currently the check clip_region == NULL is needed for correct - * functioning, since pixman_add_trapezoids() doesn't obey the - * surface clip, which is a libpixman bug , but there's no harm in - * falling through to the general case when the surface is clipped - * since libpixman would have to generate an intermediate mask anyways. - */ - if (op == CAIRO_OPERATOR_ADD && - clip_region == NULL && - _cairo_pattern_is_opaque_solid (pattern) && - dst->base.content == CAIRO_CONTENT_ALPHA && - antialias != CAIRO_ANTIALIAS_NONE) - { - _pixman_add_trapezoids (dst->pixman_image, 0, 0, traps, num_traps); - return CAIRO_STATUS_SUCCESS; - } + extents.source.x = src_x; + extents.source.y = src_y; + extents.source.width = width; + extents.source.height = height; - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; + extents.mask.x = dst_x; + extents.mask.y = dst_y; + extents.mask.width = width; + extents.mask.height = height; - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - src_x, src_y, width, height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); - if (unlikely (status)) - return status; + extents.bounded.x = dst_x; + extents.bounded.y = dst_y; + extents.bounded.width = width; + extents.bounded.height = height; - status = _cairo_image_surface_set_attributes (src, &attributes, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto CLEANUP_SOURCE; + cairo_region_get_extents (clip_region, &extents.unbounded); + extents.is_bounded = _cairo_operator_bounded_by_either (op); - mask = pixman_image_create_bits (_pixman_mask_format_from_antialias (antialias), - width, height, NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_SOURCE; + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; } - _pixman_add_trapezoids (mask, dst_x, dst_y, traps, num_traps); - - pixman_image_composite (_pixman_operator (op), - src->pixman_image, - mask, - dst->pixman_image, - src_x + attributes.x_offset, - src_y + attributes.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - pixman_image_unref (mask); + info.traps = traps; + info.num_traps = num_traps; + info.antialias = antialias; + status = _composite_traps (&info, + dst->pixman_image, + dst->pixman_format, + op, + pattern, + 0, 0, + &extents.bounded, + clip_region); - if (! _cairo_operator_bounded_by_mask (op)) { - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, - src->width, src->height, - width, height, - src_x, src_y, - 0, 0, - dst_x, dst_y, width, height, - clip_region); - } + if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded) + _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); - CLEANUP_SOURCE: - _cairo_pattern_release_surface (pattern, &src->base, &attributes); + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); return status; } -typedef struct _cairo_image_surface_span_renderer { +typedef struct _legacy_image_surface_span_renderer { cairo_span_renderer_t base; cairo_operator_t op; const cairo_pattern_t *pattern; cairo_antialias_t antialias; + cairo_region_t *clip_region; + pixman_image_t *mask; uint8_t *mask_data; uint32_t mask_stride; - cairo_image_surface_t *src; - cairo_surface_attributes_t src_attributes; - cairo_image_surface_t *mask; cairo_image_surface_t *dst; cairo_composite_rectangles_t composite_rectangles; -} cairo_image_surface_span_renderer_t; +} legacy_image_surface_span_renderer_t; void _cairo_image_surface_span_render_row ( @@ -1446,7 +4184,7 @@ _cairo_image_surface_span_renderer_render_rows ( const cairo_half_open_span_t *spans, unsigned num_spans) { - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; while (height--) _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); return CAIRO_STATUS_SUCCESS; @@ -1455,17 +4193,11 @@ _cairo_image_surface_span_renderer_render_rows ( static void _cairo_image_surface_span_renderer_destroy (void *abstract_renderer) { - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - if (!renderer) return; - - if (renderer->src != NULL) { - _cairo_pattern_release_surface (renderer->pattern, - &renderer->src->base, - &renderer->src_attributes); - } + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; + if (renderer == NULL) + return; - if (renderer->mask != NULL) - cairo_surface_destroy (&renderer->mask->base); + pixman_image_unref (renderer->mask); free (renderer); } @@ -1473,47 +4205,41 @@ _cairo_image_surface_span_renderer_destroy (void *abstract_renderer) static cairo_status_t _cairo_image_surface_span_renderer_finish (void *abstract_renderer) { - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - cairo_status_t status = CAIRO_STATUS_SUCCESS; + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; + cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; + cairo_image_surface_t *dst = renderer->dst; + pixman_image_t *src; + int src_x, src_y; - if (renderer->src == NULL || renderer->mask == NULL) - return CAIRO_STATUS_SUCCESS; + if (renderer->clip_region != NULL) { + cairo_status_t status; - status = cairo_surface_status (&renderer->mask->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; - cairo_image_surface_t *src = renderer->src; - cairo_image_surface_t *dst = renderer->dst; - cairo_surface_attributes_t *src_attributes = &renderer->src_attributes; - int width = rects->bounded.width; - int height = rects->bounded.height; + status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region); + if (unlikely (status)) + return status; + } + + src = _pixman_image_for_pattern (renderer->pattern, &rects->bounded, &src_x, &src_y); + if (src == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + + pixman_image_composite (_pixman_operator (renderer->op), + src, + renderer->mask, + dst->pixman_image, + rects->bounded.x + src_x, + rects->bounded.y + src_y, + 0, 0, + rects->bounded.x, rects->bounded.y, + rects->bounded.width, rects->bounded.height); + + if (! rects->is_bounded) + _cairo_image_surface_fixup_unbounded (dst, rects, NULL); + + if (renderer->clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); - pixman_image_composite (_pixman_operator (renderer->op), - src->pixman_image, - renderer->mask->pixman_image, - dst->pixman_image, - rects->bounded.x + src_attributes->x_offset, - rects->bounded.y + src_attributes->y_offset, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - width, height); - - if (! rects->is_bounded) { - status = _cairo_surface_composite_shape_fixup_unbounded ( - &dst->base, - src_attributes, - src->width, src->height, - width, height, - rects->bounded.x, rects->bounded.y, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - width, height, - dst->clip_region); - } - } - if (status != CAIRO_STATUS_SUCCESS) - return _cairo_span_renderer_set_error (abstract_renderer, - status); return CAIRO_STATUS_SUCCESS; } @@ -1539,14 +4265,10 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, cairo_region_t *clip_region) { cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_span_renderer_t *renderer = calloc(1, sizeof(*renderer)); - cairo_status_t status; - - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return _cairo_span_renderer_create_in_error (status); + legacy_image_surface_span_renderer_t *renderer; - if (renderer == NULL) + renderer = calloc(1, sizeof(*renderer)); + if (unlikely (renderer == NULL)) return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); renderer->base.destroy = _cairo_image_surface_span_renderer_destroy; @@ -1556,67 +4278,25 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op, renderer->pattern = pattern; renderer->antialias = antialias; renderer->dst = dst; + renderer->clip_region = clip_region; renderer->composite_rectangles = *rects; - status = _cairo_pattern_acquire_surface ( - renderer->pattern, &renderer->dst->base, - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &renderer->src, - &renderer->src_attributes); - if (status) - goto unwind; - - status = _cairo_image_surface_set_attributes ( - renderer->src, &renderer->src_attributes, - rects->bounded.x + rects->bounded.width/2, - rects->bounded.y + rects->bounded.height/2); - if (status) - goto unwind; - /* TODO: support rendering to A1 surfaces (or: go add span * compositing to pixman.) */ - renderer->mask = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_A8, - rects->bounded.width, - rects->bounded.height); - - status = cairo_surface_status (&renderer->mask->base); - - unwind: - if (status != CAIRO_STATUS_SUCCESS) { - _cairo_image_surface_span_renderer_destroy (renderer); - return _cairo_span_renderer_create_in_error (status); + renderer->mask = pixman_image_create_bits (PIXMAN_a8, + rects->bounded.width, + rects->bounded.height, + NULL, 0); + if (renderer->mask == NULL) { + free (renderer); + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); } - renderer->mask_data = renderer->mask->data - rects->bounded.x - rects->bounded.y * renderer->mask->stride; - renderer->mask_stride = renderer->mask->stride; - return &renderer->base; -} - -static cairo_bool_t -_cairo_image_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_image_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} + renderer->mask_stride = pixman_image_get_stride (renderer->mask); + renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride; -static void -_cairo_image_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + return &renderer->base; } /** @@ -1647,6 +4327,7 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { _cairo_image_surface_composite_trapezoids, _cairo_image_surface_create_span_renderer, _cairo_image_surface_check_span_renderer, + NULL, /* copy_page */ NULL, /* show_page */ _cairo_image_surface_get_extents, @@ -1657,11 +4338,12 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { NULL, /* font_fini */ NULL, /* glyph_fini */ - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ + _cairo_image_surface_paint, + _cairo_image_surface_mask, + _cairo_image_surface_stroke, + _cairo_image_surface_fill, + _cairo_image_surface_glyphs, + NULL, /* show_text_glyphs */ NULL, /* snapshot */ NULL, /* is_similar */ }; @@ -1673,7 +4355,6 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface, cairo_format_t format) { cairo_image_surface_t *clone; - cairo_surface_pattern_t pattern; cairo_status_t status; status = surface->base.status; @@ -1688,17 +4369,13 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface, if (unlikely (clone->base.status)) return clone; - _cairo_pattern_init_for_surface (&pattern, &surface->base); - status = _cairo_surface_paint (&clone->base, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (&clone->base); - return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); - } + pixman_image_composite (PIXMAN_OP_SRC, + surface->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + surface->width, surface->height); + clone->base.is_clear = FALSE; clone->base.device_transform = surface->base.device_transform; diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h index 2f483163a..c84cebf62 100644 --- a/src/cairo-mutex-list-private.h +++ b/src/cairo-mutex-list-private.h @@ -38,6 +38,8 @@ CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) +CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex) + CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index faa566549..666abc994 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -600,14 +600,8 @@ _get_image_surface (cairo_xcb_surface_t *surface, * which takes data in an arbitrary format and converts it * to something supported by that library. */ - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_masks (data, - &masks, - extents.width, - extents.height, - bytes_per_line); - if (image->base.status) - goto FAIL; + ASSERT_NOT_REACHED; + goto FAIL; } /* Let the surface take ownership of the data */ diff --git a/src/cairoint.h b/src/cairoint.h index c2d36e0c8..6e059c4ca 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -833,7 +833,6 @@ struct _cairo_image_surface { int depth; pixman_image_t *pixman_image; - cairo_region_t *clip_region; unsigned owns_data : 1; unsigned transparency : 2; @@ -2162,9 +2161,15 @@ _cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; cairo_private cairo_format_t _cairo_format_from_content (cairo_content_t content) cairo_const; +cairo_private cairo_format_t +_cairo_format_from_pixman_format (pixman_format_code_t pixman_format); + cairo_private cairo_content_t _cairo_content_from_format (cairo_format_t format) cairo_const; +cairo_private cairo_content_t +_cairo_content_from_pixman_format (pixman_format_code_t pixman_format); + cairo_private cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, pixman_format_code_t pixman_format); @@ -2184,25 +2189,11 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, int height, int stride); -cairo_private cairo_surface_t * -_cairo_image_surface_create_with_masks (unsigned char *data, - cairo_format_masks_t *format, - int width, - int height, - int stride); - cairo_private cairo_surface_t * _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height); -cairo_private cairo_surface_t * -_cairo_image_surface_create_for_data_with_content (unsigned char *data, - cairo_content_t content, - int width, - int height, - int stride); - cairo_private void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); diff --git a/test/clip-fill-unbounded.argb32.ref.png b/test/clip-fill-unbounded.argb32.ref.png index 9adf992cd..b87efd4fb 100644 Binary files a/test/clip-fill-unbounded.argb32.ref.png and b/test/clip-fill-unbounded.argb32.ref.png differ diff --git a/test/clip-fill-unbounded.rgb24.ref.png b/test/clip-fill-unbounded.rgb24.ref.png index e57e29693..915ebe19b 100644 Binary files a/test/clip-fill-unbounded.rgb24.ref.png and b/test/clip-fill-unbounded.rgb24.ref.png differ diff --git a/test/clip-stroke-unbounded.argb32.ref.png b/test/clip-stroke-unbounded.argb32.ref.png index 39b9a301b..e48537f64 100644 Binary files a/test/clip-stroke-unbounded.argb32.ref.png and b/test/clip-stroke-unbounded.argb32.ref.png differ diff --git a/test/clip-stroke-unbounded.rgb24.ref.png b/test/clip-stroke-unbounded.rgb24.ref.png index a30352ebf..d2880f10c 100644 Binary files a/test/clip-stroke-unbounded.rgb24.ref.png and b/test/clip-stroke-unbounded.rgb24.ref.png differ diff --git a/test/clip-stroke.ref.png b/test/clip-stroke.ref.png index 7af2e9bd8..dd5ae9a39 100644 Binary files a/test/clip-stroke.ref.png and b/test/clip-stroke.ref.png differ diff --git a/test/clipped-group.ref.png b/test/clipped-group.ref.png index b25c9f4db..c3fcbf81d 100644 Binary files a/test/clipped-group.ref.png and b/test/clipped-group.ref.png differ diff --git a/test/leaky-dashed-rectangle.ref.png b/test/leaky-dashed-rectangle.ref.png index c0ba7b2c0..05f45846a 100644 Binary files a/test/leaky-dashed-rectangle.ref.png and b/test/leaky-dashed-rectangle.ref.png differ diff --git a/test/scale-offset-image.xfail.png b/test/scale-offset-image.xfail.png index fef3a3995..f0db601fc 100644 Binary files a/test/scale-offset-image.xfail.png and b/test/scale-offset-image.xfail.png differ diff --git a/test/scale-offset-similar.xfail.png b/test/scale-offset-similar.xfail.png index fef3a3995..f0db601fc 100644 Binary files a/test/scale-offset-similar.xfail.png and b/test/scale-offset-similar.xfail.png differ diff --git a/test/self-intersecting.ref.png b/test/self-intersecting.ref.png index 32d143987..d554d83ee 100644 Binary files a/test/self-intersecting.ref.png and b/test/self-intersecting.ref.png differ