Commit 8a90b228 authored by Chris Wilson's avatar Chris Wilson 🤔
Browse files

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's avatarChris Wilson <chris@chris-wilson.co.uk>
parent 7971c678
......@@ -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;
snapshot = malloc (sizeof (cairo_surface_subsurface_t));
if (unlikely (snapshot == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
cairo_surface_pattern_t pattern;
cairo_surface_t *clone;
cairo_status_t status;
_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;
status = snapshot->target->status;
free (snapshot);
return _cairo_surface_create_in_error (status);
clone = _cairo_surface_create_similar_scratch (surface->target,
surface->target->content,
surface->extents.width,
surface->extents.height);
if (unlikely (clone->status))
return clone;
_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);
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 *
......
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