summaryrefslogtreecommitdiff
path: root/src/cairo-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-surface.c')
-rw-r--r--src/cairo-surface.c374
1 files changed, 297 insertions, 77 deletions
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 75f887c1f..11fbc6092 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);
@@ -64,10 +156,15 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other,
int width,
int height)
{
- if (other == NULL)
- return NULL;
+ cairo_format_t format = _cairo_format_from_content (content);
- return other->backend->create_similar (other, content, 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);
}
/**
@@ -81,9 +178,13 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other,
* 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, or NULL in
- * the case of errors. The caller owns the surface and should call
- * cairo_surface_destroy when done with it.
+ * 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,
@@ -91,12 +192,13 @@ cairo_surface_create_similar (cairo_surface_t *other,
int width,
int height)
{
- if (other == NULL)
- return NULL;
+ if (other->status)
+ return (cairo_surface_t*) &_cairo_surface_nil;
- /* XXX: Really need to make this kind of thing pass through _cairo_error. */
- if (! CAIRO_CONTENT_VALID (content))
- return NULL;
+ 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,
@@ -112,20 +214,21 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other,
{
cairo_status_t status;
cairo_surface_t *surface;
- cairo_format_t format = _cairo_format_from_content (content);
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;
@@ -148,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++;
}
@@ -157,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;
@@ -178,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;
}
/**
@@ -252,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
@@ -279,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;
}
@@ -453,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,
@@ -461,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;
@@ -468,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
@@ -497,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;
}
@@ -528,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;
@@ -561,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;
@@ -609,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++) {
@@ -631,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;
}
@@ -652,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;
@@ -678,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;
}
@@ -705,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 */
@@ -718,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++) {
@@ -746,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,
@@ -768,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;
@@ -782,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;
@@ -799,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;
@@ -834,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).
*/
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);
@@ -863,6 +1064,9 @@ _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;
@@ -882,6 +1086,7 @@ _cairo_surface_reset_clip (cairo_surface_t *surface)
if (status)
return status;
}
+
return CAIRO_STATUS_SUCCESS;
}
@@ -900,12 +1105,16 @@ _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);
}
@@ -915,6 +1124,9 @@ _cairo_surface_intersect_clip_path (cairo_surface_t *surface,
cairo_fill_rule_t fill_rule,
double tolerance)
{
+ if (surface->status)
+ return surface->status;
+
if (surface->finished)
return CAIRO_STATUS_SURFACE_FINISHED;
@@ -945,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
@@ -964,6 +1175,9 @@ _cairo_surface_set_clip_path (cairo_surface_t *surface,
{
cairo_status_t status;
+ if (surface->status)
+ return surface->status;
+
if (surface->finished)
return CAIRO_STATUS_SURFACE_FINISHED;
@@ -1004,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;
@@ -1026,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;