summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2023-01-16 08:59:32 +0000
committerAdrian Johnson <ajohnson@redneon.com>2023-01-16 08:59:32 +0000
commit745c3717aa8e91237bf90de1b2e908c72499aa0f (patch)
treefc50f38d44fdb67c751bcc4ac43668a38925c515
parent6abc8076c9cf06c1e2b92fccf57210442d471f5c (diff)
parent7146358250975ec0f29b8ba80e80a26c52526bdc (diff)
downloadcairo-745c3717aa8e91237bf90de1b2e908c72499aa0f.tar.gz
Merge branch 'fix-shared-recording-surface' into 'master'
Fix shared use of recording surfaces See merge request cairo/cairo!391
-rw-r--r--src/cairo-analysis-surface-private.h15
-rw-r--r--src/cairo-analysis-surface.c113
-rw-r--r--src/cairo-debug.c98
-rw-r--r--src/cairo-image-source.c2
-rw-r--r--src/cairo-paginated-surface.c27
-rw-r--r--src/cairo-pattern-private.h6
-rw-r--r--src/cairo-pattern.c1
-rw-r--r--src/cairo-pdf-surface-private.h7
-rw-r--r--src/cairo-pdf-surface.c29
-rw-r--r--src/cairo-ps-surface-private.h1
-rw-r--r--src/cairo-ps-surface.c34
-rw-r--r--src/cairo-quartz-surface.c3
-rw-r--r--src/cairo-recording-surface-private.h63
-rw-r--r--src/cairo-recording-surface.c527
-rw-r--r--src/cairo-script-surface.c10
-rw-r--r--src/cairo-spans-compositor.c2
-rw-r--r--src/cairo-surface-wrapper-private.h95
-rw-r--r--src/cairo-surface-wrapper.c130
-rw-r--r--src/cairo-traps-compositor.c2
-rw-r--r--src/cairo-xcb-surface-render.c3
-rw-r--r--src/cairo-xlib-source.c3
-rw-r--r--src/cairoint.h6
-rw-r--r--src/win32/cairo-win32-printing-surface.c1
-rw-r--r--test/create-regions.c435
-rw-r--r--test/meson.build1
-rw-r--r--test/pdf-tagged-text.c5
-rw-r--r--test/reference/create-regions.pdf.ref.pngbin0 -> 3227 bytes
-rw-r--r--test/reference/create-regions.ps.ref.pngbin0 -> 2524 bytes
-rw-r--r--test/reference/create-regions.svg.ref.pngbin0 -> 4514 bytes
29 files changed, 1442 insertions, 177 deletions
diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h
index 1e054c209..6489cceb8 100644
--- a/src/cairo-analysis-surface-private.h
+++ b/src/cairo-analysis-surface-private.h
@@ -38,7 +38,8 @@
#include "cairoint.h"
cairo_private cairo_surface_t *
-_cairo_analysis_surface_create (cairo_surface_t *target);
+_cairo_analysis_surface_create (cairo_surface_t *target,
+ cairo_bool_t create_region_ids);
cairo_private void
_cairo_analysis_surface_set_ctm (cairo_surface_t *surface,
@@ -64,6 +65,12 @@ cairo_private void
_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface,
cairo_box_t *bbox);
+cairo_private unsigned int
+_cairo_analysis_surface_get_source_region_id (cairo_surface_t *surface);
+
+cairo_private unsigned int
+_cairo_analysis_surface_get_mask_region_id (cairo_surface_t *surface);
+
cairo_private cairo_int_status_t
_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
cairo_int_status_t status_b);
@@ -71,4 +78,10 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
cairo_private cairo_surface_t *
_cairo_null_surface_create (cairo_content_t content);
+static inline cairo_bool_t
+_cairo_surface_is_analysis (const cairo_surface_t *surface)
+{
+ return (cairo_internal_surface_type_t)surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS;
+}
+
#endif /* CAIRO_ANALYSIS_SURFACE_H */
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 0e22b9aa9..86e10d5b0 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/*
* Copyright © 2006 Keith Packard
* Copyright © 2007 Adrian Johnson
@@ -59,6 +60,10 @@ typedef struct {
cairo_region_t fallback_region;
cairo_box_t page_bbox;
+ cairo_bool_t create_region_ids;
+ unsigned source_region_id;
+ unsigned mask_region_id;
+
cairo_bool_t has_ctm;
cairo_matrix_t ctm;
@@ -257,7 +262,8 @@ _add_operation (cairo_analysis_surface_t *surface,
static cairo_int_status_t
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
const cairo_pattern_t *pattern,
- cairo_rectangle_int_t *extents)
+ cairo_rectangle_int_t *extents,
+ unsigned int *regions_id)
{
const cairo_surface_pattern_t *surface_pattern;
cairo_analysis_surface_t *tmp;
@@ -280,7 +286,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
}
tmp = (cairo_analysis_surface_t *)
- _cairo_analysis_surface_create (surface->target);
+ _cairo_analysis_surface_create (surface->target, surface->create_region_ids);
if (unlikely (tmp->base.status)) {
status =tmp->base.status;
goto cleanup1;
@@ -295,13 +301,29 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
source = _cairo_surface_get_source (source, NULL);
surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT
- || pattern->extend == CAIRO_EXTEND_REFLECT);
- status = _cairo_recording_surface_replay_and_create_regions (source,
- &pattern->matrix,
- &tmp->base,
- surface_is_unbounded);
- if (unlikely (status))
- goto cleanup2;
+ || pattern->extend == CAIRO_EXTEND_REFLECT);
+
+ if (surface->create_region_ids) {
+ status = _cairo_recording_surface_region_array_attach (source, regions_id);
+ if (unlikely (status))
+ goto cleanup2;
+
+ status = _cairo_recording_surface_replay_and_create_regions (source,
+ *regions_id,
+ &pattern->matrix,
+ &tmp->base,
+ surface_is_unbounded);
+ if (unlikely (status))
+ goto cleanup2;
+ } else {
+ status = _cairo_recording_surface_replay_with_clip (source,
+ &pattern->matrix,
+ &tmp->base,
+ NULL, /* target clip */
+ surface_is_unbounded);
+ if (unlikely (status))
+ goto cleanup2;
+ }
/* black background or mime data fills entire extents */
if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) {
@@ -412,6 +434,8 @@ _cairo_analysis_surface_paint (void *abstract_surface,
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
if (surface->target->backend->paint == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
} else {
@@ -427,7 +451,10 @@ _cairo_analysis_surface_paint (void *abstract_surface,
&extents);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
cairo_rectangle_int_t rec_extents;
- backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ backend_status = _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
_cairo_rectangle_intersect (&extents, &rec_extents);
}
@@ -445,6 +472,8 @@ _cairo_analysis_surface_mask (void *abstract_surface,
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
if (surface->target->backend->mask == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
} else {
@@ -468,7 +497,10 @@ _cairo_analysis_surface_mask (void *abstract_surface,
src_surface = _cairo_surface_get_source (src_surface, NULL);
if (_cairo_surface_is_recording (src_surface)) {
backend_source_status =
- _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
if (_cairo_int_status_is_error (backend_source_status))
return backend_source_status;
@@ -481,7 +513,10 @@ _cairo_analysis_surface_mask (void *abstract_surface,
mask_surface = _cairo_surface_get_source (mask_surface, NULL);
if (_cairo_surface_is_recording (mask_surface)) {
backend_mask_status =
- _analyze_recording_surface_pattern (surface, mask, &rec_extents);
+ _analyze_recording_surface_pattern (surface,
+ mask,
+ &rec_extents,
+ &surface->mask_region_id);
if (_cairo_int_status_is_error (backend_mask_status))
return backend_mask_status;
@@ -520,6 +555,8 @@ _cairo_analysis_surface_stroke (void *abstract_surface,
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
if (surface->target->backend->stroke == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
} else {
@@ -538,7 +575,10 @@ _cairo_analysis_surface_stroke (void *abstract_surface,
&extents);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
cairo_rectangle_int_t rec_extents;
- backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ backend_status = _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
_cairo_rectangle_intersect (&extents, &rec_extents);
}
@@ -590,7 +630,10 @@ _cairo_analysis_surface_fill (void *abstract_surface,
&extents);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
cairo_rectangle_int_t rec_extents;
- backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ backend_status = _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
_cairo_rectangle_intersect (&extents, &rec_extents);
}
@@ -619,6 +662,9 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface,
cairo_int_status_t status, backend_status;
cairo_rectangle_int_t extents, glyph_extents;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
+
/* Adapted from _cairo_surface_show_glyphs */
if (surface->target->backend->show_glyphs != NULL) {
backend_status =
@@ -654,7 +700,10 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface,
&extents);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
cairo_rectangle_int_t rec_extents;
- backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ backend_status = _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
_cairo_rectangle_intersect (&extents, &rec_extents);
}
@@ -699,6 +748,9 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
cairo_int_status_t status, backend_status;
cairo_rectangle_int_t extents, glyph_extents;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
+
/* Adapted from _cairo_surface_show_glyphs */
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
if (surface->target->backend->show_text_glyphs != NULL) {
@@ -732,7 +784,10 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
&extents);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
cairo_rectangle_int_t rec_extents;
- backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
+ _analyze_recording_surface_pattern (surface,
+ source,
+ &rec_extents,
+ &surface->source_region_id);
_cairo_rectangle_intersect (&extents, &rec_extents);
}
@@ -760,6 +815,8 @@ _cairo_analysis_surface_tag (void *abstract_surface,
cairo_analysis_surface_t *surface = abstract_surface;
cairo_int_status_t backend_status;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
backend_status = CAIRO_INT_STATUS_SUCCESS;
if (surface->target->backend->tag != NULL) {
backend_status =
@@ -812,7 +869,8 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = {
};
cairo_surface_t *
-_cairo_analysis_surface_create (cairo_surface_t *target)
+_cairo_analysis_surface_create (cairo_surface_t *target,
+ cairo_bool_t create_region_ids)
{
cairo_analysis_surface_t *surface;
cairo_status_t status;
@@ -841,6 +899,10 @@ _cairo_analysis_surface_create (cairo_surface_t *target)
surface->has_supported = FALSE;
surface->has_unsupported = FALSE;
+ surface->create_region_ids = create_region_ids;
+ surface->source_region_id = 0;
+ surface->mask_region_id = 0;
+
_cairo_region_init (&surface->supported_region);
_cairo_region_init (&surface->fallback_region);
@@ -918,6 +980,23 @@ _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
*bbox = surface->page_bbox;
}
+unsigned int
+_cairo_analysis_surface_get_source_region_id (cairo_surface_t *abstract_surface)
+{
+ cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+ return surface->source_region_id;
+}
+
+unsigned int
+_cairo_analysis_surface_get_mask_region_id (cairo_surface_t *abstract_surface)
+{
+ cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+ return surface->mask_region_id;
+}
+
+
/* null surface type: a surface that does nothing (has no side effects, yay!) */
static cairo_int_status_t
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index a314eefbf..c83df3f47 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -319,3 +319,101 @@ _cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect)
rect->x, rect->y,
rect->width, rect->height);
}
+
+const char *
+_cairo_debug_operator_to_string (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR: return "CLEAR";
+ case CAIRO_OPERATOR_SOURCE: return "SOURCE";
+ case CAIRO_OPERATOR_OVER: return "OVER";
+ case CAIRO_OPERATOR_IN: return "IN";
+ case CAIRO_OPERATOR_OUT: return "OUT";
+ case CAIRO_OPERATOR_ATOP: return "ATOP";
+ case CAIRO_OPERATOR_DEST: return "DEST";
+ case CAIRO_OPERATOR_DEST_OVER: return "DEST_OVER";
+ case CAIRO_OPERATOR_DEST_IN: return "DEST_IN";
+ case CAIRO_OPERATOR_DEST_OUT: return "DEST_OUT";
+ case CAIRO_OPERATOR_DEST_ATOP: return "DEST_ATOP";
+ case CAIRO_OPERATOR_XOR: return "XOR";
+ case CAIRO_OPERATOR_ADD: return "ADD";
+ case CAIRO_OPERATOR_SATURATE: return "SATURATE";
+ case CAIRO_OPERATOR_MULTIPLY: return "MULTIPLY";
+ case CAIRO_OPERATOR_SCREEN: return "SCREEN";
+ case CAIRO_OPERATOR_OVERLAY: return "OVERLAY";
+ case CAIRO_OPERATOR_DARKEN: return "DARKEN";
+ case CAIRO_OPERATOR_LIGHTEN: return "LIGHTEN";
+ case CAIRO_OPERATOR_COLOR_DODGE: return "COLOR_DODGE";
+ case CAIRO_OPERATOR_COLOR_BURN: return "COLOR_BURN";
+ case CAIRO_OPERATOR_HARD_LIGHT: return "HARD_LIGHT";
+ case CAIRO_OPERATOR_SOFT_LIGHT: return "SOFT_LIGHT";
+ case CAIRO_OPERATOR_DIFFERENCE: return "DIFFERENCE";
+ case CAIRO_OPERATOR_EXCLUSION: return "EXCLUSION";
+ case CAIRO_OPERATOR_HSL_HUE: return "HSL_HUE";
+ case CAIRO_OPERATOR_HSL_SATURATION: return "HSL_SATURATION";
+ case CAIRO_OPERATOR_HSL_COLOR: return "HSL_COLOR";
+ case CAIRO_OPERATOR_HSL_LUMINOSITY: return "HSL_LUMINOSITY";
+ }
+ return "UNKNOWN";
+}
+
+const char *
+_cairo_debug_status_to_string (cairo_int_status_t status)
+{
+ switch (status) {
+ case CAIRO_INT_STATUS_SUCCESS: return "SUCCESS";
+ case CAIRO_INT_STATUS_NO_MEMORY: return "NO_MEMORY";
+ case CAIRO_INT_STATUS_INVALID_RESTORE: return "INVALID_RESTORE";
+ case CAIRO_INT_STATUS_INVALID_POP_GROUP: return "INVALID_POP_GROUP";
+ case CAIRO_INT_STATUS_NO_CURRENT_POINT: return "NO_CURRENT_POINT";
+ case CAIRO_INT_STATUS_INVALID_MATRIX: return "INVALID_MATRIX";
+ case CAIRO_INT_STATUS_INVALID_STATUS: return "INVALID_STATUS";
+ case CAIRO_INT_STATUS_NULL_POINTER: return "NULL_POINTER";
+ case CAIRO_INT_STATUS_INVALID_STRING: return "INVALID_STRING";
+ case CAIRO_INT_STATUS_INVALID_PATH_DATA: return "INVALID_PATH_DATA";
+ case CAIRO_INT_STATUS_READ_ERROR: return "READ_ERROR";
+ case CAIRO_INT_STATUS_WRITE_ERROR: return "WRITE_ERROR";
+ case CAIRO_INT_STATUS_SURFACE_FINISHED: return "SURFACE_FINISHED";
+ case CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH: return "SURFACE_TYPE_MISMATCH";
+ case CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH: return "PATTERN_TYPE_MISMATCH";
+ case CAIRO_INT_STATUS_INVALID_CONTENT: return "INVALID_CONTENT";
+ case CAIRO_INT_STATUS_INVALID_FORMAT: return "INVALID_FORMAT";
+ case CAIRO_INT_STATUS_INVALID_VISUAL: return "INVALID_VISUAL";
+ case CAIRO_INT_STATUS_FILE_NOT_FOUND: return "FILE_NOT_FOUND";
+ case CAIRO_INT_STATUS_INVALID_DASH: return "INVALID_DASH";
+ case CAIRO_INT_STATUS_INVALID_DSC_COMMENT: return "INVALID_DSC_COMMENT";
+ case CAIRO_INT_STATUS_INVALID_INDEX: return "INVALID_INDEX";
+ case CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE: return "CLIP_NOT_REPRESENTABLE";
+ case CAIRO_INT_STATUS_TEMP_FILE_ERROR: return "TEMP_FILE_ERROR";
+ case CAIRO_INT_STATUS_INVALID_STRIDE: return "INVALID_STRIDE";
+ case CAIRO_INT_STATUS_FONT_TYPE_MISMATCH: return "FONT_TYPE_MISMATCH";
+ case CAIRO_INT_STATUS_USER_FONT_IMMUTABLE: return "USER_FONT_IMMUTABLE";
+ case CAIRO_INT_STATUS_USER_FONT_ERROR: return "USER_FONT_ERROR";
+ case CAIRO_INT_STATUS_NEGATIVE_COUNT: return "NEGATIVE_COUNT";
+ case CAIRO_INT_STATUS_INVALID_CLUSTERS: return "INVALID_CLUSTERS";
+ case CAIRO_INT_STATUS_INVALID_SLANT: return "INVALID_SLANT";
+ case CAIRO_INT_STATUS_INVALID_WEIGHT: return "INVALID_WEIGHT";
+ case CAIRO_INT_STATUS_INVALID_SIZE: return "INVALID_SIZE";
+ case CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED: return "USER_FONT_NOT_IMPLEMENTED";
+ case CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH: return "DEVICE_TYPE_MISMATCH";
+ case CAIRO_INT_STATUS_DEVICE_ERROR: return "DEVICE_ERROR";
+ case CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION: return "INVALID_MESH_CONSTRUCTION";
+ case CAIRO_INT_STATUS_DEVICE_FINISHED: return "DEVICE_FINISHED";
+ case CAIRO_INT_STATUS_JBIG2_GLOBAL_MISSING: return "JBIG2_GLOBAL_MISSING";
+ case CAIRO_INT_STATUS_PNG_ERROR: return "PNG_ERROR";
+ case CAIRO_INT_STATUS_FREETYPE_ERROR: return "FREETYPE_ERROR";
+ case CAIRO_INT_STATUS_WIN32_GDI_ERROR: return "WIN32_GDI_ERROR";
+ case CAIRO_INT_STATUS_TAG_ERROR: return "TAG_ERROR";
+ case CAIRO_INT_STATUS_DWRITE_ERROR: return "DWRITE_ERROR";
+
+ case CAIRO_INT_STATUS_LAST_STATUS: return "LAST_STATUS";
+
+ case CAIRO_INT_STATUS_UNSUPPORTED: return "UNSUPPORTED";
+ case CAIRO_INT_STATUS_DEGENERATE: return "DEGENERATE";
+ case CAIRO_INT_STATUS_NOTHING_TO_DO: return "NOTHING_TO_DO";
+ case CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY: return "FLATTEN_TRANSPARENCY";
+ case CAIRO_INT_STATUS_IMAGE_FALLBACK: return "IMAGE_FALLBACK";
+ case CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN: return "ANALYZE_RECORDING_SURFACE_PATTERN";
+ }
+ return "UNKNOWN";
+}
diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
index b8c1c88f5..aafdaeded 100644
--- a/src/cairo-image-source.c
+++ b/src/cairo-image-source.c
@@ -1225,7 +1225,7 @@ _pixman_image_for_recording (cairo_image_surface_t *dst,
/* Handle recursion by returning future reads from the current image */
proxy = attach_proxy (source, clone);
- status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL);
+ status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL, FALSE);
if (clone->foreground_used)
dst->base.foreground_used = clone->foreground_used;
detach_proxy (source, proxy);
diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 07d86d070..278c1a641 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Red Hat, Inc
@@ -401,11 +402,12 @@ _paint_page (cairo_paginated_surface_t *surface)
cairo_surface_t *analysis;
cairo_int_status_t status;
cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;
+ unsigned int regions_id = 0;
if (unlikely (surface->target->status))
return surface->target->status;
- analysis = _cairo_analysis_surface_create (surface->target);
+ analysis = _cairo_analysis_surface_create (surface->target, TRUE);
if (unlikely (analysis->status))
return _cairo_surface_set_error (surface->target, analysis->status);
@@ -414,21 +416,26 @@ _paint_page (cairo_paginated_surface_t *surface)
if (unlikely (status))
goto FAIL;
+ status = _cairo_recording_surface_region_array_attach (surface->recording_surface, &regions_id);
+ if (status)
+ goto FAIL;
+
status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
+ regions_id,
NULL, analysis, FALSE);
if (status)
goto FAIL;
assert (analysis->status == CAIRO_STATUS_SUCCESS);
- if (surface->backend->set_bounding_box) {
- cairo_box_t bbox;
+ if (surface->backend->set_bounding_box) {
+ cairo_box_t bbox;
- _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
- status = surface->backend->set_bounding_box (surface->target, &bbox);
- if (unlikely (status))
- goto FAIL;
- }
+ _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
+ status = surface->backend->set_bounding_box (surface->target, &bbox);
+ if (unlikely (status))
+ goto FAIL;
+ }
if (surface->backend->set_fallback_images_required) {
cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);
@@ -467,6 +474,7 @@ _paint_page (cairo_paginated_surface_t *surface)
goto FAIL;
status = _cairo_recording_surface_replay_region (surface->recording_surface,
+ regions_id,
NULL,
surface->target,
CAIRO_RECORDING_REGION_NATIVE);
@@ -525,6 +533,9 @@ _paint_page (cairo_paginated_surface_t *surface)
}
FAIL:
+ if (regions_id)
+ _cairo_recording_surface_region_array_remove (surface->recording_surface, regions_id);
+
cairo_surface_destroy (analysis);
return _cairo_surface_set_error (surface->target, status);
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index f6138fb70..0a5e41e8b 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -87,6 +87,12 @@ typedef struct _cairo_surface_pattern {
cairo_pattern_t base;
cairo_surface_t *surface;
+
+ /* This field is only used by the wrapper surface for retreiving
+ * the region id from the target during create regions and passing
+ * the region id to the target surface during playback.
+ */
+ unsigned int region_array_id;
} cairo_surface_pattern_t;
typedef struct _cairo_gradient_stop {
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 64db0a336..070309ac6 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -561,6 +561,7 @@ _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE);
pattern->surface = cairo_surface_reference (surface);
+ pattern->region_array_id = 0;
}
static void
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 87f5ffa25..0781d9c53 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -95,6 +95,7 @@ typedef struct _cairo_pdf_source_surface_entry {
typedef struct _cairo_pdf_source_surface {
cairo_pattern_type_t type;
cairo_surface_t *surface;
+ unsigned int region_id;
cairo_pattern_t *raster_pattern;
cairo_pdf_source_surface_entry_t *hash_entry;
} cairo_pdf_source_surface_t;
@@ -279,9 +280,9 @@ struct _cairo_pdf_surface {
cairo_array_t pages;
cairo_array_t rgb_linear_functions;
cairo_array_t alpha_linear_functions;
- cairo_array_t page_patterns;
- cairo_array_t page_surfaces;
- cairo_array_t doc_surfaces;
+ cairo_array_t page_patterns; /* cairo_pdf_pattern_t */
+ cairo_array_t page_surfaces; /* cairo_pdf_source_surface_t */
+ cairo_array_t doc_surfaces; /* cairo_pdf_source_surface_t */
cairo_hash_table_t *all_surfaces;
cairo_array_t smask_groups;
cairo_array_t knockout_group;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 6fcd14f02..e3a27ed81 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -54,6 +54,7 @@
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-image-info-private.h"
+#include "cairo-recording-surface-inline.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-paginated-private.h"
@@ -1014,7 +1015,13 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
size = _cairo_array_num_elements (&surface->page_surfaces);
for (i = 0; i < size; i++) {
src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i);
- cairo_surface_destroy (src_surface->surface);
+ if (src_surface->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+ cairo_pattern_destroy (src_surface->raster_pattern);
+ } else {
+ if (_cairo_surface_is_recording (src_surface->surface) && src_surface->region_id != 0)
+ _cairo_recording_surface_region_array_remove (src_surface->surface, src_surface->region_id);
+ cairo_surface_destroy (src_surface->surface);
+ }
}
_cairo_array_truncate (&surface->page_surfaces, 0);
@@ -1723,6 +1730,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
_cairo_pdf_source_surface_init_key (surface_entry);
src_surface.hash_entry = surface_entry;
+ src_surface.region_id = 0;
if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE;
src_surface.surface = NULL;
@@ -1734,6 +1742,16 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
src_surface.type = CAIRO_PATTERN_TYPE_SURFACE;
src_surface.surface = cairo_surface_reference (source_surface);
src_surface.raster_pattern = NULL;
+ if (source_pattern) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
+ src_surface.region_id = surface_pattern->region_array_id;
+ if (_cairo_surface_is_recording (surface_pattern->surface) &&
+ surface_pattern->region_array_id != 0)
+ {
+ _cairo_recording_surface_region_array_reference (surface_pattern->surface,
+ surface_pattern->region_array_id);
+ }
+ }
}
surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
@@ -2593,7 +2611,13 @@ _cairo_pdf_surface_finish (void *abstract_surface)
size = _cairo_array_num_elements (&surface->doc_surfaces);
for (i = 0; i < size; i++) {
_cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface);
- cairo_surface_destroy (doc_surface.surface);
+ if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+ cairo_pattern_destroy (doc_surface.raster_pattern);
+ } else {
+ if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0)
+ _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id);
+ cairo_surface_destroy (doc_surface.surface);
+ }
}
_cairo_array_fini (&surface->doc_surfaces);
_cairo_hash_table_foreach (surface->all_surfaces,
@@ -3720,6 +3744,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
}
status = _cairo_recording_surface_replay_region (source,
+ pdf_source->region_id,
is_subsurface ? extents : NULL,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index f18403190..e825b57fd 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -56,6 +56,7 @@ typedef struct _cairo_ps_form {
cairo_bool_t is_image;
int id;
cairo_surface_t *src_surface;
+ unsigned int regions_id;
cairo_rectangle_int_t src_surface_extents;
cairo_bool_t src_surface_bounded;
cairo_filter_t filter;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index beccc0151..a5008df88 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -66,18 +66,19 @@
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-info-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-list-inline.h"
-#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-output-stream-private.h"
#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-inline.h"
#include "cairo-recording-surface-private.h"
+#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
-#include "cairo-output-stream-private.h"
-#include "cairo-type3-glyph-surface-private.h"
-#include "cairo-image-info-private.h"
#include "cairo-tag-attributes-private.h"
+#include "cairo-type3-glyph-surface-private.h"
#include <stdio.h>
#include <ctype.h>
@@ -176,6 +177,7 @@ typedef enum {
typedef struct {
/* input params */
cairo_surface_t *src_surface;
+ unsigned int regions_id;
cairo_operator_t op;
const cairo_rectangle_int_t *src_surface_extents;
cairo_bool_t src_surface_bounded;
@@ -286,6 +288,8 @@ _cairo_ps_form_pluck (void *entry, void *closure)
_cairo_hash_table_remove (patterns, &surface_entry->base);
free (surface_entry->unique_id);
+ if (_cairo_surface_is_recording (surface_entry->src_surface) && surface_entry->regions_id != 0)
+ _cairo_recording_surface_region_array_remove (surface_entry->src_surface, surface_entry->regions_id);
cairo_surface_destroy (surface_entry->src_surface);
free (surface_entry);
}
@@ -3308,6 +3312,7 @@ _cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface,
static cairo_status_t
_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
cairo_surface_t *recording_surface,
+ unsigned int regions_id,
const cairo_rectangle_int_t *recording_extents,
cairo_bool_t subsurface)
{
@@ -3378,6 +3383,7 @@ _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
}
status = _cairo_recording_surface_replay_region (recording_surface,
+ regions_id,
subsurface ? recording_extents : NULL,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
@@ -3530,6 +3536,9 @@ _cairo_ps_surface_use_form (cairo_ps_surface_t *surface,
source_entry->unique_id = unique_id;
source_entry->id = surface->num_forms++;
source_entry->src_surface = cairo_surface_reference (params->src_surface);
+ source_entry->regions_id = params->regions_id;
+ if (_cairo_surface_is_recording (source_entry->src_surface) && source_entry->regions_id != 0)
+ _cairo_recording_surface_region_array_reference (source_entry->src_surface, source_entry->regions_id);
source_entry->src_surface_extents = *params->src_surface_extents;
source_entry->src_surface_bounded = params->src_surface_bounded;
source_entry->required_extents = *params->src_op_extents;
@@ -3662,11 +3671,13 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) params->src_surface;
status = _cairo_ps_surface_emit_recording_surface (surface,
sub->target,
+ params->regions_id,
&sub->extents,
TRUE);
} else {
status = _cairo_ps_surface_emit_recording_surface (surface,
params->src_surface,
+ params->regions_id,
params->src_op_extents,
FALSE);
}
@@ -3709,6 +3720,7 @@ _cairo_ps_form_emit (void *entry, void *closure)
cairo_output_stream_t *old_stream;
params.src_surface = form->src_surface;
+ params.regions_id = form->regions_id;
params.op = CAIRO_OPERATOR_OVER;
params.src_surface_extents = &form->src_surface_extents;
params.src_surface_bounded = form->src_surface_bounded;
@@ -3850,11 +3862,17 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
cairo_path_fixed_t path;
cairo_emit_surface_params_t params;
cairo_image_surface_t *image = NULL;
+ unsigned int region_id = 0;
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+ region_id = surface_pattern->region_array_id;
+ }
+
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
@@ -3940,6 +3958,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
cairo_matrix_translate (&ps_p2d, x_offset, y_offset);
params.src_surface = image ? &image->base : source_surface;
+ params.regions_id = image ? 0 : region_id;
params.op = op;
params.src_surface_extents = &src_surface_extents;
params.src_surface_bounded = src_surface_bounded;
@@ -3994,12 +4013,18 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
cairo_rectangle_int_t src_op_extents;
cairo_emit_surface_params_t params;
cairo_extend_t extend = cairo_pattern_get_extend (pattern);
+ unsigned int region_id = 0;
cairo_p2d = pattern->matrix;
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+ region_id = surface_pattern->region_array_id;
+ }
+
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
@@ -4093,6 +4118,7 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
old_paint_proc = surface->paint_proc;
surface->paint_proc = TRUE;
params.src_surface = image ? &image->base : source_surface;
+ params.regions_id = image ? 0 : region_id;
params.op = op;
params.src_surface_extents = &pattern_extents;
params.src_surface_bounded = bounded;
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index aed2cced6..dbb6aa46f 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -735,7 +735,8 @@ _cairo_surface_to_cgimage (cairo_surface_t *source,
status = _cairo_recording_surface_replay_with_clip (source,
matrix,
&image_surface->base,
- NULL);
+ NULL,
+ FALSE);
if (unlikely (status)) {
cairo_surface_destroy (&image_surface->base);
return status;
diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h
index 3d325383b..7d4de1ed9 100644
--- a/src/cairo-recording-surface-private.h
+++ b/src/cairo-recording-surface-private.h
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Red Hat, Inc
@@ -55,14 +56,19 @@ typedef enum {
} cairo_command_type_t;
typedef enum {
- CAIRO_RECORDING_REGION_ALL,
+ CAIRO_RECORDING_REGION_ALL = 0,
CAIRO_RECORDING_REGION_NATIVE,
CAIRO_RECORDING_REGION_IMAGE_FALLBACK
} cairo_recording_region_type_t;
+typedef enum {
+ CAIRO_RECORDING_REPLAY,
+ CAIRO_RECORDING_CREATE_REGIONS,
+ CAIRO_RECORDING_REPLAY_REGION
+} cairo_recording_replay_type_t;
+
typedef struct _cairo_command_header {
cairo_command_type_t type;
- cairo_recording_region_type_t region;
cairo_operator_t op;
cairo_rectangle_int_t extents;
cairo_clip_t *clip;
@@ -155,8 +161,27 @@ typedef struct _cairo_recording_surface {
struct bbtree *left, *right;
cairo_command_header_t *chain;
} bbtree;
+
+ /* The mutex protects modification to all subsequent fields. */
+ cairo_mutex_t mutex;
+
+ cairo_list_t region_array_list;
+
} cairo_recording_surface_t;
+typedef struct _cairo_recording_region_element {
+ cairo_recording_region_type_t region;
+ unsigned int source_id;
+ unsigned int mask_id;
+} cairo_recording_region_element_t;
+
+typedef struct _cairo_recording_region_array {
+ unsigned int id;
+ cairo_reference_count_t ref_count;
+ cairo_array_t regions; /* cairo_recording_region_element_t */
+ cairo_list_t link;
+} cairo_recording_regions_array_t;
+
slim_hidden_proto (cairo_recording_surface_create);
cairo_private cairo_int_status_t
@@ -182,18 +207,21 @@ cairo_private 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_private cairo_status_t
-_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+_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);
+ cairo_surface_t *target,
+ cairo_bool_t surface_is_unbounded);
cairo_private cairo_status_t
_cairo_recording_surface_replay_region (cairo_surface_t *surface,
- const cairo_rectangle_int_t *surface_extents,
+ unsigned int regions_id,
+ const cairo_rectangle_int_t *surface_extents,
cairo_surface_t *target,
- cairo_recording_region_type_t region);
+ cairo_recording_region_type_t region);
cairo_private cairo_status_t
_cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording,
@@ -211,4 +239,23 @@ _cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surf
cairo_private cairo_bool_t
_cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface);
+cairo_private cairo_status_t
+_cairo_recording_surface_region_array_attach (cairo_surface_t *surface,
+ unsigned int *id);
+
+cairo_private void
+_cairo_recording_surface_region_array_reference (cairo_surface_t *surface,
+ unsigned int id);
+
+cairo_private void
+_cairo_recording_surface_region_array_remove (cairo_surface_t *surface,
+ unsigned int id);
+
+cairo_private void
+_cairo_debug_print_recording_surface (FILE *file,
+ cairo_surface_t *surface,
+ unsigned int regions_id,
+ int indent,
+ cairo_bool_t recurse);
+
#endif /* CAIRO_RECORDING_SURFACE_H */
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);
+}
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 7901b4c35..2baf873de 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -2509,7 +2509,7 @@ _cairo_script_surface_paint (void *abstract_surface,
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_paint (&surface->wrapper,
- op, source, clip);
+ op, source, 0, clip);
}
return CAIRO_STATUS_SUCCESS;
@@ -2566,7 +2566,7 @@ _cairo_script_surface_mask (void *abstract_surface,
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_mask (&surface->wrapper,
- op, source, mask, clip);
+ op, source, 0, mask, 0, clip);
}
return CAIRO_STATUS_SUCCESS;
@@ -2653,7 +2653,7 @@ _cairo_script_surface_stroke (void *abstract_surface,
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_stroke (&surface->wrapper,
- op, source, path,
+ op, source, 0, path,
style,
ctm, ctm_inverse,
tolerance, antialias,
@@ -2734,7 +2734,7 @@ _cairo_script_surface_fill (void *abstract_surface,
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_fill (&surface->wrapper,
- op, source, path,
+ op, source, 0, path,
fill_rule,
tolerance,
antialias,
@@ -3585,7 +3585,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface,
if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
- op, source,
+ op, source, 0,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c
index 50c92b25c..49c5999d2 100644
--- a/src/cairo-spans-compositor.c
+++ b/src/cairo-spans-compositor.c
@@ -612,7 +612,7 @@ composite_aligned_boxes (const cairo_spans_compositor_t *compositor,
recording_clip = _cairo_clip_from_boxes (boxes);
status = _cairo_recording_surface_replay_with_clip (unwrap_source (source),
- m, dst, recording_clip);
+ m, dst, recording_clip, FALSE);
_cairo_clip_destroy (recording_clip);
return status;
diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h
index 380ba099d..016402d7e 100644
--- a/src/cairo-surface-wrapper-private.h
+++ b/src/cairo-surface-wrapper-private.h
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
@@ -41,6 +42,7 @@
#include "cairoint.h"
#include "cairo-types-private.h"
+#include "cairo-recording-surface-private.h"
#include "cairo-surface-backend-private.h"
CAIRO_BEGIN_DECLS
@@ -54,6 +56,9 @@ struct _cairo_surface_wrapper {
cairo_rectangle_int_t extents;
const cairo_clip_t *clip;
+ unsigned int source_region_id;
+ unsigned int mask_region_id;
+
cairo_bool_t needs_transform;
};
@@ -95,60 +100,68 @@ _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
cairo_private cairo_status_t
_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_clip_t *clip);
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_clip_t *clip);
cairo_private cairo_status_t
_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_pattern_t *mask,
- const cairo_clip_t *clip);
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_pattern_t *mask,
+ unsigned int mask_region_id,
+ const cairo_clip_t *clip);
cairo_private cairo_status_t
-_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- const cairo_matrix_t *ctm_inverse,
- double tolerance,
- cairo_antialias_t antialias,
- const cairo_clip_t *clip);
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
cairo_private cairo_status_t
-_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t fill_op,
- const cairo_pattern_t *fill_source,
- cairo_fill_rule_t fill_rule,
- double fill_tolerance,
- cairo_antialias_t fill_antialias,
- const cairo_path_fixed_t*path,
- cairo_operator_t stroke_op,
- const cairo_pattern_t *stroke_source,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *stroke_ctm,
- const cairo_matrix_t *stroke_ctm_inverse,
- double stroke_tolerance,
- cairo_antialias_t stroke_antialias,
- const cairo_clip_t *clip);
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ unsigned int fill_region_id,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ unsigned int stroke_region_id,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip);
cairo_private cairo_status_t
-_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- double tolerance,
- cairo_antialias_t antialias,
- const cairo_clip_t *clip);
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
cairo_private cairo_status_t
-_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
+ unsigned int source_region_id,
const char *utf8,
int utf8_len,
const cairo_glyph_t *glyphs,
diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
index 7fb417a20..29c19c5a5 100644
--- a/src/cairo-surface-wrapper.c
+++ b/src/cairo-surface-wrapper.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Red Hat, Inc
@@ -47,12 +48,18 @@
static void
_copy_transformed_pattern (cairo_pattern_t *pattern,
const cairo_pattern_t *original,
- const cairo_matrix_t *ctm_inverse)
+ const cairo_matrix_t *ctm_inverse,
+ unsigned int region_id)
{
_cairo_pattern_init_static_copy (pattern, original);
if (! _cairo_matrix_is_identity (ctm_inverse))
_cairo_pattern_transform (pattern, ctm_inverse);
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+ surface_pattern->region_array_id = region_id;
+ }
}
cairo_status_t
@@ -129,9 +136,10 @@ _cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper,
cairo_status_t
_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_clip_t *clip)
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t *dev_clip;
@@ -144,7 +152,7 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || source_region_id != 0) {
cairo_matrix_t m;
_cairo_surface_wrapper_get_transform (wrapper, &m);
@@ -152,7 +160,7 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
- _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id);
source = &source_copy.base;
}
@@ -162,13 +170,14 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
return status;
}
-
cairo_status_t
_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_pattern_t *mask,
- const cairo_clip_t *clip)
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_pattern_t *mask,
+ unsigned int mask_region_id,
+ const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t *dev_clip;
@@ -182,7 +191,7 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || source_region_id != 0 || mask_region_id != 0) {
cairo_matrix_t m;
_cairo_surface_wrapper_get_transform (wrapper, &m);
@@ -190,10 +199,10 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
- _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id);
source = &source_copy.base;
- _copy_transformed_pattern (&mask_copy.base, mask, &m);
+ _copy_transformed_pattern (&mask_copy.base, mask, &m, mask_region_id);
mask = &mask_copy.base;
}
@@ -204,16 +213,17 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
}
cairo_status_t
-_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- const cairo_matrix_t *ctm_inverse,
- double tolerance,
- cairo_antialias_t antialias,
- const cairo_clip_t *clip)
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
@@ -229,7 +239,7 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || source_region_id != 0) {
cairo_matrix_t m;
_cairo_surface_wrapper_get_transform (wrapper, &m);
@@ -248,7 +258,7 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
- _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id);
source = &source_copy.base;
}
@@ -266,21 +276,23 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
}
cairo_status_t
-_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t fill_op,
- const cairo_pattern_t *fill_source,
- cairo_fill_rule_t fill_rule,
- double fill_tolerance,
- cairo_antialias_t fill_antialias,
- const cairo_path_fixed_t*path,
- cairo_operator_t stroke_op,
- const cairo_pattern_t *stroke_source,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *stroke_ctm,
- const cairo_matrix_t *stroke_ctm_inverse,
- double stroke_tolerance,
- cairo_antialias_t stroke_antialias,
- const cairo_clip_t *clip)
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ unsigned int fill_region_id,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ unsigned int stroke_region_id,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path;
@@ -297,7 +309,7 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || fill_region_id != 0 || stroke_region_id != 0) {
cairo_matrix_t m;
_cairo_surface_wrapper_get_transform (wrapper, &m);
@@ -316,10 +328,10 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
- _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m);
+ _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m, fill_region_id);
stroke_source = &stroke_source_copy.base;
- _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m);
+ _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m, stroke_region_id);
fill_source = &fill_source_copy.base;
}
@@ -341,14 +353,15 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
}
cairo_status_t
-_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- double tolerance,
- cairo_antialias_t antialias,
- const cairo_clip_t *clip)
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ unsigned int source_region_id,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
@@ -362,7 +375,7 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || source_region_id != 0) {
cairo_matrix_t m;
_cairo_surface_wrapper_get_transform (wrapper, &m);
@@ -377,7 +390,7 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
- _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id);
source = &source_copy.base;
}
@@ -394,9 +407,10 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
}
cairo_status_t
-_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
+ unsigned int source_region_id,
const char *utf8,
int utf8_len,
const cairo_glyph_t *glyphs,
@@ -425,7 +439,7 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
cairo_surface_get_font_options (wrapper->target, &options);
cairo_font_options_merge (&options, &scaled_font->options);
- if (wrapper->needs_transform) {
+ if (wrapper->needs_transform || source_region_id != 0) {
cairo_matrix_t m;
int i;
@@ -460,7 +474,7 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
- _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id);
source = &source_copy.base;
} else {
if (! cairo_font_options_equal (&options, &scaled_font->options)) {
@@ -622,6 +636,8 @@ _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
wrapper->has_extents = FALSE;
wrapper->extents.x = wrapper->extents.y = 0;
wrapper->clip = NULL;
+ wrapper->source_region_id = 0;
+ wrapper->mask_region_id = 0;
wrapper->needs_transform = FALSE;
if (target) {
diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c
index 3414fc268..d1402d2a3 100644
--- a/src/cairo-traps-compositor.c
+++ b/src/cairo-traps-compositor.c
@@ -1240,7 +1240,7 @@ composite_aligned_boxes (const cairo_traps_compositor_t *compositor,
recording_clip = _cairo_clip_from_boxes (boxes);
status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source),
- m, dst, recording_clip);
+ m, dst, recording_clip, FALSE);
_cairo_clip_destroy (recording_clip);
return status;
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index f0ec6d999..c58821a48 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -1112,7 +1112,8 @@ record_to_picture (cairo_surface_t *target,
status = _cairo_recording_surface_replay_with_clip (source,
&matrix, tmp,
- NULL);
+ NULL,
+ FALSE);
if (unlikely (status)) {
cairo_surface_destroy (tmp);
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c
index 4c3b99d9e..63e155e00 100644
--- a/src/cairo-xlib-source.c
+++ b/src/cairo-xlib-source.c
@@ -920,7 +920,8 @@ record_source (cairo_xlib_surface_t *dst,
recording = recording_pattern_get_surface (&pattern->base),
status = _cairo_recording_surface_replay_with_clip (recording,
&matrix, &src->base,
- NULL);
+ NULL,
+ FALSE);
cairo_surface_destroy (recording);
if (unlikely (status)) {
cairo_surface_destroy (&src->base);
diff --git a/src/cairoint.h b/src/cairoint.h
index 032d42145..65bc16f53 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1855,6 +1855,12 @@ _cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix);
cairo_private void
_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect);
+cairo_private const char *
+_cairo_debug_operator_to_string (cairo_operator_t op);
+
+cairo_private const char *
+_cairo_debug_status_to_string (cairo_int_status_t status);
+
cairo_private cairo_status_t
_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps,
const cairo_polygon_t *polygon,
diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c
index 352137a27..01a8e37aa 100644
--- a/src/win32/cairo-win32-printing-surface.c
+++ b/src/win32/cairo-win32-printing-surface.c
@@ -654,6 +654,7 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_printing_surf
SaveDC (surface->win32.dc); /* Allow clip path to be reset during replay */
status = _cairo_recording_surface_replay_region (&recording_surface->base,
+ pattern->region_array_id,
is_subsurface ? &recording_extents : NULL,
&surface->win32.base,
CAIRO_RECORDING_REGION_NATIVE);
diff --git a/test/create-regions.c b/test/create-regions.c
new file mode 100644
index 000000000..749595a44
--- /dev/null
+++ b/test/create-regions.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright © 2023 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cairo.h>
+
+#if CAIRO_HAS_PDF_SURFACE && CAIRO_HAS_PS_SURFACE && CAIRO_HAS_SVG_SURFACE
+
+#include <cairo-pdf.h>
+#include <cairo-ps.h>
+#include <cairo-svg.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include "cairo-test.h"
+#include "buffer-diff.h"
+
+/* This test is to ensure that _cairo_recording_surface_replay_and_create_regions()
+ * works with a recording surface source re-used for multiple paginated surfaces.
+ * The prior use of the source with a paginated surface should not affect the region
+ * analysis on subsequent surfaces.
+ *
+ * If test output should only contain fallback images for unsupported
+ * operations. If the recording surface is incorrectly re-using the
+ * analysis from a different target, some operations may be missing
+ * from the ouput (recording surface marked the operation as supported
+ * when it is not) or some operations may be have fallbacks for
+ * natively suported opetions (recording surface marked a supported
+ * operation as unsupprted).
+ *
+ * To create ref images, run the test for one target at a time to
+ * prevent re-use of the recording surface for different targets.
+ */
+
+
+#define SIZE 40
+#define PAD 25
+#define PAGE_SIZE (SIZE*3 + PAD*4)
+
+/* Apply a slight rotation and use a very low fallback resolution to
+ * ensure fallback images are apparent in the output. */
+#define ROTATE 5
+#define FALLBACK_PPI 18
+
+static void
+create_recordings (cairo_operator_t op,
+ cairo_surface_t **recording,
+ cairo_surface_t **recording_group)
+{
+ *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ cairo_t *cr = cairo_create (*recording);
+
+ cairo_rotate (cr, ROTATE*M_PI/180.0);
+
+ cairo_rectangle (cr, 0, 0, SIZE*3.0/4.0, SIZE*3.0/4.0);
+ cairo_set_source_rgb (cr, 1, 0, 0);
+ cairo_fill (cr);
+
+ cairo_set_operator (cr, op);
+
+ cairo_rectangle (cr, SIZE/4.0, SIZE/4.0, SIZE*3.0/4.0, SIZE*3.0/4.0);
+ cairo_set_source_rgb (cr, 0, 1, 0);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ *recording_group = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ cr = cairo_create (*recording_group);
+
+ cairo_set_source_surface (cr, *recording, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+}
+
+static void
+_xunlink (const cairo_test_context_t *ctx, const char *pathname)
+{
+ if (unlink (pathname) < 0 && errno != ENOENT) {
+ cairo_test_log (ctx, "Error: Cannot remove %s: %s\n",
+ pathname, strerror (errno));
+ exit (1);
+ }
+}
+
+static cairo_bool_t
+check_result (cairo_test_context_t *ctx,
+ const cairo_boilerplate_target_t *target,
+ const char *test_name,
+ const char *base_name,
+ cairo_surface_t *surface)
+{
+ const char *format;
+ char *ref_name;
+ char *png_name;
+ char *diff_name;
+ cairo_surface_t *test_image, *ref_image, *diff_image;
+ buffer_diff_result_t result;
+ cairo_status_t status;
+ cairo_bool_t ret;
+
+ if (target->finish_surface != NULL) {
+ status = target->finish_surface (surface);
+ if (status) {
+ cairo_test_log (ctx, "Error: Failed to finish surface: %s\n",
+ cairo_status_to_string (status));
+ cairo_surface_destroy (surface);
+ return FALSE;
+ }
+ }
+
+ xasprintf (&png_name, "%s.out.png", base_name);
+ xasprintf (&diff_name, "%s.diff.png", base_name);
+
+ test_image = target->get_image_surface (surface, 0, PAGE_SIZE, PAGE_SIZE);
+ if (cairo_surface_status (test_image)) {
+ cairo_test_log (ctx, "Error: Failed to extract page: %s\n",
+ cairo_status_to_string (cairo_surface_status (test_image)));
+ cairo_surface_destroy (test_image);
+ free (png_name);
+ free (diff_name);
+ return FALSE;
+ }
+
+ _xunlink (ctx, png_name);
+ status = cairo_surface_write_to_png (test_image, png_name);
+ if (status) {
+ cairo_test_log (ctx, "Error: Failed to write output image: %s\n",
+ cairo_status_to_string (status));
+ cairo_surface_destroy (test_image);
+ free (png_name);
+ free (diff_name);
+ return FALSE;
+ }
+
+ format = cairo_boilerplate_content_name (target->content);
+ ref_name = cairo_test_reference_filename (ctx,
+ base_name,
+ test_name,
+ target->name,
+ target->basename,
+ format,
+ CAIRO_TEST_REF_SUFFIX,
+ CAIRO_TEST_PNG_EXTENSION);
+ if (ref_name == NULL) {
+ cairo_test_log (ctx, "Error: Cannot find reference image for %s\n",
+ base_name);
+ cairo_surface_destroy (test_image);
+ free (png_name);
+ free (diff_name);
+ return FALSE;
+ }
+
+ ref_image = cairo_test_get_reference_image (ctx, ref_name,
+ target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
+ if (cairo_surface_status (ref_image)) {
+ cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
+ ref_name,
+ cairo_status_to_string (cairo_surface_status (ref_image)));
+ cairo_surface_destroy (ref_image);
+ cairo_surface_destroy (test_image);
+ free (png_name);
+ free (diff_name);
+ free (ref_name);
+ return FALSE;
+ }
+
+ diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ PAGE_SIZE, PAGE_SIZE);
+
+ ret = TRUE;
+ status = image_diff (ctx,
+ test_image, ref_image, diff_image,
+ &result);
+ _xunlink (ctx, diff_name);
+ if (status) {
+ cairo_test_log (ctx, "Error: Failed to compare images: %s\n",
+ cairo_status_to_string (status));
+ ret = FALSE;
+ } else if (image_diff_is_failure (&result, target->error_tolerance))
+ {
+ ret = FALSE;
+
+ status = cairo_surface_write_to_png (diff_image, diff_name);
+ if (status) {
+ cairo_test_log (ctx, "Error: Failed to write differences image: %s\n",
+ cairo_status_to_string (status));
+ }
+ }
+
+ cairo_surface_destroy (test_image);
+ cairo_surface_destroy (diff_image);
+ free (png_name);
+ free (diff_name);
+ free (ref_name);
+
+ return ret;
+}
+
+#define TEST_ROWS 2
+#define TEST_COLS 3
+
+static void
+draw (cairo_t *cr, cairo_surface_t *recordings[TEST_ROWS][TEST_COLS])
+{
+ cairo_translate (cr, PAD, PAD);
+ for (int row = 0; row < TEST_ROWS; row++) {
+ cairo_save (cr);
+ for (int col = 0; col < TEST_COLS; col++) {
+ cairo_save (cr);
+ cairo_set_source_surface (cr, recordings[row][col], 0, 0);
+ cairo_paint (cr);
+ cairo_restore (cr);
+ cairo_translate (cr, SIZE + PAD, 0);
+ }
+ cairo_restore (cr);
+ cairo_translate (cr, 0, SIZE + PAD);
+ }
+}
+
+#define NUM_TEST_SURFACES 3
+
+typedef struct _test_surface {
+ const cairo_boilerplate_target_t *target;
+ cairo_surface_t *surface;
+ char *base_name;
+ void *closure;
+} test_surface_t;
+
+static test_surface_t test_surfaces[NUM_TEST_SURFACES];
+
+static void
+init_test_surfaces()
+{
+ memset (test_surfaces, 0, sizeof (*test_surfaces));
+ test_surfaces[0].surface = cairo_pdf_surface_create_for_stream (NULL, NULL, PAGE_SIZE, PAGE_SIZE);
+ test_surfaces[1].surface = cairo_ps_surface_create_for_stream (NULL, NULL, PAGE_SIZE, PAGE_SIZE);
+ test_surfaces[2].surface = cairo_svg_surface_create_for_stream (NULL, NULL, PAGE_SIZE, PAGE_SIZE);
+}
+
+static void
+add_test_surface (const cairo_boilerplate_target_t *target,
+ cairo_surface_t *surface,
+ char *base_name,
+ void *closure)
+{
+ for (int i = 0; i < NUM_TEST_SURFACES; i++) {
+ if (cairo_surface_get_type (test_surfaces[i].surface) == cairo_surface_get_type (surface)) {
+ cairo_surface_destroy (test_surfaces[i].surface);
+ test_surfaces[i].target = target;
+ test_surfaces[i].surface = surface;
+ test_surfaces[i].base_name = base_name;
+ test_surfaces[i].closure = closure;
+ break;
+ }
+ }
+}
+
+static void
+destroy_test_surfaces()
+{
+ for (int i = 0; i < NUM_TEST_SURFACES; i++) {
+ cairo_surface_destroy (test_surfaces[i].surface);
+ if (test_surfaces[i].target && test_surfaces[i].target->cleanup)
+ test_surfaces[i].target->cleanup (test_surfaces[i].closure);
+ if (test_surfaces[i].base_name)
+ free (test_surfaces[i].base_name);
+ }
+}
+
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+ cairo_t *cr;
+ cairo_test_status_t ret = CAIRO_TEST_UNTESTED;
+ unsigned int i;
+ const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
+ cairo_surface_t *recordings[TEST_ROWS][TEST_COLS];
+ const char *test_name = "create-regions";
+ char *base_name;
+
+ /* Each row displays three recordings. One with operations
+ * supported by all paginated surfaces (OVER), one with operations
+ * supported by PDF but not PS or SVG surfaces (DIFFERENCE), and
+ * one with operations supported by SVG but not PDF or PS
+ * (DEST_XOR).
+ *
+ * The recordings for the first row is a single recording. The
+ * recordings for the second row contains the first row recording
+ * inside another recording to test the use of cloned recording
+ * surfaces.
+ *
+ * We are looking to see that fallback images are only used for
+ * unsupported operations.
+ */
+ create_recordings (CAIRO_OPERATOR_OVER, &recordings[0][0], &recordings[1][0]);
+ create_recordings (CAIRO_OPERATOR_DIFFERENCE, &recordings[0][1], &recordings[1][1]);
+ create_recordings (CAIRO_OPERATOR_XOR, &recordings[0][2], &recordings[1][2]);
+
+ init_test_surfaces();
+ for (i = 0; i < ctx->num_targets; i++) {
+ const cairo_boilerplate_target_t *target = ctx->targets_to_test[i];
+ cairo_surface_t *surface = NULL;
+ void *closure;
+ const char *format;
+
+ /* This test only works on surfaces that support fine grained fallbacks */
+ if (! (target->expected_type == CAIRO_SURFACE_TYPE_PDF ||
+ target->expected_type == CAIRO_SURFACE_TYPE_PS ||
+ target->expected_type ==CAIRO_SURFACE_TYPE_SVG))
+ continue;
+
+ if (! cairo_test_is_target_enabled (ctx, target->name))
+ continue;
+
+ if (ret == CAIRO_TEST_UNTESTED)
+ ret = CAIRO_TEST_SUCCESS;
+
+ format = cairo_boilerplate_content_name (target->content);
+ base_name = NULL;
+ xasprintf (&base_name, "%s/%s.%s.%s",
+ path, test_name,
+ target->name,
+ format);
+
+ surface = (target->create_surface) (base_name,
+ target->content,
+ PAGE_SIZE, PAGE_SIZE,
+ PAGE_SIZE, PAGE_SIZE,
+ CAIRO_BOILERPLATE_MODE_TEST,
+ &closure);
+ if (surface == NULL || cairo_surface_status (surface)) {
+ cairo_test_log (ctx, "Failed to generate surface: %s.%s\n",
+ target->name,
+ format);
+ ret = CAIRO_TEST_FAILURE;
+ break;
+ }
+
+ cairo_surface_set_fallback_resolution (surface, FALLBACK_PPI, FALLBACK_PPI);
+ add_test_surface (target, surface, base_name, closure);
+ }
+
+ for (int i = 0; i < NUM_TEST_SURFACES; i++) {
+ cairo_status_t status;
+
+ if (test_surfaces[i].target != NULL) {
+ cairo_test_log (ctx,
+ "Testing create-regions with %s target\n",
+ test_surfaces[i].target->name);
+ printf ("%s:\t", test_surfaces[i].base_name);
+ fflush (stdout);
+ }
+
+ cr = cairo_create (test_surfaces[i].surface);
+
+ draw (cr, recordings);
+
+ status = cairo_status (cr);
+ cairo_destroy (cr);
+ cairo_surface_finish (test_surfaces[i].surface);
+
+ if (test_surfaces[i].target) {
+ cairo_bool_t pass = FALSE;
+ if (status) {
+ cairo_test_log (ctx, "Error: Failed to create target surface: %s\n",
+ cairo_status_to_string (status));
+ ret = CAIRO_TEST_FAILURE;
+ } else {
+ /* extract the image and compare it to our reference */
+ if (! check_result (ctx, test_surfaces[i].target, test_name, test_surfaces[i].base_name, test_surfaces[i].surface))
+ ret = CAIRO_TEST_FAILURE;
+ else
+ pass = TRUE;
+ }
+
+ if (pass) {
+ printf ("PASS\n");
+ } else {
+ printf ("FAIL\n");
+ }
+ fflush (stdout);
+ }
+ }
+
+ destroy_test_surfaces();
+
+ for (int row = 0; row < TEST_ROWS; row++) {
+ for (int col = 0; col < TEST_COLS; col++) {
+ cairo_surface_destroy (recordings[row][col]);
+ }
+ }
+
+ return ret;
+}
+
+CAIRO_TEST (create_regions,
+ "Check region analysis when re-used with different surfaces",
+ "fallback", /* keywords */
+ NULL, /* requirements */
+ 0, 0,
+ preamble, NULL)
+
+#endif /* CAIRO_HAS_PDF_SURFACE && CAIRO_HAS_PS_SURFACE && CAIRO_HAS_SVG_SURFACE */
diff --git a/test/meson.build b/test/meson.build
index e670a3937..de6b30c4f 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -493,6 +493,7 @@ test_multi_page_sources = [
]
test_fallback_resolution_sources = [
+ 'create-regions.c',
'fallback-resolution.c',
]
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 378e6a920..1a2f62dac 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -42,6 +42,9 @@
#endif
#include <cairo.h>
+
+#if CAIRO_HAS_PDF_SURFACE
+
#include <cairo-pdf.h>
/* This test checks PDF with
@@ -604,3 +607,5 @@ CAIRO_TEST (pdf_tagged_text,
NULL, /* requirements */
0, 0,
preamble, NULL)
+
+#endif /* CAIRO_HAS_PDF_SURFACE */
diff --git a/test/reference/create-regions.pdf.ref.png b/test/reference/create-regions.pdf.ref.png
new file mode 100644
index 000000000..5762fbbc7
--- /dev/null
+++ b/test/reference/create-regions.pdf.ref.png
Binary files differ
diff --git a/test/reference/create-regions.ps.ref.png b/test/reference/create-regions.ps.ref.png
new file mode 100644
index 000000000..0478e249d
--- /dev/null
+++ b/test/reference/create-regions.ps.ref.png
Binary files differ
diff --git a/test/reference/create-regions.svg.ref.png b/test/reference/create-regions.svg.ref.png
new file mode 100644
index 000000000..a89dfb044
--- /dev/null
+++ b/test/reference/create-regions.svg.ref.png
Binary files differ