diff options
author | Loïc Minier <lool@dooz.org> | 2009-04-14 23:42:00 +0200 |
---|---|---|
committer | Loïc Minier <lool@dooz.org> | 2009-04-14 23:42:00 +0200 |
commit | ca1b557e8866801a5f43fd265be7421f60f54a70 (patch) | |
tree | fbaf48dd6c0c8b67afb3a3ed8534b69e417d5336 /src/cairo-surface.c | |
parent | e781cc575ab4adb1d9822c628fc9f19ed35b0c88 (diff) |
Imported Upstream version 0.6.0
Diffstat (limited to 'src/cairo-surface.c')
-rw-r--r-- | src/cairo-surface.c | 427 |
1 files changed, 342 insertions, 85 deletions
diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 318ee87..11fbc60 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -40,6 +40,97 @@ #include "cairoint.h" #include "cairo-gstate-private.h" +const cairo_surface_t _cairo_surface_nil = { + &cairo_image_surface_backend, /* backend */ + -1, /* ref_count */ + CAIRO_STATUS_NO_MEMORY, /* status */ + FALSE, /* finished */ + { 0, /* size */ + 0, /* num_elements */ + 0, /* element_size */ + NULL, /* elements */ + }, /* user_data */ + 0.0, /* device_x_offset */ + 0.0, /* device_y_offset */ + 0, /* next_clip_serial */ + 0 /* current_clip_serial */ +}; + +const cairo_surface_t _cairo_surface_nil_file_not_found = { + &cairo_image_surface_backend, /* backend */ + -1, /* ref_count */ + CAIRO_STATUS_FILE_NOT_FOUND, /* status */ + FALSE, /* finished */ + { 0, /* size */ + 0, /* num_elements */ + 0, /* element_size */ + NULL, /* elements */ + }, /* user_data */ + 0.0, /* device_x_offset */ + 0.0, /* device_y_offset */ + 0, /* next_clip_serial */ + 0 /* current_clip_serial */ +}; + +const cairo_surface_t _cairo_surface_nil_read_error = { + &cairo_image_surface_backend, /* backend */ + -1, /* ref_count */ + CAIRO_STATUS_READ_ERROR, /* status */ + FALSE, /* finished */ + { 0, /* size */ + 0, /* num_elements */ + 0, /* element_size */ + NULL, /* elements */ + }, /* user_data */ + 0.0, /* device_x_offset */ + 0.0, /* device_y_offset */ + 0, /* next_clip_serial */ + 0 /* current_clip_serial */ +}; + +/** + * _cairo_surface_set_error: + * @surface: a surface + * @status: a status value indicating an error, (eg. not + * CAIRO_STATUS_SUCCESS) + * + * Sets surface->status to @status and calls _cairo_error; + * + * All assignments of an error status to surface->status should happen + * through _cairo_surface_set_error() or else _cairo_error() should be + * called immediately after the assignment. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +static void +_cairo_surface_set_error (cairo_surface_t *surface, + cairo_status_t status) +{ + surface->status = status; + + _cairo_error (status); +} + +/** + * cairo_surface_status: + * @surface: a #cairo_surface_t + * + * Checks whether an error has previously occurred for this + * surface. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER, + * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR, + * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALUE_FORMAT, or + * %CAIRO_STATUS_INVALID_VISUAL. + **/ +cairo_status_t +cairo_surface_status (cairo_surface_t *surface) +{ + return surface->status; +} + void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend) @@ -47,6 +138,7 @@ _cairo_surface_init (cairo_surface_t *surface, surface->backend = backend; surface->ref_count = 1; + surface->status = CAIRO_STATUS_SUCCESS; surface->finished = FALSE; _cairo_user_data_array_init (&surface->user_data); @@ -59,34 +151,63 @@ _cairo_surface_init (cairo_surface_t *surface, } cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_format_t format, +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, int width, int height) { - if (other == NULL) - return NULL; + cairo_format_t format = _cairo_format_from_content (content); - return other->backend->create_similar (other, format, width, height); + if (other->status) + return (cairo_surface_t*) &_cairo_surface_nil; + + if (other->backend->create_similar) + return other->backend->create_similar (other, content, width, height); + else + return cairo_image_surface_create (format, width, height); } +/** + * cairo_surface_create_similar: + * @other: an existing surface used to select the backend of the new surface + * @content: the content for the new surface + * @width: width of the new surface, (in device-space units) + * @height: height of the new surface (in device-space units) + * + * Create a new surface that is as compatible as possible with an + * existing surface. The new surface will use the same backend as + * @other unless that is not possible for some reason. + * + * Return value: a pointer to the newly allocated surface. The caller + * owns the surface and should call cairo_surface_destroy when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if @other is already in an error state + * or any other error occurs. + **/ cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_format_t format, +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, int width, int height) { - if (other == NULL) - return NULL; + if (other->status) + return (cairo_surface_t*) &_cairo_surface_nil; - return _cairo_surface_create_similar_solid (other, format, + if (! CAIRO_CONTENT_VALID (content)) { + _cairo_error (CAIRO_STATUS_INVALID_CONTENT); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + return _cairo_surface_create_similar_solid (other, content, width, height, CAIRO_COLOR_TRANSPARENT); } cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, + cairo_content_t content, int width, int height, const cairo_color_t *color) @@ -94,18 +215,20 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_status_t status; cairo_surface_t *surface; - surface = _cairo_surface_create_similar_scratch (other, format, + surface = _cairo_surface_create_similar_scratch (other, content, width, height); - - if (surface == NULL) - surface = cairo_image_surface_create (format, width, height); + if (surface->status) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } status = _cairo_surface_fill_rectangle (surface, CAIRO_OPERATOR_SOURCE, color, 0, 0, width, height); if (status) { cairo_surface_destroy (surface); - return NULL; + _cairo_error (status); + return (cairo_surface_t*) &_cairo_surface_nil; } return surface; @@ -128,6 +251,9 @@ cairo_surface_reference (cairo_surface_t *surface) if (surface == NULL) return; + if (surface->ref_count == (unsigned int)-1) + return; + surface->ref_count++; } @@ -137,6 +263,9 @@ cairo_surface_destroy (cairo_surface_t *surface) if (surface == NULL) return; + if (surface->ref_count == (unsigned int)-1) + return; + surface->ref_count--; if (surface->ref_count) return; @@ -158,36 +287,37 @@ slim_hidden_def(cairo_surface_destroy); * that cairo will no longer access the drawable, which can be freed. * After calling cairo_surface_finish() the only valid operations on a * surface are getting and setting user data and referencing and - * destroying it. Further drawing the the surface will not affect the - * surface but set the surface status to - * CAIRO_STATUS_SURFACE_FINISHED. + * destroying it. Further drawing to the surface will not affect the + * surface but will instead trigger a CAIRO_STATUS_SURFACE_FINISHED + * error. * * When the last call to cairo_surface_destroy() decreases the * reference count to zero, cairo will call cairo_surface_finish() if * it hasn't been called already, before freeing the resources * associated with the surface. - * - * Return value: CAIRO_STATUS_SUCCESS if the surface was finished - * successfully, otherwise CAIRO_STATUS_NO_MEMORY or - * CAIRO_STATUS_WRITE_ERROR. **/ -cairo_status_t +void cairo_surface_finish (cairo_surface_t *surface) { cairo_status_t status; - if (surface->finished) - return CAIRO_STATUS_SURFACE_FINISHED; + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } - if (surface->backend->finish) { - status = surface->backend->finish (surface); - if (status) - return status; + if (surface->backend->finish == NULL) { + surface->finished = TRUE; + return; + } + + status = surface->backend->finish (surface); + if (status) { + _cairo_surface_set_error (surface, status); + return; } surface->finished = TRUE; - - return CAIRO_STATUS_SUCCESS; } /** @@ -232,11 +362,37 @@ cairo_surface_set_user_data (cairo_surface_t *surface, void *user_data, cairo_destroy_func_t destroy) { + if (surface->ref_count == -1) + return CAIRO_STATUS_NO_MEMORY; + return _cairo_user_data_array_set_data (&surface->user_data, key, user_data, destroy); } /** + * cairo_surface_get_font_options: + * @surface: a #cairo_surface_t + * @options: a #cairo_font_options_t object into which to store + * the retrieved options. All existing values are overwritten + * + * Retrieves the default font rendering options for the surface. + * This allows display surfaces to report the correct subpixel order + * for rendering on them, print surfaces to disable hinting of + * metrics and so forth. The result can then be used with + * cairo_scaled_font_create(). + **/ +void +cairo_surface_get_font_options (cairo_surface_t *surface, + cairo_font_options_t *options) +{ + if (!surface->finished && surface->backend->get_font_options) { + surface->backend->get_font_options (surface, options); + } else { + _cairo_font_options_init_default (options); + } +} + +/** * cairo_surface_set_device_offset: * @surface: a #cairo_surface_t * @x_offset: the offset in the X direction, in device units @@ -259,6 +415,16 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, double y_offset) { + if (surface->status) { + _cairo_surface_set_error (surface, surface->status); + return; + } + + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + surface->device_x_offset = x_offset; surface->device_y_offset = y_offset; } @@ -280,7 +446,7 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, * %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_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra) @@ -297,7 +463,7 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, * * Releases any resources obtained with _cairo_surface_acquire_source_image() **/ -cairo_private void +void _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra) @@ -433,7 +599,17 @@ typedef struct { void *image_extra; } fallback_state_t; -static cairo_status_t +/** + * _fallback_init: + * + * Acquire destination image surface needed for an image-based + * fallback. + * + * Return value: CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not + * visible, CAIRO_STATUS_SUCCESS if some portion is visible and all + * went well, or some error status otherwise. + **/ +static cairo_int_status_t _fallback_init (fallback_state_t *state, cairo_surface_t *dst, int x, @@ -441,6 +617,8 @@ _fallback_init (fallback_state_t *state, int width, int height) { + cairo_status_t status; + state->extents.x = x; state->extents.y = y; state->extents.width = width; @@ -448,15 +626,29 @@ _fallback_init (fallback_state_t *state, state->dst = dst; - return _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, &state->image_extra); + status = _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, + &state->image_extra); + if (status) + return status; + + /* XXX: This NULL value tucked away in state->image is a rather + * ugly interface. Cleaner would be to push the + * CAIRO_INT_STATUS_NOTHING_TO_DO value down into + * _cairo_surface_acquire_dest_image and its backend + * counterparts. */ + if (state->image == NULL) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + return CAIRO_STATUS_SUCCESS; } static void -_fallback_cleanup (fallback_state_t *state) +_fallback_fini (fallback_state_t *state) { _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, state->image_extra); + state->image, &state->image_rect, + state->image_extra); } static cairo_status_t @@ -477,18 +669,20 @@ _fallback_composite (cairo_operator_t operator, cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (status || !state.image) + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; 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); + 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_fini (&state); - _fallback_cleanup (&state); - return status; } @@ -508,6 +702,9 @@ _cairo_surface_composite (cairo_operator_t operator, { cairo_int_status_t status; + if (dst->status) + return dst->status; + if (dst->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -541,6 +738,9 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, { cairo_rectangle_t rect; + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -589,16 +789,19 @@ _fallback_fill_rectangles (cairo_surface_t *surface, } status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); - if (status || !state.image) + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; 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) { + if (offset_rects == NULL) { status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + goto DONE; } for (i = 0; i < num_rects; i++) { @@ -611,15 +814,15 @@ _fallback_fill_rectangles (cairo_surface_t *surface, rects = offset_rects; } - state.image->base.backend->fill_rectangles (&state.image->base, operator, color, - rects, num_rects); + status = state.image->base.backend->fill_rectangles (&state.image->base, + operator, color, + rects, num_rects); - if (offset_rects) - free (offset_rects); + free (offset_rects); + + DONE: + _fallback_fini (&state); - FAIL: - _fallback_cleanup (&state); - return status; } @@ -632,6 +835,9 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, { cairo_int_status_t status; + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -650,7 +856,7 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); } -cairo_private cairo_int_status_t +cairo_int_status_t _cairo_surface_fill_path (cairo_operator_t operator, cairo_pattern_t *pattern, cairo_surface_t *dst, @@ -658,11 +864,11 @@ _cairo_surface_fill_path (cairo_operator_t operator, cairo_fill_rule_t fill_rule, double tolerance) { - if (dst->backend->fill_path) - return dst->backend->fill_path (operator, pattern, dst, path, - fill_rule, tolerance); - else - return CAIRO_INT_STATUS_UNSUPPORTED; + if (dst->backend->fill_path) + return dst->backend->fill_path (operator, pattern, dst, path, + fill_rule, tolerance); + else + return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -685,8 +891,11 @@ _fallback_composite_trapezoids (cairo_operator_t operator, int i; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (status || !state.image) + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; return status; + } /* If the destination image isn't at 0,0, we need to offset the trapezoids */ @@ -698,7 +907,7 @@ _fallback_composite_trapezoids (cairo_operator_t operator, offset_traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); if (!offset_traps) { status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + goto DONE; } for (i = 0; i < num_traps; i++) { @@ -726,13 +935,12 @@ _fallback_composite_trapezoids (cairo_operator_t operator, if (offset_traps) free (offset_traps); - FAIL: - _fallback_cleanup (&state); + DONE: + _fallback_fini (&state); return status; } - cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, @@ -748,6 +956,9 @@ _cairo_surface_composite_trapezoids (cairo_operator_t operator, { cairo_int_status_t status; + if (dst->status) + return dst->status; + if (dst->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -762,16 +973,19 @@ _cairo_surface_composite_trapezoids (cairo_operator_t operator, return status; } - return _fallback_composite_trapezoids (operator, pattern, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps); + return _fallback_composite_trapezoids (operator, pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps); } cairo_status_t _cairo_surface_copy_page (cairo_surface_t *surface) { + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -779,12 +993,15 @@ _cairo_surface_copy_page (cairo_surface_t *surface) if (surface->backend->copy_page == NULL) return CAIRO_STATUS_SUCCESS; - return surface->backend->copy_page (surface); + return surface->backend->copy_page (surface); } cairo_status_t _cairo_surface_show_page (cairo_surface_t *surface) { + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -804,7 +1021,7 @@ _cairo_surface_show_page (cairo_surface_t *surface) * verify that the correct clip is set in the surface before * invoking any surface drawing function */ -cairo_private unsigned int +unsigned int _cairo_surface_get_current_clip_serial (cairo_surface_t *surface) { return surface->current_clip_serial; @@ -814,15 +1031,19 @@ _cairo_surface_get_current_clip_serial (cairo_surface_t *surface) * _cairo_surface_allocate_clip_serial: * @surface: the #cairo_surface_t to allocate a serial number from * - * Each surface has a separate set of clipping serial numbers, - * and this function allocates one from the specified surface. - * As zero is reserved for the special no-clipping case, - * this function will not return that. + * Each surface has a separate set of clipping serial numbers, and + * this function allocates one from the specified surface. As zero is + * reserved for the special no-clipping case, this function will not + * return that except for an in-error surface, (ie. surface->status != + * CAIRO_STATUS_SUCCESS). */ -cairo_private unsigned int +unsigned int _cairo_surface_allocate_clip_serial (cairo_surface_t *surface) { unsigned int serial; + + if (surface->status) + return 0; if ((serial = ++(surface->next_clip_serial)) == 0) serial = ++(surface->next_clip_serial); @@ -838,11 +1059,14 @@ _cairo_surface_allocate_clip_serial (cairo_surface_t *surface) * unclipped. It also sets the clip serial number * to zero. */ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_reset_clip (cairo_surface_t *surface) { cairo_status_t status; + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -862,6 +1086,7 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) if (status) return status; } + return CAIRO_STATUS_SUCCESS; } @@ -875,20 +1100,44 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) * the specified region and sets the surface clipping * serial number to the associated serial number. */ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region, unsigned int serial) { + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; assert (surface->backend->set_clip_region != NULL); surface->current_clip_serial = serial; + return surface->backend->set_clip_region (surface, region); } +cairo_int_status_t +_cairo_surface_intersect_clip_path (cairo_surface_t *surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + if (surface->status) + return surface->status; + + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + assert (surface->backend->intersect_clip_path != NULL); + + return surface->backend->intersect_clip_path (surface, + path, + fill_rule, + tolerance); +} + static cairo_status_t _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, cairo_clip_path_t *clip_path) @@ -908,7 +1157,6 @@ _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, clip_path->tolerance); } - /** * _cairo_surface_set_clip_path: * @surface: the #cairo_surface_t to reset the clip on @@ -920,13 +1168,16 @@ _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, * Sets the clipping path to be the intersection of the current * clipping path of the surface and the given path. **/ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_set_clip_path (cairo_surface_t *surface, cairo_clip_path_t *clip_path, unsigned int serial) { cairo_status_t status; + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -967,6 +1218,9 @@ cairo_status_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_t *rectangle) { + if (surface->status) + return surface->status; + if (surface->finished) return CAIRO_STATUS_SURFACE_FINISHED; @@ -989,6 +1243,9 @@ _cairo_surface_show_glyphs (cairo_scaled_font_t *scaled_font, { cairo_status_t status; + if (dst->status) + return dst->status; + if (dst->finished) return CAIRO_STATUS_SURFACE_FINISHED; |