diff options
Diffstat (limited to 'src/cairo-image-surface.c')
-rw-r--r-- | src/cairo-image-surface.c | 134 |
1 files changed, 112 insertions, 22 deletions
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index b21cf122..f7dbc8cf 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -69,7 +69,8 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->format = format; surface->data = (unsigned char *) pixman_image_get_data (pixman_image); - surface->owns_data = 0; + surface->owns_data = FALSE; + surface->has_clip = FALSE; surface->width = pixman_image_get_width (pixman_image); surface->height = pixman_image_get_height (pixman_image); @@ -408,8 +409,7 @@ _cairo_image_surface_clone_similar (void *abstract_surface, cairo_image_surface_t *surface = abstract_surface; if (src->backend == surface->base.backend) { - *clone_out = src; - cairo_surface_reference (src); + *clone_out = cairo_surface_reference (src); return CAIRO_STATUS_SUCCESS; } @@ -614,6 +614,16 @@ _cairo_image_surface_composite (cairo_operator_t operator, } } + if (!_cairo_operator_bounded (operator)) + _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); + if (mask) _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); @@ -645,10 +655,25 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } +static cairo_bool_t +_cairo_image_surface_is_alpha_only (cairo_image_surface_t *surface) +{ + int bpp, alpha, red, green, blue; + + if (surface->format != (cairo_format_t) -1) + return surface->format == CAIRO_FORMAT_A1 || surface->format == CAIRO_FORMAT_A8; + + pixman_format_get_masks (pixman_image_get_format (surface->pixman_image), + &bpp, &alpha, &red, &green, &blue); + + return red == 0 && blue == 0 && green == 0; +} + static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, void *abstract_dst, + cairo_antialias_t antialias, int src_x, int src_y, int dst_x, @@ -662,8 +687,35 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; cairo_int_status_t status; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; + pixman_image_t *mask; + pixman_format_t *format; + pixman_bits_t *mask_data; + int mask_stride; + int mask_bpp; + + /* Special case adding trapezoids onto a mask surface; we want to avoid + * creating an intermediate temporary mask unecessarily. + * + * 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 surface->has_clip 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 (operator == CAIRO_OPERATOR_ADD && + _cairo_pattern_is_opaque_solid (pattern) && + _cairo_image_surface_is_alpha_only (dst) && + !dst->has_clip && + antialias != CAIRO_ANTIALIAS_NONE) + { + pixman_add_trapezoids (dst->pixman_image, 0, 0, + (pixman_trapezoid_t *) traps, num_traps); + return CAIRO_STATUS_SUCCESS; + } status = _cairo_pattern_acquire_surface (pattern, &dst->base, src_x, src_y, width, height, @@ -672,28 +724,64 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, if (status) return status; - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); + status = _cairo_image_surface_set_attributes (src, &attributes); + if (status) + goto CLEANUP_SOURCE; + + switch (antialias) { + case CAIRO_ANTIALIAS_NONE: + format = pixman_format_create (PIXMAN_FORMAT_NAME_A1); + mask_stride = (width + 31)/8; + mask_bpp = 1; + break; + default: + format = pixman_format_create (PIXMAN_FORMAT_NAME_A8); + mask_stride = (width + 3) & ~3; + mask_bpp = 8; + break; + } + if (!format) { + status = CAIRO_STATUS_NO_MEMORY; + goto CLEANUP_SOURCE; + } + + /* The image must be initially transparent */ + mask_data = calloc (1, mask_stride * height); + if (!mask_data) { + status = CAIRO_STATUS_NO_MEMORY; + pixman_format_destroy (format); + goto CLEANUP_SOURCE; } - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; + mask = pixman_image_create_for_data (mask_data, format, width, height, + mask_bpp, mask_stride); + pixman_format_destroy (format); + if (!mask) { + status = CAIRO_STATUS_NO_MEMORY; + goto CLEANUP_IMAGE_DATA; + } /* XXX: The pixman_trapezoid_t cast is evil and needs to go away * somehow. */ - status = _cairo_image_surface_set_attributes (src, &attributes); - if (status == CAIRO_STATUS_SUCCESS) - pixman_composite_trapezoids (_pixman_operator (operator), - src->pixman_image, - dst->pixman_image, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - (pixman_trapezoid_t *) traps, num_traps); - + pixman_add_trapezoids (mask, - dst_x, - dst_y, + (pixman_trapezoid_t *) traps, num_traps); + + pixman_composite (_pixman_operator (operator), + 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_destroy (mask); + + CLEANUP_IMAGE_DATA: + free (mask_data); + + CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; @@ -714,6 +802,8 @@ _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, { pixman_image_set_clip_region (surface->pixman_image, region); + surface->has_clip = region != NULL; + return CAIRO_STATUS_SUCCESS; } |