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_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; }
/** * _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_analysis_surface_stroke (void *abstract_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_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->stroke == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->stroke (surface->target, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); _cairo_rectangle_intersect (&extents, &rec_extents); } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; cairo_int_status_t status; status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, &mask_extents); if (unlikely (status)) return status; _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); }
cairo_status_t _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font) { cairo_status_t status; cairo_rectangle_int_t extents; cairo_show_glyphs_info_t glyph_info; status = _cairo_surface_get_extents (surface, &extents); if (unlikely (status)) return status; if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t glyph_extents; status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents); if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &glyph_extents)) return CAIRO_STATUS_SUCCESS; } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); if (unlikely (status)) return status; glyph_info.font = scaled_font; glyph_info.glyphs = glyphs; glyph_info.num_glyphs = num_glyphs; status = _clip_and_composite (surface->clip, op, source, _cairo_surface_old_show_glyphs_draw_func, &glyph_info, surface, &extents); return status; }
cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask) { cairo_status_t status; cairo_rectangle_int_t extents, source_extents, mask_extents; status = _cairo_surface_get_extents (surface, &extents); if (unlikely (status)) return status; if (_cairo_operator_bounded_by_source (op)) { status = _cairo_pattern_get_extents (source, &source_extents); if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_pattern_get_extents (mask, &mask_extents); if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &mask_extents)) return CAIRO_STATUS_SUCCESS; } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); if (unlikely (status)) return status; status = _clip_and_composite (surface->clip, op, source, _cairo_surface_mask_draw_func, (void *) mask, surface, &extents); return status; }
cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, cairo_pattern_t *source, cairo_pattern_t *mask) { cairo_status_t status; cairo_rectangle_int16_t extents, source_extents, mask_extents; status = _cairo_surface_get_extents (surface, &extents); if (status) return status; if (_cairo_operator_bounded_by_source (op)) { status = _cairo_pattern_get_extents (source, &source_extents); if (status) return status; _cairo_rectangle_intersect (&extents, &source_extents); } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_pattern_get_extents (mask, &mask_extents); if (status) return status; _cairo_rectangle_intersect (&extents, &mask_extents); } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); if (status) return status; status = _clip_and_composite (surface->clip, op, source, _cairo_surface_mask_draw_func, mask, surface, &extents); return status; }
/* 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; }
static cairo_int_status_t _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->target->backend->show_text_glyphs != NULL) { backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs != NULL) { backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents, NULL); if (unlikely (status)) return status; _cairo_rectangle_intersect (&extents, &glyph_extents); } return _add_operation (surface, &extents, backend_status); }
static cairo_int_status_t _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->mask == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->mask (surface->target, op, source, mask, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface; src_surface = _cairo_surface_get_source (src_surface, NULL); if (_cairo_surface_is_recording (src_surface)) { backend_source_status = _analyze_recording_surface_pattern (surface, source); if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; } } if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface; mask_surface = _cairo_surface_get_source (mask_surface, NULL); if (_cairo_surface_is_recording (mask_surface)) { backend_mask_status = _analyze_recording_surface_pattern (surface, mask); if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; } } backend_status = _cairo_analysis_surface_merge_status (backend_source_status, backend_mask_status); } _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; _cairo_pattern_get_extents (mask, &mask_extents); _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); }
static cairo_int_status_t _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, int *remaining_glyphs) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; /* Adapted from _cairo_surface_show_glyphs */ if (surface->target->backend->show_glyphs) backend_status = (*surface->target->backend->show_glyphs) (surface->target, op, source, glyphs, num_glyphs, scaled_font, remaining_glyphs); else if (surface->target->backend->show_text_glyphs) backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, FALSE, scaled_font); else backend_status = CAIRO_INT_STATUS_UNSUPPORTED; 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)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents); if (status) return status; _cairo_rectangle_intersect (&extents, &glyph_extents); } status = _add_operation (surface, &extents, backend_status); return status; }
static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; cairo_int_status_t status; pixman_image_t *mask; pixman_format_code_t format; uint32_t *mask_data; pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; pixman_trapezoid_t *pixman_traps = stack_traps; int mask_stride; int i; if (height == 0 || width == 0) return CAIRO_STATUS_SUCCESS; /* Convert traps to pixman traps */ if (num_traps > ARRAY_LENGTH (stack_traps)) { pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t)); if (pixman_traps == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_traps; i++) { pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); } /* Special case adding trapezoids onto a mask surface; we want to avoid * creating an intermediate temporary mask unnecessarily. * * We make the assumption here that the portion of the trapezoids * contained within the surface is bounded by [dst_x,dst_y,width,height]; * the Cairo core code passes bounds based on the trapezoid extents. * * Currently the check surface->has_clip is needed for correct * functioning, since pixman_add_trapezoids() doesn't obey the * surface clip, which is a libpixman bug , but there's no harm in * falling through to the general case when the surface is clipped * since libpixman would have to generate an intermediate mask anyways. */ if (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern) && dst->base.content == CAIRO_CONTENT_ALPHA && ! dst->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { pixman_add_trapezoids (dst->pixman_image, 0, 0, num_traps, pixman_traps); status = CAIRO_STATUS_SUCCESS; goto finish; } status = _cairo_pattern_acquire_surface (pattern, &dst->base, src_x, src_y, width, height, (cairo_surface_t **) &src, &attributes); if (status) goto finish; status = _cairo_image_surface_set_attributes (src, &attributes); if (status) goto CLEANUP_SOURCE; switch (antialias) { case CAIRO_ANTIALIAS_NONE: format = PIXMAN_a1; mask_stride = ((width + 31) / 8) & ~0x03; break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_DEFAULT: default: format = PIXMAN_a8; mask_stride = (width + 3) & ~3; break; } /* The image must be initially transparent */ mask_data = calloc (mask_stride, height); if (mask_data == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_SOURCE; } mask = pixman_image_create_bits (format, width, height, mask_data, mask_stride); if (mask == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_IMAGE_DATA; } pixman_add_trapezoids (mask, - dst_x, - dst_y, num_traps, pixman_traps); pixman_image_composite (_pixman_operator (op), src->pixman_image, mask, dst->pixman_image, src_x + attributes.x_offset, src_y + attributes.y_offset, 0, 0, dst_x, dst_y, width, height); if (! _cairo_operator_bounded_by_mask (op)) status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height); pixman_image_unref (mask); CLEANUP_IMAGE_DATA: free (mask_data); CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); finish: if (pixman_traps != stack_traps) free (pixman_traps); 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_xynth_surface_composite_trapezoids (cairo_operator_t cairo_operator, cairo_pattern_t *pattern, void *abstract_surface, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps) { int i; int mask_bpp; int mask_stride; s_render_t *mask; unsigned char *mask_data; cairo_int_status_t status; cairo_xynth_surface_t *src; cairo_xynth_surface_t *dst; s_render_trap_t *render_traps; S_RENDER_FORMAT render_format; cairo_surface_attributes_t attributes; ENTER(); dst = (cairo_xynth_surface_t *) abstract_surface; if (num_traps > 0) { render_traps = (s_render_trap_t *) malloc(sizeof(s_render_trap_t) * num_traps); if (render_traps == NULL) { LEAVE(); status = CAIRO_STATUS_NO_MEMORY; goto out0; } } else { num_traps = 0; render_traps = NULL; } for (i = 0; i < num_traps; i++) { render_traps[i].top = traps[i].top; render_traps[i].bottom = traps[i].bottom; render_traps[i].left1x = traps[i].left.p1.x; render_traps[i].left2x = traps[i].left.p2.x; render_traps[i].right1x = traps[i].right.p1.x; render_traps[i].right2x = traps[i].right.p2.x; render_traps[i].left1y = traps[i].left.p1.y; render_traps[i].left2y = traps[i].left.p2.y; render_traps[i].right1y = traps[i].right.p1.y; render_traps[i].right2y = traps[i].right.p2.y; } if (cairo_operator == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern) && dst->cairo.content == CAIRO_CONTENT_ALPHA && !dst->render->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { s_render_add_trapezoid(dst->render, 0, 0, num_traps, render_traps); free(render_traps); return CAIRO_STATUS_SUCCESS; } status = _cairo_pattern_acquire_surface(pattern, &dst->cairo, src_x, src_y, width, height, (cairo_surface_t **) &src, &attributes); if (status) { goto out1; } status = _cairo_xynth_surface_set_attributes(src, &attributes); if (status) { goto out2; } switch (antialias) { case CAIRO_ANTIALIAS_NONE: render_format = S_RENDER_FORMAT_A1; mask_stride = (width + 31) / 8; mask_bpp = 1; break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_DEFAULT: default: render_format = S_RENDER_FORMAT_A8; mask_stride = (width + 3) & ~3; mask_bpp = 8; break; } mask_data = calloc(1, mask_stride * height); if (!mask_data) { status = CAIRO_STATUS_NO_MEMORY; goto out3; } s_render_init_for_data(&mask, mask_data, render_format, width, height, mask_bpp, mask_stride); s_render_add_trapezoid(mask, - dst_x, - dst_y, num_traps, render_traps); s_render_composite(_cairo_xynth_operator(cairo_operator), src->render, mask, dst->render, src_x + attributes.x_offset, src_y + attributes.y_offset, 0, 0, dst_x, dst_y, width, height); if (!_cairo_operator_bounded_by_mask(cairo_operator)) { status = _cairo_surface_composite_shape_fixup_unbounded(&dst->cairo, &attributes, src->render->width, src->render->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height); } s_render_uninit(mask); out3: free(mask_data); out2: _cairo_pattern_release_surface(pattern, &src->cairo, &attributes); out1: free(render_traps); out0: LEAVE(); return 0; }
cairo_status_t _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_status_t status; cairo_traps_t traps; cairo_box_t box; cairo_rectangle_int_t extents; status = _cairo_surface_get_extents (surface, &extents); if (unlikely (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; if (! _cairo_rectangle_intersect (&extents, &source_extents)) return CAIRO_STATUS_SUCCESS; } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); if (unlikely (status)) return status; if (extents.width == 0 || extents.height == 0) return CAIRO_STATUS_SUCCESS; /* Ask if the surface would like to render this combination of * op/source/dst/antialias with spans or not, but don't actually * make a renderer yet. We'll try to hit the region optimisations * in _clip_and_composite_trapezoids() if it looks like the path * is a region. */ /* TODO: Until we have a mono scan converter we won't even try * to use spans for CAIRO_ANTIALIAS_NONE. */ /* TODO: The region filling code should be lifted from * _clip_and_composite_trapezoids() and given first priority * explicitly before deciding between spans and trapezoids. */ if (antialias != CAIRO_ANTIALIAS_NONE && !_cairo_path_fixed_is_box (path, &box) && !_cairo_path_fixed_is_region (path) && _cairo_surface_check_span_renderer ( op, source, surface, antialias, NULL)) { cairo_composite_spans_fill_info_t info; info.path = path; info.fill_rule = fill_rule; info.tolerance = tolerance; info.antialias = antialias; if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t path_extents; _cairo_path_fixed_approximate_clip_extents (path, &path_extents); if (! _cairo_rectangle_intersect (&extents, &path_extents)) return CAIRO_STATUS_SUCCESS; } return _clip_and_composite ( surface->clip, op, source, _composite_spans_fill_func, &info, surface, &extents); } /* Fall back to trapezoid fills. */ _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 (unlikely (status)) { _cairo_traps_fini (&traps); return status; } status = _clip_and_composite_trapezoids (source, op, surface, &traps, surface->clip, antialias); _cairo_traps_fini (&traps); return status; }
static cairo_int_status_t _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_rectangle_int_t *mask_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents; cairo_bool_t is_empty; if (!surface->target->backend->mask) backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else backend_status = (*surface->target->backend->mask) (surface->target, op, source, mask, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; if (_cairo_surface_is_meta (surface_pattern->surface)) { backend_source_status = _analyze_meta_surface_pattern (surface, source); if (_cairo_status_is_error (backend_source_status)) return backend_source_status; } } if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; if (_cairo_surface_is_meta (surface_pattern->surface)) { backend_mask_status = _analyze_meta_surface_pattern (surface, mask); if (_cairo_status_is_error (backend_mask_status)) return backend_mask_status; } } backend_status = _cairo_analysis_surface_merge_status (backend_source_status, backend_mask_status); } 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); } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; status = _cairo_pattern_get_extents (mask, &mask_extents); if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); if (mask_extents) *mask_extents = extents; status = _add_operation (surface, &extents, backend_status); return status; }
static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; cairo_int_status_t status; pixman_image_t *mask; pixman_format_t *format; pixman_bits_t *mask_data; int mask_stride; int mask_bpp; /* Special case adding trapezoids onto a mask surface; we want to avoid * creating an intermediate temporary mask unecessarily. * * We make the assumption here that the portion of the trapezoids * contained within the surface is bounded by [dst_x,dst_y,width,height]; * the Cairo core code passes bounds based on the trapezoid extents. * * Currently the check surface->has_clip is needed for correct * functioning, since pixman_add_trapezoids() doesn't obey the * surface clip, which is a libpixman bug , but there's no harm in * falling through to the general case when the surface is clipped * since libpixman would have to generate an intermediate mask anyways. */ if (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern) && _cairo_image_surface_is_alpha_only (dst) && !dst->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { pixman_add_trapezoids (dst->pixman_image, 0, 0, (pixman_trapezoid_t *) traps, num_traps); return CAIRO_STATUS_SUCCESS; } status = _cairo_pattern_acquire_surface (pattern, &dst->base, src_x, src_y, width, height, (cairo_surface_t **) &src, &attributes); if (status) return status; status = _cairo_image_surface_set_attributes (src, &attributes); if (status) goto CLEANUP_SOURCE; switch (antialias) { case CAIRO_ANTIALIAS_NONE: format = pixman_format_create (PIXMAN_FORMAT_NAME_A1); mask_stride = (width + 31)/8; mask_bpp = 1; break; default: format = pixman_format_create (PIXMAN_FORMAT_NAME_A8); mask_stride = (width + 3) & ~3; mask_bpp = 8; break; } if (!format) { status = CAIRO_STATUS_NO_MEMORY; goto CLEANUP_SOURCE; } /* The image must be initially transparent */ mask_data = calloc (1, mask_stride * height); if (!mask_data) { status = CAIRO_STATUS_NO_MEMORY; pixman_format_destroy (format); goto CLEANUP_SOURCE; } mask = pixman_image_create_for_data (mask_data, format, width, height, mask_bpp, mask_stride); pixman_format_destroy (format); if (!mask) { status = CAIRO_STATUS_NO_MEMORY; goto CLEANUP_IMAGE_DATA; } /* XXX: The pixman_trapezoid_t cast is evil and needs to go away * somehow. */ pixman_add_trapezoids (mask, - dst_x, - dst_y, (pixman_trapezoid_t *) traps, num_traps); pixman_composite (_pixman_operator (op), src->pixman_image, mask, dst->pixman_image, src_x + attributes.x_offset, src_y + attributes.y_offset, 0, 0, dst_x, dst_y, width, height); if (!_cairo_operator_bounded_by_mask (op)) status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height); pixman_image_destroy (mask); CLEANUP_IMAGE_DATA: free (mask_data); CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; }
static cairo_int_status_t _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, cairo_rectangle_int_t *show_text_glyphs_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; cairo_bool_t is_empty; /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->target->backend->show_text_glyphs) backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, NULL); if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs) { int remaining_glyphs = num_glyphs; backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, &remaining_glyphs, NULL); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) backend_status = CAIRO_STATUS_SUCCESS; } 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)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents); if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); } if (show_text_glyphs_extents) *show_text_glyphs_extents = extents; status = _add_operation (surface, &extents, backend_status); return status; }
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; }