mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-08 07:58:02 +02:00
subsurface+recording: handle recursion
Ouch, a nasty bug surfaces after rearranging code to fix the others. Another self-copy loop this time through a subsurface of a recording surface. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
7971c678f1
commit
8a90b22897
4 changed files with 236 additions and 117 deletions
|
|
@ -528,35 +528,118 @@ _cairo_recording_surface_acquire_source_image_transformed (void *abstract_s
|
|||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
struct proxy {
|
||||
cairo_surface_t base;
|
||||
cairo_surface_t *image;
|
||||
};
|
||||
|
||||
static cairo_status_t
|
||||
proxy_acquire_source_image (void *abstract_surface,
|
||||
cairo_image_surface_t **image_out,
|
||||
void **image_extra)
|
||||
{
|
||||
struct proxy *proxy = abstract_surface;
|
||||
return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
|
||||
}
|
||||
|
||||
static void
|
||||
proxy_release_source_image (void *abstract_surface,
|
||||
cairo_image_surface_t *image,
|
||||
void *image_extra)
|
||||
{
|
||||
struct proxy *proxy = abstract_surface;
|
||||
_cairo_surface_release_source_image (proxy->image, image, image_extra);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
proxy_finish (void *abstract_surface)
|
||||
{
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const cairo_surface_backend_t proxy_backend = {
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
|
||||
proxy_finish,
|
||||
NULL,
|
||||
|
||||
NULL, /* create similar */
|
||||
NULL, /* create similar image */
|
||||
NULL, /* map to image */
|
||||
NULL, /* unmap image */
|
||||
|
||||
proxy_acquire_source_image,
|
||||
proxy_release_source_image,
|
||||
};
|
||||
|
||||
static cairo_surface_t *
|
||||
attach_proxy (cairo_surface_t *source,
|
||||
cairo_surface_t *image)
|
||||
{
|
||||
struct proxy *proxy;
|
||||
|
||||
proxy = malloc (sizeof (*proxy));
|
||||
if (unlikely (proxy == NULL))
|
||||
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
_cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
|
||||
|
||||
proxy->image = image;
|
||||
_cairo_surface_attach_snapshot (source, &proxy->base, NULL);
|
||||
|
||||
return &proxy->base;
|
||||
}
|
||||
|
||||
static void
|
||||
detach_proxy (cairo_surface_t *source,
|
||||
cairo_surface_t *proxy)
|
||||
{
|
||||
cairo_surface_finish (proxy);
|
||||
cairo_surface_destroy (proxy);
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
get_proxy (cairo_surface_t *proxy)
|
||||
{
|
||||
return ((struct proxy *)proxy)->image;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_recording_surface_acquire_source_image (void *abstract_surface,
|
||||
cairo_image_surface_t **image_out,
|
||||
void **image_extra)
|
||||
{
|
||||
cairo_matrix_t identity;
|
||||
cairo_surface_t *image;
|
||||
cairo_recording_surface_t *surface = abstract_surface;
|
||||
cairo_surface_t *image, *proxy;
|
||||
cairo_status_t status;
|
||||
|
||||
image = _cairo_surface_has_snapshot (abstract_surface,
|
||||
&_cairo_image_surface_backend);
|
||||
if (image != NULL) {
|
||||
*image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
|
||||
proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend);
|
||||
if (proxy != NULL) {
|
||||
*image_out = (cairo_image_surface_t *)
|
||||
cairo_surface_reference (get_proxy (proxy));
|
||||
*image_extra = NULL;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
cairo_matrix_init_identity (&identity);
|
||||
assert (! surface->unbounded);
|
||||
image = _cairo_image_surface_create_with_content (surface->base.content,
|
||||
surface->extents.width,
|
||||
surface->extents.height);
|
||||
if (unlikely (image->status))
|
||||
return image->status;
|
||||
|
||||
status =
|
||||
_cairo_recording_surface_acquire_source_image_transformed (abstract_surface, &identity, image_out, image_extra);
|
||||
if (unlikely (status))
|
||||
/* Handle recursion by returning future reads from the current image */
|
||||
proxy = attach_proxy (abstract_surface, image);
|
||||
status = _cairo_recording_surface_replay (&surface->base, image);
|
||||
detach_proxy (abstract_surface, proxy);
|
||||
|
||||
if (unlikely (status)) {
|
||||
cairo_surface_destroy (image);
|
||||
return status;
|
||||
}
|
||||
|
||||
_cairo_surface_attach_snapshot (abstract_surface,
|
||||
&(*image_out)->base,
|
||||
NULL);
|
||||
*image_out = (cairo_image_surface_t *) image;
|
||||
*image_extra = NULL;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -1035,20 +1035,77 @@ _emit_mesh_pattern (cairo_script_surface_t *surface,
|
|||
return CAIRO_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 ",
|
||||
surface->base.unique_id);
|
||||
|
||||
_cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
|
||||
cairo_surface_destroy (&surface->base);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_emit_recording_surface_pattern (cairo_script_surface_t *surface,
|
||||
cairo_recording_surface_t *source)
|
||||
{
|
||||
cairo_script_implicit_context_t old_cr;
|
||||
cairo_script_context_t *ctx = to_context (surface);
|
||||
cairo_script_surface_t *similar;
|
||||
cairo_surface_t *snapshot;
|
||||
cairo_rectangle_t r, *extents;
|
||||
cairo_status_t status;
|
||||
|
||||
snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
|
||||
if (snapshot) {
|
||||
_cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
extents = NULL;
|
||||
if (_cairo_recording_surface_get_bounds (&source->base, &r))
|
||||
extents = &r;
|
||||
|
||||
similar = _cairo_script_surface_create_internal (to_context (surface),
|
||||
similar = _cairo_script_surface_create_internal (ctx,
|
||||
source->base.content,
|
||||
extents,
|
||||
NULL);
|
||||
|
|
@ -1057,21 +1114,24 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface,
|
|||
|
||||
similar->base.is_clear = TRUE;
|
||||
|
||||
_cairo_output_stream_printf (to_context (surface)->stream,
|
||||
"//%s ",
|
||||
_cairo_output_stream_printf (ctx->stream, "//%s ",
|
||||
_content_to_string (source->base.content));
|
||||
if (extents) {
|
||||
_cairo_output_stream_printf (to_context (surface)->stream,
|
||||
"[%f %f %f %f]",
|
||||
_cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
|
||||
extents->x, extents->y,
|
||||
extents->width, extents->height);
|
||||
} else
|
||||
_cairo_output_stream_puts (to_context (surface)->stream, "[]");
|
||||
_cairo_output_stream_puts (to_context (surface)->stream,
|
||||
" record dup context\n");
|
||||
_cairo_output_stream_puts (ctx->stream, "[]");
|
||||
_cairo_output_stream_puts (ctx->stream, " record\n");
|
||||
|
||||
attach_snapshot (ctx, &source->base);
|
||||
|
||||
_cairo_output_stream_puts (ctx->stream, "dup context\n");
|
||||
|
||||
target_push (similar);
|
||||
similar->emitted = TRUE;
|
||||
|
||||
|
||||
old_cr = surface->cr;
|
||||
_cairo_script_implicit_context_init (&surface->cr);
|
||||
status = _cairo_recording_surface_replay (&source->base, &similar->base);
|
||||
|
|
@ -1085,7 +1145,7 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface,
|
|||
cairo_list_del (&similar->operand.link);
|
||||
assert (target_is_active (surface));
|
||||
|
||||
_cairo_output_stream_puts (to_context (surface)->stream, "pop ");
|
||||
_cairo_output_stream_puts (ctx->stream, "pop ");
|
||||
cairo_surface_destroy (&similar->base);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
|
@ -1497,55 +1557,6 @@ _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)
|
||||
|
|
@ -1563,7 +1574,7 @@ _emit_surface_pattern (cairo_script_surface_t *surface,
|
|||
snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
|
||||
if (snapshot) {
|
||||
_cairo_output_stream_printf (ctx->stream,
|
||||
"s%d pattern",
|
||||
"s%d pattern ",
|
||||
snapshot->unique_id);
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,15 @@ _cairo_surface_snapshot_finish (void *abstract_surface)
|
|||
return status;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_surface_snapshot_flush (void *abstract_surface)
|
||||
{
|
||||
cairo_surface_snapshot_t *surface = abstract_surface;
|
||||
|
||||
cairo_surface_flush (surface->target);
|
||||
return surface->target->status;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_surface_snapshot_acquire_source_image (void *abstract_surface,
|
||||
cairo_image_surface_t **image_out,
|
||||
|
|
@ -110,6 +119,9 @@ static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
|
|||
NULL, /* copy_page */
|
||||
NULL, /* show_page */
|
||||
_cairo_surface_snapshot_get_extents,
|
||||
NULL, /* old-show-glyphs */
|
||||
NULL, /* get-font-options */
|
||||
_cairo_surface_snapshot_flush,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "cairo-image-surface-private.h"
|
||||
#include "cairo-recording-surface-private.h"
|
||||
#include "cairo-surface-offset-private.h"
|
||||
#include "cairo-surface-snapshot-private.h"
|
||||
#include "cairo-surface-subsurface-private.h"
|
||||
|
||||
static const cairo_surface_backend_t _cairo_surface_subsurface_backend;
|
||||
|
|
@ -290,24 +291,6 @@ struct extra {
|
|||
void *image_extra;
|
||||
};
|
||||
|
||||
static void
|
||||
cairo_surface_paint_to_target (cairo_surface_t *target,
|
||||
cairo_surface_subsurface_t *subsurface)
|
||||
{
|
||||
cairo_t *cr;
|
||||
|
||||
cr = cairo_create (target);
|
||||
|
||||
cairo_set_source_surface (cr,
|
||||
subsurface->target,
|
||||
- subsurface->extents.x,
|
||||
- subsurface->extents.y);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_surface_subsurface_acquire_source_image (void *abstract_surface,
|
||||
cairo_image_surface_t **image_out,
|
||||
|
|
@ -322,8 +305,7 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac
|
|||
cairo_bool_t ret;
|
||||
|
||||
if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) {
|
||||
cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target;
|
||||
cairo_surface_t *snapshot;
|
||||
cairo_surface_t *meta, *snapshot;
|
||||
|
||||
snapshot = _cairo_surface_has_snapshot (&surface->base,
|
||||
&_cairo_image_surface_backend);
|
||||
|
|
@ -333,17 +315,32 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac
|
|||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (! _cairo_surface_has_snapshot (&meta->base,
|
||||
&_cairo_image_surface_backend))
|
||||
{
|
||||
meta = surface->target;
|
||||
if (surface->target->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
|
||||
meta = _cairo_surface_snapshot_get_target (meta);
|
||||
|
||||
if (! _cairo_surface_has_snapshot (meta, &_cairo_image_surface_backend)) {
|
||||
cairo_surface_pattern_t pattern;
|
||||
|
||||
image = (cairo_image_surface_t *)
|
||||
_cairo_image_surface_create_with_content (meta->base.content,
|
||||
_cairo_image_surface_create_with_content (meta->content,
|
||||
surface->extents.width,
|
||||
surface->extents.height);
|
||||
if (unlikely (image->base.status))
|
||||
return image->base.status;
|
||||
|
||||
cairo_surface_paint_to_target (&image->base, surface);
|
||||
_cairo_pattern_init_for_surface (&pattern, &image->base);
|
||||
cairo_matrix_init_translate (&pattern.base.matrix,
|
||||
-surface->extents.x, -surface->extents.y);
|
||||
pattern.base.filter = CAIRO_FILTER_NEAREST;
|
||||
status = _cairo_surface_paint (&image->base,
|
||||
CAIRO_OPERATOR_SOURCE,
|
||||
&pattern.base, NULL);
|
||||
_cairo_pattern_fini (&pattern.base);
|
||||
if (unlikely (status)) {
|
||||
cairo_surface_destroy (&image->base);
|
||||
return status;
|
||||
}
|
||||
|
||||
_cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
|
||||
|
||||
|
|
@ -387,6 +384,8 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac
|
|||
|
||||
image->base.is_clear = FALSE;
|
||||
} else {
|
||||
cairo_surface_pattern_t pattern;
|
||||
|
||||
image = (cairo_image_surface_t *)
|
||||
_cairo_image_surface_create_with_pixman_format (NULL,
|
||||
extra->image->pixman_format,
|
||||
|
|
@ -396,7 +395,18 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac
|
|||
if (unlikely ((status = image->base.status)))
|
||||
goto CLEANUP_IMAGE;
|
||||
|
||||
cairo_surface_paint_to_target (&image->base, surface);
|
||||
_cairo_pattern_init_for_surface (&pattern, &image->base);
|
||||
cairo_matrix_init_translate (&pattern.base.matrix,
|
||||
-surface->extents.x, -surface->extents.y);
|
||||
pattern.base.filter = CAIRO_FILTER_NEAREST;
|
||||
status = _cairo_surface_paint (&image->base,
|
||||
CAIRO_OPERATOR_SOURCE,
|
||||
&pattern.base, NULL);
|
||||
_cairo_pattern_fini (&pattern.base);
|
||||
if (unlikely (status)) {
|
||||
cairo_surface_destroy (&image->base);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
*image_out = image;
|
||||
|
|
@ -431,29 +441,32 @@ static cairo_surface_t *
|
|||
_cairo_surface_subsurface_snapshot (void *abstract_surface)
|
||||
{
|
||||
cairo_surface_subsurface_t *surface = abstract_surface;
|
||||
cairo_surface_subsurface_t *snapshot;
|
||||
cairo_surface_pattern_t pattern;
|
||||
cairo_surface_t *clone;
|
||||
cairo_status_t status;
|
||||
|
||||
snapshot = malloc (sizeof (cairo_surface_subsurface_t));
|
||||
if (unlikely (snapshot == NULL))
|
||||
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||||
clone = _cairo_surface_create_similar_scratch (surface->target,
|
||||
surface->target->content,
|
||||
surface->extents.width,
|
||||
surface->extents.height);
|
||||
if (unlikely (clone->status))
|
||||
return clone;
|
||||
|
||||
_cairo_surface_init (&snapshot->base,
|
||||
&_cairo_surface_subsurface_backend,
|
||||
NULL, /* device */
|
||||
surface->target->content);
|
||||
snapshot->target = _cairo_surface_snapshot (surface->target);
|
||||
if (unlikely (snapshot->target->status)) {
|
||||
cairo_status_t status;
|
||||
_cairo_pattern_init_for_surface (&pattern, surface->target);
|
||||
cairo_matrix_init_translate (&pattern.base.matrix,
|
||||
-surface->extents.x, -surface->extents.y);
|
||||
pattern.base.filter = CAIRO_FILTER_NEAREST;
|
||||
status = _cairo_surface_paint (clone,
|
||||
CAIRO_OPERATOR_SOURCE,
|
||||
&pattern.base, NULL);
|
||||
_cairo_pattern_fini (&pattern.base);
|
||||
|
||||
status = snapshot->target->status;
|
||||
free (snapshot);
|
||||
return _cairo_surface_create_in_error (status);
|
||||
if (unlikely (status)) {
|
||||
cairo_surface_destroy (clone);
|
||||
clone = _cairo_surface_create_in_error (status);
|
||||
}
|
||||
|
||||
snapshot->base.type = snapshot->target->type;
|
||||
snapshot->extents = surface->extents;
|
||||
|
||||
return &snapshot->base;
|
||||
return clone;
|
||||
}
|
||||
|
||||
static cairo_t *
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue