diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2023-01-16 08:59:32 +0000 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2023-01-16 08:59:32 +0000 |
commit | 745c3717aa8e91237bf90de1b2e908c72499aa0f (patch) | |
tree | fc50f38d44fdb67c751bcc4ac43668a38925c515 | |
parent | 6abc8076c9cf06c1e2b92fccf57210442d471f5c (diff) | |
parent | 7146358250975ec0f29b8ba80e80a26c52526bdc (diff) | |
download | cairo-745c3717aa8e91237bf90de1b2e908c72499aa0f.tar.gz |
Merge branch 'fix-shared-recording-surface' into 'master'
Fix shared use of recording surfaces
See merge request cairo/cairo!391
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, ®ions_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 (®ion_array->regions, 0); + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + cairo_recording_region_element_t *region_element = ®ion_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 (®ion_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 (®ion_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 (®ion_array->ref_count, 1); + + _cairo_array_init (®ion_array->regions, sizeof (cairo_recording_region_element_t)); + + CAIRO_MUTEX_LOCK (surface->mutex); + cairo_list_add (®ion_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 (®ion_array->ref_count)) + cairo_list_del (®ion_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 (®ion_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 (®ions_array->regions) == 0); + void *array_elems; + status = _cairo_array_allocate (®ions_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 (®ions_array->regions) == num_elements); + } + } + elements = _cairo_array_index (&surface->commands, 0); + if (regions_array) + region_elements = _cairo_array_index (®ions_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 = ®ion_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 = ®ion_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, ¶ms); @@ -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, ¶ms); @@ -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, ¶ms); @@ -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, ¶ms); @@ -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 (®ions_array->regions) == num_elements); + region_elements = _cairo_array_index (®ions_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 = ®ion_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 Binary files differnew file mode 100644 index 000000000..5762fbbc7 --- /dev/null +++ b/test/reference/create-regions.pdf.ref.png diff --git a/test/reference/create-regions.ps.ref.png b/test/reference/create-regions.ps.ref.png Binary files differnew file mode 100644 index 000000000..0478e249d --- /dev/null +++ b/test/reference/create-regions.ps.ref.png diff --git a/test/reference/create-regions.svg.ref.png b/test/reference/create-regions.svg.ref.png Binary files differnew file mode 100644 index 000000000..a89dfb044 --- /dev/null +++ b/test/reference/create-regions.svg.ref.png |