From bf92255edd20595a6eb220c6ee9d6aa40b244eef Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Tue, 21 Aug 2007 22:27:57 +0930 Subject: [PATCH] PS: Add finer-grained image fallback support The analysis surface now keeps track of two regions: supported operations, and unsupported operations. If the target surface returns CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, the analysis surface will check if any previous operation intersects with this operation. If there is nothing previously drawn under the operation, the status is changed to supported. The meta surface has two new functions: _cairo_meta_surface_replay_region() _cairo_meta_surface_replay_and_create_regions() During the analysis stage, the paginated surface replays the meta surface using _cairo_meta_surface_replay_and_create_regions(). The return status from each analyzed operation is saved in the meta surface. The _cairo_meta_surface_replay_region() function allows only operations from either the supported or unsupported region to be replayed. This allows the paginated surface to replay only the supported operations before emitting a fallback image for each rectangle in the unsupported region. --- src/cairo-analysis-surface-private.h | 3 + src/cairo-analysis-surface.c | 358 +++++++++++++++++++++++---- src/cairo-meta-surface-private.h | 34 ++- src/cairo-meta-surface.c | 78 +++++- src/cairo-paginated-surface.c | 139 ++++++++--- src/cairo-ps-surface.c | 97 +++++--- src/cairo-region-private.h | 4 + src/cairo-region.c | 13 + src/cairoint.h | 4 +- 9 files changed, 591 insertions(+), 139 deletions(-) diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h index 056972acf..c17b0ef66 100644 --- a/src/cairo-analysis-surface-private.h +++ b/src/cairo-analysis-surface-private.h @@ -48,6 +48,9 @@ _cairo_analysis_surface_get_supported (cairo_surface_t *surface); cairo_private cairo_region_t * _cairo_analysis_surface_get_unsupported (cairo_surface_t *unsupported); +cairo_private cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *unsupported); + cairo_private cairo_bool_t _cairo_analysis_surface_has_unsupported (cairo_surface_t *unsupported); diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index 3dbc73cbc..b2295ea34 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -1,5 +1,6 @@ /* * Copyright © 2006 Keith Packard + * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -30,12 +31,14 @@ * * Contributor(s): * Keith Packard + * Adrian Johnson */ #include "cairoint.h" #include "cairo-analysis-surface-private.h" #include "cairo-paginated-private.h" +#include "cairo-region-private.h" typedef struct { cairo_surface_t base; @@ -44,9 +47,121 @@ typedef struct { cairo_surface_t *target; - cairo_bool_t fallback; + cairo_bool_t has_supported; + cairo_bool_t has_unsupported; + + cairo_region_t supported_region; + cairo_region_t fallback_region; + cairo_rectangle_int_t current_clip; + } cairo_analysis_surface_t; +static cairo_int_status_t +_cairo_analysis_surface_add_operation (cairo_analysis_surface_t *surface, + cairo_rectangle_int_t *rect, + cairo_int_status_t backend_status) +{ + cairo_int_status_t status; + + if (rect->width == 0 || rect->height == 0) + return CAIRO_STATUS_SUCCESS; + + /* If the operation is completely enclosed within the fallback + * region there is no benefit in emitting a native operation as + * the fallback image will be painted on top. + */ + if (_cairo_region_contains_rectangle (&surface->fallback_region, rect) == PIXMAN_REGION_IN) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { + /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates + * that the backend only supports this operation if the + * transparency removed. If the extents of this operation does + * not intersect any other native operation, the operation is + * natively supported and the backend will blend the + * transparency into the white background. + */ + if (_cairo_region_contains_rectangle (&surface->supported_region, rect) == PIXMAN_REGION_OUT) + backend_status = CAIRO_STATUS_SUCCESS; + } + + if (backend_status == CAIRO_STATUS_SUCCESS) { + /* Add the operation to the supported region. Operations in + * this region will be emitted as native operations. + */ + surface->has_supported = TRUE; + status = _cairo_region_union_rect (&surface->supported_region, + &surface->supported_region, + rect); + return status; + } + + /* Add the operation to the unsupported region. This region will + * be painted as an image after all native operations have been + * emitted. + */ + surface->has_unsupported = TRUE; + status = _cairo_region_union_rect (&surface->fallback_region, + &surface->fallback_region, + rect); + + /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate + * unsupported operations to the meta surface as using + * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to + * invoke the cairo-surface-fallback path then return + * CAIRO_STATUS_SUCCESS. + */ + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + else + return status; +} + +static cairo_status_t +_cairo_analysis_surface_finish (void *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + _cairo_region_fini (&surface->supported_region); + _cairo_region_fini (&surface->fallback_region); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_analysis_surface_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_analysis_surface_t *surface = abstract_surface; + double x1, y1, x2, y2; + cairo_rectangle_int_t extent; + cairo_status_t status; + + if (path == NULL) { + surface->current_clip.x = 0; + surface->current_clip.y = 0; + surface->current_clip.width = surface->width; + surface->current_clip.height = surface->height; + status = CAIRO_STATUS_SUCCESS; + } else { + status = _cairo_path_fixed_bounds (path, &x1, &y1, &x2, &y2); + if (status) + return status; + + extent.x = floor (x1); + extent.y = floor (y1); + extent.width = ceil (x2) - extent.x; + extent.height = ceil (y2) - extent.y; + + _cairo_rectangle_intersect (&surface->current_clip, &extent); + } + + return status; +} + static cairo_int_status_t _cairo_analysis_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) @@ -62,17 +177,32 @@ _cairo_analysis_surface_paint (void *abstract_surface, cairo_pattern_t *source) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_status_t status, backend_status; + cairo_rectangle_int_t extents; if (!surface->target->backend->paint) - status = CAIRO_INT_STATUS_UNSUPPORTED; + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else - status = (*surface->target->backend->paint) (surface->target, op, - source); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - surface->fallback = TRUE; - status = CAIRO_STATUS_SUCCESS; + backend_status = (*surface->target->backend->paint) (surface->target, op, + source); + + status = _cairo_surface_get_extents (&surface->base, &extents); + if (status) + return status; + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); + if (status) + return status; + + _cairo_rectangle_intersect (&extents, &source_extents); } + + _cairo_rectangle_intersect (&extents, &surface->current_clip); + + status = _cairo_analysis_surface_add_operation (surface, &extents, backend_status); + return status; } @@ -83,17 +213,38 @@ _cairo_analysis_surface_mask (void *abstract_surface, cairo_pattern_t *mask) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_status_t status, backend_status; + cairo_rectangle_int_t extents; if (!surface->target->backend->mask) - status = CAIRO_INT_STATUS_UNSUPPORTED; + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else - status = (*surface->target->backend->mask) (surface->target, op, - source, mask); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - surface->fallback = TRUE; - status = CAIRO_STATUS_SUCCESS; + backend_status = (*surface->target->backend->mask) (surface->target, op, + source, mask); + + status = _cairo_surface_get_extents (&surface->base, &extents); + if (status) + return status; + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); + if (status) + return status; + + _cairo_rectangle_intersect (&extents, &source_extents); + + status = _cairo_pattern_get_extents (mask, &source_extents); + if (status) + return status; + + _cairo_rectangle_intersect (&extents, &source_extents); } + + _cairo_rectangle_intersect (&extents, &surface->current_clip); + + status = _cairo_analysis_surface_add_operation (surface, &extents, backend_status); + return status; } @@ -109,19 +260,61 @@ _cairo_analysis_surface_stroke (void *abstract_surface, cairo_antialias_t antialias) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_status_t status, backend_status; + cairo_traps_t traps; + cairo_box_t box; + cairo_rectangle_int_t extents; if (!surface->target->backend->stroke) - status = CAIRO_INT_STATUS_UNSUPPORTED; + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else - status = (*surface->target->backend->stroke) (surface->target, op, - source, path, style, - ctm, ctm_inverse, - tolerance, antialias); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - surface->fallback = TRUE; - status = CAIRO_STATUS_SUCCESS; + backend_status = (*surface->target->backend->stroke) (surface->target, op, + source, path, style, + ctm, ctm_inverse, + tolerance, antialias); + + status = _cairo_surface_get_extents (&surface->base, &extents); + if (status) + return status; + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); + if (status) + return status; + + _cairo_rectangle_intersect (&extents, &source_extents); } + + _cairo_rectangle_intersect (&extents, &surface->current_clip); + + box.p1.x = _cairo_fixed_from_int (extents.x); + box.p1.y = _cairo_fixed_from_int (extents.y); + box.p2.x = _cairo_fixed_from_int (extents.x + extents.width); + box.p2.y = _cairo_fixed_from_int (extents.y + extents.height); + + _cairo_traps_init (&traps); + + _cairo_traps_limit (&traps, &box); + + status = _cairo_path_fixed_stroke_to_traps (path, + style, + ctm, ctm_inverse, + tolerance, + &traps); + if (status) + goto FINISH; + + _cairo_traps_extents (&traps, &box); + extents.x = _cairo_fixed_integer_floor (box.p1.x); + extents.y = _cairo_fixed_integer_floor (box.p1.y); + extents.width = _cairo_fixed_integer_ceil (box.p2.x) - extents.x; + extents.height = _cairo_fixed_integer_ceil (box.p2.y) - extents.y; + status = _cairo_analysis_surface_add_operation (surface, &extents, backend_status); + +FINISH: + _cairo_traps_fini (&traps); + return status; } @@ -135,18 +328,59 @@ _cairo_analysis_surface_fill (void *abstract_surface, cairo_antialias_t antialias) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_status_t status, backend_status; + cairo_traps_t traps; + cairo_box_t box; + cairo_rectangle_int_t extents; if (!surface->target->backend->fill) - status = CAIRO_INT_STATUS_UNSUPPORTED; + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else - status = (*surface->target->backend->fill) (surface->target, op, + backend_status = (*surface->target->backend->fill) (surface->target, op, source, path, fill_rule, tolerance, antialias); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - surface->fallback = TRUE; - status = CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_get_extents (&surface->base, &extents); + if (status) + return status; + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); + if (status) + return status; + + _cairo_rectangle_intersect (&extents, &source_extents); } + + _cairo_rectangle_intersect (&extents, &surface->current_clip); + + box.p1.x = _cairo_fixed_from_int (extents.x); + box.p1.y = _cairo_fixed_from_int (extents.y); + box.p2.x = _cairo_fixed_from_int (extents.x + extents.width); + box.p2.y = _cairo_fixed_from_int (extents.y + extents.height); + + _cairo_traps_init (&traps); + + _cairo_traps_limit (&traps, &box); + + status = _cairo_path_fixed_fill_to_traps (path, + fill_rule, + tolerance, + &traps); + if (status) + goto FINISH; + + _cairo_traps_extents (&traps, &box); + extents.x = _cairo_fixed_integer_floor (box.p1.x); + extents.y = _cairo_fixed_integer_floor (box.p1.y); + extents.width = _cairo_fixed_integer_ceil (box.p2.x) - extents.x; + extents.height = _cairo_fixed_integer_ceil (box.p2.y) - extents.y; + status = _cairo_analysis_surface_add_operation (surface, &extents, backend_status); + +FINISH: + _cairo_traps_fini (&traps); + return status; } @@ -159,26 +393,34 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_scaled_font_t *scaled_font) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_status_t status, backend_status; + cairo_rectangle_int_t extent; if (!surface->target->backend->show_glyphs) - status = CAIRO_INT_STATUS_UNSUPPORTED; + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else - status = (*surface->target->backend->show_glyphs) (surface->target, op, + backend_status = (*surface->target->backend->show_glyphs) (surface->target, op, source, glyphs, num_glyphs, scaled_font); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - surface->fallback = TRUE; - status = CAIRO_STATUS_SUCCESS; - } + + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &extent); + if (status) + return status; + + _cairo_rectangle_intersect (&extent, &surface->current_clip); + status = _cairo_analysis_surface_add_operation (surface, &extent, backend_status); + return status; } static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, NULL, /* create_similar */ - NULL, /* finish_surface */ + _cairo_analysis_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* acquire_dest_image */ @@ -190,7 +432,7 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ - NULL, /* clip_path */ + _cairo_analysis_surface_intersect_clip_path, _cairo_analysis_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -206,7 +448,7 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* snapshot */ }; -cairo_private cairo_surface_t * +cairo_surface_t * _cairo_analysis_surface_create (cairo_surface_t *target, int width, int height) @@ -226,7 +468,15 @@ _cairo_analysis_surface_create (cairo_surface_t *target, surface->height = height; surface->target = target; - surface->fallback = FALSE; + surface->has_supported = FALSE; + surface->has_unsupported = FALSE; + _cairo_region_init (&surface->supported_region); + _cairo_region_init (&surface->fallback_region); + + surface->current_clip.x = 0; + surface->current_clip.y = 0; + surface->current_clip.width = width; + surface->current_clip.height = height; return &surface->base; FAIL: @@ -234,24 +484,34 @@ FAIL: return NULL; } -cairo_private cairo_region_t * +cairo_region_t * _cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) { - /* XXX */ - return NULL; + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->supported_region; } -cairo_private cairo_region_t * +cairo_region_t * _cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) { - /* XXX */ - return NULL; + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->fallback_region; } -cairo_private cairo_bool_t +cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->has_supported; +} + +cairo_bool_t _cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - return surface->fallback; + return surface->has_unsupported; } diff --git a/src/cairo-meta-surface-private.h b/src/cairo-meta-surface-private.h index 3f2d390ce..b6d573026 100644 --- a/src/cairo-meta-surface-private.h +++ b/src/cairo-meta-surface-private.h @@ -31,6 +31,7 @@ * * Contributor(s): * Kristian Høgsberg + * Adrian Johnson */ #ifndef CAIRO_META_SURFACE_H @@ -57,21 +58,32 @@ typedef enum { } cairo_command_type_t; -typedef struct _cairo_command_paint { +typedef enum { + CAIRO_META_REGION_ALL, + CAIRO_META_REGION_NATIVE, + CAIRO_META_REGION_IMAGE_FALLBACK, +} cairo_meta_region_type_t; + +typedef struct _cairo_command_header { cairo_command_type_t type; + cairo_meta_region_type_t region; +} cairo_command_header_t; + +typedef struct _cairo_command_paint { + cairo_command_header_t header; cairo_operator_t op; cairo_pattern_union_t source; } cairo_command_paint_t; typedef struct _cairo_command_mask { - cairo_command_type_t type; + cairo_command_header_t header; cairo_operator_t op; cairo_pattern_union_t source; cairo_pattern_union_t mask; } cairo_command_mask_t; typedef struct _cairo_command_stroke { - cairo_command_type_t type; + cairo_command_header_t header; cairo_operator_t op; cairo_pattern_union_t source; cairo_path_fixed_t path; @@ -83,7 +95,7 @@ typedef struct _cairo_command_stroke { } cairo_command_stroke_t; typedef struct _cairo_command_fill { - cairo_command_type_t type; + cairo_command_header_t header; cairo_operator_t op; cairo_pattern_union_t source; cairo_path_fixed_t path; @@ -93,7 +105,7 @@ typedef struct _cairo_command_fill { } cairo_command_fill_t; typedef struct _cairo_command_show_glyphs { - cairo_command_type_t type; + cairo_command_header_t header; cairo_operator_t op; cairo_pattern_union_t source; cairo_glyph_t *glyphs; @@ -102,7 +114,7 @@ typedef struct _cairo_command_show_glyphs { } cairo_command_show_glyphs_t; typedef struct _cairo_command_intersect_clip_path { - cairo_command_type_t type; + cairo_command_header_t header; cairo_path_fixed_t *path_pointer; cairo_path_fixed_t path; cairo_fill_rule_t fill_rule; @@ -111,7 +123,7 @@ typedef struct _cairo_command_intersect_clip_path { } cairo_command_intersect_clip_path_t; typedef union _cairo_command { - cairo_command_type_t type; + cairo_command_header_t header; /* The 5 basic drawing operations. */ cairo_command_paint_t paint; @@ -151,6 +163,14 @@ cairo_private cairo_status_t _cairo_meta_surface_replay (cairo_surface_t *surface, cairo_surface_t *target); +cairo_private cairo_status_t +_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target); +cairo_private cairo_status_t +_cairo_meta_surface_replay_region (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_meta_region_type_t region); + cairo_private cairo_bool_t _cairo_surface_is_meta (const cairo_surface_t *surface); diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index 9997baf9e..35039ad41 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,6 +33,7 @@ * Contributor(s): * Kristian Høgsberg * Carl Worth + * Adrian Johnson */ /* A meta surface is a surface that records all drawing operations at @@ -123,7 +125,7 @@ _cairo_meta_surface_finish (void *abstract_surface) elements = _cairo_array_index (&meta->commands, 0); for (i = 0; i < num_elements; i++) { command = elements[i]; - switch (command->type) { + switch (command->header.type) { /* 5 basic drawing operations */ @@ -253,7 +255,8 @@ _cairo_meta_surface_paint (void *abstract_surface, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_PAINT; + command->header.type = CAIRO_COMMAND_PAINT; + command->header.region = CAIRO_META_REGION_ALL; command->op = op; status = _init_pattern_with_snapshot (&command->source.base, source); @@ -287,7 +290,8 @@ _cairo_meta_surface_mask (void *abstract_surface, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_MASK; + command->header.type = CAIRO_COMMAND_MASK; + command->header.region = CAIRO_META_REGION_ALL; command->op = op; status = _init_pattern_with_snapshot (&command->source.base, source); @@ -332,7 +336,8 @@ _cairo_meta_surface_stroke (void *abstract_surface, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_STROKE; + command->header.type = CAIRO_COMMAND_STROKE; + command->header.region = CAIRO_META_REGION_ALL; command->op = op; status = _init_pattern_with_snapshot (&command->source.base, source); @@ -386,7 +391,8 @@ _cairo_meta_surface_fill (void *abstract_surface, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_FILL; + command->header.type = CAIRO_COMMAND_FILL; + command->header.region = CAIRO_META_REGION_ALL; command->op = op; status = _init_pattern_with_snapshot (&command->source.base, source); @@ -432,7 +438,8 @@ _cairo_meta_surface_show_glyphs (void *abstract_surface, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_SHOW_GLYPHS; + command->header.type = CAIRO_COMMAND_SHOW_GLYPHS; + command->header.region = CAIRO_META_REGION_ALL; command->op = op; status = _init_pattern_with_snapshot (&command->source.base, source); @@ -521,7 +528,8 @@ _cairo_meta_surface_intersect_clip_path (void *dst, if (command == NULL) return CAIRO_STATUS_NO_MEMORY; - command->type = CAIRO_COMMAND_INTERSECT_CLIP_PATH; + command->header.type = CAIRO_COMMAND_INTERSECT_CLIP_PATH; + command->header.region = CAIRO_META_REGION_ALL; if (path) { status = _cairo_path_fixed_init_copy (&command->path, path); @@ -622,7 +630,7 @@ static const cairo_surface_backend_t cairo_meta_surface_backend = { static cairo_path_fixed_t * _cairo_command_get_path (cairo_command_t *command) { - switch (command->type) { + switch (command->header.type) { case CAIRO_COMMAND_PAINT: case CAIRO_COMMAND_MASK: case CAIRO_COMMAND_SHOW_GLYPHS: @@ -639,9 +647,11 @@ _cairo_command_get_path (cairo_command_t *command) return NULL; } -cairo_status_t -_cairo_meta_surface_replay (cairo_surface_t *surface, - cairo_surface_t *target) +static cairo_status_t +_cairo_meta_surface_replay_internal (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_bool_t create_regions, + cairo_meta_region_type_t region) { cairo_meta_surface_t *meta; cairo_command_t *command, **elements; @@ -665,9 +675,14 @@ _cairo_meta_surface_replay (cairo_surface_t *surface, for (i = meta->replay_start_idx; i < num_elements; i++) { command = elements[i]; + if (!create_regions && region != CAIRO_META_REGION_ALL) { + if (command->header.region != region) + continue; + } + /* For all commands except intersect_clip_path, we have to * ensure the current clip gets set on the surface. */ - if (command->type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) { + if (command->header.type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) { status = _cairo_surface_set_clip (target, &clip); if (status) break; @@ -682,7 +697,7 @@ _cairo_meta_surface_replay (cairo_surface_t *surface, dev_path = &path_copy; } - switch (command->type) { + switch (command->header.type) { case CAIRO_COMMAND_PAINT: status = _cairo_surface_paint (target, command->paint.op, @@ -770,6 +785,7 @@ _cairo_meta_surface_replay (cairo_surface_t *surface, command->intersect_clip_path.tolerance, command->intersect_clip_path.antialias, target); + assert (status == 0); break; default: ASSERT_NOT_REACHED; @@ -778,6 +794,14 @@ _cairo_meta_surface_replay (cairo_surface_t *surface, if (dev_path == &path_copy) _cairo_path_fixed_fini (&path_copy); + if (create_regions) { + if (status == CAIRO_STATUS_SUCCESS) { + command->header.region = CAIRO_META_REGION_NATIVE; + } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { + command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK; + status = CAIRO_STATUS_SUCCESS; + } + } if (status) break; } @@ -786,3 +810,31 @@ _cairo_meta_surface_replay (cairo_surface_t *surface, return status; } + +cairo_status_t +_cairo_meta_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_meta_surface_replay_internal (surface, target, FALSE, CAIRO_META_REGION_ALL); +} + +/* Replay meta to surface. When the return status of each operation is + * one of CAIRO_STATUS_SUCCESS, CAIRO_INT_STATUS_UNSUPPORTED, or + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation + * will be stored in the meta surface. Any other status will abort the + * replay and return the status. + */ +cairo_status_t +_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_meta_surface_replay_internal (surface, target, TRUE, CAIRO_META_REGION_ALL); +} + +cairo_status_t +_cairo_meta_surface_replay_region (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_meta_region_type_t region) +{ + return _cairo_meta_surface_replay_internal (surface, target, FALSE, region); +} diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index d68703712..1886beff5 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,6 +33,7 @@ * Contributor(s): * Carl Worth * Keith Packard + * Adrian Johnson */ /* The paginated surface layer exists to provide as much code sharing @@ -209,13 +211,55 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } +static cairo_int_status_t +_paint_fallback_image (cairo_paginated_surface_t *surface, + cairo_box_int_t *box) +{ + double x_scale = surface->base.x_fallback_resolution / 72.0; + double y_scale = surface->base.y_fallback_resolution / 72.0; + cairo_matrix_t matrix; + int x, y, width, height; + cairo_status_t status; + cairo_surface_t *image; + cairo_pattern_t *pattern; + + x = box->p1.x; + y = box->p1.y; + width = box->p2.x - x; + height = box->p2.y - y; + image = _cairo_paginated_surface_create_image_surface (surface, + width * x_scale, + height * y_scale); + _cairo_surface_set_device_scale (image, x_scale, y_scale); + /* set_device_offset just sets the x0/y0 components of the matrix; + * so we have to do the scaling manually. */ + cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); + + status = _cairo_meta_surface_replay (surface->meta, image); + if (status) + goto CLEANUP_IMAGE; + + pattern = cairo_pattern_create_for_surface (image); + cairo_matrix_init (&matrix, x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); + cairo_pattern_set_matrix (pattern, &matrix); + + status = _cairo_surface_paint (surface->target, + CAIRO_OPERATOR_SOURCE, + pattern); + + cairo_pattern_destroy (pattern); +CLEANUP_IMAGE: + cairo_surface_destroy (image); + + return status; +} + static cairo_int_status_t _paint_page (cairo_paginated_surface_t *surface) { cairo_surface_t *analysis; - cairo_surface_t *image; - cairo_pattern_t *pattern; cairo_status_t status; + cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; analysis = _cairo_analysis_surface_create (surface->target, surface->width, surface->height); @@ -223,7 +267,7 @@ _paint_page (cairo_paginated_surface_t *surface) return CAIRO_STATUS_NO_MEMORY; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); - status = _cairo_meta_surface_replay (surface->meta, analysis); + status = _cairo_meta_surface_replay_and_create_regions (surface->meta, analysis); surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER); if (status || analysis->status) { @@ -233,35 +277,70 @@ _paint_page (cairo_paginated_surface_t *surface) return status; } - if (_cairo_analysis_surface_has_unsupported (analysis)) - { - double x_scale = surface->base.x_fallback_resolution / 72.0; - double y_scale = surface->base.y_fallback_resolution / 72.0; - cairo_matrix_t matrix; - - image = _cairo_paginated_surface_create_image_surface (surface, - surface->width * x_scale, - surface->height * y_scale); - _cairo_surface_set_device_scale (image, x_scale, y_scale); - - status = _cairo_meta_surface_replay (surface->meta, image); - if (status) - goto CLEANUP_IMAGE; - - pattern = cairo_pattern_create_for_surface (image); - cairo_matrix_init_scale (&matrix, x_scale, y_scale); - cairo_pattern_set_matrix (pattern, &matrix); - - status = _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, pattern); - - cairo_pattern_destroy (pattern); - - CLEANUP_IMAGE: - cairo_surface_destroy (image); + /* Finer grained fallbacks are currently only supported for PostScript surfaces */ + if (surface->target->type == CAIRO_SURFACE_TYPE_PS) { + has_supported = _cairo_analysis_surface_has_supported (analysis); + has_page_fallback = FALSE; + has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis); + } else { + if (_cairo_analysis_surface_has_unsupported (analysis)) { + has_supported = FALSE; + has_page_fallback = TRUE; + } else { + has_supported = TRUE; + has_page_fallback = FALSE; + } + has_finegrained_fallback = FALSE; } - else + + if (has_supported) { + status = _cairo_meta_surface_replay_region (surface->meta, + surface->target, + CAIRO_META_REGION_NATIVE); + if (status) + return status; + } + + if (has_page_fallback) { - status = _cairo_meta_surface_replay (surface->meta, surface->target); + cairo_box_int_t box; + + box.p1.x = 0; + box.p1.y = 0; + box.p2.x = surface->width; + box.p2.y = surface->height; + status = _paint_fallback_image (surface, &box); + if (status) + return status; + } + + if (has_finegrained_fallback) + { + cairo_region_t *region; + cairo_box_int_t *boxes; + int num_boxes, i; + + /* Reset clip region before drawing the fall back images */ + status = _cairo_surface_intersect_clip_path (surface->target, + NULL, + CAIRO_FILL_RULE_WINDING, + CAIRO_GSTATE_TOLERANCE_DEFAULT, + CAIRO_ANTIALIAS_DEFAULT); + if (status) + return status; + + region = _cairo_analysis_surface_get_unsupported (analysis); + status = _cairo_region_get_boxes (region, &num_boxes, &boxes); + if (status) + return status; + for (i = 0; i < num_boxes; i++) { + status = _paint_fallback_image (surface, &boxes[i]); + if (status) { + _cairo_region_boxes_fini (region, boxes); + return status; + } + } + _cairo_region_boxes_fini (region, boxes); } cairo_surface_destroy (analysis); diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 05746a8bf..df7821259 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -1359,34 +1359,53 @@ pattern_supported (const cairo_pattern_t *pattern) } static cairo_int_status_t -_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) +_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) { - if (surface->force_fallbacks) - return FALSE; + if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; if (! pattern_supported (pattern)) - return FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op == CAIRO_OPERATOR_SOURCE) + return CAIRO_STATUS_SUCCESS; + + if (op != CAIRO_OPERATOR_OVER) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If + * the pattern contains transparency, we return + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transarency into the white + * background to convert the pattern to opaque. + */ if (_cairo_operator_always_opaque (op)) - return TRUE; + return CAIRO_STATUS_SUCCESS; if (_cairo_operator_always_translucent (op)) - return FALSE; + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - return _cairo_pattern_is_opaque (pattern); -} - -static cairo_int_status_t -_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - if (_cairo_ps_surface_operation_supported (surface, op, pattern)) + if (_cairo_pattern_is_opaque (pattern)) return CAIRO_STATUS_SUCCESS; else - return CAIRO_INT_STATUS_UNSUPPORTED; + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; +} + +static cairo_bool_t +_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (_cairo_ps_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) + return TRUE; + else + return FALSE; } /* The "standard" implementation limit for PostScript string sizes is @@ -1514,8 +1533,8 @@ _string_array_stream_create (cairo_output_stream_t *output) static cairo_status_t _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, - cairo_image_surface_t *image, - const char *name) + cairo_image_surface_t *image, + const char *name) { cairo_status_t status, status2; unsigned char *rgb, *compressed; @@ -1659,24 +1678,33 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, } static void -_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, - cairo_solid_pattern_t *pattern) +_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, + cairo_solid_pattern_t *pattern) { - if (color_is_gray (&pattern->color)) + cairo_color_t color = pattern->color; + + if (!CAIRO_COLOR_IS_OPAQUE(&color)) { + /* Blend into white */ + color.red = color.red*color.alpha + 1 - color.alpha; + color.green = color.green*color.alpha + 1 - color.alpha; + color.blue = color.blue*color.alpha + 1 - color.alpha; + } + + if (color_is_gray (&color)) _cairo_output_stream_printf (surface->stream, "%f G\n", - pattern->color.red); + color.red); else _cairo_output_stream_printf (surface->stream, "%f %f %f R\n", - pattern->color.red, - pattern->color.green, - pattern->color.blue); + color.red, + color.green, + color.blue); } static cairo_status_t -_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) +_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern) { cairo_status_t status; double bbox_width, bbox_height; @@ -1919,15 +1947,7 @@ _cairo_ps_surface_paint (void *abstract_surface, if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_ps_surface_analyze_operation (surface, op, source); - /* XXX: It would be nice to be able to assert this condition - * here. But, we actually allow one 'cheat' that is used when - * painting the final image-based fallbacks. The final fallbacks - * do have alpha which we support by blending with white. This is - * possible only because there is nothing between the fallback - * images and the paper, nor is anything painted above. */ - /* - assert (_cairo_ps_surface_operation_supported (op, source)); - */ + assert (_cairo_ps_surface_operation_supported (surface, op, source)); _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_paint\n"); @@ -2017,7 +2037,6 @@ _cairo_ps_surface_stroke (void *abstract_surface, assert (_cairo_ps_surface_operation_supported (surface, op, source)); - _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_stroke\n"); diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h index a3ee75965..7f92f9c1a 100644 --- a/src/cairo-region-private.h +++ b/src/cairo-region-private.h @@ -102,4 +102,8 @@ cairo_private void _cairo_region_translate (cairo_region_t *region, int x, int y); +cairo_private pixman_region_overlap_t +_cairo_region_contains_rectangle (cairo_region_t *region, cairo_rectangle_int_t *box); + + #endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/src/cairo-region.c b/src/cairo-region.c index fe2b405b5..4fe683251 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -207,3 +207,16 @@ _cairo_region_translate (cairo_region_t *region, { pixman_region_translate (®ion->rgn, x, y); } + +pixman_region_overlap_t +_cairo_region_contains_rectangle (cairo_region_t *region, cairo_rectangle_int_t *rect) +{ + pixman_box16_t pbox; + + pbox.x1 = rect->x; + pbox.y1 = rect->y; + pbox.x2 = rect->x + rect->width; + pbox.y2 = rect->y + rect->height; + + return pixman_region_contains_rectangle (®ion->rgn, &pbox); +} diff --git a/src/cairoint.h b/src/cairoint.h index dca8e6ab3..fd3a647d6 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -325,7 +325,9 @@ typedef enum cairo_int_status { CAIRO_INT_STATUS_DEGENERATE = 1000, CAIRO_INT_STATUS_UNSUPPORTED, CAIRO_INT_STATUS_NOTHING_TO_DO, - CAIRO_INT_STATUS_CACHE_EMPTY + CAIRO_INT_STATUS_CACHE_EMPTY, + CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, + CAIRO_INT_STATUS_IMAGE_FALLBACK } cairo_int_status_t; typedef enum cairo_internal_surface_type {