diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2009-05-26 21:07:07 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2009-06-02 15:13:47 +0100 |
commit | cffb398f5a484000be458d04ef8f8bce3f6c7e3d (patch) | |
tree | 3b620279d063b567389a24b8533b6c1801dec673 /src | |
parent | 7ed050fd435f17d25c7b757b02cfe200f8779fc2 (diff) |
Add a generic cow-snapshotting framework
Provide a mechanism for backends to attach and remove snapshots. This can
be used by backends to provide a cache for _cairo_surface_clone_similar(),
or by the meta-surfaces to only emit a single pattern for each unique
snapshot.
In order to prevent stale data being returned upon a snapshot operation,
if the surface is modified (via the 5 high level operations, and on
notification of external modification) we break the association with any
current snapshot of the surface and thus preserve the current data for
their use.
Diffstat (limited to 'src')
-rw-r--r-- | src/cairo-meta-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-surface-fallback.c | 2 | ||||
-rw-r--r-- | src/cairo-surface-private.h | 7 | ||||
-rw-r--r-- | src/cairo-surface.c | 272 | ||||
-rw-r--r-- | src/cairoint.h | 12 |
5 files changed, 227 insertions, 67 deletions
diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index 6938526d..d505adcf 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -542,7 +542,6 @@ _cairo_meta_surface_snapshot (void *abstract_other) _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, other->base.content); - meta->base.is_snapshot = TRUE; meta->width_pixels = other->width_pixels; meta->height_pixels = other->height_pixels; diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c index e1a87ee4..830c1b3d 100644 --- a/src/cairo-surface-fallback.c +++ b/src/cairo-surface-fallback.c @@ -1196,7 +1196,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, int x1, y1, x2, y2; int i; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (num_rects <= 0) return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h index b07f7806..c25b6dc8 100644 --- a/src/cairo-surface-private.h +++ b/src/cairo-surface-private.h @@ -43,6 +43,8 @@ #include "cairo-types-private.h" #include "cairo-reference-count-private.h" +typedef void (*cairo_surface_func_t) (cairo_surface_t *); + struct _cairo_surface { const cairo_surface_backend_t *backend; @@ -94,7 +96,10 @@ struct _cairo_surface { unsigned int current_clip_serial; /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ - cairo_bool_t is_snapshot; + cairo_surface_t *snapshot_of; + cairo_surface_func_t snapshot_detach; + /* current snapshots of this surface */ + cairo_array_t snapshots; /* * Surface font options, falling back to backend's default options, diff --git a/src/cairo-surface.c b/src/cairo-surface.c index d21ebf24..2a5fbc1a 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -62,7 +62,13 @@ const cairo_surface_t name = { \ NULL, /* clip */ \ 0, /* next_clip_serial */ \ 0, /* current_clip_serial */ \ - FALSE, /* is_snapshot */ \ + NULL, /* snapshot_of */ \ + NULL, /* snapshot_detach */ \ + { 0, /* size */ \ + 0, /* num_elements */ \ + 0, /* element_size */ \ + NULL, /* elements */ \ + }, /* snapshots */ \ FALSE, /* has_font_options */ \ { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ @@ -204,6 +210,113 @@ _cairo_surface_allocate_unique_id (void) #endif } +static cairo_bool_t +_cairo_surface_has_snapshots (cairo_surface_t *surface) +{ + return surface->snapshots.num_elements != 0; +} + +static void +_cairo_surface_detach_snapshots (cairo_surface_t *surface) +{ + cairo_surface_t **snapshots; + unsigned int i; + + if (! _cairo_surface_has_snapshots (surface)) + return; + + /* XXX do something intelligent! */ + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + snapshots[i]->snapshot_of = NULL; + + if (snapshots[i]->snapshot_detach != NULL) + snapshots[i]->snapshot_detach (snapshots[i]); + } + surface->snapshots.num_elements = 0; + + assert (! _cairo_surface_has_snapshots (surface)); +} + +cairo_status_t +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func) +{ + assert (surface != snapshot); + + if (snapshot->snapshot_of != NULL) + _cairo_surface_detach_snapshot (snapshot); + + snapshot->snapshot_of = surface; + snapshot->snapshot_detach = detach_func; + + return _cairo_array_append (&surface->snapshots, &snapshot); +} + +cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend) +{ + cairo_surface_t **snapshots; + unsigned int i; + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + if (snapshots[i]->backend == backend) + return snapshots[i]; + } + + return NULL; +} + +void +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot) +{ + cairo_surface_t *surface; + cairo_surface_t **snapshots; + unsigned int i; + + assert (snapshot->snapshot_of != NULL); + surface = snapshot->snapshot_of; + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + if (snapshots[i] == snapshot) + break; + } + assert (i < surface->snapshots.num_elements); + + surface->snapshots.num_elements--; + memmove (&snapshots[i], + &snapshots[i+1], + sizeof (cairo_surface_t *)*(surface->snapshots.num_elements - i)); + + snapshot->snapshot_of = NULL; + + if (snapshot->snapshot_detach != NULL) + snapshot->snapshot_detach (snapshot); +} + +static cairo_bool_t +_cairo_surface_is_writable (cairo_surface_t *surface) +{ + return ! surface->finished && + surface->snapshot_of == NULL && + ! _cairo_surface_has_snapshots (surface); +} + +static void +_cairo_surface_begin_modification (cairo_surface_t *surface) +{ + assert (surface->status == CAIRO_STATUS_SUCCESS); + assert (! surface->finished); + assert (surface->snapshot_of == NULL); + + _cairo_surface_detach_snapshots (surface); +} + void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, @@ -236,7 +349,8 @@ _cairo_surface_init (cairo_surface_t *surface, surface->next_clip_serial = 0; surface->current_clip_serial = 0; - surface->is_snapshot = FALSE; + _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *)); + surface->snapshot_of = NULL; surface->has_font_options = FALSE; } @@ -466,6 +580,7 @@ cairo_surface_destroy (cairo_surface_t *surface) _cairo_user_data_array_fini (&surface->user_data); _cairo_user_data_array_fini (&surface->mime_data); + _cairo_array_fini (&surface->snapshots); free (surface); } @@ -565,6 +680,9 @@ cairo_surface_finish (cairo_surface_t *surface) } surface->finished = TRUE; + + if (surface->snapshot_of != NULL) + _cairo_surface_detach_snapshot (surface); } slim_hidden_def (cairo_surface_finish); @@ -801,7 +919,7 @@ _cairo_surface_set_font_options (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, @@ -877,6 +995,9 @@ cairo_surface_flush (cairo_surface_t *surface) if (surface->finished) return; + /* update the current snapshots *before* the user updates the surface */ + _cairo_surface_detach_snapshots (surface); + if (surface->backend->flush) { status = surface->backend->flush (surface); if (unlikely (status)) @@ -896,11 +1017,6 @@ slim_hidden_def (cairo_surface_flush); void cairo_surface_mark_dirty (cairo_surface_t *surface) { - if (surface->status) - return; - - assert (! surface->is_snapshot); - cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1); } @@ -932,13 +1048,18 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + /* The application *should* have called cairo_surface_flush() before + * modifying the surface independently of cairo (and thus having to + * call mark_dirty()). */ + assert (! _cairo_surface_has_snapshots (surface)); + /* Always reset the clip here, to avoid having external calls to * clip manipulation functions of the underlying device clip result * in a desync between the cairo clip and the backend clip, due to @@ -990,13 +1111,15 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->device_transform.xx = sx; surface->device_transform.yy = sy; surface->device_transform.xy = 0.0; @@ -1036,13 +1159,15 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->device_transform.x0 = x_offset; surface->device_transform.y0 = y_offset; @@ -1119,13 +1244,15 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->x_fallback_resolution = x_pixels_per_inch; surface->y_fallback_resolution = y_pixels_per_inch; } @@ -1263,7 +1390,7 @@ _cairo_surface_acquire_dest_image (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (!surface->finished); + assert (_cairo_surface_is_writable (surface)); if (surface->backend->acquire_dest_image == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1300,7 +1427,7 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_rectangle_int_t *image_rect, void *image_extra) { - assert (!surface->finished); + assert (_cairo_surface_is_writable (surface)); if (surface->backend->release_dest_image) surface->backend->release_dest_image (surface, interest_rect, @@ -1460,18 +1587,41 @@ _cairo_surface_snapshot (cairo_surface_t *surface) if (surface->finished) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - if (surface->is_snapshot) + if (surface->snapshot_of != NULL) return cairo_surface_reference (surface); - snapshot = NULL; - if (surface->backend->snapshot != NULL) + snapshot = _cairo_surface_has_snapshot (surface, surface->backend); + if (snapshot != NULL) + return cairo_surface_reference (snapshot); + + if (surface->backend->snapshot != NULL) { snapshot = surface->backend->snapshot (surface); + if (unlikely (snapshot->status)) + return snapshot; + + /* Is this surface just a proxy - e.g. paginated surfaces? */ + if (snapshot->backend != surface->backend) { + cairo_surface_t *previous; + + previous = _cairo_surface_has_snapshot (surface, + snapshot->backend); + if (previous != NULL) { + cairo_surface_destroy (snapshot); + return cairo_surface_reference (previous); + } + } + } - if (snapshot == NULL) - snapshot = _cairo_surface_fallback_snapshot (surface); + if (snapshot == NULL) { + snapshot = _cairo_surface_has_snapshot (surface, + &_cairo_image_surface_backend); + if (snapshot != NULL) + return cairo_surface_reference (snapshot); - if (unlikely (snapshot->status)) - return snapshot; + snapshot = _cairo_surface_fallback_snapshot (surface); + if (unlikely (snapshot->status)) + return snapshot; + } status = _cairo_surface_copy_mime_data (snapshot, surface); if (unlikely (status)) { @@ -1482,7 +1632,11 @@ _cairo_surface_snapshot (cairo_surface_t *surface) snapshot->device_transform = surface->device_transform; snapshot->device_transform_inverse = surface->device_transform_inverse; - snapshot->is_snapshot = TRUE; + status = _cairo_surface_attach_snapshot (surface, snapshot, NULL); + if (unlikely (status)) { + cairo_surface_destroy (snapshot); + return _cairo_surface_create_in_error (status); + } return snapshot; } @@ -1532,6 +1686,11 @@ _cairo_surface_composite (cairo_operator_t op, { cairo_int_status_t status; + if (dst->status) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + if (mask) { /* These operators aren't interpreted the same way by the backends; * they are implemented in terms of other operators in cairo-gstate.c @@ -1539,14 +1698,6 @@ _cairo_surface_composite (cairo_operator_t op, assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); } - if (dst->status) - return dst->status; - - assert (! dst->is_snapshot); - - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); - if (dst->backend->composite) { status = dst->backend->composite (op, src, mask, dst, @@ -1596,10 +1747,7 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (surface)); rect.x = x; rect.y = y; @@ -1637,7 +1785,7 @@ _cairo_surface_fill_region (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + assert (_cairo_surface_is_writable (surface)); num_rects = cairo_region_num_rectangles (region); if (num_rects == 0) @@ -1692,10 +1840,7 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (surface)); if (num_rects == 0) return CAIRO_STATUS_SUCCESS; @@ -1724,7 +1869,7 @@ _cairo_surface_paint (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); status = _cairo_surface_copy_pattern_for_destination (&source, surface, @@ -1761,7 +1906,7 @@ _cairo_surface_mask (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); status = _cairo_surface_copy_pattern_for_destination (&source, surface, @@ -1816,6 +1961,8 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, if (surface->status) return surface->status; + _cairo_surface_begin_modification (surface); + if (surface->backend->fill_stroke) { cairo_pattern_union_t dev_stroke_source; cairo_pattern_union_t dev_fill_source; @@ -1894,7 +2041,7 @@ _cairo_surface_stroke (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); status = _cairo_surface_copy_pattern_for_destination (&source, surface, @@ -1943,7 +2090,7 @@ _cairo_surface_fill (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); status = _cairo_surface_copy_pattern_for_destination (&source, surface, @@ -1987,18 +2134,15 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, { cairo_int_status_t status; - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); if (dst->backend->composite_trapezoids) { status = dst->backend->composite_trapezoids (op, @@ -2028,7 +2172,7 @@ _cairo_surface_create_span_renderer (cairo_operator_t op, cairo_antialias_t antialias, const cairo_composite_rectangles_t *rects) { - assert (! dst->is_snapshot); + assert (dst->snapshot_of == NULL); if (dst->status) return _cairo_span_renderer_create_in_error (dst->status); @@ -2055,7 +2199,7 @@ _cairo_surface_check_span_renderer (cairo_operator_t op, { cairo_int_status_t status; - assert (! dst->is_snapshot); + assert (dst->snapshot_of == NULL); if (dst->status) return FALSE; @@ -2096,7 +2240,7 @@ cairo_surface_copy_page (cairo_surface_t *surface) if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, @@ -2133,7 +2277,7 @@ cairo_surface_show_page (cairo_surface_t *surface) if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, @@ -2251,6 +2395,9 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, if (surface->status) return surface->status; + if (surface->finished) + return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + assert (surface->backend->set_clip_region != NULL); status = surface->backend->set_clip_region (surface, region); @@ -2571,11 +2718,11 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (!num_glyphs && !utf8_len) + if (num_glyphs == 0 && utf8_len == 0) return CAIRO_STATUS_SUCCESS; + _cairo_surface_begin_modification (surface); + status = _cairo_surface_copy_pattern_for_destination (&source, surface, &dev_source.base); @@ -2702,10 +2849,7 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, if (dst->status) return dst->status; - assert (! dst->is_snapshot); - - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (dst)); if (dst->backend->old_show_glyphs) { status = dst->backend->old_show_glyphs (scaled_font, @@ -2820,7 +2964,7 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, * non-repeating sources and masks. Other sources and masks can be ignored. @@ -2898,7 +3042,7 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, * non-repeating sources and masks. Other sources and masks can be ignored. diff --git a/src/cairoint.h b/src/cairoint.h index fe2a1af3..581bc189 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1985,6 +1985,18 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_private cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface); +cairo_private cairo_status_t +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func); + +cairo_private cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend); + +cairo_private void +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot); + cairo_private cairo_bool_t _cairo_surface_is_similar (cairo_surface_t *surface_a, cairo_surface_t *surface_b, |