diff --git a/ChangeLog b/ChangeLog index 953d42d8a..65738cb0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2005-12-07 Carl Worth + + * ROADMAP: Note that self-copy now works with the PS backend. + + * src/cairo-array.c: (_cairo_array_init), + (_cairo_array_init_snapshot), (_cairo_array_fini), + (_cairo_array_grow_by), (_cairo_array_truncate), + (_cairo_array_append), (_cairo_array_append_multiple), + (_cairo_array_allocate): Add _cairo_array_init_snapshot and checks + for is_snapshot throughout. + + * src/cairoint.h: + * src/cairo-surface.c: (_cairo_surface_acquire_source_image), + (_fallback_snapshot), (_cairo_surface_snapshot): Add a new + surface->backend->snapshot function. + + * src/cairo-meta-surface-private.h: + * src/cairo-meta-surface.c: (_cairo_meta_surface_create), + (_cairo_meta_surface_create_similar), (_cairo_meta_surface_finish), + (_cairo_meta_surface_acquire_source_image), + (_cairo_meta_surface_release_source_image), + (_cairo_meta_surface_snapshot), (_cairo_meta_surface_get_extents): + Implement _cairo_meta_surface_snapshot and + _cairo_meta_surface_acquire/release_source_image. Change + _cairo_meta_surface_create to require the width and height in + pixels to be used when replaying for purposed of + _cairo_meta_surface_aquire_source_image. + + * src/cairo-ps-surface.c: + (_cairo_ps_surface_create_for_stream_internal), + (_cairo_ps_surface_show_page), (_cairo_ps_surface_snapshot), + (_ps_output_old_show_glyphs): Track change in prototype of + _cairo_meta_surface_create. Implement _cairo_ps_surface_snapshot + by deferring down into _cairo_meta_surface_snapshot. + 2005-12-07 Carl Worth * test/cairo-test.c: (create_ps_surface): Fix to check diff --git a/ROADMAP b/ROADMAP index 1c5e57870..255714c03 100644 --- a/ROADMAP +++ b/ROADMAP @@ -11,17 +11,17 @@ The release won't happen without these being complete. ======================================================== A. PS backend ---------- - 1. Mark PS backend as supported: + ✓1. Mark PS backend as supported: ✓a. Incorporate into test suite - b. Correct output for the entire test suite + ✓b. Correct output for the entire test suite ✓clip-operator ✓composite-integer-translate-source ✓linear-gradient ✓operator-clear ✓operator-source - self-copy + ✓self-copy ✓text-pattern ✓trap-clip ✓unbounded-operator diff --git a/src/cairo-array.c b/src/cairo-array.c index dac460f52..7153c7c58 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -58,6 +58,29 @@ _cairo_array_init (cairo_array_t *array, int element_size) array->num_elements = 0; array->element_size = element_size; array->elements = NULL; + + array->is_snapshot = FALSE; +} + +/** + * _cairo_array_init_snapshot: + * @array: A #cairo_array_t to be initialized as a snapshot + * @other: The #cairo_array_t from which to create the snapshot + * + * Initialize @array as an immutable copy of @other. It is an error to + * call an array-modifying function (other than _cairo_array_fini) on + * @array after calling this function. + **/ +void +_cairo_array_init_snapshot (cairo_array_t *array, + const cairo_array_t *other) +{ + array->size = other->size; + array->num_elements = other->num_elements; + array->element_size = other->element_size; + array->elements = other->elements; + + array->is_snapshot = TRUE; } /** @@ -70,6 +93,9 @@ _cairo_array_init (cairo_array_t *array, int element_size) void _cairo_array_fini (cairo_array_t *array) { + if (array->is_snapshot) + return; + free (array->elements); } @@ -88,6 +114,8 @@ _cairo_array_grow_by (cairo_array_t *array, int additional) int required_size = array->num_elements + additional; int new_size; + assert (! array->is_snapshot); + if (required_size <= old_size) return CAIRO_STATUS_SUCCESS; @@ -123,6 +151,8 @@ _cairo_array_grow_by (cairo_array_t *array, int additional) void _cairo_array_truncate (cairo_array_t *array, int num_elements) { + assert (! array->is_snapshot); + if (num_elements < array->num_elements) array->num_elements = num_elements; } @@ -184,6 +214,8 @@ cairo_status_t _cairo_array_append (cairo_array_t *array, const void *element) { + assert (! array->is_snapshot); + return _cairo_array_append_multiple (array, element, 1); } @@ -206,6 +238,8 @@ _cairo_array_append_multiple (cairo_array_t *array, cairo_status_t status; void *dest; + assert (! array->is_snapshot); + status = _cairo_array_allocate (array, num_elements, &dest); if (status) return status; @@ -234,6 +268,8 @@ _cairo_array_allocate (cairo_array_t *array, { cairo_status_t status; + assert (! array->is_snapshot); + status = _cairo_array_grow_by (array, num_elements); if (status) return status; diff --git a/src/cairo-meta-surface-private.h b/src/cairo-meta-surface-private.h index 1a27b483e..8a31a5cff 100644 --- a/src/cairo-meta-surface-private.h +++ b/src/cairo-meta-surface-private.h @@ -179,13 +179,21 @@ typedef union _cairo_command { typedef struct _cairo_meta_surface { cairo_surface_t base; + + /* A meta-surface is logically unbounded, but when used as a + * source we need to render it to an image, so we need a size at + * which to create that image. */ + int width_pixels; + int height_pixels; + cairo_array_t commands; + cairo_surface_t *commands_owner; } cairo_meta_surface_t; cairo_private cairo_surface_t * -_cairo_meta_surface_create (void); +_cairo_meta_surface_create (int width_pixels, int height_pixels); -cairo_private cairo_int_status_t +cairo_private cairo_status_t _cairo_meta_surface_replay (cairo_surface_t *surface, cairo_surface_t *target); diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index f5f0c0336..2f57a0440 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -41,7 +41,7 @@ static const cairo_surface_backend_t cairo_meta_surface_backend; cairo_surface_t * -_cairo_meta_surface_create (void) +_cairo_meta_surface_create (int width_pixels, int height_pixels) { cairo_meta_surface_t *meta; @@ -52,7 +52,12 @@ _cairo_meta_surface_create (void) } _cairo_surface_init (&meta->base, &cairo_meta_surface_backend); + + meta->width_pixels = width_pixels; + meta->height_pixels = height_pixels; + _cairo_array_init (&meta->commands, sizeof (cairo_command_t *)); + meta->commands_owner = NULL; return &meta->base; } @@ -63,7 +68,7 @@ _cairo_meta_surface_create_similar (void *abstract_surface, int width, int height) { - return _cairo_meta_surface_create (); + return _cairo_meta_surface_create (width, height); } static cairo_status_t @@ -74,6 +79,11 @@ _cairo_meta_surface_finish (void *abstract_surface) cairo_command_t **elements; int i, num_elements; + if (meta->commands_owner) { + cairo_surface_destroy (meta->commands_owner); + return CAIRO_STATUS_SUCCESS; + } + num_elements = meta->commands.num_elements; elements = (cairo_command_t **) meta->commands.elements; for (i = 0; i < num_elements; i++) { @@ -149,6 +159,39 @@ _cairo_meta_surface_finish (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_meta_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_status_t status; + cairo_meta_surface_t *surface = abstract_surface; + cairo_surface_t *image; + + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + surface->width_pixels, + surface->height_pixels); + + status = _cairo_meta_surface_replay (&surface->base, image); + if (status) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + + return status; +} + +static void +_cairo_meta_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + static cairo_status_t _init_pattern_with_snapshot (cairo_pattern_t *pattern, const cairo_pattern_t *other) @@ -397,6 +440,43 @@ _cairo_meta_surface_show_glyphs (void *abstract_surface, return status; } +/** + * _cairo_meta_surface_snapshot + * @surface: a #cairo_surface_t which must be a meta surface + * + * Make an immutable copy of @surface. It is an error to call a + * surface-modifying function on the result of this function. + * + * The caller owns the return value and should call + * cairo_surface_destroy when finished with it. This function will not + * return NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. + **/ +static cairo_surface_t * +_cairo_meta_surface_snapshot (void *abstract_other) +{ + cairo_meta_surface_t *other = abstract_other; + cairo_meta_surface_t *meta; + + meta = malloc (sizeof (cairo_meta_surface_t)); + if (meta == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + _cairo_surface_init (&meta->base, &cairo_meta_surface_backend); + meta->base.is_snapshot = TRUE; + + meta->width_pixels = other->width_pixels; + meta->height_pixels = other->height_pixels; + + _cairo_array_init_snapshot (&meta->commands, &other->commands); + meta->commands_owner = cairo_surface_reference (&other->base); + + return &meta->base; +} + static cairo_int_status_t _cairo_meta_surface_composite (cairo_operator_t operator, cairo_pattern_t *src_pattern, @@ -594,6 +674,26 @@ _cairo_meta_surface_intersect_clip_path (void *dst, return CAIRO_STATUS_SUCCESS; } +/* A meta-surface is logically unbounded, but when it is used as a + * source, the drawing code can optimize based on the extents of the + * surface. + * + * XXX: The optimization being attempted here would only actually work + * if the meta-surface kept track of its extents as commands were + * added to it. + */ +static cairo_int_status_t +_cairo_meta_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = CAIRO_MAXSHORT; + rectangle->height = CAIRO_MAXSHORT; + + return CAIRO_STATUS_SUCCESS; +} + /** * _cairo_surface_is_meta: * @surface: a #cairo_surface_t @@ -611,8 +711,8 @@ _cairo_surface_is_meta (const cairo_surface_t *surface) static const cairo_surface_backend_t cairo_meta_surface_backend = { _cairo_meta_surface_create_similar, _cairo_meta_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ + _cairo_meta_surface_acquire_source_image, + _cairo_meta_surface_release_source_image, NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ NULL, /* clone_similar */ @@ -623,7 +723,7 @@ static const cairo_surface_backend_t cairo_meta_surface_backend = { NULL, /* show_page */ NULL, /* set_clip_region */ _cairo_meta_surface_intersect_clip_path, - NULL, /* get_extents */ + _cairo_meta_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ NULL, /* flush */ @@ -639,10 +739,12 @@ static const cairo_surface_backend_t cairo_meta_surface_backend = { _cairo_meta_surface_mask, _cairo_meta_surface_stroke, _cairo_meta_surface_fill, - _cairo_meta_surface_show_glyphs + _cairo_meta_surface_show_glyphs, + + _cairo_meta_surface_snapshot }; -cairo_int_status_t +cairo_status_t _cairo_meta_surface_replay (cairo_surface_t *surface, cairo_surface_t *target) { diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 36db6f5a4..a38092e3b 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -106,7 +106,8 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->base.device_x_scale = surface->x_dpi / 72.0; surface->base.device_y_scale = surface->y_dpi / 72.0; - surface->current_page = _cairo_meta_surface_create (); + surface->current_page = _cairo_meta_surface_create (width * surface->x_dpi / 72, + height * surface->y_dpi / 72); if (surface->current_page->status) { free (surface); _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -343,7 +344,8 @@ _cairo_ps_surface_show_page (void *abstract_surface) if (status) return status; - surface->current_page = _cairo_meta_surface_create (); + surface->current_page = _cairo_meta_surface_create (surface->width * surface->x_dpi / 72, + surface->height * surface->y_dpi / 72); if (surface->current_page->status) return CAIRO_STATUS_NO_MEMORY; @@ -549,6 +551,16 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, scaled_font); } +static cairo_surface_t * +_cairo_ps_surface_snapshot (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + assert (_cairo_surface_is_meta (surface->current_page)); + + return _cairo_surface_snapshot (surface->current_page); +} + static const cairo_surface_backend_t cairo_ps_surface_backend = { NULL, /* create_similar */ _cairo_ps_surface_finish, @@ -578,7 +590,9 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_mask, _cairo_ps_surface_stroke, _cairo_ps_surface_fill, - _cairo_ps_surface_show_glyphs + _cairo_ps_surface_show_glyphs, + + _cairo_ps_surface_snapshot }; static cairo_int_status_t @@ -1400,6 +1414,9 @@ _ps_output_old_show_glyphs (cairo_scaled_font_t *scaled_font, if (! _cairo_scaled_font_is_ft (scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; + if (surface->fallback) + return CAIRO_STATUS_SUCCESS; + if (pattern_operation_needs_fallback (operator, pattern)) return _ps_output_add_fallback_area (surface, dest_x, dest_y, width, height); diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 34dff5dd1..8c17405e7 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -581,7 +581,8 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, { assert (!surface->finished); - return surface->backend->acquire_source_image (surface, image_out, image_extra); + return surface->backend->acquire_source_image (surface, + image_out, image_extra); } /** @@ -722,21 +723,11 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, return status; } -/** - * _cairo_surface_snapshot - * @surface: a #cairo_surface_t - * @snapshot_out: return value surface---not necessarily of the same type as @surface - * - * Make an immutable copy of @surface. It is an error to call a - * surface-modifying function on the result of this function. - * - * The caller owns the return value and should call - * cairo_surface_destroy when finished with it. This function will not - * return NULL, but will return a nil surface instead. - **/ -cairo_surface_t * -_cairo_surface_snapshot (cairo_surface_t *surface) +/* XXX: Shouldn't really need to do this here. */ +#include "cairo-meta-surface-private.h" +static cairo_surface_t * +_fallback_snapshot (cairo_surface_t *surface) { cairo_surface_t *snapshot; cairo_status_t status; @@ -744,12 +735,6 @@ _cairo_surface_snapshot (cairo_surface_t *surface) cairo_image_surface_t *image; void *image_extra; - if (surface->finished) - return (cairo_surface_t *) &_cairo_surface_nil; - - /* XXX: Will need to do something very different here to snapshot - * a meta-surface. */ - status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (status != CAIRO_STATUS_SUCCESS) @@ -783,6 +768,32 @@ _cairo_surface_snapshot (cairo_surface_t *surface) return snapshot; } +/** + * _cairo_surface_snapshot + * @surface: a #cairo_surface_t + * + * Make an immutable copy of @surface. It is an error to call a + * surface-modifying function on the result of this function. + * + * The caller owns the return value and should call + * cairo_surface_destroy when finished with it. This function will not + * return NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. Note that the return surface + * may not necessarily be of the same type as @surface. + **/ +cairo_surface_t * +_cairo_surface_snapshot (cairo_surface_t *surface) +{ + if (surface->finished) + return (cairo_surface_t *) &_cairo_surface_nil; + + if (surface->backend->snapshot) + return surface->backend->snapshot (surface); + + return _fallback_snapshot (surface); +} + typedef struct { cairo_surface_t *dst; cairo_rectangle_t extents; diff --git a/src/cairoint.h b/src/cairoint.h index b27624dcb..868faee34 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -318,11 +318,17 @@ struct _cairo_array { int num_elements; int element_size; char *elements; + + cairo_bool_t is_snapshot; }; cairo_private void _cairo_array_init (cairo_array_t *array, int element_size); +void +_cairo_array_init_snapshot (cairo_array_t *array, + const cairo_array_t *other); + cairo_private void _cairo_array_fini (cairo_array_t *array); @@ -790,6 +796,9 @@ struct _cairo_surface_backend { const cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font); + + cairo_surface_t * + (*snapshot) (void *surface); }; typedef struct _cairo_format_masks { @@ -1843,6 +1852,9 @@ _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, cairo_private cairo_bool_t _cairo_surface_is_image (const cairo_surface_t *surface); +cairo_private cairo_bool_t +_cairo_surface_is_meta (const cairo_surface_t *surface); + /* cairo_pen.c */ cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen,