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