static cairo_status_t
_cairo_default_context_paint_with_alpha (void *abstract_cr,
        double alpha)
{
    cairo_default_context_t *cr = abstract_cr;
    cairo_solid_pattern_t pattern;
    cairo_status_t status;
    cairo_color_t color;

    if (CAIRO_ALPHA_IS_OPAQUE (alpha))
        return _cairo_gstate_paint (cr->gstate);

    if (CAIRO_ALPHA_IS_ZERO (alpha) &&
            _cairo_operator_bounded_by_mask (cr->gstate->op)) {
        return CAIRO_STATUS_SUCCESS;
    }

    _cairo_color_init_rgba (&color, 0., 0., 0., alpha);
    _cairo_pattern_init_solid (&pattern, &color);

    status = _cairo_gstate_mask (cr->gstate, &pattern.base);
    _cairo_pattern_fini (&pattern.base);

    return status;
}
static cairo_status_t
_cairo_surface_old_show_glyphs_draw_func (void                          *closure,
					  cairo_operator_t               op,
					  cairo_pattern_t               *src,
					  cairo_surface_t               *dst,
					  int                            dst_x,
					  int                            dst_y,
					  const cairo_rectangle_int_t *extents)
{
    cairo_show_glyphs_info_t *glyph_info = closure;
    cairo_pattern_union_t pattern;
    cairo_status_t status;

    /* Modifying the glyph array is fine because we know that this function
     * will be called only once, and we've already made a copy of the
     * glyphs in the wrapper.
     */
    if (dst_x != 0 || dst_y != 0) {
	int i;

	for (i = 0; i < glyph_info->num_glyphs; ++i)
	{
	    ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x;
	    ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y;
	}
    }

    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);
    if (!src)
	src = &pattern.base;

    status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
					     dst,
					     extents->x, extents->y,
					     extents->x - dst_x,
					     extents->y - dst_y,
					     extents->width,
					     extents->height,
					     glyph_info->glyphs,
					     glyph_info->num_glyphs);

    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;

    status = _cairo_scaled_font_show_glyphs (glyph_info->font,
					     op,
					     src, dst,
					     extents->x,         extents->y,
					     extents->x - dst_x,
					     extents->y - dst_y,
					     extents->width,     extents->height,
					     glyph_info->glyphs,
					     glyph_info->num_glyphs);

    if (src == &pattern.base)
	_cairo_pattern_fini (&pattern.base);

    return status;
}
cairo_int_status_t
_cairo_gl_surface_fill_rectangles (void			   *abstract_dst,
				   cairo_operator_t	    op,
				   const cairo_color_t     *color,
				   cairo_rectangle_int_t   *rects,
				   int			    num_rects)
{
    cairo_gl_surface_t *dst = abstract_dst;
    cairo_solid_pattern_t solid;
    cairo_gl_context_t *ctx;
    cairo_status_t status;
    cairo_gl_composite_t setup;
    int i;

    status = _cairo_gl_surface_deferred_clear (dst);
    if (unlikely (status))
	    return status;

    status = _cairo_gl_composite_init (&setup, op, dst,
                                       FALSE,
                                       /* XXX */ NULL);
    if (unlikely (status))
        goto CLEANUP;

    _cairo_pattern_init_solid (&solid, color);
    status = _cairo_gl_composite_set_source (&setup, &solid.base,
                                             0, 0,
                                             0, 0,
                                             0, 0,
                                             FALSE);
    if (unlikely (status))
        goto CLEANUP;

    status = _cairo_gl_composite_set_mask (&setup, NULL,
                                           0, 0,
                                           0, 0,
                                           0, 0);
    if (unlikely (status))
        goto CLEANUP;

    status = _cairo_gl_composite_begin (&setup, &ctx);
    if (unlikely (status))
        goto CLEANUP;

    for (i = 0; i < num_rects; i++) {
        _cairo_gl_composite_emit_rect (ctx,
                                       rects[i].x,
                                       rects[i].y,
                                       rects[i].x + rects[i].width,
                                       rects[i].y + rects[i].height,
                                       0);
    }

    status = _cairo_gl_context_release (ctx, status);

  CLEANUP:
    _cairo_gl_composite_fini (&setup);

    return status;
}
static cairo_status_t
_composite_traps_draw_func (void                          *closure,
			    cairo_operator_t               op,
			    cairo_pattern_t               *src,
			    cairo_surface_t               *dst,
			    int                            dst_x,
			    int                            dst_y,
			    const cairo_rectangle_int_t   *extents)
{
    cairo_composite_traps_info_t *info = closure;
    cairo_pattern_union_t pattern;
    cairo_status_t status;

    if (dst_x != 0 || dst_y != 0)
	_cairo_traps_translate (info->traps, - dst_x, - dst_y);

    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);
    if (!src)
	src = &pattern.base;

    status = _cairo_surface_composite_trapezoids (op,
						  src, dst, info->antialias,
						  extents->x,         extents->y,
						  extents->x - dst_x, extents->y - dst_y,
						  extents->width,     extents->height,
						  info->traps->traps,
						  info->traps->num_traps);
    _cairo_pattern_fini (&pattern.base);

    return status;
}
/**
 * _clip_and_composite:
 * @clip: a #cairo_clip_t
 * @op: the operator to draw with
 * @src: source pattern
 * @draw_func: function that can be called to draw with the mask onto a surface.
 * @draw_closure: data to pass to @draw_func.
 * @dst: destination surface
 * @extents: rectangle holding a bounding box for the operation; this
 *           rectangle will be used as the size for the temporary
 *           surface.
 *
 * When there is a surface clip, we typically need to create an intermediate
 * surface. This function handles the logic of creating a temporary surface
 * drawing to it, then compositing the result onto the target surface.
 *
 * @draw_func is to called to draw the mask; it will be called no more
 * than once.
 *
 * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded.
 **/
static cairo_status_t
_clip_and_composite (cairo_clip_t                  *clip,
		     cairo_operator_t               op,
		     cairo_pattern_t               *src,
		     cairo_draw_func_t              draw_func,
		     void                          *draw_closure,
		     cairo_surface_t               *dst,
		     const cairo_rectangle_int_t   *extents)
{
    cairo_pattern_union_t solid_pattern;
    cairo_status_t status;

    if (_cairo_rectangle_empty (extents))
	/* Nothing to do */
	return CAIRO_STATUS_SUCCESS;

    if (op == CAIRO_OPERATOR_CLEAR) {
	_cairo_pattern_init_solid (&solid_pattern.solid, CAIRO_COLOR_WHITE,
				   CAIRO_CONTENT_COLOR);
	src = &solid_pattern.base;
	op = CAIRO_OPERATOR_DEST_OUT;
    }

    if ((clip && clip->surface) || op == CAIRO_OPERATOR_SOURCE)
    {
	if (op == CAIRO_OPERATOR_SOURCE)
	    status = _clip_and_composite_source (clip,
						 src,
						 draw_func, draw_closure,
						 dst, extents);
	else if (_cairo_operator_bounded_by_mask (op))
	    status = _clip_and_composite_with_mask (clip, op,
						    src,
						    draw_func, draw_closure,
						    dst, extents);
	else
	    status = _clip_and_composite_combine (clip, op,
						  src,
						  draw_func, draw_closure,
						  dst, extents);
    }
    else
    {
	status = (*draw_func) (draw_closure, op,
			       src, dst,
			       0, 0,
			       extents);
    }

    if (src == &solid_pattern.base)
	_cairo_pattern_fini (&solid_pattern.base);

    return status;
}
static cairo_int_status_t
_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t	*dst,
			                cairo_operator_t	 op,
					const cairo_pattern_t	*source,
					cairo_glyph_t		*glyphs,
					int			 num_glyphs,
					const cairo_rectangle_int_t *glyph_extents,
					cairo_scaled_font_t	*scaled_font,
					cairo_clip_t		*clip,
					int			*remaining_glyphs)
{
    cairo_surface_t *mask;
    cairo_status_t status;
    cairo_solid_pattern_t solid;
    int i;

    mask = cairo_gl_surface_create (dst->ctx,
	                            CAIRO_CONTENT_ALPHA,
				    glyph_extents->width,
				    glyph_extents->height);
    if (unlikely (mask->status))
	return mask->status;

    for (i = 0; i < num_glyphs; i++) {
	glyphs[i].x -= glyph_extents->x;
	glyphs[i].y -= glyph_extents->y;
    }

    _cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA);
    status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0,
	                     CAIRO_OPERATOR_ADD, &solid.base,
	                     glyphs, num_glyphs, glyph_extents,
			     scaled_font, NULL, remaining_glyphs);
    if (likely (status == CAIRO_STATUS_SUCCESS)) {
	cairo_surface_pattern_t mask_pattern;

	_cairo_pattern_init_for_surface (&mask_pattern, mask);
	cairo_matrix_init_translate (&mask_pattern.base.matrix,
		                     -glyph_extents->x, -glyph_extents->y);
	status = _cairo_surface_mask (&dst->base, op,
		                      source, &mask_pattern.base, clip);
	_cairo_pattern_fini (&mask_pattern.base);
    } else {
	for (i = 0; i < num_glyphs; i++) {
	    glyphs[i].x += glyph_extents->x;
	    glyphs[i].y += glyph_extents->y;
	}
	*remaining_glyphs = num_glyphs;
    }

    cairo_surface_destroy (mask);
    return status;
}
static cairo_status_t
_composite_spans_fill_func (void                          *closure,
			    cairo_operator_t               op,
			    const cairo_pattern_t         *src,
			    cairo_surface_t               *dst,
			    int                            dst_x,
			    int                            dst_y,
			    const cairo_rectangle_int_t   *extents)
{
    cairo_composite_rectangles_t rects;
    cairo_composite_spans_fill_info_t *info = closure;
    cairo_pattern_union_t pattern;
    cairo_status_t status = CAIRO_STATUS_SUCCESS;

    _cairo_composite_rectangles_init (
	&rects, extents->x, extents->y,
	extents->width, extents->height);

    /* The incoming dst_x/y are where we're pretending the origin of
     * the dst surface is -- *not* the offset of a rectangle where
     * we'd like to place the result. */
    rects.dst.x -= dst_x;
    rects.dst.y -= dst_y;

    /* We're called without a source pattern from
     * _create_composite_mask_pattern(). */
    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);
    if (src == NULL)
	src = &pattern.base;

    status = _cairo_path_fixed_fill_using_spans (
	op, src, info->path, dst,
	info->fill_rule, info->tolerance, info->antialias,
	&rects);

    _cairo_pattern_fini (&pattern.base);
    return status;
}
Beispiel #8
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;
}
Beispiel #9
0
static cairo_surface_t *
_get_image (cairo_xcb_surface_t		 *surface,
	    cairo_bool_t		  use_shm,
	    int x, int y,
	    int width, int height)
{
    cairo_surface_t *image;
    cairo_xcb_connection_t *connection;
    xcb_get_image_reply_t *reply;
    cairo_int_status_t status;

    assert (surface->fallback == NULL);
    assert (x >= 0);
    assert (y >= 0);
    assert (x + width <= surface->width);
    assert (y + height <= surface->height);

    if (surface->deferred_clear) {
	image =
	    _cairo_image_surface_create_with_pixman_format (NULL,
							    surface->pixman_format,
							    width, height,
							    0);
	if (surface->deferred_clear_color.alpha_short > 0x00ff) {
	    cairo_solid_pattern_t solid;

	    _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
	    status = _cairo_surface_paint (image,
					   CAIRO_OPERATOR_SOURCE,
					   &solid.base,
					   NULL);
	    if (unlikely (status)) {
		cairo_surface_destroy (image);
		image = _cairo_surface_create_in_error (status);
	    }
	}
	return image;
    }

    connection = surface->connection;

    status = _cairo_xcb_connection_acquire (connection);
    if (unlikely (status))
	return _cairo_surface_create_in_error (status);

    if (use_shm) {
	image = _get_shm_image (surface, x, y, width, height);
	if (image) {
	    if (image->status) {
		_cairo_xcb_connection_release (connection);
		return image;
	    }
	    cairo_surface_destroy (image);
	}
    }

    status = _cairo_xcb_connection_get_image (connection,
					      surface->drawable,
					      x, y,
					      width, height,
					      &reply);
    if (unlikely (status))
	goto FAIL;

    if (reply == NULL && ! surface->owns_pixmap) {
	/* xcb_get_image_t from a window is dangerous because it can
	 * produce errors if the window is unmapped or partially
	 * outside the screen. We could check for errors and
	 * retry, but to keep things simple, we just create a
	 * temporary pixmap
	 *
	 * If we hit this fallback too often, we should remember so and
	 * skip the round-trip from the above GetImage request,
	 * similar to what cairo-xlib does.
	 */
	xcb_pixmap_t pixmap;
	xcb_gcontext_t gc;

	gc = _cairo_xcb_screen_get_gc (surface->screen,
				       surface->drawable,
				       surface->depth);
	pixmap = _cairo_xcb_connection_create_pixmap (connection,
						      surface->depth,
						      surface->drawable,
						      width, height);

	/* XXX IncludeInferiors? */
	_cairo_xcb_connection_copy_area (connection,
					 surface->drawable,
					 pixmap, gc,
					 x, y,
					 0, 0,
					 width, height);

	_cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);

	status = _cairo_xcb_connection_get_image (connection,
						  pixmap,
						  0, 0,
						  width, height,
						  &reply);
	_cairo_xcb_connection_free_pixmap (connection, pixmap);

	if (unlikely (status))
	    goto FAIL;
    }

    if (unlikely (reply == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto FAIL;
    }

    /* XXX byte swap */
    /* XXX format conversion */
    assert (reply->depth == surface->depth);

    image = _cairo_image_surface_create_with_pixman_format
	(xcb_get_image_data (reply),
	 surface->pixman_format,
	 width, height,
	 CAIRO_STRIDE_FOR_WIDTH_BPP (width,
				     PIXMAN_FORMAT_BPP (surface->pixman_format)));
    status = image->status;
    if (unlikely (status)) {
	free (reply);
	goto FAIL;
    }

    /* XXX */
    pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);

    _cairo_xcb_connection_release (connection);

    return image;

FAIL:
    _cairo_xcb_connection_release (connection);
    return _cairo_surface_create_in_error (status);
}
/* Composites a region representing a set of trapezoids.
 */
static cairo_status_t
_composite_trap_region (cairo_clip_t            *clip,
			cairo_pattern_t         *src,
			cairo_operator_t         op,
			cairo_surface_t         *dst,
			cairo_region_t          *trap_region,
			cairo_rectangle_int_t   *extents)
{
    cairo_status_t status;
    cairo_pattern_union_t solid_pattern;
    cairo_pattern_union_t mask;
    int num_rects = _cairo_region_num_boxes (trap_region);
    unsigned int clip_serial;
    cairo_surface_t *clip_surface = clip ? clip->surface : NULL;

    if (clip_surface && op == CAIRO_OPERATOR_CLEAR) {
	_cairo_pattern_init_solid (&solid_pattern.solid, CAIRO_COLOR_WHITE,
				   CAIRO_CONTENT_COLOR);
	src = &solid_pattern.base;
	op = CAIRO_OPERATOR_DEST_OUT;
    }

    if (num_rects == 0)
	return CAIRO_STATUS_SUCCESS;

    if (num_rects > 1) {
      if (_cairo_surface_get_clip_mode (dst) != CAIRO_CLIP_MODE_REGION)
	    return CAIRO_INT_STATUS_UNSUPPORTED;

	clip_serial = _cairo_surface_allocate_clip_serial (dst);
	status = _cairo_surface_set_clip_region (dst,
						 trap_region,
						 clip_serial);
	if (status)
	    return status;
    }

    if (clip_surface)
	_cairo_pattern_init_for_surface (&mask.surface, clip_surface);

    status = _cairo_surface_composite (op,
				       src,
				       clip_surface ? &mask.base : NULL,
				       dst,
				       extents->x, extents->y,
				       extents->x - (clip_surface ? clip->surface_rect.x : 0),
				       extents->y - (clip_surface ? clip->surface_rect.y : 0),
				       extents->x, extents->y,
				       extents->width, extents->height);

    /* Restore the original clip if we modified it temporarily. */
    if (num_rects > 1) {
	cairo_status_t status2 = _cairo_surface_set_clip (dst, clip);
	if (status == CAIRO_STATUS_SUCCESS)
	    status = status2;
    }

    if (clip_surface)
      _cairo_pattern_fini (&mask.base);

    if (src == &solid_pattern.base)
	_cairo_pattern_fini (&solid_pattern.base);

    return status;
}
Beispiel #11
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;
}
Beispiel #12
0
static cairo_status_t
_cairo_clip_intersect_mask_using_spans (cairo_clip_t       *clip,
					cairo_path_fixed_t *path,
					cairo_fill_rule_t   fill_rule,
					double		    tolerance,
					cairo_antialias_t   antialias,
					cairo_surface_t    *target)
{
    cairo_span_renderer_t *renderer = NULL;
    cairo_pattern_union_t pattern;
    cairo_rectangle_int_t surface_rect;
    cairo_surface_t *surface = NULL;
    cairo_status_t status;
    cairo_operator_t op;
    cairo_composite_rectangles_t rects;

    if (clip->all_clipped)
	return CAIRO_STATUS_SUCCESS;

    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);

    /* If we have a clip surface we're going to use IN to combine our
     * new clip with the old clip.  The ADD is done to a transparent
     * surface, as that's a fast way of doing it currently.  We should
     * really be using SOURCE instead, but _cairo_surface_composite()
     * checks that it's not called with SOURCE or DEST. */
    op = clip->surface ? CAIRO_OPERATOR_IN : CAIRO_OPERATOR_ADD;

    /* Test if the target can composite spans.  We're going to assume
     * this is a good indicator of whether a similar surface is going
     * to be able to composite spans too. */
    if ( !_cairo_surface_check_span_renderer (op,
					      &pattern.base,
					      target,
					      antialias,
					      NULL))
    {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto BAIL;
    }

    status = _cairo_surface_get_extents (target, &surface_rect);
    if (status)
	goto BAIL;

    /* We'll create a new surface the size of the intersection of the
     * old mask surface and the extents of the new clip path. */
    {
	cairo_rectangle_int_t extents;

	_cairo_path_fixed_approximate_clip_extents (path, &extents);
	if (! _cairo_rectangle_intersect (&surface_rect, &extents))
	    goto SUCCESS;

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

    /* Make the new mask surface and optionally initialise it from the
     * previous clip if we have one. */
    surface = _cairo_surface_create_similar_solid (target,
						   CAIRO_CONTENT_ALPHA,
						   surface_rect.width,
						   surface_rect.height,
						   CAIRO_COLOR_TRANSPARENT);
    if (surface->status) {
	_cairo_pattern_fini (&pattern.base);
	return surface->status;
    }

    if (clip->surface) {
	cairo_surface_pattern_t old_clip;
	_cairo_pattern_init_for_surface (&old_clip, clip->surface);
	status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
					   &old_clip.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 (&old_clip.base);
	if (status)
	    goto BAIL;
    }

    _cairo_composite_rectangles_init (&rects,
				      surface_rect.x,
				      surface_rect.y,
				      surface_rect.width,
				      surface_rect.height);
    rects.dst.x = 0;
    rects.dst.y = 0;

    /* Render the new clipping path into the new mask surface. We've
     * chosen op to either combine the new clip path with the existing
     * clip mask (if there is one) or just render it. */
    status =_cairo_path_fixed_fill_using_spans (op, &pattern.base,
						path, surface,
						fill_rule, tolerance,
						antialias, &rects);
    if (status)
	goto BAIL;

 SUCCESS:
    if (clip->surface != NULL)
	cairo_surface_destroy (clip->surface);
    clip->surface = surface;
    clip->surface_rect = surface_rect;
    clip->serial = _cairo_surface_allocate_clip_serial (target);
    surface = NULL;

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

 BAIL:
    if (renderer)
	renderer->destroy(renderer);
    if (surface)
	cairo_surface_destroy (surface);
    _cairo_pattern_fini (&pattern.base);
    return status;
}
cairo_int_status_t
_cairo_gl_surface_show_glyphs (void			*abstract_dst,
			       cairo_operator_t		 op,
			       const cairo_pattern_t	*source,
			       cairo_glyph_t		*glyphs,
			       int			 num_glyphs,
			       cairo_scaled_font_t	*scaled_font,
			       cairo_clip_t		*clip,
			       int			*remaining_glyphs)
{
    cairo_gl_surface_t *dst = abstract_dst;
    cairo_rectangle_int_t surface_extents;
    cairo_rectangle_int_t extents;
    cairo_region_t *clip_region = NULL;
    cairo_solid_pattern_t solid_pattern;
    cairo_bool_t overlap, use_mask = FALSE;
    cairo_status_t status;

    if (! GLEW_ARB_vertex_buffer_object)
	return UNSUPPORTED ("requires ARB_vertex_buffer_object");

    if (! _cairo_gl_operator_is_supported (op))
	return UNSUPPORTED ("unsupported operator");

    if (! _cairo_operator_bounded_by_mask (op))
	use_mask |= TRUE;

    /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
     * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
     * is:
     *     mask IN clip ? src OP dest : dest
     * or more simply:
     *     mask IN CLIP ? 0 : dest
     *
     * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
     *
     * The model we use in _cairo_gl_set_operator() is Render's:
     *     src IN mask IN clip OP dest
     * which would boil down to:
     *     0 (bounded by the extents of the drawing).
     *
     * However, we can do a Render operation using an opaque source
     * and DEST_OUT to produce:
     *    1 IN mask IN clip DEST_OUT dest
     * which is
     *    mask IN clip ? 0 : dest
     */
    if (op == CAIRO_OPERATOR_CLEAR) {
	_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
				   CAIRO_CONTENT_COLOR);
	source = &solid_pattern.base;
	op = CAIRO_OPERATOR_DEST_OUT;
    }

    /* For SOURCE, cairo's rendering equation is:
     *     (mask IN clip) ? src OP dest : dest
     * or more simply:
     *     (mask IN clip) ? src : dest.
     *
     * If we just used the Render equation, we would get:
     *     (src IN mask IN clip) OP dest
     * or:
     *     (src IN mask IN clip) bounded by extents of the drawing.
     *
     * The trick is that for GL blending, we only get our 4 source values
     * into the blender, and since we need all 4 components of source, we
     * can't also get the mask IN clip into the blender.  But if we did
     * two passes we could make it work:
     *     dest = (mask IN clip) DEST_OUT dest
     *     dest = src IN mask IN clip ADD dest
     *
     * But for now, composite via an intermediate mask.
     */
    if (op == CAIRO_OPERATOR_SOURCE)
	use_mask |= TRUE;

    /* XXX we don't need ownership of the font as we use a global
     * glyph cache -- but we do need scaled_glyph eviction notification. :-(
     */
    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
	return UNSUPPORTED ("do not control font");

    /* If the glyphs overlap, we need to build an intermediate mask rather
     * then perform the compositing directly.
     */
    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
						      glyphs, num_glyphs,
						      &extents,
						      &overlap);
    if (unlikely (status))
	return status;

    use_mask |= overlap;

    if (clip != NULL) {
	status = _cairo_clip_get_region (clip, &clip_region);
	/* the empty clip should never be propagated this far */
	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
	if (unlikely (_cairo_status_is_error (status)))
	    return status;

	use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED;

	if (! _cairo_rectangle_intersect (&extents,
		                          _cairo_clip_get_extents (clip)))
	    goto EMPTY;
    }

    surface_extents.x = surface_extents.y = 0;
    surface_extents.width = dst->width;
    surface_extents.height = dst->height;
    if (! _cairo_rectangle_intersect (&extents, &surface_extents))
	goto EMPTY;

    if (use_mask) {
	return _cairo_gl_surface_show_glyphs_via_mask (dst, op,
			                               source,
			                               glyphs, num_glyphs,
						       &extents,
						       scaled_font,
						       clip,
						       remaining_glyphs);
    }

    return _render_glyphs (dst, extents.x, extents.y,
	                   op, source,
			   glyphs, num_glyphs, &extents,
			   scaled_font, clip_region, remaining_glyphs);

EMPTY:
    *remaining_glyphs = 0;
    if (! _cairo_operator_bounded_by_mask (op))
	return _cairo_surface_paint (&dst->base, op, source, clip);
    else
	return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_clip_combine_with_surface (cairo_clip_t *clip,
				  cairo_surface_t *dst,
				  const cairo_rectangle_int_t *extents)
{
    cairo_pattern_union_t pattern;
    cairo_clip_path_t *clip_path = clip->path;
    cairo_bool_t need_translate;
    cairo_status_t status;

    assert (clip_path != NULL);

    if (clip_path->surface != NULL &&
	clip_path->surface->backend == dst->backend)
    {
	_cairo_pattern_init_for_surface (&pattern.surface,
					 clip_path->surface);
	cairo_matrix_init_translate (&pattern.base.matrix,
				     extents->x - clip_path->extents.x,
				     extents->y - clip_path->extents.y);
	status = _cairo_surface_paint (dst,
				       CAIRO_OPERATOR_IN,
				       &pattern.base,
				       NULL);

	_cairo_pattern_fini (&pattern.base);

	return status;
    }

    _cairo_pattern_init_solid (&pattern.solid,
			       CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);

    need_translate = extents->x | extents->y;
    do {
	status = _cairo_clip_path_to_region (clip_path);
	if (unlikely (_cairo_status_is_error (status)))
	    return status;

	if (status == CAIRO_STATUS_SUCCESS)
	    return _combine_region (dst, clip_path->region, extents);

	if (clip_path->surface != NULL &&
	    clip_path->surface->backend == dst->backend)
	{
	    _cairo_pattern_init_for_surface (&pattern.surface,
					     clip_path->surface);
	    cairo_matrix_init_translate (&pattern.base.matrix,
					 extents->x - clip_path->extents.x,
					 extents->y - clip_path->extents.y);
	    status = _cairo_surface_paint (dst,
					   CAIRO_OPERATOR_IN,
					   &pattern.base,
					   NULL);

	    _cairo_pattern_fini (&pattern.base);

	    return status;
	}

	if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) {
	    cairo_region_t clip_region;

	    _cairo_region_init_rectangle (&clip_region, &clip_path->extents);
	    status = _combine_region (dst, &clip_region, extents);
	} else {
	    if (need_translate) {
		_cairo_path_fixed_translate (&clip_path->path,
					     _cairo_fixed_from_int (-extents->x),
					     _cairo_fixed_from_int (-extents->y));
	    }
	    status = _cairo_surface_fill (dst,
					  CAIRO_OPERATOR_IN,
					  &pattern.base,
					  &clip_path->path,
					  clip_path->fill_rule,
					  clip_path->tolerance,
					  clip_path->antialias,
					  NULL);
	    if (need_translate) {
		_cairo_path_fixed_translate (&clip_path->path,
					     _cairo_fixed_from_int (extents->x),
					     _cairo_fixed_from_int (extents->y));
	    }
	}

	if (unlikely (status))
	    return status;
    } while ((clip_path = clip_path->prev) != NULL);

    return CAIRO_STATUS_SUCCESS;
}
static cairo_surface_t *
_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
			      cairo_surface_t *target)
{
    cairo_surface_t *surface;
    cairo_pattern_union_t pattern;
    cairo_status_t status;
    const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
    cairo_clip_path_t *prev;
    cairo_bool_t need_translate;

    if (clip_path->surface != NULL &&
	clip_path->surface->backend == target->backend)
    {
	return cairo_surface_reference (clip_path->surface);
    }

    surface = _cairo_surface_create_similar_solid (target,
						   CAIRO_CONTENT_ALPHA,
						   clip_extents->width,
						   clip_extents->height,
						   CAIRO_COLOR_TRANSPARENT,
						   FALSE);
    if (surface == NULL) {
	if (clip_path->surface != NULL &&
	    clip_path->surface->backend == &_cairo_image_surface_backend)
	{
	    return cairo_surface_reference (clip_path->surface);
	}

	surface =
	    _cairo_image_surface_create_with_content (CAIRO_CONTENT_ALPHA,
						      clip_extents->width,
						      clip_extents->height);
    }
    if (unlikely (surface->status))
	return surface;

    _cairo_pattern_init_solid (&pattern.solid,
			       CAIRO_COLOR_WHITE,
			       CAIRO_CONTENT_COLOR);

    status = _cairo_clip_path_to_region (clip_path);
    if (unlikely (_cairo_status_is_error (status)))
	goto BAIL;

    need_translate = clip_extents->x | clip_extents->y;
    if (status == CAIRO_STATUS_SUCCESS) {
	if (need_translate) {
	    cairo_region_translate (clip_path->region,
				    -clip_extents->x, -clip_extents->y);
	}
	status = _cairo_surface_fill_region (surface,
					     CAIRO_OPERATOR_SOURCE,
					     CAIRO_COLOR_WHITE,
					     clip_path->region);
	if (need_translate) {
	    cairo_region_translate (clip_path->region,
				    clip_extents->x, clip_extents->y);
	}
	if (unlikely (status))
	    goto BAIL;

	goto DONE;
    } else {
	if (need_translate) {
	    _cairo_path_fixed_translate (&clip_path->path,
					 _cairo_fixed_from_int (-clip_extents->x),
					 _cairo_fixed_from_int (-clip_extents->y));
	}
	status = _cairo_surface_fill (surface,
				      CAIRO_OPERATOR_OVER,
				      &pattern.base,
				      &clip_path->path,
				      clip_path->fill_rule,
				      clip_path->tolerance,
				      clip_path->antialias,
				      NULL);
	if (need_translate) {
	    _cairo_path_fixed_translate (&clip_path->path,
					 _cairo_fixed_from_int (clip_extents->x),
					 _cairo_fixed_from_int (clip_extents->y));
	}

	if (unlikely (status))
	    goto BAIL;
    }

    prev = clip_path->prev;
  NEXT_PATH:
    if (prev != NULL) {
	status = _cairo_clip_path_to_region (prev);
	if (unlikely (_cairo_status_is_error (status)))
	    goto BAIL;

	if (status == CAIRO_STATUS_SUCCESS) {
	    status = _combine_region (surface, prev->region, clip_extents);
	    if (unlikely (status))
		goto BAIL;
	} else if (prev->flags & CAIRO_CLIP_PATH_IS_BOX) {
	    /* a simple box only affects the extents */
	} else if (prev->path.is_rectilinear) {
	    if (need_translate) {
		_cairo_path_fixed_translate (&prev->path,
					     _cairo_fixed_from_int (-clip_extents->x),
					     _cairo_fixed_from_int (-clip_extents->y));
	    }
	    status = _cairo_surface_fill (surface,
					  CAIRO_OPERATOR_IN,
					  &pattern.base,
					  &prev->path,
					  prev->fill_rule,
					  prev->tolerance,
					  prev->antialias,
					  NULL);
	    if (need_translate) {
		_cairo_path_fixed_translate (&prev->path,
					     _cairo_fixed_from_int (clip_extents->x),
					     _cairo_fixed_from_int (clip_extents->y));
	    }

	    if (unlikely (status))
		goto BAIL;

	    prev = prev->prev;
	    goto NEXT_PATH;
	} else {
	    cairo_surface_t *prev_surface;

	    prev_surface = _cairo_clip_path_get_surface (prev, target);
	    _cairo_pattern_init_for_surface (&pattern.surface, prev_surface);
	    cairo_surface_destroy (prev_surface);

	    cairo_matrix_init_translate (&pattern.base.matrix,
					 -prev->extents.x + clip_extents->x,
					 -prev->extents.y + clip_extents->y);
	    status = _cairo_surface_paint (surface,
					   CAIRO_OPERATOR_IN,
					   &pattern.base,
					   NULL);
	    _cairo_pattern_fini (&pattern.base);

	    if (unlikely (status))
		goto BAIL;
	}
    }

  DONE:
    cairo_surface_destroy (clip_path->surface);
    return clip_path->surface = cairo_surface_reference (surface);

  BAIL:
    cairo_surface_destroy (surface);
    return _cairo_surface_create_in_error (status);
}