cairo/src/cairo-surface-fallback.c
Chris Wilson de99f84188 [fallback] Only eliminate the clip if the operation is bounded
For unbounded operations we still need to pass along the clip in order to
correctly limit the extents of the operation.
2009-09-04 08:55:19 +01:00

1809 lines
52 KiB
C

/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairoint.h"
#include "cairo-surface-fallback-private.h"
#include "cairo-clip-private.h"
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
typedef struct {
cairo_surface_t *dst;
cairo_rectangle_int_t extents;
cairo_image_surface_t *image;
cairo_rectangle_int_t image_rect;
void *image_extra;
} fallback_state_t;
/**
* _fallback_init:
*
* Acquire destination image surface needed for an image-based
* fallback.
*
* Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not
* visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all
* went well, or some error status otherwise.
**/
static cairo_int_status_t
_fallback_init (fallback_state_t *state,
cairo_surface_t *dst,
int x,
int y,
int width,
int height)
{
cairo_status_t status;
state->extents.x = x;
state->extents.y = y;
state->extents.width = width;
state->extents.height = height;
state->dst = dst;
status = _cairo_surface_acquire_dest_image (dst, &state->extents,
&state->image, &state->image_rect,
&state->image_extra);
if (unlikely (status))
return status;
/* XXX: This NULL value tucked away in state->image is a rather
* ugly interface. Cleaner would be to push the
* CAIRO_INT_STATUS_NOTHING_TO_DO value down into
* _cairo_surface_acquire_dest_image and its backend
* counterparts. */
assert (state->image != NULL);
return CAIRO_STATUS_SUCCESS;
}
static void
_fallback_fini (fallback_state_t *state)
{
_cairo_surface_release_dest_image (state->dst, &state->extents,
state->image, &state->image_rect,
state->image_extra);
}
typedef cairo_status_t
(*cairo_draw_func_t) (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region);
static cairo_status_t
_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern,
cairo_clip_t *clip,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *mask;
cairo_region_t *clip_region = NULL;
cairo_solid_pattern_t solid;
cairo_status_t status;
cairo_bool_t clip_surface = FALSE;
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
assert (! _cairo_status_is_error (status));
/* The all-clipped state should never propagate this far. */
assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
if (clip_region && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
}
/* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with
* a mask (as called via _cairo_surface_mask) triggers assertion failures.
*/
mask = _cairo_surface_create_similar_solid (dst,
CAIRO_CONTENT_ALPHA,
extents->width,
extents->height,
CAIRO_COLOR_TRANSPARENT,
TRUE);
if (unlikely (mask->status))
return mask->status;
_cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA);
status = draw_func (draw_closure, CAIRO_OPERATOR_ADD,
&solid.base, mask,
extents->x, extents->y,
extents,
clip_region);
if (unlikely (status))
goto CLEANUP_SURFACE;
if (clip_surface)
status = _cairo_clip_combine_with_surface (clip, mask, extents);
_cairo_pattern_init_for_surface (mask_pattern, mask);
CLEANUP_SURFACE:
cairo_surface_destroy (mask);
return status;
}
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
_clip_and_composite_with_mask (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_pattern_t mask_pattern;
cairo_status_t status;
status = _create_composite_mask_pattern (&mask_pattern,
clip,
draw_func, draw_closure,
dst, extents);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_surface_composite (op,
src, &mask_pattern.base, dst,
extents->x, extents->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
_cairo_pattern_fini (&mask_pattern.base);
}
return status;
}
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
_clip_and_composite_combine (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *intermediate;
cairo_surface_pattern_t pattern;
cairo_surface_pattern_t clip_pattern;
cairo_surface_t *clip_surface;
cairo_status_t status;
/* We'd be better off here creating a surface identical in format
* to dst, but we have no way of getting that information.
* A CAIRO_CONTENT_CLONE or something might be useful.
*/
intermediate =
_cairo_surface_create_similar_scratch (dst,
CAIRO_CONTENT_COLOR_ALPHA,
extents->width,
extents->height);
if (intermediate == NULL) {
intermediate =
_cairo_image_surface_create_with_content (CAIRO_CONTENT_COLOR_ALPHA,
extents->width,
extents->width);
}
if (unlikely (intermediate->status))
return intermediate->status;
/* Initialize the intermediate surface from the destination surface */
_cairo_pattern_init_for_surface (&pattern, dst);
status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL, intermediate,
extents->x, extents->y,
0, 0,
0, 0,
extents->width, extents->height,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status))
goto CLEANUP_SURFACE;
status = (*draw_func) (draw_closure, op,
src, intermediate,
extents->x, extents->y,
extents,
NULL);
if (unlikely (status))
goto CLEANUP_SURFACE;
assert (clip->path != NULL);
clip_surface = _cairo_clip_get_surface (clip, dst);
if (unlikely (clip_surface->status))
goto CLEANUP_SURFACE;
_cairo_pattern_init_for_surface (&clip_pattern, clip_surface);
cairo_surface_destroy (clip_surface);
/* Combine that with the clip */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN,
&clip_pattern.base, NULL, intermediate,
extents->x - clip->path->extents.x,
extents->y - clip->path->extents.y,
0, 0,
0, 0,
extents->width, extents->height,
NULL);
if (unlikely (status))
goto CLEANUP_SURFACE;
/* Punch the clip out of the destination */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
&clip_pattern.base, NULL, dst,
extents->x - clip->path->extents.x,
extents->y - clip->path->extents.y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
if (unlikely (status))
goto CLEANUP_SURFACE;
/* Now add the two results together */
_cairo_pattern_init_for_surface (&pattern, intermediate);
status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
&pattern.base, NULL, dst,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
_cairo_pattern_fini (&pattern.base);
CLEANUP_SURFACE:
_cairo_pattern_fini (&clip_pattern.base);
cairo_surface_destroy (intermediate);
return status;
}
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
_clip_and_composite_source (cairo_clip_t *clip,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_pattern_t mask_pattern;
cairo_region_t *clip_region = NULL;
cairo_status_t status;
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
assert (! _cairo_status_is_error (status));
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
/* a solitary clip rectangle is already accommodated by extents */
if (clip_region && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
}
/* Create a surface that is mask IN clip */
status = _create_composite_mask_pattern (&mask_pattern,
clip,
draw_func, draw_closure,
dst, extents);
if (unlikely (status))
return status;
/* Compute dest' = dest OUT (mask IN clip) */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
&mask_pattern.base, NULL, dst,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
clip_region);
if (unlikely (status))
goto CLEANUP_MASK_PATTERN;
/* Now compute (src IN (mask IN clip)) ADD dest' */
status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
src, &mask_pattern.base, dst,
extents->x, extents->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
clip_region);
CLEANUP_MASK_PATTERN:
_cairo_pattern_fini (&mask_pattern.base);
return status;
}
static int
_cairo_rectangle_empty (const cairo_rectangle_int_t *rect)
{
return rect->width == 0 || rect->height == 0;
}
/**
* _clip_and_composite:
* @clip: a #cairo_clip_t
* @op: the operator to draw with
* @src: source pattern
* @draw_func: function that can be called to draw with the mask onto a surface.
* @draw_closure: data to pass to @draw_func.
* @dst: destination surface
* @extents: rectangle holding a bounding box for the operation; this
* rectangle will be used as the size for the temporary
* surface.
*
* When there is a surface clip, we typically need to create an intermediate
* surface. This function handles the logic of creating a temporary surface
* drawing to it, then compositing the result onto the target surface.
*
* @draw_func is to called to draw the mask; it will be called no more
* than once.
*
* Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded.
**/
static cairo_status_t
_clip_and_composite (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_solid_pattern_t solid_pattern;
cairo_status_t status;
if (_cairo_rectangle_empty (extents))
/* Nothing to do */
return CAIRO_STATUS_SUCCESS;
if (op == CAIRO_OPERATOR_CLEAR) {
_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
CAIRO_CONTENT_COLOR);
src = &solid_pattern.base;
op = CAIRO_OPERATOR_DEST_OUT;
}
if (op == CAIRO_OPERATOR_SOURCE) {
status = _clip_and_composite_source (clip,
src,
draw_func, draw_closure,
dst, extents);
} else {
cairo_bool_t clip_surface = FALSE;
cairo_region_t *clip_region = NULL;
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
assert (! _cairo_status_is_error (status));
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
if (clip_surface) {
if (_cairo_operator_bounded_by_mask (op)) {
status = _clip_and_composite_with_mask (clip, op,
src,
draw_func, draw_closure,
dst, extents);
} else {
status = _clip_and_composite_combine (clip, op,
src,
draw_func, draw_closure,
dst, extents);
}
} else {
/* a solitary clip rectangle is already accommodated by extents */
if (clip_region && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
status = draw_func (draw_closure, op,
src, dst,
0, 0,
extents,
clip_region);
}
}
return status;
}
/* Composites a region representing a set of trapezoids.
*/
static cairo_status_t
_composite_trap_region (cairo_clip_t *clip,
const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_region_t *trap_region,
const cairo_rectangle_int_t *extents)
{
cairo_status_t status;
cairo_solid_pattern_t solid_pattern;
cairo_surface_pattern_t mask_pattern;
cairo_pattern_t *mask = NULL;
int mask_x = 0, mask_y =0;
if (clip != NULL) {
cairo_surface_t *clip_surface = NULL;
const cairo_rectangle_int_t *clip_extents;
clip_surface = _cairo_clip_get_surface (clip, dst);
if (unlikely (clip_surface->status))
return clip_surface->status;
if (op == CAIRO_OPERATOR_CLEAR) {
_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
CAIRO_CONTENT_COLOR);
src = &solid_pattern.base;
op = CAIRO_OPERATOR_DEST_OUT;
}
_cairo_pattern_init_for_surface (&mask_pattern, clip_surface);
cairo_surface_destroy (clip_surface);
clip_extents = _cairo_clip_get_extents (clip);
mask_x = extents->x - clip_extents->x;
mask_y = extents->y - clip_extents->y;
mask = &mask_pattern.base;
}
/* reduce a solitary clipping region to the extents */
if (cairo_region_num_rectangles (trap_region) == 1)
trap_region = NULL;
status = _cairo_surface_composite (op, src, mask, dst,
extents->x, extents->y,
mask_x, mask_y,
extents->x, extents->y,
extents->width, extents->height,
trap_region);
if (mask != NULL)
_cairo_pattern_fini (mask);
return status;
}
typedef struct {
cairo_traps_t *traps;
cairo_antialias_t antialias;
} cairo_composite_traps_info_t;
static cairo_status_t
_composite_traps_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_composite_traps_info_t *info = closure;
if (dst_x != 0 || dst_y != 0)
_cairo_traps_translate (info->traps, - dst_x, - dst_y);
return _cairo_surface_composite_trapezoids (op,
src, dst, info->antialias,
extents->x, extents->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
info->traps->traps,
info->traps->num_traps,
clip_region);
}
enum {
HAS_CLEAR_REGION = 0x1,
};
static cairo_status_t
_clip_and_composite_region (const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_region_t *trap_region,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_region_t clear_region;
unsigned int has_region = 0;
cairo_status_t status;
if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) {
/* If we optimize drawing with an unbounded operator to
* _cairo_surface_fill_rectangles() or to drawing with a
* clip region, then we have an additional region to clear.
*/
_cairo_region_init_rectangle (&clear_region, extents);
status = cairo_region_subtract (&clear_region, trap_region);
if (unlikely (status))
return status;
if (! cairo_region_is_empty (&clear_region))
has_region |= HAS_CLEAR_REGION;
}
if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) &&
clip == NULL)
{
const cairo_color_t *color;
if (op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
else
color = &((cairo_solid_pattern_t *)src)->color;
/* Solid rectangles special case */
status = _cairo_surface_fill_region (dst, op, color, trap_region);
} else {
/* For a simple rectangle, we can just use composite(), for more
* rectangles, we have to set a clip region. The cost of rasterizing
* trapezoids is pretty high for most backends currently, so it's
* worthwhile even if a region is needed.
*
* If we have a clip surface, we set it as the mask; this only works
* for bounded operators other than SOURCE; for unbounded operators,
* clip and mask cannot be interchanged. For SOURCE, the operator
* as implemented by the backends is different in its handling
* of the mask then what we want.
*
* CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has
* more than rectangle and the destination doesn't support clip
* regions. In that case, we fall through.
*/
status = _composite_trap_region (clip, src, op, dst,
trap_region, extents);
}
if (has_region & HAS_CLEAR_REGION) {
if (status == CAIRO_STATUS_SUCCESS) {
status = _cairo_surface_fill_region (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear_region);
}
_cairo_region_fini (&clear_region);
}
return status;
}
/* avoid using region code to re-validate boxes */
static cairo_status_t
_fill_rectangles (cairo_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_traps_t *traps,
cairo_clip_t *clip)
{
const cairo_color_t *color;
cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
cairo_rectangle_int_t *rects = stack_rects;
cairo_status_t status;
int i;
if (! traps->is_rectilinear || ! traps->maybe_region)
return CAIRO_INT_STATUS_UNSUPPORTED;
/* XXX: convert clip region to geometric boxes? */
if (clip != NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
/* XXX: fallback for the region_subtract() operation */
if (! _cairo_operator_bounded_by_mask (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (traps->has_intersections) {
if (traps->is_rectangular) {
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
} else {
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
}
if (unlikely (status))
return status;
}
for (i = 0; i < traps->num_traps; i++) {
if (! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
traps->maybe_region = FALSE;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (traps->num_traps,
sizeof (cairo_rectangle_int_t));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < traps->num_traps; i++) {
int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
rects[i].x = x1;
rects[i].y = y1;
rects[i].width = x2 - x1;
rects[i].height = y2 - y1;
}
if (op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
else
color = &((cairo_solid_pattern_t *)src)->color;
status = _cairo_surface_fill_rectangles (dst, op, color, rects, i);
if (rects != stack_rects)
free (rects);
return status;
}
/* Warning: This call modifies the coordinates of traps */
static cairo_status_t
_clip_and_composite_trapezoids (const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_composite_traps_info_t traps_info;
cairo_region_t *clip_region = NULL;
cairo_bool_t clip_surface = FALSE;
cairo_status_t status;
if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op))
return CAIRO_STATUS_SUCCESS;
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (_cairo_status_is_error (status)))
return status;
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
/* Use a fast path if the trapezoids consist of a simple region,
* but we can only do this if we do not have a clip surface, or can
* substitute the mask with the clip.
*/
if (! clip_surface ||
(_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
{
cairo_region_t *trap_region = NULL;
status = _fill_rectangles (dst, op, src, traps, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
status = _cairo_traps_extract_region (traps, &trap_region);
if (unlikely (_cairo_status_is_error (status)))
return status;
if (trap_region != NULL) {
status = cairo_region_intersect_rectangle (trap_region, extents);
if (unlikely (status)) {
cairo_region_destroy (trap_region);
return status;
}
if (clip_region != NULL) {
status = cairo_region_intersect (trap_region, clip_region);
if (unlikely (status)) {
cairo_region_destroy (trap_region);
return status;
}
}
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t trap_extents;
cairo_region_get_extents (trap_region, &trap_extents);
if (! _cairo_rectangle_intersect (extents, &trap_extents)) {
cairo_region_destroy (trap_region);
return CAIRO_STATUS_SUCCESS;
}
}
status = _clip_and_composite_region (src, op, dst,
trap_region,
clip_surface ? clip : NULL,
extents);
cairo_region_destroy (trap_region);
if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
return status;
}
}
/* No fast path, exclude self-intersections and clip trapezoids. */
if (traps->has_intersections) {
if (traps->is_rectangular)
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
else if (traps->is_rectilinear)
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
else
status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
return status;
}
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
traps_info.traps = traps;
traps_info.antialias = antialias;
return _clip_and_composite (clip, op, src,
_composite_traps_draw_func,
&traps_info, dst, extents);
}
typedef struct {
cairo_polygon_t *polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
} cairo_composite_spans_info_t;
static cairo_status_t
_composite_spans_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_composite_rectangles_t rects;
cairo_composite_spans_info_t *info = closure;
_cairo_composite_rectangles_init (&rects,
extents->x, extents->y,
extents->width, extents->height);
/* The incoming dst_x/y are where we're pretending the origin of
* the dst surface is -- *not* the offset of a rectangle where
* we'd like to place the result. */
rects.dst.x -= dst_x;
rects.dst.y -= dst_y;
return _cairo_surface_composite_polygon (dst, op, src,
info->fill_rule,
info->antialias,
&rects,
info->polygon,
clip_region);
}
static cairo_status_t
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
{
if (clip != NULL) {
if (! _cairo_rectangle_intersect (extents,
_cairo_clip_get_extents (clip)))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
return _cairo_clip_rectangle (clip, extents);
} else if (_cairo_rectangle_empty (extents))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_clip_contains_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *rect)
{
cairo_clip_path_t *clip_path;
clip_path = clip->path;
if (clip_path->extents.x > rect->x ||
clip_path->extents.y > rect->y ||
clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
{
return FALSE;
}
do {
cairo_box_t box;
if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
return FALSE;
if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
box.p1.y > _cairo_fixed_from_int (rect->y) ||
box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
{
return FALSE;
}
} while ((clip_path = clip_path->prev) != NULL);
return TRUE;
}
static inline cairo_status_t
_clip_to_boxes (cairo_clip_t **clip,
const cairo_rectangle_int_t *extents,
cairo_bool_t is_bounded,
cairo_box_t **boxes,
int *num_boxes)
{
cairo_status_t status;
if (*clip == NULL) {
status = CAIRO_STATUS_SUCCESS;
goto EXTENTS;
}
status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
switch ((int) status) {
case CAIRO_STATUS_SUCCESS:
if (is_bounded)
*clip = NULL;
goto DONE;
case CAIRO_INT_STATUS_UNSUPPORTED:
status = CAIRO_STATUS_SUCCESS;
goto EXTENTS;
}
EXTENTS:
_cairo_box_from_rectangle (&(*boxes)[0], extents);
*num_boxes = 1;
DONE:
return status;
}
cairo_status_t
_cairo_surface_fallback_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_clip_path_t *clip_path = clip ? clip->path : NULL;
cairo_box_t boxes_stack[32], *boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_traps_t traps;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
is_bounded = FALSE;
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
_cairo_pattern_get_extents (source, &source_extents);
if (! _cairo_rectangle_intersect (&extents, &source_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents))
clip = NULL;
status = _rectangle_intersect_clip (&extents, clip);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
/* If the clip cannot be reduced to a set of boxes, we will need to
* use a clipmask. Paint is special as it is the only operation that
* does not implicitly use a mask, so we may be able to reduce this
* operation to a fill...
*/
if (clip != NULL && clip_path->prev == NULL &&
_cairo_operator_bounded_by_mask (op))
{
return _cairo_surface_fill (surface, op, source,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
NULL);
}
status = _cairo_traps_init_boxes (&traps, boxes, num_boxes);
if (unlikely (status))
goto CLEANUP_BOXES;
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, CAIRO_ANTIALIAS_DEFAULT,
clip, &extents);
_cairo_traps_fini (&traps);
CLEANUP_BOXES:
if (boxes != boxes_stack)
free (boxes);
return status;
}
static cairo_status_t
_cairo_surface_mask_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_pattern_t *mask = closure;
if (src) {
return _cairo_surface_composite (op,
src, mask, dst,
extents->x, extents->y,
extents->x, extents->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
clip_region);
} else {
return _cairo_surface_composite (op,
mask, NULL, dst,
extents->x, extents->y,
0, 0, /* unused */
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
clip_region);
}
}
cairo_status_t
_cairo_surface_fallback_mask (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
{
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_status_t status;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
is_bounded = FALSE;
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
_cairo_pattern_get_extents (source, &source_extents);
if (! _cairo_rectangle_intersect (&extents, &source_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t mask_extents;
_cairo_pattern_get_extents (mask, &mask_extents);
if (! _cairo_rectangle_intersect (&extents, &mask_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents))
clip = NULL;
status = _rectangle_intersect_clip (&extents, clip);
if (status) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
return _clip_and_composite (clip, op, source,
_cairo_surface_mask_draw_func,
(void *) mask,
surface, &extents);
}
cairo_status_t
_cairo_surface_fallback_stroke (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t boxes_stack[32], *boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_status_t status;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
is_bounded = FALSE;
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
_cairo_pattern_get_extents (source, &source_extents);
if (! _cairo_rectangle_intersect (&extents, &source_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t path_extents;
_cairo_path_fixed_approximate_stroke_extents (path,
stroke_style, ctm,
&path_extents);
if (! _cairo_rectangle_intersect (&extents, &path_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents))
clip = NULL;
status = _rectangle_intersect_clip (&extents, clip);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, boxes, num_boxes);
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, boxes, num_boxes);
if (path->is_rectilinear) {
status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
stroke_style,
ctm,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
if (_cairo_status_is_error (status))
goto CLEANUP;
}
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (unlikely (status))
goto CLEANUP;
if (polygon.num_edges == 0)
goto DO_TRAPS;
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t polygon_extents;
_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
goto CLEANUP;
}
if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
cairo_composite_spans_info_t info;
info.polygon = &polygon;
info.fill_rule = CAIRO_FILL_RULE_WINDING;
info.antialias = antialias;
status = _clip_and_composite (clip, op, source,
_composite_spans_draw_func,
&info, surface, &extents);
goto CLEANUP;
}
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
goto CLEANUP;
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip, &extents);
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
if (boxes != boxes_stack)
free (boxes);
return status;
}
cairo_status_t
_cairo_surface_fallback_fill (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t boxes_stack[32], *boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_bool_t is_rectilinear;
cairo_status_t status;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
is_bounded = FALSE;
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
_cairo_pattern_get_extents (source, &source_extents);
if (! _cairo_rectangle_intersect (&extents, &source_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t path_extents;
_cairo_path_fixed_approximate_fill_extents (path, &path_extents);
if (! _cairo_rectangle_intersect (&extents, &path_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (is_bounded) {
if (clip != NULL && _clip_contains_rectangle (clip, &extents))
clip = NULL;
if (clip != NULL && clip->path->prev == NULL &&
_cairo_path_fixed_equal (&clip->path->path, path))
{
clip = NULL;
}
}
status = _rectangle_intersect_clip (&extents, clip);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
status = _clip_to_boxes (&clip, &extents, is_bounded, &boxes, &num_boxes);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, boxes, num_boxes);
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, boxes, num_boxes);
if (path->is_empty_fill)
goto DO_TRAPS;
is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
if (is_rectilinear) {
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
if (_cairo_status_is_error (status))
goto CLEANUP;
}
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (unlikely (status))
goto CLEANUP;
if (polygon.num_edges == 0)
goto DO_TRAPS;
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t polygon_extents;
_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
goto CLEANUP;
}
if (is_rectilinear) {
status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
&polygon,
fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
if (unlikely (_cairo_status_is_error (status)))
goto CLEANUP;
}
if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
cairo_composite_spans_info_t info;
info.polygon = &polygon;
info.fill_rule = fill_rule;
info.antialias = antialias;
status = _clip_and_composite (clip, op, source,
_composite_spans_draw_func,
&info, surface, &extents);
goto CLEANUP;
}
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
fill_rule);
if (unlikely (status))
goto CLEANUP;
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip, &extents);
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
if (boxes != boxes_stack)
free (boxes);
return status;
}
typedef struct {
cairo_scaled_font_t *font;
cairo_glyph_t *glyphs;
int num_glyphs;
} cairo_show_glyphs_info_t;
static cairo_status_t
_cairo_surface_old_show_glyphs_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_show_glyphs_info_t *glyph_info = closure;
cairo_status_t status;
/* Modifying the glyph array is fine because we know that this function
* will be called only once, and we've already made a copy of the
* glyphs in the wrapper.
*/
if (dst_x != 0 || dst_y != 0) {
int i;
for (i = 0; i < glyph_info->num_glyphs; ++i) {
((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x;
((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y;
}
}
status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
dst,
extents->x, extents->y,
extents->x - dst_x,
extents->y - dst_y,
extents->width,
extents->height,
glyph_info->glyphs,
glyph_info->num_glyphs,
clip_region);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
return _cairo_scaled_font_show_glyphs (glyph_info->font,
op,
src, dst,
extents->x, extents->y,
extents->x - dst_x,
extents->y - dst_y,
extents->width, extents->height,
glyph_info->glyphs,
glyph_info->num_glyphs,
clip_region);
}
cairo_status_t
_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_rectangle_int_t extents;
cairo_show_glyphs_info_t glyph_info;
cairo_bool_t is_bounded;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
is_bounded = FALSE;
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
_cairo_pattern_get_extents (source, &source_extents);
if (! _cairo_rectangle_intersect (&extents, &source_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t glyph_extents;
status = _cairo_scaled_font_glyph_device_extents (scaled_font,
glyphs,
num_glyphs,
&glyph_extents,
NULL);
if (unlikely (status))
return status;
if (! _cairo_rectangle_intersect (&extents, &glyph_extents))
return CAIRO_STATUS_SUCCESS;
is_bounded = TRUE;
}
if (is_bounded && clip != NULL && _clip_contains_rectangle (clip, &extents))
clip = NULL;
status = _rectangle_intersect_clip (&extents, clip);
if (status) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
glyph_info.font = scaled_font;
glyph_info.glyphs = glyphs;
glyph_info.num_glyphs = num_glyphs;
return _clip_and_composite (clip, op, source,
_cairo_surface_old_show_glyphs_draw_func,
&glyph_info,
surface,
&extents);
}
cairo_surface_t *
_cairo_surface_fallback_snapshot (cairo_surface_t *surface)
{
cairo_surface_t *snapshot;
cairo_status_t status;
cairo_format_t format;
cairo_surface_pattern_t pattern;
cairo_image_surface_t *image;
void *image_extra;
status = _cairo_surface_acquire_source_image (surface,
&image, &image_extra);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
format = image->format;
if (format == CAIRO_FORMAT_INVALID) {
/* Non-standard images formats can be generated when retrieving
* images from unusual xservers, for example.
*/
format = _cairo_format_from_content (image->base.content);
}
snapshot = cairo_image_surface_create (format,
image->width,
image->height);
if (cairo_surface_status (snapshot)) {
_cairo_surface_release_source_image (surface, image, image_extra);
return snapshot;
}
_cairo_pattern_init_for_surface (&pattern, &image->base);
status = _cairo_surface_paint (snapshot,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
_cairo_surface_release_source_image (surface, image, image_extra);
if (unlikely (status)) {
cairo_surface_destroy (snapshot);
return _cairo_surface_create_in_error (status);
}
return snapshot;
}
cairo_status_t
_cairo_surface_fallback_composite (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height,
cairo_region_t *clip_region)
{
fallback_state_t state;
cairo_region_t *fallback_region = NULL;
cairo_status_t status;
status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
/* We know this will never fail with the image backend; but
* instead of calling into it directly, we call
* _cairo_surface_composite so that we get the correct device
* offset handling.
*/
if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) {
fallback_region = cairo_region_copy (clip_region);
status = fallback_region->status;
if (unlikely (status))
goto FAIL;
cairo_region_translate (fallback_region,
-state.image_rect.x,
-state.image_rect.y);
clip_region = fallback_region;
}
status = _cairo_surface_composite (op, src, mask,
&state.image->base,
src_x, src_y, mask_x, mask_y,
dst_x - state.image_rect.x,
dst_y - state.image_rect.y,
width, height,
clip_region);
FAIL:
if (fallback_region != NULL)
cairo_region_destroy (fallback_region);
_fallback_fini (&state);
return status;
}
cairo_status_t
_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
fallback_state_t state;
cairo_rectangle_int_t *offset_rects = NULL;
cairo_status_t status;
int x1, y1, x2, y2;
int i;
assert (surface->snapshot_of == NULL);
if (num_rects <= 0)
return CAIRO_STATUS_SUCCESS;
/* Compute the bounds of the rectangles, so that we know what area of the
* destination surface to fetch
*/
x1 = rects[0].x;
y1 = rects[0].y;
x2 = rects[0].x + rects[0].width;
y2 = rects[0].y + rects[0].height;
for (i = 1; i < num_rects; i++) {
if (rects[i].x < x1)
x1 = rects[i].x;
if (rects[i].y < y1)
y1 = rects[i].y;
if ((int) (rects[i].x + rects[i].width) > x2)
x2 = rects[i].x + rects[i].width;
if ((int) (rects[i].y + rects[i].height) > y2)
y2 = rects[i].y + rects[i].height;
}
status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
/* If the fetched image isn't at 0,0, we need to offset the rectangles */
if (state.image_rect.x != 0 || state.image_rect.y != 0) {
offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t));
if (unlikely (offset_rects == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
for (i = 0; i < num_rects; i++) {
offset_rects[i].x = rects[i].x - state.image_rect.x;
offset_rects[i].y = rects[i].y - state.image_rect.y;
offset_rects[i].width = rects[i].width;
offset_rects[i].height = rects[i].height;
}
rects = offset_rects;
}
status = _cairo_surface_fill_rectangles (&state.image->base,
op, color,
rects, num_rects);
free (offset_rects);
DONE:
_fallback_fini (&state);
return status;
}
cairo_status_t
_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
cairo_antialias_t antialias,
int src_x,
int src_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height,
cairo_trapezoid_t *traps,
int num_traps,
cairo_region_t *clip_region)
{
fallback_state_t state;
cairo_region_t *fallback_region = NULL;
cairo_trapezoid_t *offset_traps = NULL;
cairo_status_t status;
status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
return status;
}
/* If the destination image isn't at 0,0, we need to offset the trapezoids */
if (state.image_rect.x != 0 || state.image_rect.y != 0) {
offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t));
if (offset_traps == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
_cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps,
- state.image_rect.x, - state.image_rect.y,
1.0, 1.0);
traps = offset_traps;
/* similarly we need to adjust the region */
if (clip_region != NULL) {
fallback_region = cairo_region_copy (clip_region);
status = fallback_region->status;
if (unlikely (status))
goto FAIL;
cairo_region_translate (fallback_region,
-state.image_rect.x,
-state.image_rect.y);
clip_region = fallback_region;
}
}
status = _cairo_surface_composite_trapezoids (op, pattern,
&state.image->base,
antialias,
src_x, src_y,
dst_x - state.image_rect.x,
dst_y - state.image_rect.y,
width, height,
traps, num_traps,
clip_region);
if (offset_traps != NULL)
free (offset_traps);
FAIL:
if (fallback_region != NULL)
cairo_region_destroy (fallback_region);
_fallback_fini (&state);
return status;
}
cairo_status_t
_cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
cairo_surface_t *src,
cairo_content_t content,
int src_x,
int src_y,
int width,
int height,
int *clone_offset_x,
int *clone_offset_y,
cairo_surface_t **clone_out)
{
cairo_surface_t *new_surface;
cairo_surface_pattern_t pattern;
cairo_status_t status;
new_surface = _cairo_surface_create_similar_scratch (surface,
src->content & content,
width, height);
if (new_surface == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (new_surface->status))
return new_surface->status;
/* We have to copy these here, so that the coordinate spaces are correct */
new_surface->device_transform = src->device_transform;
new_surface->device_transform_inverse = src->device_transform_inverse;
_cairo_pattern_init_for_surface (&pattern, src);
cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (new_surface,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status)) {
cairo_surface_destroy (new_surface);
return status;
}
*clone_offset_x = src_x;
*clone_offset_y = src_y;
*clone_out = new_surface;
return CAIRO_STATUS_SUCCESS;
}