Commit 99fa5ff6 authored by Chris Wilson's avatar Chris Wilson 🤔
Browse files

snapshot: Defer acquisition



Fixes 'xlib-expose-event' but triggers an infinite loop in self-copy.
Signed-off-by: Chris Wilson's avatarChris Wilson <chris@chris-wilson.co.uk>
parent 79aa04fd
......@@ -41,6 +41,7 @@
#include "cairo-error-private.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-region-private.h"
......@@ -119,6 +120,8 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
source = surface_pattern->surface;
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = _cairo_surface_snapshot_get_target (source);
if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
source = sub->target;
......
......@@ -737,6 +737,36 @@ _cairo_image_surface_create_similar (void *abstract_other,
width, height);
}
static cairo_surface_t *
_cairo_image_surface_snapshot (void *abstract_surface)
{
cairo_image_surface_t *image = abstract_surface;
cairo_image_surface_t *clone;
clone = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
image->pixman_format,
image->width,
image->height,
0);
if (unlikely (clone->base.status))
return &clone->base;
if (clone->stride == image->stride) {
memcpy (clone->data, image->data, clone->stride * clone->height);
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, clone->pixman_image,
0, 0,
0, 0,
0, 0,
image->width, image->height);
}
clone->base.is_clear = FALSE;
return &clone->base;
}
static cairo_surface_t *
_cairo_image_surface_map_to_image (void *abstract_other,
const cairo_rectangle_int_t *extents)
......@@ -4823,8 +4853,7 @@ const cairo_surface_backend_t _cairo_image_surface_backend = {
_cairo_image_surface_stroke,
_cairo_image_surface_fill,
_cairo_image_surface_glyphs,
NULL, /* show_text_glyphs */
NULL, /* snapshot */
_cairo_image_surface_snapshot,
NULL, /* is_similar */
};
......
......@@ -56,6 +56,7 @@
#include "cairo-paginated-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-type3-glyph-surface-private.h"
......@@ -1110,9 +1111,13 @@ _get_source_surface_size (cairo_surface_t *source,
*height = sub->extents.height;
} else {
cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source;
cairo_recording_surface_t *recording_surface;
cairo_box_t bbox;
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = _cairo_surface_snapshot_get_target (source);
recording_surface = (cairo_recording_surface_t *) source;
status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
if (unlikely (status))
return status;
......@@ -2366,7 +2371,7 @@ BAIL:
static cairo_status_t
_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
cairo_surface_t *recording_surface,
cairo_surface_t *source,
cairo_pdf_resource_t resource)
{
double old_width, old_height;
......@@ -2376,7 +2381,11 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
cairo_int_status_t status;
int alpha = 0;
is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents);
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = _cairo_surface_snapshot_get_target (source);
is_bounded = _cairo_surface_get_extents (source,
&recording_extents);
assert (is_bounded);
old_width = surface->width;
......@@ -2396,7 +2405,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) {
if (cairo_surface_get_content (source) == CAIRO_CONTENT_COLOR) {
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (unlikely (status))
return status;
......@@ -2408,7 +2417,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
surface->height);
}
status = _cairo_recording_surface_replay_region (recording_surface,
status = _cairo_recording_surface_replay_region (source,
NULL,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
......
......@@ -1280,6 +1280,31 @@ _undef (void *data)
free (def);
}
static cairo_status_t
attach_undef_tag (cairo_script_context_t *ctx, cairo_surface_t *surface)
{
struct def *tag;
cairo_status_t status;
tag = malloc (sizeof (*tag));
if (unlikely (tag == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
tag->ctx = ctx;
tag->tag = surface->unique_id;
tag->user_data = &surface->user_data;
cairo_list_add (&tag->link, &ctx->defines);
status = _cairo_user_data_array_set_data (&surface->user_data,
(cairo_user_data_key_t *) ctx,
tag, _undef);
if (unlikely (status)) {
free (tag);
return status;
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_image_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
......@@ -1290,7 +1315,6 @@ _emit_image_surface (cairo_script_surface_t *surface,
cairo_int_status_t status, status2;
const uint8_t *mime_data;
unsigned long mime_data_length;
struct def *tag;
if (_cairo_user_data_array_get_data (&image->base.user_data,
(cairo_user_data_key_t *) ctx))
......@@ -1384,21 +1408,9 @@ _emit_image_surface (cairo_script_surface_t *surface,
cairo_surface_destroy (&clone->base);
}
tag = malloc (sizeof (*tag));
if (unlikely (tag == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
tag->ctx = ctx;
tag->tag = image->base.unique_id;
tag->user_data = &image->base.user_data;
cairo_list_add (&tag->link, &ctx->defines);
status = _cairo_user_data_array_set_data (&image->base.user_data,
(cairo_user_data_key_t *) ctx,
tag, _undef);
if (unlikely (status)) {
free (tag);
status = attach_undef_tag (ctx, &image->base);
if (unlikely (status))
return status;
}
_cairo_output_stream_printf (ctx->stream,
"dup /s%u exch def ",
......@@ -1443,21 +1455,15 @@ static cairo_int_status_t
_emit_image_surface_pattern (cairo_script_surface_t *surface,
cairo_surface_t *source)
{
cairo_surface_t *snapshot;
cairo_image_surface_t *image;
cairo_status_t status;
void *extra;
/* XXX keeping a copy is nasty, but we want to hook into the surface's
* lifetime. Using a snapshot is a convenient method.
*/
snapshot = _cairo_surface_snapshot (source);
status = _cairo_surface_acquire_source_image (snapshot, &image, &extra);
status = _cairo_surface_acquire_source_image (source, &image, &extra);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _emit_image_surface (surface, image);
_cairo_surface_release_source_image (snapshot, image, extra);
_cairo_surface_release_source_image (source, image, extra);
}
cairo_surface_destroy (snapshot);
return status;
}
......@@ -1492,19 +1498,82 @@ _emit_subsurface_pattern (cairo_script_surface_t *surface,
return CAIRO_INT_STATUS_SUCCESS;
}
struct script_snapshot {
cairo_surface_t base;
};
static cairo_status_t
script_snapshot_finish (void *abstract_surface)
{
return CAIRO_STATUS_SUCCESS;
}
static const cairo_surface_backend_t script_snapshot_backend = {
CAIRO_SURFACE_TYPE_SCRIPT,
script_snapshot_finish,
};
static void
detach_snapshot (cairo_surface_t *abstract_surface)
{
cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
cairo_script_context_t *ctx = to_context (surface);
_cairo_output_stream_printf (ctx->stream,
"/s%d undef\n ",
surface->base.unique_id);
}
static void
attach_snapshot (cairo_script_context_t *ctx,
cairo_surface_t *source)
{
struct script_snapshot *surface;
surface = malloc (sizeof (*surface));
if (unlikely (surface == NULL))
return;
_cairo_surface_init (&surface->base,
&script_snapshot_backend,
&ctx->base,
source->content);
_cairo_output_stream_printf (ctx->stream,
"dup /s%d exch def\n ",
surface->base.unique_id);
_cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
cairo_surface_destroy (&surface->base);
}
static cairo_int_status_t
_emit_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_surface_pattern_t *surface_pattern;
cairo_surface_t *source;
cairo_surface_t *source, *snapshot;
cairo_surface_t *take_snapshot = NULL;
cairo_int_status_t status;
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = surface_pattern->surface;
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = ((cairo_surface_snapshot_t *) source)->target;
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
if (snapshot) {
_cairo_output_stream_printf (ctx->stream,
"s%d pattern",
snapshot->unique_id);
return CAIRO_INT_STATUS_SUCCESS;
}
if (_cairo_surface_snapshot_is_reused (source))
take_snapshot = source;
source = _cairo_surface_snapshot_get_target (source);
}
switch ((int) source->backend->type) {
case CAIRO_SURFACE_TYPE_RECORDING:
......@@ -1523,7 +1592,10 @@ _emit_surface_pattern (cairo_script_surface_t *surface,
if (unlikely (status))
return status;
_cairo_output_stream_puts (to_context (surface)->stream, "pattern");
if (take_snapshot)
attach_snapshot (ctx, take_snapshot);
_cairo_output_stream_puts (ctx->stream, "pattern");
return CAIRO_INT_STATUS_SUCCESS;
}
......@@ -1797,6 +1869,8 @@ _emit_path (cairo_script_surface_t *surface,
double x2 = _cairo_fixed_to_double (box.p2.x);
double y2 = _cairo_fixed_to_double (box.p2.y);
assert (x1 > -9999);
_cairo_output_stream_printf (ctx->stream,
" %f %f %f %f rectangle",
x1, y1, x2 - x1, y2 - y1);
......
......@@ -45,4 +45,16 @@ struct _cairo_surface_snapshot {
cairo_surface_t *clone;
};
static inline cairo_bool_t
_cairo_surface_snapshot_is_reused (cairo_surface_t *surface)
{
return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2;
}
static inline cairo_surface_t *
_cairo_surface_snapshot_get_target (cairo_surface_t *surface)
{
return ((cairo_surface_snapshot_t *) surface)->target;
}
#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
......@@ -117,7 +117,7 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
{
cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
cairo_image_surface_t *image;
cairo_image_surface_t *clone;
cairo_surface_t *clone;
void *extra;
cairo_status_t status;
......@@ -127,41 +127,26 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
* been lost.
*/
if (snapshot->target->backend->snapshot != NULL) {
clone = snapshot->target->backend->snapshot (snapshot->target);
if (clone != NULL)
goto done;
}
/* XXX copy to a similar surface, leave acquisition till later? */
status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
if (unlikely (status)) {
snapshot->target = _cairo_surface_create_in_error (status);
status = _cairo_surface_set_error (surface, status);
return;
}
clone = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
image->pixman_format,
image->width,
image->height,
0);
if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) {
if (clone->stride == image->stride) {
memcpy (clone->data, image->data, image->stride * image->height);
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, clone->pixman_image,
0, 0,
0, 0,
0, 0,
image->width, image->height);
}
clone->base.is_clear = FALSE;
snapshot->clone = &clone->base;
} else {
snapshot->clone = &clone->base;
status = _cairo_surface_set_error (surface, clone->base.status);
}
clone = image->base.backend->snapshot (&image->base);
_cairo_surface_release_source_image (snapshot->target, image, extra);
snapshot->target = snapshot->clone;
snapshot->base.type = snapshot->target->type;
done:
status = _cairo_surface_set_error (surface, clone->status);
snapshot->target = snapshot->clone = clone;
snapshot->base.type = clone->type;
}
/**
......@@ -192,38 +177,14 @@ _cairo_surface_snapshot (cairo_surface_t *surface)
if (unlikely (surface->status))
return _cairo_surface_create_in_error (surface->status);
if (surface->finished)
if (unlikely (surface->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (surface->snapshot_of != NULL)
return cairo_surface_reference (surface);
if (surface->backend->snapshot != NULL) {
cairo_surface_t *snap;
snap = _cairo_surface_has_snapshot (surface, surface->backend);
if (snap != NULL)
return cairo_surface_reference (snap);
snap = surface->backend->snapshot (surface);
if (snap != NULL) {
if (unlikely (snap->status))
return snap;
status = _cairo_surface_copy_mime_data (snap, surface);
if (unlikely (status)) {
cairo_surface_destroy (snap);
return _cairo_surface_create_in_error (status);
}
snap->device_transform = surface->device_transform;
snap->device_transform_inverse = surface->device_transform_inverse;
_cairo_surface_attach_snapshot (surface, snap, NULL);
return snap;
}
}
if (surface->backend == &_cairo_surface_snapshot_backend)
return cairo_surface_reference (surface);
snapshot = (cairo_surface_snapshot_t *)
_cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
......
......@@ -328,12 +328,12 @@ _cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
{
assert (snapshot->snapshot_of != NULL);
snapshot->snapshot_of = NULL;
cairo_list_del (&snapshot->snapshot);
if (snapshot->snapshot_detach != NULL)
snapshot->snapshot_detach (snapshot);
snapshot->snapshot_of = NULL;
cairo_list_del (&snapshot->snapshot);
cairo_surface_destroy (snapshot);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment