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 {