summaryrefslogtreecommitdiff
path: root/src/cairo-recording-surface.c
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2023-01-01 15:14:08 +1030
committerAdrian Johnson <ajohnson@redneon.com>2023-01-15 19:29:28 +1030
commit7146358250975ec0f29b8ba80e80a26c52526bdc (patch)
treee37c45b2f6c4523aab202b331a4094949b189040 /src/cairo-recording-surface.c
parenta2b376ed787fc35270421730863411a0c047f564 (diff)
downloadcairo-7146358250975ec0f29b8ba80e80a26c52526bdc.tar.gz
Fix shared use of recording surface with paginated targets
The problem is _cairo_recording_surface_replay_and_create_regions() stores the cairo_recording_region_type_t in the same structure as the recording commands. This does not work well when the recording surface is used as source by multiple surfaces Fix this by moving the cairo_recording_region_type_t into a separate struct cairo_recording_regions_array_t. This struct is stored in a list that allows multiple create regions results to be store in the surface. The new function _cairo_recording_surface_region_array_attach() is used to create a new cairo_recording_regions_array_t, attach it to the recording surface and return a unique region id. The _cairo_recording_surface_replay_and_create_regions() and _cairo_recording_surface_replay_region() functions use this region id to identify the cairo_recording_regions_array_t. To handle nested recording surfaces, when replaying a recording, the region id is passed to the target as an extra parameter in the surface pattern. The wrapper surface makes a temporary copy of the pattern to ensure the snapshot pattern in the recording surface is not modified. cairo_recording_regions_array_t has a reference count so the target can hold on to the cairo_recording_regions_array_t after the paginated surface has called _cairo_recording_surface_region_array_remove().
Diffstat (limited to 'src/cairo-recording-surface.c')
-rw-r--r--src/cairo-recording-surface.c527
1 files changed, 502 insertions, 25 deletions
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index f8bac4b2e..a7de8df5b 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -86,16 +86,12 @@
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-wrapper-private.h"
#include "cairo-traps-private.h"
-typedef enum {
- CAIRO_RECORDING_REPLAY,
- CAIRO_RECORDING_CREATE_REGIONS
-} cairo_recording_replay_type_t;
-
typedef struct _cairo_recording_surface_replay_params {
const cairo_rectangle_int_t *surface_extents;
const cairo_matrix_t *surface_transform;
@@ -104,6 +100,7 @@ typedef struct _cairo_recording_surface_replay_params {
cairo_bool_t surface_is_unbounded;
cairo_recording_replay_type_t type;
cairo_recording_region_type_t region;
+ unsigned int regions_id;
const cairo_color_t *foreground_color;
cairo_bool_t foreground_used;
} cairo_recording_surface_replay_params_t;
@@ -437,6 +434,10 @@ cairo_recording_surface_create (cairo_content_t content,
surface->has_bilevel_alpha = FALSE;
surface->has_only_op_over = FALSE;
+ CAIRO_MUTEX_INIT (surface->mutex);
+
+ cairo_list_init (&surface->region_array_list);
+
return &surface->base;
}
slim_hidden_def (cairo_recording_surface_create);
@@ -454,12 +455,88 @@ _cairo_recording_surface_create_similar (void *abstract_surface,
return cairo_recording_surface_create (content, &extents);
}
+static void
+destroy_pattern_region_array (const cairo_pattern_t *pattern,
+ unsigned int region_id)
+{
+ if (region_id != 0) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+ if (_cairo_surface_is_recording (surface_pattern->surface))
+ _cairo_recording_surface_region_array_remove (surface_pattern->surface, region_id);
+ }
+ }
+}
+
+static void
+_cairo_recording_surface_region_array_destroy (cairo_recording_surface_t *surface,
+ cairo_recording_regions_array_t *region_array)
+{
+ cairo_command_t **elements;
+ cairo_recording_region_element_t *region_elements;
+ int i, num_elements;
+
+ num_elements = surface->commands.num_elements;
+ elements = _cairo_array_index (&surface->commands, 0);
+ region_elements = _cairo_array_index (&region_array->regions, 0);
+ for (i = 0; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+ cairo_recording_region_element_t *region_element = &region_elements[i];
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ destroy_pattern_region_array (&command->paint.source.base, region_element->source_id);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ destroy_pattern_region_array (&command->mask.source.base, region_element->source_id);
+ destroy_pattern_region_array (&command->mask.mask.base, region_element->mask_id);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ destroy_pattern_region_array (&command->stroke.source.base, region_element->source_id);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ destroy_pattern_region_array (&command->fill.source.base, region_element->source_id);
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ destroy_pattern_region_array (&command->show_text_glyphs.source.base, region_element->source_id);
+ break;
+
+ case CAIRO_COMMAND_TAG:
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+ }
+
+ _cairo_array_fini (&region_array->regions);
+ free (region_array);
+}
+
static cairo_status_t
_cairo_recording_surface_finish (void *abstract_surface)
{
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_t **elements;
int i, num_elements;
+ cairo_recording_regions_array_t *region_array, *region_next;
+
+ /* Normally backend surfaces hold a reference to the surface as
+ * well as the region and free the region before the surface. So
+ * the regions should already be freed at this point but just in
+ * case we ensure the regions are freed before destroying the
+ * surface. */
+ cairo_list_foreach_entry_safe (region_array, region_next,
+ cairo_recording_regions_array_t,
+ &surface->region_array_list, link)
+ {
+ cairo_list_del (&region_array->link);
+ _cairo_recording_surface_region_array_destroy (surface, region_array);
+ }
num_elements = surface->commands.num_elements;
elements = _cairo_array_index (&surface->commands, 0);
@@ -660,7 +737,6 @@ _command_init (cairo_recording_surface_t *surface,
command->type = type;
command->op = op;
- command->region = CAIRO_RECORDING_REGION_ALL;
command->extents = composite ? composite->unbounded : _cairo_empty_rectangle;
command->chain = NULL;
@@ -1162,7 +1238,6 @@ _command_init_copy (cairo_recording_surface_t *surface,
{
dst->type = src->type;
dst->op = src->op;
- dst->region = CAIRO_RECORDING_REGION_ALL;
dst->extents = src->extents;
dst->chain = NULL;
@@ -1561,6 +1636,10 @@ _cairo_recording_surface_snapshot (void *abstract_other)
surface->has_bilevel_alpha = other->has_bilevel_alpha;
surface->has_only_op_over = other->has_only_op_over;
+ CAIRO_MUTEX_INIT (surface->mutex);
+
+ cairo_list_init (&surface->region_array_list);
+
_cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
status = _cairo_recording_surface_copy (surface, other);
if (unlikely (status)) {
@@ -1626,6 +1705,123 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = {
_cairo_recording_surface_tag,
};
+static unsigned int
+_cairo_recording_surface_regions_allocate_unique_id (void)
+{
+ static cairo_atomic_int_t unique_id;
+
+#if CAIRO_NO_MUTEX
+ if (++unique_id == 0)
+ unique_id = 1;
+ return unique_id;
+#else
+ cairo_atomic_int_t old, id;
+
+ do {
+ old = _cairo_atomic_uint_get (&unique_id);
+ id = old + 1;
+ if (id == 0)
+ id = 1;
+ } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id));
+
+ return id;
+#endif
+}
+
+static cairo_recording_regions_array_t *
+_cairo_recording_surface_region_array_find (cairo_recording_surface_t *surface,
+ unsigned int id)
+{
+ cairo_recording_regions_array_t *regions;
+
+ cairo_list_foreach_entry (regions, cairo_recording_regions_array_t,
+ &surface->region_array_list, link)
+ {
+ if (regions->id == id)
+ return regions;
+ }
+
+ return NULL;
+}
+
+/* Create and initialize a new #cairo_recording_regions_array_t. Attach
+ * it to the recording surface and return its id
+ */
+cairo_status_t
+_cairo_recording_surface_region_array_attach (cairo_surface_t *abstract_surface,
+ unsigned int *id)
+{
+ cairo_recording_regions_array_t *region_array;
+ cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
+
+ assert (_cairo_surface_is_recording (abstract_surface));
+
+ region_array = _cairo_malloc (sizeof (cairo_recording_regions_array_t));
+ if (region_array == NULL) {
+ *id = 0;
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ region_array->id = _cairo_recording_surface_regions_allocate_unique_id ();
+
+ CAIRO_REFERENCE_COUNT_INIT (&region_array->ref_count, 1);
+
+ _cairo_array_init (&region_array->regions, sizeof (cairo_recording_region_element_t));
+
+ CAIRO_MUTEX_LOCK (surface->mutex);
+ cairo_list_add (&region_array->link, &surface->region_array_list);
+ CAIRO_MUTEX_UNLOCK (surface->mutex);
+
+ *id = region_array->id;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_recording_surface_region_array_remove (cairo_surface_t *abstract_surface,
+ unsigned int id)
+{
+ cairo_recording_regions_array_t *region_array;
+ cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
+
+ if (id == 0)
+ return;
+
+ assert (_cairo_surface_is_recording (abstract_surface));
+
+ CAIRO_MUTEX_LOCK (surface->mutex);
+ region_array = _cairo_recording_surface_region_array_find (surface, id);
+ if (region_array) {
+ if (_cairo_reference_count_dec_and_test (&region_array->ref_count))
+ cairo_list_del (&region_array->link);
+ else
+ region_array = NULL;
+ }
+
+ CAIRO_MUTEX_UNLOCK (surface->mutex);
+
+ if (region_array)
+ _cairo_recording_surface_region_array_destroy (surface, region_array);
+}
+
+void
+_cairo_recording_surface_region_array_reference (cairo_surface_t *abstract_surface,
+ unsigned int id)
+{
+ cairo_recording_regions_array_t *region_array;
+ cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
+
+ assert (_cairo_surface_is_recording (abstract_surface));
+
+ CAIRO_MUTEX_LOCK (surface->mutex);
+ region_array = _cairo_recording_surface_region_array_find (surface, id);
+ if (region_array) {
+ _cairo_reference_count_inc (&region_array->ref_count);
+ }
+
+ CAIRO_MUTEX_UNLOCK (surface->mutex);
+}
+
cairo_int_status_t
_cairo_recording_surface_get_path (cairo_surface_t *abstract_surface,
cairo_path_fixed_t *path)
@@ -1799,8 +1995,8 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
{
cairo_surface_wrapper_t wrapper;
cairo_command_t **elements;
- cairo_bool_t replay_all =
- params->type == CAIRO_RECORDING_CREATE_REGIONS || params->region == CAIRO_RECORDING_REGION_ALL;
+ cairo_recording_regions_array_t *regions_array = NULL;
+ cairo_recording_region_element_t *region_elements = NULL;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_rectangle_int_t extents;
cairo_bool_t use_indices = FALSE;
@@ -1821,6 +2017,11 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
assert (_cairo_surface_is_recording (&surface->base));
+ if (params->regions_id != 0) {
+ regions_array = _cairo_recording_surface_region_array_find (surface, params->regions_id);
+ assert (regions_array != NULL);
+ }
+
_cairo_surface_wrapper_init (&wrapper, params->target);
if (params->surface_extents)
_cairo_surface_wrapper_intersect_extents (&wrapper, params->surface_extents);
@@ -1845,18 +2046,48 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
surface->has_only_op_over = TRUE;
num_elements = surface->commands.num_elements;
+ if (regions_array) {
+ if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
+ /* Re-running create regions with the same region id is not supported. */
+ assert (_cairo_array_num_elements (&regions_array->regions) == 0);
+ void *array_elems;
+ status = _cairo_array_allocate (&regions_array->regions, num_elements, &array_elems);
+ if (unlikely (status))
+ return status;
+
+ /* Set regions to CAIRO_RECORDING_REGION_ALL and ids to 0 */
+ memset (array_elems, 0, num_elements * sizeof (cairo_recording_region_element_t));
+ } else {
+ assert (_cairo_array_num_elements (&regions_array->regions) == num_elements);
+ }
+ }
+
elements = _cairo_array_index (&surface->commands, 0);
+ if (regions_array)
+ region_elements = _cairo_array_index (&regions_array->regions, 0);
+
if (extents.width < r->width || extents.height < r->height) {
num_elements =
_cairo_recording_surface_get_visible_commands (surface, &extents);
use_indices = num_elements != surface->commands.num_elements;
}
+ cairo_bool_t target_is_analysis = _cairo_surface_is_analysis (params->target);
+
for (i = 0; i < num_elements; i++) {
cairo_command_t *command = elements[use_indices ? surface->indices[i] : i];
+ cairo_recording_region_element_t *region_element = NULL;
+ unsigned int source_region_id = 0;
+ unsigned int mask_region_id = 0;
+
+ if (region_elements)
+ region_element = &region_elements[use_indices ? surface->indices[i] : i];
- if (! replay_all && command->header.region != params->region)
+ if (region_element && params->type == CAIRO_RECORDING_REPLAY_REGION &&
+ region_element->region != params->region)
+ {
continue;
+ }
if (! _cairo_rectangle_intersects (&extents, &command->header.extents)) {
if (command->header.type != CAIRO_COMMAND_TAG)
@@ -1865,22 +2096,35 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
switch (command->header.type) {
case CAIRO_COMMAND_PAINT:
+ if (region_element)
+ source_region_id = region_element->source_id;
+
status = _cairo_surface_wrapper_paint (&wrapper,
command->header.op,
&command->paint.source.base,
+ source_region_id,
command->header.clip);
if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
_cairo_recording_surface_merge_source_attributes (surface,
command->header.op,
&command->paint.source.base);
+ if (region_element && target_is_analysis)
+ region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
}
break;
case CAIRO_COMMAND_MASK:
+ if (region_element) {
+ source_region_id = region_element->source_id;
+ mask_region_id = region_element->mask_id;
+ }
+
status = _cairo_surface_wrapper_mask (&wrapper,
command->header.op,
&command->mask.source.base,
+ source_region_id,
&command->mask.mask.base,
+ mask_region_id,
command->header.clip);
if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
_cairo_recording_surface_merge_source_attributes (surface,
@@ -1889,13 +2133,21 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
_cairo_recording_surface_merge_source_attributes (surface,
command->header.op,
&command->mask.mask.base);
+ if (region_element && target_is_analysis) {
+ region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
+ region_element->mask_id = _cairo_analysis_surface_get_mask_region_id (params->target);
+ }
}
break;
case CAIRO_COMMAND_STROKE:
+ if (region_element)
+ source_region_id = region_element->source_id;
+
status = _cairo_surface_wrapper_stroke (&wrapper,
command->header.op,
&command->stroke.source.base,
+ source_region_id,
&command->stroke.path,
&command->stroke.style,
&command->stroke.ctm,
@@ -1907,23 +2159,39 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
_cairo_recording_surface_merge_source_attributes (surface,
command->header.op,
&command->stroke.source.base);
+ if (region_element && target_is_analysis)
+ region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
}
break;
case CAIRO_COMMAND_FILL:
status = CAIRO_INT_STATUS_UNSUPPORTED;
- if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
- cairo_command_t *stroke_command;
+ if (region_element)
+ source_region_id = region_element->source_id;
- stroke_command = NULL;
- if (params->type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1)
+ if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
+ cairo_command_t *stroke_command = NULL;
+ cairo_recording_region_element_t *stroke_region_element = NULL;
+ unsigned stroke_region_id = 0;
+
+ /* The analysis surface does not implement
+ * fill_stroke. When creating regions the fill and
+ * stroke commands are tested separately.
+ */
+ if (params->type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) {
stroke_command = elements[i + 1];
+ if (region_elements)
+ stroke_region_element = &region_elements[i + 1];
+ }
- if (stroke_command != NULL &&
- params->type == CAIRO_RECORDING_REPLAY &&
+ if (stroke_region_element)
+ stroke_region_id = stroke_region_element->source_id;
+
+ if (stroke_command && stroke_region_element &&
+ params->type == CAIRO_RECORDING_REPLAY_REGION &&
params->region != CAIRO_RECORDING_REGION_ALL)
{
- if (stroke_command->header.region != params->region)
+ if (stroke_region_element->region != params->region)
stroke_command = NULL;
}
@@ -1937,12 +2205,14 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_fill_stroke (&wrapper,
command->header.op,
&command->fill.source.base,
+ source_region_id,
command->fill.fill_rule,
command->fill.tolerance,
command->fill.antialias,
&command->fill.path,
stroke_command->header.op,
&stroke_command->stroke.source.base,
+ stroke_region_id,
&stroke_command->stroke.style,
&stroke_command->stroke.ctm,
&stroke_command->stroke.ctm_inverse,
@@ -1964,6 +2234,7 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_fill (&wrapper,
command->header.op,
&command->fill.source.base,
+ source_region_id,
&command->fill.path,
command->fill.fill_rule,
command->fill.tolerance,
@@ -1973,14 +2244,20 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
_cairo_recording_surface_merge_source_attributes (surface,
command->header.op,
&command->fill.source.base);
+ if (region_element && target_is_analysis)
+ region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
}
}
break;
case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ if (region_element)
+ source_region_id = region_element->source_id;
+
status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
command->header.op,
&command->show_text_glyphs.source.base,
+ source_region_id,
command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
@@ -1991,6 +2268,9 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
_cairo_recording_surface_merge_source_attributes (surface,
command->header.op,
&command->show_text_glyphs.source.base);
+ if (region_element && target_is_analysis)
+ region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
+
}
break;
@@ -2009,11 +2289,11 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
status = CAIRO_INT_STATUS_SUCCESS;
- if (params->type == CAIRO_RECORDING_CREATE_REGIONS && command->header.region != CAIRO_RECORDING_REGION_NATIVE) {
+ if (params->type == CAIRO_RECORDING_CREATE_REGIONS && region_element) {
if (status == CAIRO_INT_STATUS_SUCCESS) {
- command->header.region = CAIRO_RECORDING_REGION_NATIVE;
+ region_element->region = CAIRO_RECORDING_REGION_NATIVE;
} else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
- command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
+ region_element->region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
status = CAIRO_INT_STATUS_SUCCESS;
} else {
assert (_cairo_int_status_is_error (status));
@@ -2042,7 +2322,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
{
cairo_surface_wrapper_t wrapper;
cairo_command_t **elements, *command;
- cairo_int_status_t status;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
if (unlikely (surface->base.status))
return surface->base.status;
@@ -2071,6 +2351,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_paint (&wrapper,
command->header.op,
&command->paint.source.base,
+ 0,
command->header.clip);
break;
@@ -2078,7 +2359,9 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_mask (&wrapper,
command->header.op,
&command->mask.source.base,
+ 0,
&command->mask.mask.base,
+ 0,
command->header.clip);
break;
@@ -2086,6 +2369,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_stroke (&wrapper,
command->header.op,
&command->stroke.source.base,
+ 0,
&command->stroke.path,
&command->stroke.style,
&command->stroke.ctm,
@@ -2099,6 +2383,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_fill (&wrapper,
command->header.op,
&command->fill.source.base,
+ 0,
&command->fill.path,
command->fill.fill_rule,
command->fill.tolerance,
@@ -2110,6 +2395,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
command->header.op,
&command->show_text_glyphs.source.base,
+ 0,
command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
@@ -2157,6 +2443,7 @@ _cairo_recording_surface_replay (cairo_surface_t *surface,
params.surface_is_unbounded = FALSE;
params.type = CAIRO_RECORDING_REPLAY;
params.region = CAIRO_RECORDING_REGION_ALL;
+ params.regions_id = 0;
params.foreground_color = NULL;
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
@@ -2178,6 +2465,7 @@ _cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surf
params.surface_is_unbounded = FALSE;
params.type = CAIRO_RECORDING_REPLAY;
params.region = CAIRO_RECORDING_REGION_ALL;
+ params.regions_id = 0;
params.foreground_color = foreground_color;
params.foreground_used = FALSE;
@@ -2191,7 +2479,8 @@ cairo_status_t
_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
const cairo_matrix_t *surface_transform,
cairo_surface_t *target,
- const cairo_clip_t *target_clip)
+ const cairo_clip_t *target_clip,
+ cairo_bool_t surface_is_unbounded)
{
cairo_recording_surface_replay_params_t params;
@@ -2199,9 +2488,10 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
params.surface_transform = surface_transform;
params.target = target;
params.target_clip = target_clip;
- params.surface_is_unbounded = FALSE;
+ params.surface_is_unbounded = surface_is_unbounded;
params.type = CAIRO_RECORDING_REPLAY;
params.region = CAIRO_RECORDING_REGION_ALL;
+ params.regions_id = 0;
params.foreground_color = NULL;
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
@@ -2215,6 +2505,7 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
*/
cairo_status_t
_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+ unsigned int regions_id,
const cairo_matrix_t *surface_transform,
cairo_surface_t *target,
cairo_bool_t surface_is_unbounded)
@@ -2228,6 +2519,7 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
params.surface_is_unbounded = surface_is_unbounded;
params.type = CAIRO_RECORDING_CREATE_REGIONS;
params.region = CAIRO_RECORDING_REGION_ALL;
+ params.regions_id = regions_id;
params.foreground_color = NULL;
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
@@ -2235,6 +2527,7 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
cairo_status_t
_cairo_recording_surface_replay_region (cairo_surface_t *surface,
+ unsigned int regions_id,
const cairo_rectangle_int_t *surface_extents,
cairo_surface_t *target,
cairo_recording_region_type_t region)
@@ -2246,8 +2539,9 @@ _cairo_recording_surface_replay_region (cairo_surface_t *surface,
params.target = target;
params.target_clip = NULL;
params.surface_is_unbounded = FALSE;
- params.type = CAIRO_RECORDING_REPLAY;
+ params.type = CAIRO_RECORDING_REPLAY_REGION;
params.region = region;
+ params.regions_id = regions_id;
params.foreground_color = NULL;
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
@@ -2263,7 +2557,7 @@ _recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
cairo_status_t status;
null_surface = _cairo_null_surface_create (surface->base.content);
- analysis_surface = _cairo_analysis_surface_create (null_surface);
+ analysis_surface = _cairo_analysis_surface_create (null_surface, FALSE);
cairo_surface_destroy (null_surface);
status = analysis_surface->status;
@@ -2395,3 +2689,186 @@ _cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface)
{
return surface->has_only_op_over;
}
+
+static void
+print_indent (FILE *file, int indent)
+{
+ fprintf (file, "%*s", indent * 2, "");
+}
+
+static void
+print_pattern (FILE *file,
+ const cairo_pattern_t *pattern,
+ unsigned int region_id,
+ int indent,
+ cairo_bool_t recurse)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID: {
+ cairo_solid_pattern_t *p = (cairo_solid_pattern_t *) pattern;
+ if (pattern->is_userfont_foreground) {
+ fprintf (file, "solid foreground\n");
+ } else {
+ fprintf (file, "solid rgba: %f %f %f %f\n",
+ p->color.red,
+ p->color.green,
+ p->color.blue,
+ p->color.alpha);
+ }
+ } break;
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *p = (cairo_surface_pattern_t *) pattern;
+ fprintf (file, "surface ");
+ if (p->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ fprintf (file, "recording id: %d\n", p->surface->unique_id);
+ if (recurse) {
+ _cairo_debug_print_recording_surface (file, p->surface,
+ region_id,
+ indent + 1, recurse);
+ }
+ } else if (p->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *image = (cairo_image_surface_t *)p->surface;
+ fprintf (file, "image format: ");
+ switch (image->format) {
+ case CAIRO_FORMAT_INVALID: fputs ("INVALID", file); break;
+ case CAIRO_FORMAT_ARGB32: fputs ("ARGB32", file); break;
+ case CAIRO_FORMAT_RGB24: fputs ("RGB24", file); break;
+ case CAIRO_FORMAT_A8: fputs ("A8", file); break;
+ case CAIRO_FORMAT_A1: fputs ("A1", file); break;
+ case CAIRO_FORMAT_RGB16_565: fputs ("RGB16_565", file); break;
+ case CAIRO_FORMAT_RGB30: fputs ("RGB30", file); break;
+ case CAIRO_FORMAT_RGB96F: fputs ("RGB96F", file); break;
+ case CAIRO_FORMAT_RGBA128F: fputs ("RGBA128F", file); break;
+ }
+ fprintf (file, " width: %d height: %d\n", image->width, image->height);
+ } else {
+ fprintf (file, "type %d\n", p->surface->type);
+ }
+ } break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ fprintf (file, "linear\n");
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ fprintf (file, "radial\n");
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ fprintf (file, "mesh\n");
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ fprintf (file, "raster\n");
+ break;
+ }
+}
+
+void
+_cairo_debug_print_recording_surface (FILE *file,
+ cairo_surface_t *surface,
+ unsigned int regions_id,
+ int indent,
+ cairo_bool_t recurse)
+{
+ cairo_command_t **elements;
+ cairo_recording_region_element_t *region_elements = NULL;
+ unsigned int i, num_elements;
+ cairo_recording_surface_t *recording_surface;
+ cairo_surface_t *free_me = NULL;
+ char common[100];
+
+ if (_cairo_surface_is_snapshot (surface))
+ free_me = surface = _cairo_surface_snapshot_get_target (surface);
+
+ assert (_cairo_surface_is_recording (surface));
+ recording_surface = (cairo_recording_surface_t *)surface;
+
+ print_indent (file, indent);
+ indent++;
+ fprintf(file, "recording surface id: %d regions id: %d\n", recording_surface->base.unique_id, regions_id);
+ num_elements = recording_surface->commands.num_elements;
+ elements = _cairo_array_index (&recording_surface->commands, 0);
+
+ if (regions_id != 0) {
+ cairo_recording_regions_array_t *regions_array;
+ regions_array = _cairo_recording_surface_region_array_find (recording_surface, regions_id);
+ assert (regions_array != NULL);
+ assert (_cairo_array_num_elements (&regions_array->regions) == num_elements);
+ region_elements = _cairo_array_index (&regions_array->regions, 0);
+ }
+
+ for (i = 0; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+ unsigned int source_region_id = 0;
+ unsigned int mask_region_id = 0;
+
+ common[0] = 0;
+ if (region_elements) {
+ cairo_recording_region_element_t *region_element = &region_elements[i];
+ strcpy (common, "region: ");
+ switch (region_element->region) {
+ case CAIRO_RECORDING_REGION_ALL: strcat (common, "all"); break;
+ case CAIRO_RECORDING_REGION_NATIVE: strcat (common, "native"); break;
+ case CAIRO_RECORDING_REGION_IMAGE_FALLBACK: strcat (common, "fallback"); break;
+ }
+ source_region_id = region_element->source_id;
+ mask_region_id = region_element->mask_id;
+ }
+ sprintf (common + strlen(common), " op: %s", _cairo_debug_operator_to_string (command->header.op));
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ print_indent (file, indent);
+ fprintf(file, "%d PAINT %s source: ", i, common);
+ print_pattern (file, &command->paint.source.base, source_region_id, indent + 1, recurse);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ print_indent (file, indent);
+ fprintf(file, "%d MASK %s\n", i, common);
+ print_indent (file, indent + 1);
+ fprintf(file, "source: ");
+ print_pattern (file, &command->mask.source.base, source_region_id, indent + 1, recurse);
+ print_indent (file, indent + 1);
+ fprintf(file, "mask: ");
+ print_pattern (file, &command->mask.mask.base, mask_region_id, indent + 1, recurse);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ print_indent (file, indent);
+ fprintf(file, "%d STROKE %s source:", i, common);
+ print_pattern (file, &command->stroke.source.base, source_region_id, indent + 1, recurse);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ print_indent (file, indent);
+ fprintf(file, "%d FILL %s source: ", i, common);
+ print_pattern (file, &command->fill.source.base, source_region_id, indent + 1, recurse);
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ print_indent (file, indent);
+ fprintf(file, "%d SHOW_TEXT_GLYPHS %s font_type: ", i, common);
+ switch (command->show_text_glyphs.scaled_font->backend->type) {
+ case CAIRO_FONT_TYPE_TOY: fputs ("toy", file); break;
+ case CAIRO_FONT_TYPE_FT: fputs ("ft", file); break;
+ case CAIRO_FONT_TYPE_WIN32: fputs ("win32", file); break;
+ case CAIRO_FONT_TYPE_QUARTZ: fputs ("quartz", file); break;
+ case CAIRO_FONT_TYPE_USER: fputs ("user", file); break;
+ case CAIRO_FONT_TYPE_DWRITE: fputs ("dwrite", file); break;
+ }
+ fprintf (file, " glyphs:");
+ for (unsigned j = 0; j < command->show_text_glyphs.num_glyphs; j++)
+ fprintf (file, " %ld", command->show_text_glyphs.glyphs[j].index);
+ fprintf (file, " source:");
+ print_pattern (file, &command->show_text_glyphs.source.base, source_region_id, indent + 1, recurse);
+ break;
+
+ case CAIRO_COMMAND_TAG:
+ print_indent (file, indent);
+ fprintf(file, "%d TAG\n", i);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+ }
+ cairo_surface_destroy (free_me);
+}