コード例 #1
0
ファイル: cairo-surface-wrapper.c プロジェクト: igagis/cairo
cairo_bool_t
_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
				    cairo_rectangle_int_t   *extents)
{
    if (wrapper->has_extents) {
	if (_cairo_surface_get_extents (wrapper->target, extents))
	    _cairo_rectangle_intersect (extents, &wrapper->extents);
	else
	    *extents = wrapper->extents;

	return TRUE;
    } else {
	return _cairo_surface_get_extents (wrapper->target, extents);
    }
}
コード例 #2
0
static cairo_status_t
_cairo_paginated_surface_acquire_source_image (void	       *abstract_surface,
					       cairo_image_surface_t **image_out,
					       void		   **image_extra)
{
    cairo_paginated_surface_t *surface = abstract_surface;
    cairo_surface_t *image;
    cairo_status_t status;
    cairo_rectangle_int_t extents;

    status = _cairo_surface_get_extents (surface->target, &extents);
    if (status)
	return status;

    image = _cairo_paginated_surface_create_image_surface (surface,
							   extents.width,
							   extents.height);

    status = _cairo_meta_surface_replay (surface->meta, image);
    if (status) {
	cairo_surface_destroy (image);
	return status;
    }

    *image_out = (cairo_image_surface_t*) image;
    *image_extra = NULL;

    return CAIRO_STATUS_SUCCESS;
}
コード例 #3
0
cairo_status_t
_cairo_surface_fallback_fill (cairo_surface_t		*surface,
			      cairo_operator_t		 op,
			      cairo_pattern_t		*source,
			      cairo_path_fixed_t	*path,
			      cairo_fill_rule_t		 fill_rule,
			      double			 tolerance,
			      cairo_antialias_t		 antialias)
{
    cairo_status_t status;
    cairo_traps_t traps;
    cairo_box_t box;
    cairo_rectangle_int_t extents;

    status = _cairo_surface_get_extents (surface, &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_clip_intersect_to_rectangle (surface->clip, &extents);
    if (status)
        return status;

    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) {
	_cairo_traps_fini (&traps);
	return status;
    }

    status = _clip_and_composite_trapezoids (source,
					     op,
					     surface,
					     &traps,
					     surface->clip,
					     antialias);

    _cairo_traps_fini (&traps);

    return status;
}
コード例 #4
0
static cairo_int_status_t
_cairo_analysis_surface_stroke (void			*abstract_surface,
				cairo_operator_t	 op,
				const cairo_pattern_t	*source,
				cairo_path_fixed_t	*path,
				cairo_stroke_style_t	*style,
				cairo_matrix_t		*ctm,
				cairo_matrix_t		*ctm_inverse,
				double			 tolerance,
				cairo_antialias_t	 antialias,
				cairo_rectangle_int_t   *stroke_extents)
{
    cairo_analysis_surface_t *surface = abstract_surface;
    cairo_status_t	     status, backend_status;
    cairo_rectangle_int_t    extents;
    cairo_bool_t             is_empty;

    if (!surface->target->backend->stroke)
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
    else
	backend_status = (*surface->target->backend->stroke) (surface->target, op,
							      source, path, style,
							      ctm, ctm_inverse,
							      tolerance, antialias, NULL);

    if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
	backend_status = _analyze_meta_surface_pattern (surface, source);

    status = _cairo_surface_get_extents (&surface->base, &extents);
    if (_cairo_status_is_error (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 (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &source_extents);
    }

    is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip);

    if (_cairo_operator_bounded_by_mask (op)) {
	cairo_rectangle_int_t mask_extents;

	_cairo_path_fixed_approximate_stroke_extents (path,
						      style, ctm,
						      &mask_extents);

	is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
    }
    if (stroke_extents)
	*stroke_extents = extents;

    status = _add_operation (surface, &extents, backend_status);

    return status;
}
コード例 #5
0
static cairo_status_t
_cairo_paginated_surface_acquire_source_image (void	       *abstract_surface,
					       cairo_image_surface_t **image_out,
					       void		   **image_extra)
{
    cairo_paginated_surface_t *surface = abstract_surface;
    cairo_bool_t is_bounded;
    cairo_surface_t *image;
    cairo_status_t status;
    cairo_rectangle_int_t extents;

    is_bounded = _cairo_surface_get_extents (surface->target, &extents);
    if (! is_bounded)
	return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED;

    image = _cairo_paginated_surface_create_image_surface (surface,
							   extents.width,
							   extents.height);

    status = _cairo_recording_surface_replay (surface->recording_surface, image);
    if (unlikely (status)) {
	cairo_surface_destroy (image);
	return status;
    }

    *image_out = (cairo_image_surface_t*) image;
    *image_extra = NULL;

    return CAIRO_STATUS_SUCCESS;
}
コード例 #6
0
static cairo_bool_t
_cairo_surface_snapshot_get_extents (void                  *abstract_surface,
				     cairo_rectangle_int_t *extents)
{
    cairo_surface_snapshot_t *surface = abstract_surface;

    return _cairo_surface_get_extents (surface->target, extents);
}
コード例 #7
0
static cairo_int_status_t
_cairo_analysis_surface_get_extents (void			*abstract_surface,
				     cairo_rectangle_int_t	*rectangle)
{
    cairo_analysis_surface_t *surface = abstract_surface;

    return _cairo_surface_get_extents (surface->target, rectangle);
}
コード例 #8
0
static cairo_int_status_t
_test_paginated_surface_get_extents (void			*abstract_surface,
				     cairo_rectangle_int_t	*rectangle)
{
    test_paginated_surface_t *surface = abstract_surface;

    return _cairo_surface_get_extents (surface->target, rectangle);
}
コード例 #9
0
static cairo_int_status_t
_test_fallback_surface_get_extents (void		            *abstract_surface,
				    cairo_rectangle_int16_t *rectangle)
{
    test_fallback_surface_t *surface = abstract_surface;

    return _cairo_surface_get_extents (surface->backing, rectangle);
}
コード例 #10
0
ファイル: cairo-surface-wrapper.c プロジェクト: MiKTeX/miktex
cairo_bool_t
_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
					   cairo_bool_t surface_is_unbounded,
					   cairo_rectangle_int_t *extents)
{
    cairo_rectangle_int_t clip;
    cairo_bool_t has_clip = FALSE;

    if (!surface_is_unbounded)
	has_clip = _cairo_surface_get_extents (wrapper->target, &clip);

    if (wrapper->clip) {
	if (has_clip) {
	    if (! _cairo_rectangle_intersect (&clip,
					      _cairo_clip_get_extents (wrapper->clip)))
		return FALSE;
	} else {
	    has_clip = TRUE;
	    clip = *_cairo_clip_get_extents (wrapper->clip);
	}
    }

    if (has_clip && wrapper->needs_transform) {
	cairo_matrix_t m;
	double x1, y1, x2, y2;

	_cairo_surface_wrapper_get_inverse_transform (wrapper, &m);

	x1 = clip.x;
	y1 = clip.y;
	x2 = clip.x + clip.width;
	y2 = clip.y + clip.height;

	_cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL);

	clip.x = floor (x1);
	clip.y = floor (y1);
	clip.width  = ceil (x2) - clip.x;
	clip.height = ceil (y2) - clip.y;
    }

    if (has_clip) {
	if (wrapper->has_extents) {
	    *extents = wrapper->extents;
	    return _cairo_rectangle_intersect (extents, &clip);
	} else {
	    *extents = clip;
	    return TRUE;
	}
    } else if (wrapper->has_extents) {
	*extents = wrapper->extents;
	return TRUE;
    } else {
	_cairo_unbounded_rectangle_init (extents);
	return TRUE;
    }
}
コード例 #11
0
static inline cairo_bool_t
_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
				  cairo_surface_t *surface,
				  cairo_operator_t op,
				  const cairo_pattern_t *source,
				  const cairo_clip_t *clip,
				  cairo_bool_t *should_be_lazy)
{
    if (_cairo_clip_is_all_clipped (clip))
	return FALSE;

    extents->surface = surface;
    extents->op = op;

    _cairo_surface_get_extents (surface, &extents->destination);
    extents->clip = NULL;

    extents->unbounded = extents->destination;
    extents->is_bounded = _cairo_operator_bounded_by_either (op);

    if (*should_be_lazy)
        *should_be_lazy = _cairo_composite_rectangles_check_lazy_init (extents,
                                                                       surface,
                                                                       source);

    if ((! *should_be_lazy) &&
        (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
                                               _cairo_clip_get_extents (clip))))
        return FALSE;

    extents->bounded = extents->unbounded;

	extents->original_source_pattern = source;
	if (! *should_be_lazy) {
		_cairo_composite_reduce_pattern (source, &extents->source_pattern);

		_cairo_pattern_get_extents (&extents->source_pattern.base,
                                    &extents->source);
	} else
        _cairo_pattern_get_extents (extents->original_source_pattern,
                                    &extents->source);

    if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
	if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
	    return FALSE;
    }

    extents->original_mask_pattern = NULL;
    extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
    extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
    extents->mask_pattern.solid.color.alpha_short = 0xffff;

    return TRUE;
}
コード例 #12
0
static cairo_int_status_t
_analyze_meta_surface_pattern (cairo_analysis_surface_t	*surface,
			       const cairo_pattern_t *pattern)
{
    cairo_surface_t *analysis = &surface->base;
    const cairo_surface_pattern_t *surface_pattern;
    cairo_status_t status;
    cairo_bool_t old_has_ctm;
    cairo_matrix_t old_ctm, p2d;
    cairo_rectangle_int_t old_clip;
    cairo_rectangle_int_t meta_extents;
    int old_width;
    int old_height;

    assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
    surface_pattern = (const cairo_surface_pattern_t *) pattern;
    assert (_cairo_surface_is_meta (surface_pattern->surface));

    old_width = surface->width;
    old_height = surface->height;
    old_clip = surface->current_clip;
    status = _cairo_surface_get_extents (surface_pattern->surface, &meta_extents);
    if (_cairo_status_is_error (status))
	return status;

    surface->width = meta_extents.width;
    surface->height = meta_extents.height;
    surface->current_clip.x = 0;
    surface->current_clip.y = 0;
    surface->current_clip.width = surface->width;
    surface->current_clip.height = surface->height;
    old_ctm = surface->ctm;
    old_has_ctm = surface->has_ctm;
    p2d = pattern->matrix;
    status = cairo_matrix_invert (&p2d);
    /* _cairo_pattern_set_matrix guarantees invertibility */
    assert (status == CAIRO_STATUS_SUCCESS);

    cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm);
    surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);

    status = _cairo_meta_surface_replay_and_create_regions (surface_pattern->surface,
							    analysis);
    if (status == CAIRO_STATUS_SUCCESS)
	status = analysis->status;

    surface->ctm = old_ctm;
    surface->has_ctm = old_has_ctm;
    surface->current_clip = old_clip;
    surface->width = old_width;
    surface->height = old_height;

    return status;
}
コード例 #13
0
static cairo_bool_t
_cairo_surface_snapshot_get_extents (void                  *abstract_surface,
				     cairo_rectangle_int_t *extents)
{
    cairo_surface_snapshot_t *surface = abstract_surface;
    cairo_surface_t *target;
    cairo_bool_t bounded;

    target = _cairo_surface_snapshot_get_target (&surface->base);
    bounded = _cairo_surface_get_extents (target, extents);
    cairo_surface_destroy (target);

    return bounded;
}
コード例 #14
0
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_status_t status;
    cairo_rectangle_int_t extents;
    cairo_show_glyphs_info_t glyph_info;

    status = _cairo_surface_get_extents (surface, &extents);
    if (unlikely (status))
	return status;

    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);
	if (unlikely (status))
	    return status;

	if (! _cairo_rectangle_intersect (&extents, &glyph_extents))
	    return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
    if (unlikely (status))
	return status;

    glyph_info.font = scaled_font;
    glyph_info.glyphs = glyphs;
    glyph_info.num_glyphs = num_glyphs;

    status = _clip_and_composite (surface->clip,
				  op,
				  source,
				  _cairo_surface_old_show_glyphs_draw_func,
				  &glyph_info,
				  surface,
				  &extents);

    return status;
}
コード例 #15
0
cairo_status_t
_cairo_surface_fallback_paint (cairo_surface_t	*surface,
			       cairo_operator_t	 op,
			       cairo_pattern_t	*source)
{
    cairo_status_t status;
    cairo_rectangle_int_t extents;
    cairo_box_t box;
    cairo_traps_t traps;

    status = _cairo_surface_get_extents (surface, &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_clip_intersect_to_rectangle (surface->clip, &extents);
    if (status)
	return status;

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

    status = _cairo_traps_init_box (&traps, &box);
    if (status)
	return status;

    status = _clip_and_composite_trapezoids (source,
				             op,
					     surface,
					     &traps,
					     surface->clip,
					     CAIRO_ANTIALIAS_NONE);

    _cairo_traps_fini (&traps);

    return status;
}
コード例 #16
0
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_status_t status;
    cairo_rectangle_int_t extents, source_extents, mask_extents;

    status = _cairo_surface_get_extents (surface, &extents);
    if (unlikely (status))
	return status;

    if (_cairo_operator_bounded_by_source (op)) {
	status = _cairo_pattern_get_extents (source, &source_extents);
	if (unlikely (status))
	    return status;

	if (! _cairo_rectangle_intersect (&extents, &source_extents))
	    return CAIRO_STATUS_SUCCESS;
    }

    if (_cairo_operator_bounded_by_mask (op)) {
	status = _cairo_pattern_get_extents (mask, &mask_extents);
	if (unlikely (status))
	    return status;

	if (! _cairo_rectangle_intersect (&extents, &mask_extents))
	    return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
    if (unlikely (status))
	return status;

    status = _clip_and_composite (surface->clip, op,
				  source,
				  _cairo_surface_mask_draw_func,
				  (void *) mask,
				  surface,
				  &extents);

    return status;
}
コード例 #17
0
cairo_status_t
_cairo_surface_fallback_paint (cairo_surface_t		*surface,
			       cairo_operator_t		 op,
			       const cairo_pattern_t	*source)
{
    cairo_status_t status;
    cairo_rectangle_int_t extents;
    cairo_box_t box;
    cairo_traps_t traps;

    status = _cairo_surface_get_extents (surface, &extents);
    if (unlikely (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 (unlikely (status))
	    return status;

	if (! _cairo_rectangle_intersect (&extents, &source_extents))
	    return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
    if (unlikely (status))
	return status;

    _cairo_box_from_rectangle (&box, &extents);

    _cairo_traps_init_box (&traps, &box);

    status = _clip_and_composite_trapezoids (source,
				             op,
					     surface,
					     &traps,
					     surface->clip,
					     CAIRO_ANTIALIAS_NONE);

    _cairo_traps_fini (&traps);

    return status;
}
コード例 #18
0
static cairo_surface_t *
_create_recording_surface_for_target (cairo_surface_t *target,
				      cairo_content_t content)
{
    cairo_rectangle_int_t rect;

    if (_cairo_surface_get_extents (target, &rect)) {
	cairo_rectangle_t recording_extents;

	recording_extents.x = rect.x;
	recording_extents.y = rect.y;
	recording_extents.width = rect.width;
	recording_extents.height = rect.height;

	return cairo_recording_surface_create (content, &recording_extents);
    } else {
	return cairo_recording_surface_create (content, NULL);
    }
}
コード例 #19
0
cairo_status_t
_cairo_surface_fallback_mask (cairo_surface_t		*surface,
			      cairo_operator_t		 op,
			      cairo_pattern_t		*source,
			      cairo_pattern_t		*mask)
{
    cairo_status_t status;
    cairo_rectangle_int16_t extents, source_extents, mask_extents;

    status = _cairo_surface_get_extents (surface, &extents);
    if (status)
	return status;

    if (_cairo_operator_bounded_by_source (op)) {
	status = _cairo_pattern_get_extents (source, &source_extents);
	if (status)
	    return status;

	_cairo_rectangle_intersect (&extents, &source_extents);
    }

    if (_cairo_operator_bounded_by_mask (op)) {
	status = _cairo_pattern_get_extents (mask, &mask_extents);
	if (status)
	    return status;

	_cairo_rectangle_intersect (&extents, &mask_extents);
    }

    status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
    if (status)
	return status;

    status = _clip_and_composite (surface->clip, op,
				  source,
				  _cairo_surface_mask_draw_func,
				  mask,
				  surface,
				  &extents);

    return status;
}
コード例 #20
0
static cairo_int_status_t
_cairo_analysis_surface_paint (void			*abstract_surface,
			       cairo_operator_t		op,
			       const cairo_pattern_t	*source,
			       cairo_rectangle_int_t    *paint_extents)
{
    cairo_analysis_surface_t *surface = abstract_surface;
    cairo_status_t	     status, backend_status;
    cairo_rectangle_int_t  extents;
    cairo_bool_t is_empty;

    if (!surface->target->backend->paint)
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
    else
	backend_status = (*surface->target->backend->paint) (surface->target, op,
                                                             source, NULL);

    if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
	backend_status = _analyze_meta_surface_pattern (surface, source);

    status = _cairo_surface_get_extents (&surface->base, &extents);
    if (_cairo_status_is_error (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 (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &source_extents);
    }

    is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip);
    if (paint_extents)
	*paint_extents = extents;

    status = _add_operation (surface, &extents, backend_status);

    return status;
}
コード例 #21
0
static void
_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
					   cairo_operator_t op,
					   const cairo_pattern_t *source,
					   const cairo_clip_t *clip,
					   cairo_rectangle_int_t *extents)
{
    cairo_bool_t is_empty;

    is_empty = _cairo_surface_get_extents (&surface->base, extents);

    if (_cairo_operator_bounded_by_source (op)) {
	cairo_rectangle_int_t source_extents;

	_cairo_pattern_get_extents (source, &source_extents);
	_cairo_rectangle_intersect (extents, &source_extents);
    }

    _rectangle_intersect_clip (extents, clip);
}
コード例 #22
0
static cairo_surface_t *
_cairo_paginated_surface_snapshot (void *abstract_other)
{
    cairo_status_t status;
    cairo_paginated_surface_t *other = abstract_other;

    /* XXX: Just making a snapshot of other->meta is what we really
     * want. But this currently triggers a bug somewhere (the "mask"
     * test from the test suite segfaults).
     *
     * For now, we'll create a new image surface and replay onto
     * that. It would be tempting to replay into other->image and then
     * return a snapshot of that, but that will cause the self-copy
     * test to fail, (since our replay will be affected by a clip that
     * should not have any effect on the use of the resulting snapshot
     * as a source).
     */

#if 0
    return _cairo_surface_snapshot (other->meta);
#else
    cairo_rectangle_int16_t extents;
    cairo_surface_t *surface;

    status = _cairo_surface_get_extents (other->target, &extents);
    if (status)
	return (cairo_surface_t*) &_cairo_surface_nil;

    surface = _cairo_paginated_surface_create_image_surface (other,
							     extents.width,
							     extents.height);

    status = _cairo_meta_surface_replay (other->meta, surface);
    if (status) {
	cairo_surface_destroy (surface);
	surface = (cairo_surface_t*) &_cairo_surface_nil;
    }

    return surface;
#endif
}
コード例 #23
0
static cairo_status_t
_cairo_default_context_push_group (void *abstract_cr, cairo_content_t content)
{
    cairo_default_context_t *cr = abstract_cr;
    cairo_surface_t *group_surface;
    cairo_clip_t *clip;
    cairo_status_t status;

    clip = _cairo_gstate_get_clip (cr->gstate);
    if (_cairo_clip_is_all_clipped (clip)) {
        group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
        status = group_surface->status;
        if (unlikely (status))
            goto bail;
    } else {
        cairo_surface_t *parent_surface;
        cairo_rectangle_int_t extents;
        cairo_bool_t bounded, is_empty;

        parent_surface = _cairo_gstate_get_target (cr->gstate);

        if (unlikely (parent_surface->status))
            return parent_surface->status;
        if (unlikely (parent_surface->finished))
            return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);

        /* Get the extents that we'll use in creating our new group surface */
        bounded = _cairo_surface_get_extents (parent_surface, &extents);
        if (clip)
            /* XXX: This assignment just fixes a compiler warning? */
            is_empty = _cairo_rectangle_intersect (&extents,
                                                   _cairo_clip_get_extents (clip));

        if (!bounded) {
            /* XXX: Generic solution? */
            group_surface = cairo_recording_surface_create (content, NULL);
            extents.x = extents.y = 0;
        } else {
            group_surface = _cairo_surface_create_similar_solid (parent_surface,
                            content,
                            extents.width,
                            extents.height,
                            CAIRO_COLOR_TRANSPARENT);
        }
        status = group_surface->status;
        if (unlikely (status))
            goto bail;

        /* Set device offsets on the new surface so that logically it appears at
         * the same location on the parent surface -- when we pop_group this,
         * the source pattern will get fixed up for the appropriate target surface
         * device offsets, so we want to set our own surface offsets from /that/,
         * and not from the device origin. */
        cairo_surface_set_device_offset (group_surface,
                                         parent_surface->device_transform.x0 - extents.x,
                                         parent_surface->device_transform.y0 - extents.y);

        /* If we have a current path, we need to adjust it to compensate for
         * the device offset just applied. */
        _cairo_path_fixed_translate (cr->path,
                                     _cairo_fixed_from_int (-extents.x),
                                     _cairo_fixed_from_int (-extents.y));
    }

    /* create a new gstate for the redirect */
    status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
    if (unlikely (status))
        goto bail;

    status = _cairo_gstate_redirect_target (cr->gstate, group_surface);

bail:
    cairo_surface_destroy (group_surface);
    return status;
}
コード例 #24
0
static cairo_int_status_t
_cairo_analysis_surface_show_text_glyphs (void			    *abstract_surface,
					  cairo_operator_t	     op,
					  const cairo_pattern_t	    *source,
					  const char		    *utf8,
					  int			     utf8_len,
					  cairo_glyph_t		    *glyphs,
					  int			     num_glyphs,
					  const cairo_text_cluster_t *clusters,
					  int			     num_clusters,
					  cairo_text_cluster_flags_t cluster_flags,
					  cairo_scaled_font_t	    *scaled_font,
					  cairo_rectangle_int_t     *show_text_glyphs_extents)
{
    cairo_analysis_surface_t *surface = abstract_surface;
    cairo_status_t	     status, backend_status;
    cairo_rectangle_int_t    extents, glyph_extents;
    cairo_bool_t             is_empty;

    /* Adapted from _cairo_surface_show_glyphs */
    backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
    if (surface->target->backend->show_text_glyphs)
	backend_status = surface->target->backend->show_text_glyphs (surface->target, op,
								     source,
								     utf8, utf8_len,
								     glyphs, num_glyphs,
								     clusters, num_clusters, cluster_flags,
								     scaled_font, NULL);
    if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs) {
	int remaining_glyphs = num_glyphs;
	backend_status = surface->target->backend->show_glyphs (surface->target, op,
								source,
								glyphs, num_glyphs,
								scaled_font,
								&remaining_glyphs, NULL);
	glyphs += num_glyphs - remaining_glyphs;
	num_glyphs = remaining_glyphs;
	if (remaining_glyphs == 0)
	    backend_status = CAIRO_STATUS_SUCCESS;
    }

    if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
	backend_status = _analyze_meta_surface_pattern (surface, source);

    status = _cairo_surface_get_extents (&surface->base, &extents);
    if (_cairo_status_is_error (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 (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &source_extents);
    }

    is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip);

    if (_cairo_operator_bounded_by_mask (op)) {
	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
							  glyphs,
							  num_glyphs,
							  &glyph_extents);
	if (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
    }
    if (show_text_glyphs_extents)
	*show_text_glyphs_extents = extents;

    status = _add_operation (surface, &extents, backend_status);

    return status;
}
コード例 #25
0
ファイル: cairo-clip.c プロジェクト: Dirbaio/libgdiplus
static cairo_status_t
_cairo_clip_intersect_mask (cairo_clip_t      *clip,
			    cairo_traps_t     *traps,
			    cairo_antialias_t antialias,
			    cairo_surface_t   *target)
{
    cairo_pattern_union_t pattern;
    cairo_box_t extents;
    cairo_rectangle_int_t surface_rect, target_rect;
    cairo_surface_t *surface;
    cairo_status_t status;

    if (clip->all_clipped)
	return CAIRO_STATUS_SUCCESS;

    /* Represent the clip as a mask surface.  We create a new surface
     * the size of the intersection of the old mask surface and the
     * extents of the new clip path. */

    _cairo_traps_extents (traps, &extents);
    _cairo_box_round_to_rectangle (&extents, &surface_rect);

    if (clip->surface != NULL)
	_cairo_rectangle_intersect (&surface_rect, &clip->surface_rect);

    /* Intersect with the target surface rectangle so we don't use
     * more memory and time than we need to. */

    status = _cairo_surface_get_extents (target, &target_rect);
    if (!status)
	_cairo_rectangle_intersect (&surface_rect, &target_rect);

    if (surface_rect.width == 0 || surface_rect.height == 0) {
	surface = NULL;
	status = CAIRO_STATUS_SUCCESS;
	if (clip->surface != NULL)
	    cairo_surface_destroy (clip->surface);
	goto DONE;
    }

    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);
    /* The clipping operation should ideally be something like the following to
     * avoid having to do as many passes over the data

	if (clip->surface != NULL) {
	    _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
	} else {
	    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);
	}
	status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
						  &pattern.base,
						  surface,
						  antialias,
						  0, 0,
						  0, 0,
						  surface_rect.width,
						  surface_rect.height,
						  traps->traps,
						  traps->num_traps);

	However this operation is not accelerated by pixman

	I believe the best possible operation would probably an unbounded SRC
	operator.  Using SRC we could potentially avoid having to initialize
	the surface which would be ideal from an efficiency point of view.
	However, _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) is
	bounded by the mask.

    */

    surface = _cairo_surface_create_similar_solid (target,
						   CAIRO_CONTENT_ALPHA,
						   surface_rect.width,
						   surface_rect.height,
						   CAIRO_COLOR_TRANSPARENT,
						   &pattern.base);
    if (surface->status) {
	_cairo_pattern_fini (&pattern.base);
	return surface->status;
    }

    /* Render the new clipping path into the new mask surface. */

    _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y);

    status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD,
						  &pattern.base,
						  surface,
						  antialias,
						  0, 0,
						  0, 0,
						  surface_rect.width,
						  surface_rect.height,
						  traps->traps,
						  traps->num_traps);

    _cairo_pattern_fini (&pattern.base);

    if (status) {
	cairo_surface_destroy (surface);
	return status;
    }

    /* If there was a clip surface already, combine it with the new
     * mask surface using the IN operator, so we get the intersection
     * of the old and new clipping paths. */

    if (clip->surface != NULL) {
	_cairo_pattern_init_for_surface (&pattern.surface, clip->surface);

	status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
					   &pattern.base,
					   NULL,
					   surface,
					   surface_rect.x - clip->surface_rect.x,
					   surface_rect.y - clip->surface_rect.y,
					   0, 0,
					   0, 0,
					   surface_rect.width,
					   surface_rect.height);

	_cairo_pattern_fini (&pattern.base);

	if (status) {
	    cairo_surface_destroy (surface);
	    return status;
	}

	cairo_surface_destroy (clip->surface);
    }

 DONE:
    clip->surface = surface;
    clip->surface_rect = surface_rect;
    clip->serial = _cairo_surface_allocate_clip_serial (target);

    if (surface_rect.width == 0 || surface_rect.height == 0)
	_cairo_clip_set_all_clipped (clip, target);

    return status;
}
コード例 #26
0
static cairo_int_status_t
_cairo_analysis_surface_mask (void		*abstract_surface,
			      cairo_operator_t	 op,
			      const cairo_pattern_t	*source,
			      const cairo_pattern_t	*mask,
			      cairo_rectangle_int_t 	*mask_extents)
{
    cairo_analysis_surface_t *surface = abstract_surface;
    cairo_int_status_t	      status, backend_status;
    cairo_rectangle_int_t   extents;
    cairo_bool_t is_empty;

    if (!surface->target->backend->mask)
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
    else
	backend_status = (*surface->target->backend->mask) (surface->target, op,
                                                            source, mask, NULL);

    if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) {
	cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS;
	cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;

	if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
	    const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source;
	    if (_cairo_surface_is_meta (surface_pattern->surface)) {
		backend_source_status =
		    _analyze_meta_surface_pattern (surface, source);
		if (_cairo_status_is_error (backend_source_status))
		    return backend_source_status;
	    }
	}

	if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
	    if (_cairo_surface_is_meta (surface_pattern->surface)) {
		backend_mask_status =
		    _analyze_meta_surface_pattern (surface, mask);
		if (_cairo_status_is_error (backend_mask_status))
		    return backend_mask_status;
	    }
	}

	backend_status =
	    _cairo_analysis_surface_merge_status (backend_source_status,
						  backend_mask_status);
    }

    status = _cairo_surface_get_extents (&surface->base, &extents);
    if (_cairo_status_is_error (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 (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &source_extents);
    }

    if (_cairo_operator_bounded_by_mask (op)) {
	cairo_rectangle_int_t mask_extents;

	status = _cairo_pattern_get_extents (mask, &mask_extents);
	if (unlikely (status))
	    return status;

	is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
    }

    is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip);
    if (mask_extents)
	*mask_extents = extents;

    status = _add_operation (surface, &extents, backend_status);

    return status;
}
コード例 #27
0
ファイル: cairo-clip.c プロジェクト: Dirbaio/libgdiplus
cairo_rectangle_list_t *
_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
{
    cairo_rectangle_list_t *list;
    cairo_rectangle_t *rectangles = NULL;
    int n_boxes = 0;

    if (clip->all_clipped)
	goto DONE;

    if (clip->path || clip->surface) {
	_cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
	return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
    }

    if (clip->has_region) {
	cairo_box_int_t *boxes;
        int i;

	if (_cairo_region_get_boxes (&clip->region, &n_boxes, &boxes))
	    return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;

	if (n_boxes) {
	    rectangles = _cairo_malloc_ab (n_boxes, sizeof (cairo_rectangle_t));
	    if (rectangles == NULL) {
		_cairo_region_boxes_fini (&clip->region, boxes);
		_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
		return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
	    }

	    for (i = 0; i < n_boxes; ++i) {
		cairo_rectangle_int_t clip_rect = { boxes[i].p1.x, boxes[i].p1.y,
						    boxes[i].p2.x - boxes[i].p1.x,
						    boxes[i].p2.y - boxes[i].p1.y };

		if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) {
		    _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
		    _cairo_region_boxes_fini (&clip->region, boxes);
		    free (rectangles);
		    return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
		}
	    }
	}

	_cairo_region_boxes_fini (&clip->region, boxes);
    } else {
        cairo_rectangle_int_t extents;

	n_boxes = 1;

	rectangles = malloc(sizeof (cairo_rectangle_t));
	if (rectangles == NULL) {
	    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
	    return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
	}

	if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate), &extents) ||
	    !_cairo_clip_int_rect_to_user(gstate, &extents, rectangles))
	{
	    _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
	    free (rectangles);
	    return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
	}
    }

 DONE:
    list = malloc (sizeof (cairo_rectangle_list_t));
    if (list == NULL) {
	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
        free (rectangles);
        return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
    }

    list->status = CAIRO_STATUS_SUCCESS;
    list->rectangles = rectangles;
    list->num_rectangles = n_boxes;
    return list;
}
コード例 #28
0
/* Warning: This call modifies the coordinates of traps */
static cairo_status_t
_clip_and_composite_trapezoids (cairo_pattern_t *src,
				cairo_operator_t op,
				cairo_surface_t *dst,
				cairo_traps_t *traps,
				cairo_clip_t *clip,
				cairo_antialias_t antialias)
{
    cairo_status_t status;
    cairo_region_t trap_region;
    cairo_region_t clear_region;
    cairo_bool_t has_trap_region = FALSE;
    cairo_bool_t has_clear_region = FALSE;
    cairo_rectangle_int_t extents;
    cairo_composite_traps_info_t traps_info;

    if (traps->num_traps == 0)
        return CAIRO_STATUS_SUCCESS;

    status = _cairo_surface_get_extents (dst, &extents);

    if (status)
        return status;

    status = _cairo_traps_extract_region (traps, &trap_region);

    if (CAIRO_INT_STATUS_UNSUPPORTED == status) {
        has_trap_region = FALSE;
    } else if (status) {
        return status;
    } else {
        has_trap_region = TRUE;
    }

    if (_cairo_operator_bounded_by_mask (op)) {
        cairo_rectangle_int_t trap_extents;

        if (has_trap_region) {
            status = _cairo_clip_intersect_to_region (clip, &trap_region);

            if (status)
                goto out;

            _cairo_region_get_extents (&trap_region, &trap_extents);
        } else {
            cairo_box_t trap_box;
            _cairo_traps_extents (traps, &trap_box);
            _cairo_box_round_to_rectangle (&trap_box, &trap_extents);
        }

        _cairo_rectangle_intersect (&extents, &trap_extents);
        status = _cairo_clip_intersect_to_rectangle (clip, &extents);

        if (status)
            goto out;
    } else {
        cairo_surface_t *clip_surface = clip ? clip->surface : NULL;

        if (has_trap_region && !clip_surface) {
            /* 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_rect (&clear_region, &extents);

            has_clear_region = TRUE;
            status = _cairo_clip_intersect_to_region (clip, &clear_region);

            if (status)
                goto out;

            _cairo_region_get_extents (&clear_region, &extents);

            status = _cairo_region_subtract (&clear_region, &clear_region, &trap_region);
            if (status)
                goto out;

            if (!_cairo_region_not_empty (&clear_region)) {
                _cairo_region_fini (&clear_region);
                has_clear_region = FALSE;
            }
        } else {
            status = _cairo_clip_intersect_to_rectangle (clip, &extents);
        }
    }

    if (status)
        goto out;

    if (has_trap_region) {
        cairo_surface_t *clip_surface = clip ? clip->surface : NULL;

        if ((src->type == CAIRO_PATTERN_TYPE_SOLID ||
             op == CAIRO_OPERATOR_CLEAR) && !clip_surface) {
            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);

            if (!status && has_clear_region)
                status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR,
                                                     CAIRO_COLOR_TRANSPARENT,
                                                     &clear_region);

            goto out;
        }

        if ((_cairo_operator_bounded_by_mask (op) &&
             op != CAIRO_OPERATOR_SOURCE) || !clip_surface) {
            /* 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 it's 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 (status != CAIRO_INT_STATUS_UNSUPPORTED) {
                if (!status && has_clear_region)
                    status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR,
                                                         CAIRO_COLOR_TRANSPARENT,
                                                         &clear_region);
                goto out;
            }
        }
    }

    traps_info.traps = traps;
    traps_info.antialias = antialias;

    status = _clip_and_composite (clip, op, src,
                                  _composite_traps_draw_func,
                                  &traps_info, dst, &extents);

out:
    if (has_trap_region)
        _cairo_region_fini (&trap_region);
    if (has_clear_region)
        _cairo_region_fini (&clear_region);

    return status;
}
コード例 #29
0
ファイル: cairo-clip.c プロジェクト: manoj-makkuboy/magnetism
static cairo_status_t
_cairo_clip_intersect_mask (cairo_clip_t      *clip,
			    cairo_traps_t     *traps,
			    cairo_antialias_t antialias,
			    cairo_surface_t   *target)
{
    cairo_pattern_union_t pattern;
    cairo_box_t extents;
    cairo_rectangle_int16_t surface_rect, target_rect;
    cairo_surface_t *surface;
    cairo_status_t status;

    /* Represent the clip as a mask surface.  We create a new surface
     * the size of the intersection of the old mask surface and the
     * extents of the new clip path. */

    _cairo_traps_extents (traps, &extents);
    _cairo_box_round_to_rectangle (&extents, &surface_rect);

    if (clip->surface != NULL)
	_cairo_rectangle_intersect (&surface_rect, &clip->surface_rect);

    /* Intersect with the target surface rectangle so we don't use
     * more memory and time than we need to. */

    status = _cairo_surface_get_extents (target, &target_rect);
    if (!status)
	_cairo_rectangle_intersect (&surface_rect, &target_rect);

    surface = _cairo_surface_create_similar_solid (target,
						   CAIRO_CONTENT_ALPHA,
						   surface_rect.width,
						   surface_rect.height,
						   CAIRO_COLOR_WHITE);
    if (surface->status)
	return CAIRO_STATUS_NO_MEMORY;

    /* Render the new clipping path into the new mask surface. */

    _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y);
    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE);

    status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
						  &pattern.base,
						  surface,
						  antialias,
						  0, 0,
						  0, 0,
						  surface_rect.width,
						  surface_rect.height,
						  traps->traps,
						  traps->num_traps);

    _cairo_pattern_fini (&pattern.base);

    if (status) {
	cairo_surface_destroy (surface);
	return status;
    }

    /* If there was a clip surface already, combine it with the new
     * mask surface using the IN operator, so we get the intersection
     * of the old and new clipping paths. */

    if (clip->surface != NULL) {
	_cairo_pattern_init_for_surface (&pattern.surface, clip->surface);

	status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
					   &pattern.base,
					   NULL,
					   surface,
					   surface_rect.x - clip->surface_rect.x,
					   surface_rect.y - clip->surface_rect.y,
					   0, 0,
					   0, 0,
					   surface_rect.width,
					   surface_rect.height);

	_cairo_pattern_fini (&pattern.base);

	if (status) {
	    cairo_surface_destroy (surface);
	    return status;
	}

	cairo_surface_destroy (clip->surface);
    }

    clip->surface = surface;
    clip->surface_rect = surface_rect;
    clip->serial = _cairo_surface_allocate_clip_serial (target);

    return status;
}
コード例 #30
0
static cairo_int_status_t
_paint_page (cairo_paginated_surface_t *surface)
{
    cairo_surface_t *analysis;
    cairo_int_status_t status;
    cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;

    if (unlikely (surface->target->status))
	return (cairo_int_status_t)surface->target->status;

    analysis = _cairo_analysis_surface_create (surface->target);
    if (unlikely (analysis->status))
	return (cairo_int_status_t)_cairo_surface_set_error (surface->target, (cairo_int_status_t)analysis->status);

    surface->backend->set_paginated_mode (surface->target,
	                                  CAIRO_PAGINATED_MODE_ANALYZE);
    status = (cairo_int_status_t)_cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
								 analysis);
    if (status)
	goto FAIL;

    assert (analysis->status == CAIRO_STATUS_SUCCESS);

     if (surface->backend->set_bounding_box) {
	 cairo_box_t bbox;

	 _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
	 status = surface->backend->set_bounding_box (surface->target, &bbox);
	 if (unlikely (status))
	     goto FAIL;
     }

    if (surface->backend->set_fallback_images_required) {
	cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);

	status = surface->backend->set_fallback_images_required (surface->target,
								 has_fallbacks);
	if (unlikely (status))
	    goto FAIL;
    }

    /* Finer grained fallbacks are currently only supported for some
     * surface types */
    if (surface->backend->supports_fine_grained_fallbacks != NULL &&
	surface->backend->supports_fine_grained_fallbacks (surface->target))
    {
	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;
    }

    if (has_supported) {
	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_RENDER);

	status = (cairo_int_status_t)_cairo_recording_surface_replay_region (surface->recording_surface,
							 NULL,
							 surface->target,
							 CAIRO_RECORDING_REGION_NATIVE);
	assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
	if (unlikely (status))
	    goto FAIL;
    }

    if (has_page_fallback) {
	cairo_rectangle_int_t extents;
	cairo_bool_t is_bounded;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

	is_bounded = _cairo_surface_get_extents (surface->target, &extents);
	if (! is_bounded) {
	    status = CAIRO_INT_STATUS_UNSUPPORTED;
	    goto FAIL;
	}

	status = _paint_fallback_image (surface, &extents);
	if (unlikely (status))
	    goto FAIL;
    }

    if (has_finegrained_fallback) {
        cairo_region_t *region;
        int num_rects, i;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

	region = _cairo_analysis_surface_get_unsupported (analysis);

	num_rects = cairo_region_num_rectangles (region);
	for (i = 0; i < num_rects; i++) {
	    cairo_rectangle_int_t rect;

	    cairo_region_get_rectangle (region, i, &rect);
	    status = _paint_fallback_image (surface, &rect);
	    if (unlikely (status))
		goto FAIL;
	}
    }

  FAIL:
    cairo_surface_destroy (analysis);

    return _cairo_surface_set_error (surface->target, status);
}