static cairo_int_status_t _cairo_analysis_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_stroke_style_t *style, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_rectangle_int_t *stroke_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; cairo_rectangle_int_t extents; cairo_bool_t is_empty; if (!surface->target->backend->stroke) backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else backend_status = (*surface->target->backend->stroke) (surface->target, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); } is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &mask_extents); is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } if (stroke_extents) *stroke_extents = extents; status = _add_operation (surface, &extents, backend_status); return status; }
cairo_int_status_t _cairo_composite_rectangles_lazy_init_for_stroke (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_clip_t *clip) { cairo_bool_t should_be_lazy = TRUE; if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip, &should_be_lazy)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (! should_be_lazy) { _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } extents->clip = _cairo_clip_copy (clip); return CAIRO_INT_STATUS_SUCCESS; }
static cairo_int_status_t _prevent_overlapping_drawing (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm) { cairo_rectangle_int_t stroke_extents; const cairo_pattern_t *pattern = composite->original_source_pattern; cairo_pattern_type_t type = cairo_pattern_get_type ((cairo_pattern_t *) pattern); if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX: improve me - since we have lazy init, we cannot use sample area */ if (type == CAIRO_PATTERN_TYPE_SOLID && _cairo_pattern_is_opaque_solid (pattern)) return CAIRO_INT_STATUS_SUCCESS; if (glIsEnabled (GL_STENCIL_TEST) == FALSE) { /* In case we have pending operations we have to flush before adding the stencil buffer. */ _cairo_gl_composite_flush (ctx); /* Enable the stencil buffer, even if we are not using it for clipping, so we can use it below to prevent overlapping shapes. We initialize it all to one here which represents infinite clip. */ if (! ctx->states_cache.depth_mask) { glDepthMask (GL_TRUE); ctx->states_cache.depth_mask = TRUE; } glEnable (GL_STENCIL_TEST); /* If we don't have clip, then we will setup clip extents based on approximate stroke extent. */ if (! setup->clip) { _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &stroke_extents); _cairo_gl_scissor_to_extents (setup->dst, &stroke_extents); } glClearStencil (1); glClear (GL_STENCIL_BUFFER_BIT); glStencilFunc (GL_EQUAL, 1, 1); } /* This means that once we draw to a particular pixel nothing else can be drawn there until the stencil buffer is reset or the stencil test is disabled. */ glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO); /* we need to clean up clip cache */ _cairo_clip_destroy (ctx->clip); ctx->clip = NULL; return CAIRO_INT_STATUS_SUCCESS; }
static cairo_int_status_t _prevent_overlapping_strokes (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm) { cairo_rectangle_int_t stroke_extents; if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_pattern_is_opaque (&composite->source_pattern.base, &composite->source_sample_area)) return CAIRO_INT_STATUS_SUCCESS; if (glIsEnabled (GL_STENCIL_TEST) == FALSE) { cairo_bool_t scissor_was_enabled; /* In case we have pending operations we have to flush before adding the stencil buffer. */ _cairo_gl_composite_flush (ctx); /* Enable the stencil buffer, even if we are not using it for clipping, so we can use it below to prevent overlapping shapes. We initialize it all to one here which represents infinite clip. */ glDepthMask (GL_TRUE); glEnable (GL_STENCIL_TEST); /* We scissor here so that we don't have to clear the entire stencil * buffer. If the scissor test is already enabled, it was enabled * for clipping. In that case, instead of calculating an intersection, * we just reuse it, and risk clearing too much. */ scissor_was_enabled = glIsEnabled (GL_SCISSOR_TEST); if (! scissor_was_enabled) { _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, FALSE, /* is_vector */ &stroke_extents); _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents); } glClearStencil (1); glClear (GL_STENCIL_BUFFER_BIT); if (! scissor_was_enabled) glDisable (GL_SCISSOR_TEST); glStencilFunc (GL_EQUAL, 1, 1); } /* This means that once we draw to a particular pixel nothing else can be drawn there until the stencil buffer is reset or the stencil test is disabled. */ glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO); _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); setup->dst->clip_on_stencil_buffer = NULL; return CAIRO_INT_STATUS_SUCCESS; }
cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); }