diff options
Diffstat (limited to 'src/cairo-surface.c')
-rw-r--r-- | src/cairo-surface.c | 540 |
1 files changed, 369 insertions, 171 deletions
diff --git a/src/cairo-surface.c b/src/cairo-surface.c index a457d2062..330d58b1e 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -151,16 +151,151 @@ _cairo_surface_pixels_per_inch (cairo_surface_t *surface) return surface->backend->pixels_per_inch (surface); } -cairo_image_surface_t * -_cairo_surface_get_image (cairo_surface_t *surface) +/** + * _cairo_surface_acquire_source_image: + * @surface: a #cairo_surface_t + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_extra: location to store image specific backend data + * + * Gets an image surface to use when drawing as a fallback when drawing with + * @surface as a source. _cairo_surface_release_source_image() must be called + * when finished. + * + * Return value: %CAIRO_STATUS_SUCCESS if a an image was stored in @image_out. + * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified + * surface. Or %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra) { - return surface->backend->get_image (surface); + return surface->backend->acquire_source_image (surface, image_out, image_extra); } +/** + * _cairo_surface_release_source_image: + * @surface: a #cairo_surface_t + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Releases any resources obtained with _cairo_surface_acquire_source_image() + **/ +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + surface->backend->release_source_image (surface, image, image_extra); +} + +/** + * _cairo_surface_acquire_dest_image: + * @surface: a #cairo_surface_t + * @interest_rect: area of @surface for which fallback drawing is being done. + * A value of %NULL indicates that the entire surface is desired. + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_rect: location to store area of the original surface occupied + * by the surface stored in @image. + * @image_extra: location to store image specific backend data + * + * Retrieves a local image for a surface for implementing a fallback drawing + * operation. After calling this function, the implementation of the fallback + * drawing operation draws the primitive to the surface stored in @image_out + * then calls _cairo_surface_release_dest_fallback(), + * which, if a temporary surface was created, copies the bits back to the + * main surface and frees the temporary surface. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that + * the backend can't draw with fallbacks. It's possible for the routine + * to store NULL in @local_out and return %CAIRO_STATUS_SUCCESS; + * that indicates that no part of @interest_rect is visible, so no drawing + * is necessary. _cairo_surface_release_dest_fallback() should not be called in that + * case. + **/ +cairo_status_t +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) +{ + return surface->backend->acquire_dest_image (surface, interest_rect, + image_out, image_rect, image_extra); +} + +/** + * _cairo_surface_end_fallback: + * @surface: a #cairo_surface_t + * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() + * @image: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if + * necessary, copying the image from @image back to @surface and freeing any + * resources that were allocated. + **/ +void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + surface->backend->release_dest_image (surface, interest_rect, + image, image_rect, image_extra); +} + +/** + * _cairo_surface_clone_similar: + * @surface: a #cairo_surface_t + * @src: the source image + * @clone_out: location to store a surface compatible with @surface + * and with contents identical to @src. The caller must call + * cairo_surface_destroy() on the result. + * + * Creates a surface with contents identical to @src but that + * can be used efficiently with @surface. If @surface and @src are + * already compatible then it may return a new reference to @src. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored + * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another + * error like %CAIRO_STATUS_NO_MEMORY. + **/ cairo_status_t -_cairo_surface_set_image (cairo_surface_t *surface, cairo_image_surface_t *image) +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - return surface->backend->set_image (surface, image); + cairo_status_t status; + cairo_image_surface_t *image; + void *image_extra; + + status = surface->backend->clone_similar (surface, src, clone_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + status = surface->backend->clone_similar (surface, &image->base, clone_out); + + /* If the above failed point, we could implement a full fallback + * using acquire_dest_image, but that's going to be very + * inefficient compared to a backend-specific implementation of + * clone_similar() with an image source. So we don't bother + */ + + _cairo_surface_release_source_image (src, image, image_extra); + return status; } cairo_status_t @@ -169,9 +304,7 @@ cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) if (surface == NULL) return CAIRO_STATUS_NULL_POINTER; - cairo_matrix_copy (&surface->matrix, matrix); - - return surface->backend->set_matrix (surface, matrix); + return cairo_matrix_copy (&surface->matrix, matrix); } slim_hidden_def(cairo_surface_set_matrix); @@ -192,7 +325,7 @@ cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) return CAIRO_STATUS_NULL_POINTER; surface->filter = filter; - return surface->backend->set_filter (surface, filter); + return CAIRO_STATUS_SUCCESS; } cairo_filter_t @@ -224,14 +357,81 @@ cairo_surface_set_repeat (cairo_surface_t *surface, int repeat) surface->repeat = repeat; - return surface->backend->set_repeat (surface, repeat); + return CAIRO_STATUS_SUCCESS; } slim_hidden_def(cairo_surface_set_repeat); +typedef struct { + cairo_surface_t *dst; + cairo_rectangle_t extents; + cairo_image_surface_t *image; + cairo_rectangle_t image_rect; + void *image_extra; +} fallback_state_t; + +static cairo_status_t +_fallback_init (fallback_state_t *state, + cairo_surface_t *dst, + int x, + int y, + int width, + int height) +{ + state->extents.x = x; + state->extents.y = y; + state->extents.width = width; + state->extents.height = height; + + state->dst = dst; + + return _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, &state->image_extra); +} + +static void +_fallback_cleanup (fallback_state_t *state) +{ + _cairo_surface_release_dest_image (state->dst, &state->extents, + state->image, &state->image_rect, state->image_extra); +} + +static cairo_status_t +_fallback_composite (cairo_operator_t operator, + cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *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) +{ + fallback_state_t state; + cairo_status_t status; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; + + state.image->base.backend->composite (operator, src, mask, + &state.image->base, + src_x, src_y, mask_x, mask_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height); + + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_composite (cairo_operator_t operator, - cairo_surface_t *src, - cairo_surface_t *mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -243,7 +443,6 @@ _cairo_surface_composite (cairo_operator_t operator, unsigned int height) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *mask_image = 0, *dst_image; status = dst->backend->composite (operator, src, mask, dst, @@ -254,28 +453,12 @@ _cairo_surface_composite (cairo_operator_t operator, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - if (mask) - mask_image = _cairo_surface_get_image (mask); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite (operator, - &src_image->base, - mask ? &mask_image->base : NULL, - dst_image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - if (mask) - cairo_surface_destroy (&mask_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite (operator, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); } cairo_status_t @@ -297,6 +480,77 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, return _cairo_surface_fill_rectangles (surface, operator, color, &rect, 1); } +static cairo_status_t +_fallback_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + fallback_state_t state; + cairo_rectangle_t *offset_rects = NULL; + cairo_status_t status; + int x1, y1, x2, y2; + int i; + + if (num_rects <= 0) + return CAIRO_STATUS_SUCCESS; + + /* Compute the bounds of the rectangles, so that we know what area of the + * destination surface to fetch + */ + x1 = rects[0].x; + y1 = rects[0].y; + x2 = rects[0].x + rects[0].width; + y2 = rects[0].y + rects[0].height; + + for (i = 1; i < num_rects; i++) { + if (rects[0].x < x1) + x1 = rects[0].x; + if (rects[0].y < y1) + y1 = rects[0].y; + + if (rects[0].x + rects[0].width > x2) + x2 = rects[0].x + rects[0].width; + if (rects[0].y + rects[0].height > y2) + y2 = rects[0].y + rects[0].height; + } + + status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); + if (!CAIRO_OK (status) || !state.image) + return status; + + /* If the fetched image isn't at 0,0, we need to offset the rectangles */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + offset_rects = malloc (sizeof (cairo_rectangle_t) * num_rects); + if (!offset_rects) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + for (i = 0; i < num_rects; i++) { + offset_rects[i].x = rects[i].x - state.image_rect.x; + offset_rects[i].y = rects[i].y - state.image_rect.y; + offset_rects[i].width = rects[i].width; + offset_rects[i].height = rects[i].height; + } + + rects = offset_rects; + } + + state.image->base.backend->fill_rectangles (&state.image->base, operator, color, + rects, num_rects); + + if (offset_rects) + free (offset_rects); + + FAIL: + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_operator_t operator, @@ -305,7 +559,6 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, int num_rects) { cairo_int_status_t status; - cairo_image_surface_t *surface_image; if (num_rects == 0) return CAIRO_STATUS_SUCCESS; @@ -317,54 +570,105 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - surface_image = _cairo_surface_get_image (surface); + return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); +} + +static cairo_status_t +_fallback_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) +{ + fallback_state_t state; + cairo_trapezoid_t *offset_traps = NULL; + cairo_status_t status; + int i; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; - surface_image->base.backend->fill_rectangles (surface_image, - operator, - color, - rects, num_rects); + /* If the destination image isn't at 0,0, we need to offset the trapezoids */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + + cairo_fixed_t xoff = _cairo_fixed_from_int (state.image_rect.x); + cairo_fixed_t yoff = _cairo_fixed_from_int (state.image_rect.y); + + offset_traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); + if (!offset_traps) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } - status = _cairo_surface_set_image (surface, surface_image); + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = traps[i].top - yoff; + offset_traps[i].bottom = traps[i].bottom - yoff; + offset_traps[i].left.p1.x = traps[i].left.p1.x - xoff; + offset_traps[i].left.p1.y = traps[i].left.p1.y - yoff; + offset_traps[i].left.p2.x = traps[i].left.p2.x - xoff; + offset_traps[i].left.p2.y = traps[i].left.p2.y - yoff; + offset_traps[i].right.p1.x = traps[i].right.p1.x - xoff; + offset_traps[i].right.p1.y = traps[i].right.p1.y - yoff; + offset_traps[i].right.p2.x = traps[i].right.p2.x - xoff; + offset_traps[i].right.p2.y = traps[i].right.p2.y - yoff; + } - cairo_surface_destroy (&surface_image->base); + traps = offset_traps; + } + state.image->base.backend->composite_trapezoids (operator, pattern, + &state.image->base, + src_x, src_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height, traps, num_traps); + if (offset_traps) + free (offset_traps); + + FAIL: + _fallback_cleanup (&state); + return status; } + cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *src, + cairo_pattern_t *pattern, cairo_surface_t *dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *dst_image; status = dst->backend->composite_trapezoids (operator, - src, dst, - x_src, y_src, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, traps, num_traps); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite_trapezoids (operator, - &src_image->base, - dst_image, - x_src, y_src, - traps, num_traps); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite_trapezoids (operator, pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps); } cairo_status_t @@ -402,109 +706,3 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *reg { return surface->backend->set_clip_region (surface, region); } - -cairo_status_t -_cairo_surface_create_pattern (cairo_surface_t *surface, - cairo_pattern_t *pattern, - cairo_box_t *box) -{ - cairo_int_status_t status; - - status = surface->backend->create_pattern (surface, pattern, box); - - /* The backend cannot accelerate this pattern, lets create an - unaccelerated source instead. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - - status = CAIRO_STATUS_SUCCESS; - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_image_surface_t *image; - - image = _cairo_pattern_get_image (pattern, box); - if (image) { - pattern->source = &image->base; - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - - } break; - case CAIRO_PATTERN_SOLID: - pattern->source = - _cairo_surface_create_similar_solid (surface, - CAIRO_FORMAT_ARGB32, - 1, 1, - &pattern->color); - if (pattern->source) { - cairo_surface_set_repeat (pattern->source, 1); - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - break; - case CAIRO_PATTERN_SURFACE: - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* handle pattern opacity */ - if (pattern->color.alpha != 1.0) { - double x = box->p1.x >> 16; - double y = box->p1.y >> 16; - int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16); - int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16); - cairo_pattern_t alpha; - - pattern->source = - cairo_surface_create_similar (surface, - CAIRO_FORMAT_ARGB32, - width, height); - if (pattern->source) { - _cairo_pattern_init_solid (&alpha, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&alpha, pattern->color.alpha); - - status = _cairo_surface_create_pattern (pattern->source, - &alpha, box); - - if (status == CAIRO_STATUS_SUCCESS) { - int save_repeat = pattern->u.surface.surface->repeat; - - if (pattern->extend == CAIRO_EXTEND_REPEAT || - pattern->u.surface.surface->repeat == 1) - cairo_surface_set_repeat (pattern->u.surface.surface, 1); - else - cairo_surface_set_repeat (pattern->u.surface.surface, 0); - - status = - _cairo_surface_composite (CAIRO_OPERATOR_OVER, - pattern->u.surface.surface, - alpha.source, - pattern->source, - 0, 0, 0, 0, 0, 0, - width, height); - - cairo_surface_set_repeat (pattern->u.surface.surface, - save_repeat); - - if (status == CAIRO_STATUS_SUCCESS) - _cairo_pattern_set_source_offset (pattern, x, y); - else - cairo_surface_destroy (pattern->source); - } - - _cairo_pattern_fini (&alpha); - } - } - - if (status != CAIRO_STATUS_SUCCESS) { - pattern->source = pattern->u.surface.surface; - cairo_surface_reference (pattern->u.surface.surface); - - return CAIRO_STATUS_SUCCESS; - } - break; - } - } - - return status; -} |