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.
This commit is contained in:
Adrian Johnson 2007-08-21 22:27:57 +09:30
parent bf4bdbb607
commit bf92255edd
9 changed files with 591 additions and 139 deletions

View file

@ -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);

View file

@ -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 <keithp@keithp.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
#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;
}

View file

@ -31,6 +31,7 @@
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
#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);

View file

@ -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 <krh@redhat.com>
* Carl Worth <cworth@cworth.org>
* Adrian Johnson <ajohnson@redneon.com>
*/
/* 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);
}

View file

@ -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 <cworth@cworth.org>
* Keith Packard <keithp@keithp.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
/* 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);

View file

@ -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");

View file

@ -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 */

View file

@ -207,3 +207,16 @@ _cairo_region_translate (cairo_region_t *region,
{
pixman_region_translate (&region->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 (&region->rgn, &pbox);
}

View file

@ -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 {