Beispiel #1
0
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;
    }
}
cairo_int_status_t
_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
					    cairo_surface_t *surface,
					    cairo_operator_t		 op,
					    const cairo_pattern_t	*source,
					    const cairo_clip_t		*clip)
{
    if (! _cairo_composite_rectangles_init (extents,
					    surface, op, source, clip))
    {
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
    }

    extents->mask = extents->destination;

    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
    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);

    return CAIRO_STATUS_SUCCESS;
}
static inline cairo_bool_t
_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
				  int width, int height,
				  cairo_operator_t op,
				  const cairo_pattern_t *source,
				  cairo_clip_t *clip)
{
    extents->unbounded.x = extents->unbounded.y = 0;
    extents->unbounded.width  = width;
    extents->unbounded.height = height;

    if (clip != NULL) {
	const cairo_rectangle_int_t *clip_extents;

	clip_extents = _cairo_clip_get_extents (clip);
	if (clip_extents == NULL)
	    return FALSE;

	if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents))
	    return FALSE;
    }

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

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

    return TRUE;
}
static cairo_int_status_t
_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
				       const cairo_clip_t *clip)
{
    if ((!_cairo_rectangle_intersect (&extents->bounded, &extents->mask)) &&
        (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK))
	return CAIRO_INT_STATUS_NOTHING_TO_DO;

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

    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
    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 (! _cairo_rectangle_intersect (&extents->bounded,
				      _cairo_clip_get_extents (extents->clip)) &&
	extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
    {
	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) {
	    _cairo_composite_rectangles_fini (extents);
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
	}
    }

    return CAIRO_STATUS_SUCCESS;
}
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;
}
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;
}
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;
}
static void
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
{
    if (clip != NULL)
	_cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
}
Beispiel #9
0
static cairo_int_status_t
_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup,
					    cairo_gl_context_t *ctx,
					    int vertex_size,
					    cairo_bool_t equal_clip)
{
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;

    cairo_gl_surface_t *dst = setup->dst;
    cairo_clip_t *clip = setup->clip;
    cairo_traps_t traps;
    const cairo_rectangle_int_t *clip_extents;

    if (clip->num_boxes == 1 && clip->path == NULL) {
	_scissor_to_box (dst, &clip->boxes[0]);
	goto disable_stencil_buffer_and_return;
    }

    if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto disable_stencil_buffer_and_return;
    }

    if (! ctx->states_cache.depth_mask ) {
	glDepthMask (GL_TRUE);
	ctx->states_cache.depth_mask = TRUE;
    }
    glEnable (GL_STENCIL_TEST);
    clip_extents = _cairo_clip_get_extents ((const cairo_clip_t *)clip);
    _cairo_gl_scissor_to_extents (dst, clip_extents);

    if (equal_clip)
	return CAIRO_INT_STATUS_SUCCESS;

    glClearStencil (0);
    glClear (GL_STENCIL_BUFFER_BIT);
    glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glStencilFunc (GL_EQUAL, 1, 0xffffffff);
    glColorMask (0, 0, 0, 0);

    _cairo_traps_init (&traps);
    status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip, &traps);
    _cairo_traps_fini (&traps);

    if (unlikely (status)) {
	glColorMask (1, 1, 1, 1);
	goto disable_stencil_buffer_and_return;
    }

    /* We want to only render to the stencil buffer, so draw everything now.
       Flushing also unbinds the VBO, which we want to rebind for regular
       drawing. */
    _cairo_gl_composite_flush (ctx);
    _cairo_gl_composite_setup_vbo (ctx, vertex_size);

    glColorMask (1, 1, 1, 1);
    glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc (GL_EQUAL, 1, 0xffffffff);
    return CAIRO_INT_STATUS_SUCCESS;

disable_stencil_buffer_and_return:
    _disable_stencil_buffer ();
    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;
}
static cairo_int_status_t
_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup,
					    cairo_gl_context_t *ctx,
					    int vertex_size)
{
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;

    cairo_gl_surface_t *dst = setup->dst;
    cairo_clip_t *clip = setup->clip;

    if (clip->num_boxes == 1 && clip->path == NULL) {
	_scissor_to_box (dst, &clip->boxes[0]);
	goto disable_stencil_buffer_and_return;
    }

    if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto disable_stencil_buffer_and_return;
    }

    /* We only want to clear the part of the stencil buffer
     * that we are about to use. It also does not hurt to
     * scissor around the painted clip. */
    _cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip));

    /* The clip is not rectangular, so use the stencil buffer. */
    glDepthMask (GL_TRUE);
    glEnable (GL_STENCIL_TEST);

    /* Texture surfaces have private depth/stencil buffers, so we can
     * rely on any previous clip being cached there. */
    if (_cairo_gl_surface_is_texture (setup->dst)) {
	cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer;
	if (_cairo_clip_equal (old_clip, setup->clip))
	    goto activate_stencil_buffer_and_return;

	if (old_clip) {
	    _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
	}

	setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip);
    }

    glClearStencil (0);
    glClear (GL_STENCIL_BUFFER_BIT);

    glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glStencilFunc (GL_EQUAL, 1, 0xffffffff);
    glColorMask (0, 0, 0, 0);

    status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip);

    if (unlikely (status)) {
	glColorMask (1, 1, 1, 1);
	goto disable_stencil_buffer_and_return;
    }

    /* We want to only render to the stencil buffer, so draw everything now.
       Flushing also unbinds the VBO, which we want to rebind for regular
       drawing. */
    _cairo_gl_composite_flush (ctx);
    _cairo_gl_composite_setup_vbo (ctx, vertex_size);

activate_stencil_buffer_and_return:
    glColorMask (1, 1, 1, 1);
    glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc (GL_EQUAL, 1, 0xffffffff);
    return CAIRO_INT_STATUS_SUCCESS;

disable_stencil_buffer_and_return:
    _disable_stencil_buffer ();
    return status;
}