/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
					      cairo_stroke_style_t *style,
					      const cairo_matrix_t *ctm,
					      cairo_rectangle_int_t *extents)
{
    cairo_path_bounder_t bounder;
    cairo_status_t status;

    _cairo_path_bounder_init (&bounder);

    status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
					  _cairo_path_bounder_move_to,
					  _cairo_path_bounder_line_to,
					  _cairo_path_bounder_curve_to,
					  _cairo_path_bounder_close_path,
					  &bounder);
    assert (status == CAIRO_STATUS_SUCCESS);

    if (bounder.has_point) {
	double dx, dy;

	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);

	bounder.extents.p1.x -= _cairo_fixed_from_double (dx);
	bounder.extents.p2.x += _cairo_fixed_from_double (dx);
	bounder.extents.p1.y -= _cairo_fixed_from_double (dy);
	bounder.extents.p2.y += _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else if (bounder.has_initial_point) {
	double dx, dy;

	/* accommodate capping of degenerate paths */

	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);

	bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
	bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
	bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
	bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }
}
cairo_status_t
_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path,
				  cairo_stroke_style_t *stroke_style,
				  const cairo_matrix_t *ctm,
				  const cairo_matrix_t *ctm_inverse,
				  double tolerance,
				  cairo_rectangle_int_t *extents)
{
    cairo_traps_t traps;
    cairo_box_t bbox;
    cairo_status_t status;

    _cairo_traps_init (&traps);

    status = _cairo_path_fixed_stroke_to_traps (path,
						stroke_style,
						ctm,
						ctm_inverse,
						tolerance,
						&traps);

    _cairo_traps_extents (&traps, &bbox);
    _cairo_traps_fini (&traps);

    _cairo_box_round_to_rectangle (&bbox, extents);

    return status;
}
Ejemplo n.º 3
0
/* A slightly better approximation than above - we actually decompose the
 * Bezier, but we continue to ignore winding.
 */
void
_cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path,
					    cairo_rectangle_int_t *extents)
{
    cairo_path_bounder_t bounder;
    cairo_status_t status;

    _cairo_path_bounder_init (&bounder);

    status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
					  _cairo_path_bounder_move_to,
					  _cairo_path_bounder_line_to,
					  _cairo_path_bounder_curve_to,
					  _cairo_path_bounder_close_path,
					  &bounder);
    assert (status == CAIRO_STATUS_SUCCESS);

    if (bounder.has_point) {
	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }

    _cairo_path_bounder_fini (&bounder);
}
void
_cairo_path_fixed_fill_extents (const cairo_path_fixed_t	*path,
				cairo_fill_rule_t	 fill_rule,
				double			 tolerance,
				cairo_rectangle_int_t	*extents)
{
    cairo_path_bounder_t bounder;
    cairo_status_t status;

    _cairo_path_bounder_init (&bounder);

    status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD,
					       _cairo_path_bounder_move_to,
					       _cairo_path_bounder_line_to,
					       _cairo_path_bounder_close_path,
					       &bounder, tolerance);
    assert (status == CAIRO_STATUS_SUCCESS);

    if (bounder.has_point) {
	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }
}
Ejemplo n.º 5
0
cairo_int_status_t
_cairo_rasterise_polygon_to_traps (cairo_polygon_t			*polygon,
				   cairo_fill_rule_t			 fill_rule,
				   cairo_antialias_t			 antialias,
				   cairo_traps_t *traps)
{
    struct cairo_trap_renderer renderer;
    cairo_scan_converter_t *converter;
    cairo_int_status_t status;
    cairo_rectangle_int_t r;

    TRACE ((stderr, "%s: fill_rule=%d, antialias=%d\n",
	    __FUNCTION__, fill_rule, antialias));
    assert(antialias == CAIRO_ANTIALIAS_NONE);

    renderer.traps = traps;
    renderer.base.render_rows = span_to_traps;

    _cairo_box_round_to_rectangle (&polygon->extents, &r);
    converter = _cairo_mono_scan_converter_create (r.x, r.y,
						   r.x + r.width,
						   r.y + r.height,
						   fill_rule);
    status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
	status = converter->generate (converter, &renderer.base);
    converter->destroy (converter);
    return status;
}
Ejemplo n.º 6
0
cairo_status_t
_cairo_rasterise_polygon_to_boxes (cairo_polygon_t			*polygon,
				   cairo_fill_rule_t			 fill_rule,
				   cairo_boxes_t *boxes)
{
    struct cairo_box_renderer renderer;
    cairo_scan_converter_t *converter;
    cairo_int_status_t status;
    cairo_rectangle_int_t r;

    TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule));

    _cairo_box_round_to_rectangle (&polygon->extents, &r);
    converter = _cairo_mono_scan_converter_create (r.x, r.y,
						   r.x + r.width,
						   r.y + r.height,
						   fill_rule);
    status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
    if (unlikely (status))
	goto cleanup_converter;

    renderer.boxes = boxes;
    renderer.base.render_rows = span_to_boxes;

    status = converter->generate (converter, &renderer.base);
cleanup_converter:
    converter->destroy (converter);
    return status;
}
Ejemplo n.º 7
0
static cairo_int_status_t
_scissor_and_clip (cairo_gl_context_t		*ctx,
		   cairo_gl_composite_t		*setup,
		   cairo_composite_rectangles_t	*composite,
		   cairo_bool_t			*used_stencil_buffer)
{
    cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
    cairo_rectangle_int_t *bounds = &composite->unbounded;
    cairo_clip_t *clip = composite->clip;
    cairo_rectangle_int_t r;

    *used_stencil_buffer = FALSE;

    if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) {
	_scissor_to_rectangle (dst, bounds);
	return CAIRO_INT_STATUS_SUCCESS;
    }

    /* If we cannot reduce the clip to a rectangular region,
       we scissor and clip using the stencil buffer */
    if (clip->num_boxes > 1 || clip->path != NULL) {
	*used_stencil_buffer = TRUE;
	_scissor_to_rectangle (dst, bounds);
	return _draw_clip_to_stencil_buffer (ctx, setup, clip);
    }

    /* Always interpret the clip path as ANTIALIAS_NONE */
    _cairo_box_round_to_rectangle (clip->boxes, &r);
    _scissor_to_rectangle (dst, &r);
    return CAIRO_INT_STATUS_SUCCESS;
}
Ejemplo n.º 8
0
static cairo_status_t
_cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t       *clip_path,
				         cairo_rectangle_int_t   *rectangle)
{
    while (clip_path) {
        cairo_status_t status;
        cairo_traps_t traps;
        cairo_box_t extents;
        cairo_rectangle_int_t extents_rect;

        _cairo_traps_init (&traps);

        status = _cairo_path_fixed_fill_to_traps (&clip_path->path,
                                                  clip_path->fill_rule,
                                                  clip_path->tolerance,
                                                  &traps);
        if (status) {
            _cairo_traps_fini (&traps);
            return status;
        }

        _cairo_traps_extents (&traps, &extents);
        _cairo_box_round_to_rectangle (&extents, &extents_rect);
        _cairo_rectangle_intersect (rectangle, &extents_rect);

        _cairo_traps_fini (&traps);

        clip_path = clip_path->prev;
    }

    return CAIRO_STATUS_SUCCESS;
}
Ejemplo n.º 9
0
cairo_status_t
_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t	*path,
				  const cairo_stroke_style_t	*stroke_style,
				  const cairo_matrix_t		*ctm,
				  const cairo_matrix_t		*ctm_inverse,
				  double			 tolerance,
				  cairo_rectangle_int_t		*extents)
{
    cairo_polygon_t polygon;
    cairo_status_t status;
    cairo_stroke_style_t style;

    /* When calculating extents for vector surfaces, ensure lines thinner
     * than the fixed point resolution are not optimized away. */
    double min_line_width = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2);
    if (stroke_style->line_width < min_line_width)
    {
	style = *stroke_style;
	style.line_width = min_line_width;
	stroke_style = &style;
    }

    _cairo_polygon_init (&polygon, NULL, 0);
    status = _cairo_path_fixed_stroke_to_polygon (path,
						  stroke_style,
						  ctm, ctm_inverse,
						  tolerance,
						  &polygon);
    _cairo_box_round_to_rectangle (&polygon.extents, extents);
    _cairo_polygon_fini (&polygon);

    return status;
}
Ejemplo n.º 10
0
cairo_bool_t
_cairo_clip_contains_box (const cairo_clip_t *clip,
			  const cairo_box_t *box)
{
    cairo_rectangle_int_t rect;

    _cairo_box_round_to_rectangle (box, &rect);
    return _cairo_clip_contains_rectangle_box(clip, &rect, box);
}
Ejemplo n.º 11
0
cairo_clip_t *
_cairo_clip_intersect_box (cairo_clip_t *clip,
			   const cairo_box_t *box)
{
    cairo_rectangle_int_t r;

    _cairo_box_round_to_rectangle (box, &r);
    if (r.width == 0 || r.height == 0)
	return _cairo_clip_set_all_clipped (clip);

    return _cairo_clip_intersect_rectangle_box (clip, &r, box);
}
Ejemplo n.º 12
0
cairo_status_t
cairo_xml_for_recording_surface (cairo_device_t	 *device,
				 cairo_surface_t *recording_surface)
{
    cairo_box_t bbox;
    cairo_rectangle_int_t extents;
    cairo_surface_t *surface;
    cairo_xml_t *xml;
    cairo_status_t status;

    if (unlikely (device->status))
	return device->status;

    if (unlikely (recording_surface->status))
	return recording_surface->status;

    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
	return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);

    if (unlikely (! _cairo_surface_is_recording (recording_surface)))
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);

    status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
						&bbox, NULL);
    if (unlikely (status))
	return status;

    _cairo_box_round_to_rectangle (&bbox, &extents);
    surface = _cairo_xml_surface_create_internal (device,
						  recording_surface->content,
						  extents.width,
						  extents.height);
    if (unlikely (surface->status))
	return surface->status;

    xml = (cairo_xml_t *) device;

    _cairo_xml_printf (xml,
		       "<surface content='%s' width='%d' height='%d'>",
		       _content_to_string (recording_surface->content),
		       extents.width, extents.height);
    _cairo_xml_indent (xml, 2);

    cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
    status = _cairo_recording_surface_replay (recording_surface, surface);
    cairo_surface_destroy (surface);

    _cairo_xml_indent (xml, -2);
    _cairo_xml_printf (xml, "</surface>");

    return status;
}
Ejemplo n.º 13
0
void
_cairo_path_fixed_fill_extents (const cairo_path_fixed_t	*path,
                                cairo_fill_rule_t	 fill_rule,
                                double			 tolerance,
                                cairo_rectangle_int_t	*extents)
{
    if (path->extents.p1.x < path->extents.p2.x &&
            path->extents.p1.y < path->extents.p2.y) {
        _cairo_box_round_to_rectangle (&path->extents, extents);
    } else {
        extents->x = extents->y = 0;
        extents->width = extents->height = 0;
    }
}
Ejemplo n.º 14
0
cairo_int_status_t
_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
					      cairo_surface_t		*surface,
					      cairo_operator_t		 op,
					      const cairo_pattern_t	*source,
					      const cairo_polygon_t	*polygon,
					      const cairo_clip_t		*clip)
{
    if (! _cairo_composite_rectangles_init (extents,
					    surface, op, source, clip))
    {
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
    }

    _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
    return _cairo_composite_rectangles_intersect (extents, clip);
}
Ejemplo n.º 15
0
cairo_int_status_t
_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
					      cairo_surface_t		*surface,
					      cairo_operator_t		 op,
					      const cairo_pattern_t	*source,
					      const cairo_boxes_t	*boxes,
					      const cairo_clip_t		*clip)
{
    cairo_box_t box;

    if (! _cairo_composite_rectangles_init (extents,
					    surface, op, source, clip))
    {
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
    }

    _cairo_boxes_extents (boxes, &box);
    _cairo_box_round_to_rectangle (&box, &extents->mask);
    return _cairo_composite_rectangles_intersect (extents, clip);
}
Ejemplo n.º 16
0
cairo_status_t
_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t	*path,
                                  const cairo_stroke_style_t	*stroke_style,
                                  const cairo_matrix_t		*ctm,
                                  const cairo_matrix_t		*ctm_inverse,
                                  double			 tolerance,
                                  cairo_rectangle_int_t		*extents)
{
    cairo_polygon_t polygon;
    cairo_status_t status;

    _cairo_polygon_init (&polygon, NULL, 0);
    status = _cairo_path_fixed_stroke_to_polygon (path,
             stroke_style,
             ctm, ctm_inverse,
             tolerance,
             &polygon);
    _cairo_box_round_to_rectangle (&polygon.extents, extents);
    _cairo_polygon_fini (&polygon);

    return status;
}
Ejemplo n.º 17
0
/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
					      const cairo_stroke_style_t *style,
					      const cairo_matrix_t *ctm,
					      cairo_bool_t is_vector,
					      cairo_rectangle_int_t *extents)
{
    if (path->has_extents) {
	cairo_box_t box_extents;
	double dx, dy;

	_cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
	if (is_vector)
	{
	    /* When calculating extents for vector surfaces, ensure lines thinner
	     * than the fixed point resolution are not optimized away. */
	    double min = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2);
	    if (dx < min)
		dx = min;

	    if (dy < min)
		dy = min;
	}

	box_extents = path->extents;
	box_extents.p1.x -= _cairo_fixed_from_double (dx);
	box_extents.p1.y -= _cairo_fixed_from_double (dy);
	box_extents.p2.x += _cairo_fixed_from_double (dx);
	box_extents.p2.y += _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&box_extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }
}
Ejemplo n.º 18
0
/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
        const cairo_stroke_style_t *style,
        const cairo_matrix_t *ctm,
        cairo_rectangle_int_t *extents)
{
    if (path->has_extents) {
        cairo_box_t box_extents;
        double dx, dy;

        _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);

        box_extents = path->extents;
        box_extents.p1.x -= _cairo_fixed_from_double (dx);
        box_extents.p1.y -= _cairo_fixed_from_double (dy);
        box_extents.p2.x += _cairo_fixed_from_double (dx);
        box_extents.p2.y += _cairo_fixed_from_double (dy);

        _cairo_box_round_to_rectangle (&box_extents, extents);
    } else {
        extents->x = extents->y = 0;
        extents->width = extents->height = 0;
    }
}
Ejemplo n.º 19
0
cairo_clip_t *
_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
{
    cairo_box_t extents;
    cairo_clip_t *clip = _cairo_clip_create ();
    if (clip == NULL)
	return _cairo_clip_set_all_clipped (clip);

    /* XXX cow-boxes? */
    if(boxes->num_boxes == 1) {
	clip->boxes = &clip->embedded_box;
	clip->boxes[0] = boxes->chunks.base[0];
	clip->num_boxes = 1;
    } else {
	clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
	if (clip->boxes == NULL)
	    return _cairo_clip_set_all_clipped (clip);
    }

    _cairo_boxes_extents (boxes, &extents);
    _cairo_box_round_to_rectangle (&extents, &clip->extents);

    return clip;
}
Ejemplo n.º 20
0
cairo_int_status_t
_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
						    const cairo_box_t *box)
{
    cairo_rectangle_int_t mask;
    cairo_clip_t *clip;

    _cairo_box_round_to_rectangle (box, &mask);
    if (mask.x == extents->mask.x &&
	mask.y == extents->mask.y &&
	mask.width  == extents->mask.width &&
	mask.height == extents->mask.height)
    {
	return CAIRO_INT_STATUS_SUCCESS;
    }

    _cairo_rectangle_intersect (&extents->mask, &mask);

    mask = extents->bounded;
    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
	extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
	return CAIRO_INT_STATUS_NOTHING_TO_DO;

    if (mask.width  == extents->bounded.width &&
	mask.height == extents->bounded.height)
	return CAIRO_INT_STATUS_SUCCESS;

    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
	extents->unbounded = extents->bounded;
    } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
	if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
    }

    clip = extents->clip;
    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
    if (clip != extents->clip)
	_cairo_clip_destroy (clip);

    if (_cairo_clip_is_all_clipped (extents->clip))
	return CAIRO_INT_STATUS_NOTHING_TO_DO;

    if (! _cairo_rectangle_intersect (&extents->unbounded,
				      _cairo_clip_get_extents (extents->clip)))
	return CAIRO_INT_STATUS_NOTHING_TO_DO;

    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
	_cairo_pattern_sampled_area (&extents->source_pattern.base,
				     &extents->bounded,
				     &extents->source_sample_area);
    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
	_cairo_pattern_sampled_area (&extents->mask_pattern.base,
				     &extents->bounded,
				     &extents->mask_sample_area);
	if (extents->mask_sample_area.width == 0 ||
	    extents->mask_sample_area.height == 0)
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
    }

    return CAIRO_INT_STATUS_SUCCESS;
}
Ejemplo n.º 21
0
static cairo_clip_t *
_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
				     const cairo_rectangle_int_t *r,
				     const cairo_box_t *box)
{
    cairo_box_t extents_box;
    cairo_bool_t changed = FALSE;
    int i, j;

    if (clip == NULL) {
	clip = _cairo_clip_create ();
	if (clip == NULL)
	    return _cairo_clip_set_all_clipped (clip);
    }

    if (clip->num_boxes == 0) {
	clip->boxes = &clip->embedded_box;
	clip->boxes[0] = *box;
	clip->num_boxes = 1;
	if (clip->path == NULL) {
	    clip->extents = *r;
	} else {
	    if (! _cairo_rectangle_intersect (&clip->extents, r))
		clip = _cairo_clip_set_all_clipped (clip);
	}
	if (clip->path == NULL)
	    clip->is_region = _cairo_box_is_pixel_aligned (box);
	return clip;
    }

    /* Does the new box wholly subsume the clip? Perform a cheap check
     * for the common condition of a single clip rectangle.
     */
    if (clip->num_boxes == 1 &&
	clip->boxes[0].p1.x >= box->p1.x &&
	clip->boxes[0].p1.y >= box->p1.y &&
	clip->boxes[0].p2.x <= box->p2.x &&
	clip->boxes[0].p2.y <= box->p2.y)
    {
	return clip;
    }

    for (i = j = 0; i < clip->num_boxes; i++) {
	cairo_box_t *b = &clip->boxes[j];

	if (j != i)
	    *b = clip->boxes[i];

	if (box->p1.x > b->p1.x)
	    b->p1.x = box->p1.x, changed = TRUE;
	if (box->p2.x < b->p2.x)
	    b->p2.x = box->p2.x, changed = TRUE;

	if (box->p1.y > b->p1.y)
	    b->p1.y = box->p1.y, changed = TRUE;
	if (box->p2.y < b->p2.y)
	    b->p2.y = box->p2.y, changed = TRUE;

	j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
    }
    clip->num_boxes = j;

    if (clip->num_boxes == 0)
	return _cairo_clip_set_all_clipped (clip);

    if (! changed)
	return clip;

    extents_box = clip->boxes[0];
    for (i = 1; i < clip->num_boxes; i++) {
	    if (clip->boxes[i].p1.x < extents_box.p1.x)
		extents_box.p1.x = clip->boxes[i].p1.x;

	    if (clip->boxes[i].p1.y < extents_box.p1.y)
		extents_box.p1.y = clip->boxes[i].p1.y;

	    if (clip->boxes[i].p2.x > extents_box.p2.x)
		extents_box.p2.x = clip->boxes[i].p2.x;

	    if (clip->boxes[i].p2.y > extents_box.p2.y)
		extents_box.p2.y = clip->boxes[i].p2.y;
    }

    if (clip->path == NULL) {
	_cairo_box_round_to_rectangle (&extents_box, &clip->extents);
    } else {
	cairo_rectangle_int_t extents_rect;

	_cairo_box_round_to_rectangle (&extents_box, &extents_rect);
	if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
	    return _cairo_clip_set_all_clipped (clip);
    }

    if (clip->region) {
	cairo_region_destroy (clip->region);
	clip->region = NULL;
    }

    clip->is_region = FALSE;
    return clip;
}
Ejemplo n.º 22
0
cairo_clip_t *
_cairo_clip_intersect_boxes (cairo_clip_t *clip,
			     const cairo_boxes_t *boxes)
{
    cairo_boxes_t clip_boxes;
    cairo_box_t limits;
    cairo_rectangle_int_t extents;

    if (_cairo_clip_is_all_clipped (clip))
	return clip;

    if (boxes->num_boxes == 0)
	return _cairo_clip_set_all_clipped (clip);

    if (boxes->num_boxes == 1)
	return _cairo_clip_intersect_box (clip, boxes->chunks.base);

    if (clip == NULL)
	clip = _cairo_clip_create ();

    if (clip->num_boxes) {
	_cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
	if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
	    clip = _cairo_clip_set_all_clipped (clip);
	    goto out;
	}

	if (clip->boxes != &clip->embedded_box)
	    free (clip->boxes);

	clip->boxes = NULL;
	boxes = &clip_boxes;
    }

    if (boxes->num_boxes == 0) {
	clip = _cairo_clip_set_all_clipped (clip);
	goto out;
    } else if (boxes->num_boxes == 1) {
	clip->boxes = &clip->embedded_box;
	clip->boxes[0] = boxes->chunks.base[0];
	clip->num_boxes = 1;
    } else {
	clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
    }
    _cairo_boxes_extents (boxes, &limits);

    _cairo_box_round_to_rectangle (&limits, &extents);
    if (clip->path == NULL)
	clip->extents = extents;
    else if (! _cairo_rectangle_intersect (&clip->extents, &extents))
	clip = _cairo_clip_set_all_clipped (clip);

    if (clip->region) {
	cairo_region_destroy (clip->region);
	clip->region = NULL;
    }
    clip->is_region = FALSE;

out:
    if (boxes == &clip_boxes)
	_cairo_boxes_fini (&clip_boxes);

    return clip;
}
Ejemplo n.º 23
0
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;
}
Ejemplo n.º 24
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;
}
Ejemplo n.º 25
0
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;
}
Ejemplo n.º 26
0
static cairo_int_status_t
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
				    const cairo_pattern_t    *pattern,
				    cairo_rectangle_int_t    *extents)
{
    const cairo_surface_pattern_t *surface_pattern;
    cairo_analysis_surface_t *tmp;
    cairo_surface_t *source, *proxy;
    cairo_matrix_t p2d;
    cairo_int_status_t status, analysis_status;
    cairo_bool_t surface_is_unbounded;
    cairo_bool_t unused;

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

    proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
    if (proxy != NULL) {
	/* nothing untoward found so far */
	return CAIRO_STATUS_SUCCESS;
    }

    tmp = (cairo_analysis_surface_t *)
	_cairo_analysis_surface_create (surface->target);
    if (unlikely (tmp->base.status)) {
	status =tmp->base.status;
	goto cleanup1;
    }
    proxy = attach_proxy (source, &tmp->base);

    p2d = pattern->matrix;
    status = cairo_matrix_invert (&p2d);
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    _cairo_analysis_surface_set_ctm (&tmp->base, &p2d);


    source = _cairo_surface_get_source (source, NULL);
    surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT
				     || pattern->extend == CAIRO_EXTEND_REFLECT);
    status = _cairo_recording_surface_replay_and_create_regions (source,
								 &pattern->matrix,
								 &tmp->base,
								 surface_is_unbounded);
    if (unlikely (status))
	goto cleanup2;

    /* black background or mime data fills entire extents */
    if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) {
	cairo_rectangle_int_t rect;

	if (_cairo_surface_get_extents (source, &rect)) {
	    cairo_box_t bbox;

	    _cairo_box_from_rectangle (&bbox, &rect);
	    _cairo_matrix_transform_bounding_box_fixed (&p2d, &bbox, NULL);
	    _cairo_box_round_to_rectangle (&bbox, &rect);
	    status = _add_operation (tmp, &rect, CAIRO_INT_STATUS_SUCCESS);
	    if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
		status = CAIRO_INT_STATUS_SUCCESS;
	    if (unlikely (status))
		goto cleanup2;
	}
    }

    if (tmp->has_supported) {
	surface->has_supported = TRUE;
	unused = cairo_region_union (&surface->supported_region, &tmp->supported_region);
    }

    if (tmp->has_unsupported) {
	surface->has_unsupported = TRUE;
	unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region);
    }

    analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
    if (pattern->extend != CAIRO_EXTEND_NONE) {
	_cairo_unbounded_rectangle_init (extents);
    } else {
	status = cairo_matrix_invert (&tmp->ctm);
	_cairo_matrix_transform_bounding_box_fixed (&tmp->ctm,
						    &tmp->page_bbox, NULL);
	_cairo_box_round_to_rectangle (&tmp->page_bbox, extents);
    }

  cleanup2:
    detach_proxy (proxy);
  cleanup1:
    cairo_surface_destroy (&tmp->base);

    if (unlikely (status))
	return status;

    return analysis_status;
}
Ejemplo n.º 27
0
static cairo_int_status_t
_add_operation  (cairo_analysis_surface_t *surface,
		 cairo_rectangle_int_t    *rect,
		 cairo_int_status_t        backend_status)
{
    cairo_int_status_t status;
    cairo_box_t bbox;

    if (rect->width == 0 || rect->height == 0) {
	/* Even though the operation is not visible we must be careful
	 * to not allow unsupported operations to be replayed to the
	 * backend during CAIRO_PAGINATED_MODE_RENDER */
	if (backend_status == CAIRO_STATUS_SUCCESS ||
	    backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
	{
	    return CAIRO_STATUS_SUCCESS;
	}
	else
	{
	    return CAIRO_INT_STATUS_IMAGE_FALLBACK;
	}
    }

    _cairo_box_from_rectangle (&bbox, rect);

    if (surface->has_ctm) {

	_cairo_matrix_transform_bounding_box_fixed (&surface->ctm, &bbox, NULL);

	if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
	    /* Even though the operation is not visible we must be
	     * careful to not allow unsupported operations to be
	     * replayed to the backend during
	     * CAIRO_PAGINATED_MODE_RENDER */
	    if (backend_status == CAIRO_STATUS_SUCCESS ||
		backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
	    {
		return CAIRO_STATUS_SUCCESS;
	    }
	    else
	    {
		return CAIRO_INT_STATUS_IMAGE_FALLBACK;
	    }
	}

	_cairo_box_round_to_rectangle (&bbox, rect);
    }

    if (surface->first_op) {
	surface->first_op = FALSE;
	surface->page_bbox = bbox;
    } else {
	if (bbox.p1.x < surface->page_bbox.p1.x)
	    surface->page_bbox.p1.x = bbox.p1.x;
	if (bbox.p1.y < surface->page_bbox.p1.y)
	    surface->page_bbox.p1.y = bbox.p1.y;
	if (bbox.p2.x > surface->page_bbox.p2.x)
	    surface->page_bbox.p2.x = bbox.p2.x;
	if (bbox.p2.y > surface->page_bbox.p2.y)
	    surface->page_bbox.p2.y = bbox.p2.y;
    }

    /* If the operation is completely enclosed within the fallback
     * region there is no benefit in emitting a native operation as
     * the fallback image will be painted on top.
     */
    if (_cairo_region_contains_rectangle (&surface->fallback_region, rect) == PIXMAN_REGION_IN)
	return CAIRO_INT_STATUS_IMAGE_FALLBACK;

    if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
	/* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
	 * that the backend only supports this operation if the
	 * transparency removed. If the extents of this operation does
	 * not intersect any other native operation, the operation is
	 * natively supported and the backend will blend the
	 * transparency into the white background.
	 */
	if (_cairo_region_contains_rectangle (&surface->supported_region, rect) == PIXMAN_REGION_OUT)
	    backend_status = CAIRO_STATUS_SUCCESS;
    }

    if (backend_status == CAIRO_STATUS_SUCCESS) {
	/* Add the operation to the supported region. Operations in
	 * this region will be emitted as native operations.
	 */
	surface->has_supported = TRUE;
	status = _cairo_region_union_rect (&surface->supported_region,
					   &surface->supported_region,
					   rect);
	return status;
    }

    /* Add the operation to the unsupported region. This region will
     * be painted as an image after all native operations have been
     * emitted.
     */
    surface->has_unsupported = TRUE;
    status = _cairo_region_union_rect (&surface->fallback_region,
				       &surface->fallback_region,
				       rect);

    /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
     * unsupported operations to the meta surface as using
     * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
     * invoke the cairo-surface-fallback path then return
     * CAIRO_STATUS_SUCCESS.
     */
    if (status == CAIRO_STATUS_SUCCESS)
	return CAIRO_INT_STATUS_IMAGE_FALLBACK;
    else
	return status;
}
Ejemplo n.º 28
0
static cairo_int_status_t
_cairo_analysis_surface_fill (void			*abstract_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_analysis_surface_t *surface = abstract_surface;
    cairo_status_t	     status, backend_status;
    cairo_traps_t            traps;
    cairo_rectangle_int_t  extents;

    if (!surface->target->backend->fill)
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
    else
	backend_status = (*surface->target->backend->fill) (surface->target, op,
						    source, path, fill_rule,
						    tolerance, antialias);

    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 (status && status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;

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

	_cairo_rectangle_intersect (&extents, &source_extents);
    }

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

    if (_cairo_operator_bounded_by_mask (op)) {
	cairo_box_t box;

	_cairo_box_from_rectangle (&box, &extents);

	_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;
	}

	_cairo_traps_extents (&traps, &box);
	_cairo_traps_fini (&traps);

        _cairo_box_round_to_rectangle (&box, &extents);
    }

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

    return status;
}