diff options
Diffstat (limited to 'src/cairo-gstate.c')
-rw-r--r-- | src/cairo-gstate.c | 1451 |
1 files changed, 598 insertions, 853 deletions
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index ed3359b99..a657f15e5 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -39,6 +39,7 @@ #include "cairoint.h" +#include "cairo-clip-private.h" #include "cairo-gstate-private.h" static cairo_status_t @@ -53,28 +54,16 @@ _cairo_gstate_fini (cairo_gstate_t *gstate); static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps); - -static cairo_status_t -_cairo_gstate_ensure_font (cairo_gstate_t *gstate); + cairo_traps_t *traps); static cairo_status_t _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); -static void -_cairo_gstate_unset_font (cairo_gstate_t *gstate); - -static void -_cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src); - -static void -_cairo_clip_path_reference (cairo_clip_path_t *clip_path); +static cairo_status_t +_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); static void -_cairo_clip_path_destroy (cairo_clip_path_t *clip_path); +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); cairo_gstate_t * _cairo_gstate_create (cairo_surface_t *target) @@ -103,6 +92,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + gstate->antialias = CAIRO_ANTIALIAS_DEFAULT; gstate->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; gstate->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; @@ -124,18 +114,14 @@ _cairo_gstate_init (cairo_gstate_t *gstate, _cairo_font_options_init_default (&gstate->font_options); - gstate->clip.mode = _cairo_surface_get_clip_mode (target); - gstate->clip.region = NULL; - gstate->clip.surface = NULL; - gstate->clip.serial = 0; - gstate->clip.path = NULL; + _cairo_clip_init (&gstate->clip, target); _cairo_gstate_identity_matrix (gstate); + cairo_matrix_init_identity (&gstate->source_ctm_inverse); _cairo_pen_init_empty (&gstate->pen_regular); - gstate->target = target; - cairo_surface_reference (gstate->target); + gstate->target = cairo_surface_reference (target); gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); if (gstate->source->status) @@ -165,10 +151,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); } - if (other->clip.region) { - gstate->clip.region = pixman_region_create (); - pixman_region_copy (gstate->clip.region, other->clip.region); - } + _cairo_clip_init_copy (&gstate->clip, &other->clip); if (gstate->font_face) cairo_font_face_reference (gstate->font_face); @@ -177,8 +160,6 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) cairo_scaled_font_reference (gstate->scaled_font); cairo_surface_reference (gstate->target); - cairo_surface_reference (gstate->clip.surface); - _cairo_clip_path_reference (gstate->clip.path); cairo_pattern_reference (gstate->source); @@ -212,18 +193,7 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) gstate->target = NULL; } - if (gstate->clip.surface) - cairo_surface_destroy (gstate->clip.surface); - gstate->clip.surface = NULL; - - if (gstate->clip.path) - _cairo_clip_path_destroy (gstate->clip.path); - gstate->clip.path = NULL; - - if (gstate->clip.region) - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = NULL; - gstate->clip.serial = 0; + _cairo_clip_fini (&gstate->clip); cairo_pattern_destroy (gstate->source); @@ -343,61 +313,6 @@ _cairo_gstate_end_group (cairo_gstate_t *gstate) } */ -static cairo_status_t -_cairo_gstate_set_clip (cairo_gstate_t *gstate) -{ - cairo_surface_t *surface = gstate->target; - - if (!surface) - return CAIRO_STATUS_NULL_POINTER; - if (gstate->clip.serial == _cairo_surface_get_current_clip_serial (surface)) - return CAIRO_STATUS_SUCCESS; - - if (gstate->clip.path) - return _cairo_surface_set_clip_path (surface, - gstate->clip.path, - gstate->clip.serial); - - if (gstate->clip.region) - return _cairo_surface_set_clip_region (surface, - gstate->clip.region, - gstate->clip.serial); - - return _cairo_surface_reset_clip (surface); -} - -static cairo_status_t -_cairo_gstate_get_clip_extents (cairo_gstate_t *gstate, - cairo_rectangle_t *rectangle) -{ - cairo_status_t status; - - status = _cairo_surface_get_extents (gstate->target, rectangle); - if (status) - return status; - /* check path extents here */ - - if (gstate->clip.region) { - pixman_box16_t *clip_box; - cairo_rectangle_t clip_rect; - - /* get region extents as a box */ - clip_box = pixman_region_extents (gstate->clip.region); - /* convert to a rectangle */ - clip_rect.x = clip_box->x1; - clip_rect.width = clip_box->x2 - clip_box->x1; - clip_rect.y = clip_box->y1; - clip_rect.height = clip_box->y2 - clip_box->y1; - /* intersect with surface extents */ - _cairo_rectangle_intersect (rectangle, &clip_rect); - } - - if (gstate->clip.surface) - _cairo_rectangle_intersect (rectangle, &gstate->clip.surface_rect); - - return CAIRO_STATUS_SUCCESS; -} - cairo_surface_t * _cairo_gstate_get_target (cairo_gstate_t *gstate) { @@ -414,6 +329,7 @@ _cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_reference (source); cairo_pattern_destroy (gstate->source); gstate->source = source; + gstate->source_ctm_inverse = gstate->ctm_inverse; return CAIRO_STATUS_SUCCESS; } @@ -559,7 +475,7 @@ _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) { cairo_matrix_t tmp; - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_translate (&tmp, tx, ty); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -578,7 +494,7 @@ _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) if (sx == 0 || sy == 0) return CAIRO_STATUS_INVALID_MATRIX; - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_scale (&tmp, sx, sy); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -594,7 +510,7 @@ _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) { cairo_matrix_t tmp; - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_rotate (&tmp, angle); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -611,7 +527,7 @@ _cairo_gstate_transform (cairo_gstate_t *gstate, { cairo_matrix_t tmp; - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); tmp = *matrix; cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -628,7 +544,7 @@ _cairo_gstate_set_matrix (cairo_gstate_t *gstate, { cairo_status_t status; - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); gstate->ctm = *matrix; @@ -643,7 +559,7 @@ _cairo_gstate_set_matrix (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_identity (&gstate->ctm); cairo_matrix_init_identity (&gstate->ctm_inverse); @@ -717,11 +633,15 @@ _cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) */ static void -_cairo_gstate_pattern_transform (cairo_gstate_t *gstate, - cairo_pattern_t *pattern) +_cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + cairo_pattern_t *original, + cairo_matrix_t *ctm_inverse) { - cairo_matrix_t tmp_matrix = gstate->ctm_inverse; - + cairo_matrix_t tmp_matrix = *ctm_inverse; + + _cairo_pattern_init_copy (pattern, original); + if (gstate->target) cairo_matrix_translate (&tmp_matrix, - gstate->target->device_x_offset, @@ -730,6 +650,25 @@ _cairo_gstate_pattern_transform (cairo_gstate_t *gstate, _cairo_pattern_transform (pattern, &tmp_matrix); } +static void +_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, + cairo_pattern_t *pattern) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + gstate->source, + &gstate->source_ctm_inverse); +} + +static void +_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + cairo_pattern_t *mask) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + mask, + &gstate->ctm_inverse); +} + cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate) { @@ -741,11 +680,14 @@ _cairo_gstate_paint (cairo_gstate_t *gstate) if (gstate->source->status) return gstate->source->status; - status = _cairo_gstate_set_clip (gstate); + status = _cairo_surface_set_clip (gstate->target, &gstate->clip); if (status) return status; - status = _cairo_gstate_get_clip_extents (gstate, &rectangle); + status = _cairo_surface_get_extents (gstate->target, &rectangle); + if (status) + return status; + status = _cairo_clip_intersect_to_rectangle (&gstate->clip, &rectangle); if (status) return status; @@ -757,119 +699,310 @@ _cairo_gstate_paint (cairo_gstate_t *gstate) if (status) return status; - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->target, - &traps); + _cairo_gstate_clip_and_composite_trapezoids (gstate, &traps); _cairo_traps_fini (&traps); return CAIRO_STATUS_SUCCESS; } -/* Combines @gstate->clip_surface using the IN operator with - * the given intermediate surface, which corresponds to the - * rectangle of the destination space given by @extents. +/** + * _cairo_operator_bounded: + * @operator: a #cairo_operator_t + * + * A bounded operator is one where a source or mask pixel + * of zero results in no effect on the destination image. + * + * Unbounded operators often require special handling; if you, for + * example, draw trapezoids with an unbounded operator, the effect + * extends past the bounding box of the trapezoids. + * + * Return value: %TRUE if the operator is bounded + **/ +cairo_bool_t +_cairo_operator_bounded (cairo_operator_t operator) +{ + switch (operator) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return TRUE; + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +typedef cairo_status_t (*cairo_draw_func_t) (void *closure, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_t *extents); + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask */ static cairo_status_t -_cairo_gstate_combine_clip_surface (cairo_gstate_t *gstate, - cairo_surface_t *intermediate, - cairo_rectangle_t *extents) +_cairo_gstate_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_t *extents) { - cairo_pattern_union_t pattern; + cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; cairo_status_t status; - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); + intermediate = cairo_surface_create_similar (clip->surface, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height); + if (intermediate->status) + return CAIRO_STATUS_NO_MEMORY; + + status = (*draw_func) (draw_closure, CAIRO_OPERATOR_SOURCE, + NULL, intermediate, + extents->x, extents->y, + extents); + if (status) + goto CLEANUP_SURFACE; - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents->x - gstate->clip.surface_rect.x, - extents->y - gstate->clip.surface_rect.y, - 0, 0, - 0, 0, - extents->width, extents->height); + status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_IN, + intermediate, + extents->x, extents->y, + extents); + if (status) + goto CLEANUP_SURFACE; - _cairo_pattern_fini (&pattern.base); + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + + status = _cairo_surface_composite (operator, + src, &intermediate_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + _cairo_pattern_fini (&intermediate_pattern.base); + + CLEANUP_SURFACE: + cairo_surface_destroy (intermediate); return status; } -/* Creates a region from a cairo_rectangle_t */ +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ static cairo_status_t -_region_new_from_rect (cairo_rectangle_t *rect, - pixman_region16_t **region) -{ - *region = pixman_region_create (); - if (pixman_region_union_rect (*region, *region, - rect->x, rect->y, - rect->width, rect->height) != PIXMAN_REGION_STATUS_SUCCESS) { - pixman_region_destroy (*region); +_cairo_gstate_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_t *extents) +{ + cairo_surface_t *intermediate; + cairo_surface_pattern_t dst_pattern; + cairo_surface_pattern_t intermediate_pattern; + cairo_status_t status; + + /* We'd be better off here creating a surface identical in format + * to dst, but we have no way of getting that information. + * A CAIRO_CONTENT_CLONE or something might be useful. + */ + intermediate = cairo_surface_create_similar (dst, + CAIRO_CONTENT_COLOR_ALPHA, + extents->width, + extents->height); + if (intermediate->status) return CAIRO_STATUS_NO_MEMORY; - } - return CAIRO_STATUS_SUCCESS; -} + /* Initialize the intermediate surface from the destination surface + */ + _cairo_pattern_init_for_surface (&dst_pattern, dst); -/* Gets the bounding box of a region as a cairo_rectangle_t */ -static void -_region_rect_extents (pixman_region16_t *region, - cairo_rectangle_t *rect) -{ - pixman_box16_t *region_extents = pixman_region_extents (region); + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &dst_pattern.base, NULL, intermediate, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height); - rect->x = region_extents->x1; - rect->y = region_extents->y1; - rect->width = region_extents->x2 - region_extents->x1; - rect->height = region_extents->y2 - region_extents->y1; -} + _cairo_pattern_fini (&dst_pattern.base); -/* Intersects @region with the clipping bounds (both region - * and surface) of @gstate - */ -static cairo_status_t -_cairo_gstate_intersect_clip (cairo_gstate_t *gstate, - pixman_region16_t *region) -{ - if (gstate->clip.region) - pixman_region_intersect (region, gstate->clip.region, region); + if (status) + goto CLEANUP_SURFACE; + + status = (*draw_func) (draw_closure, operator, + src, intermediate, + extents->x, extents->y, + extents); + if (status) + goto CLEANUP_SURFACE; + + /* Combine that with the clip + */ + status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_DEST_IN, + intermediate, + extents->x, extents->y, + extents); + if (status) + goto CLEANUP_SURFACE; + + /* Punch the clip out of the destination + */ + status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_DEST_OUT, + dst, + 0, 0, + extents); + if (status) + goto CLEANUP_SURFACE; - if (gstate->clip.surface) { - pixman_region16_t *clip_rect; - cairo_status_t status; + /* Now add the two results together + */ + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - status = _region_new_from_rect (&gstate->clip.surface_rect, &clip_rect); - if (status) - return status; - - if (pixman_region_intersect (region, - clip_rect, - region) != PIXMAN_REGION_STATUS_SUCCESS) - status = CAIRO_STATUS_NO_MEMORY; + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &intermediate_pattern.base, NULL, dst, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); - pixman_region_destroy (clip_rect); + _cairo_pattern_fini (&intermediate_pattern.base); + + CLEANUP_SURFACE: + cairo_surface_destroy (intermediate); - if (status) - return status; - } + return status; +} - return CAIRO_STATUS_SUCCESS; +static int +_cairo_rectangle_empty (const cairo_rectangle_t *rect) +{ + return rect->width == 0 || rect->height == 0; } +/** + * _cairo_gstate_clip_and_composite: + * @gstate: a #cairo_gstate_t + * @operator: 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 +_cairo_gstate_clip_and_composite (cairo_clip_t *clip, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_t *extents) +{ + if (_cairo_rectangle_empty (extents)) + /* Nothing to do */ + return CAIRO_STATUS_SUCCESS; + + if (clip->surface) + { + if (_cairo_operator_bounded (operator)) + return _cairo_gstate_clip_and_composite_with_mask (clip, operator, + src, + draw_func, draw_closure, + dst, extents); + else + return _cairo_gstate_clip_and_composite_combine (clip, operator, + src, + draw_func, draw_closure, + dst, extents); + } + else + { + return (*draw_func) (draw_closure, operator, + src, dst, + 0, 0, + extents); + } +} + + static cairo_status_t _get_mask_extents (cairo_gstate_t *gstate, cairo_pattern_t *mask, cairo_rectangle_t *extents) { + cairo_status_t status; + /* * XXX should take mask extents into account, but - * that involves checking the transform... For now, + * that involves checking the transform and + * _cairo_operator_bounded (operator)... For now, * be lazy and just use the destination extents */ - return _cairo_gstate_get_clip_extents (gstate, extents); + status = _cairo_surface_get_extents (gstate->target, extents); + if (status) + return status; + + return _cairo_clip_intersect_to_rectangle (&gstate->clip, extents); +} + +static cairo_status_t +_cairo_gstate_mask_draw_func (void *closure, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_t *extents) +{ + cairo_pattern_t *mask = closure; + + if (src) + return _cairo_surface_composite (operator, + src, mask, dst, + extents->x, extents->y, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + else + return _cairo_surface_composite (operator, + mask, NULL, dst, + extents->x, extents->y, + 0, 0, /* unused */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); } cairo_status_t @@ -877,11 +1010,8 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_t *mask) { cairo_rectangle_t extents; - cairo_pattern_union_t pattern; - cairo_surface_pattern_t intermediate_pattern; - cairo_pattern_t *effective_mask; + cairo_pattern_union_t source_pattern, mask_pattern; cairo_status_t status; - int mask_x, mask_y; if (mask->status) return mask->status; @@ -889,69 +1019,23 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, if (gstate->source->status) return gstate->source->status; - status = _cairo_gstate_set_clip (gstate); + status = _cairo_surface_set_clip (gstate->target, &gstate->clip); if (status) return status; - _get_mask_extents (gstate, mask, &extents); + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - if (gstate->clip.surface) { - /* When there is clip surface, we'll need to create a - * temporary surface that combines the clip and mask - */ - cairo_surface_t *intermediate; - - intermediate = cairo_surface_create_similar (gstate->clip.surface, - CAIRO_CONTENT_ALPHA, - extents.width, - extents.height); - if (intermediate->status) - return CAIRO_STATUS_NO_MEMORY; - - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - mask, NULL, intermediate, - extents.x, extents.y, - 0, 0, - 0, 0, - extents.width, extents.height); - if (status) { - cairo_surface_destroy (intermediate); - return status; - } - - status = _cairo_gstate_combine_clip_surface (gstate, intermediate, &extents); - if (status) { - cairo_surface_destroy (intermediate); - return status; - } - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - cairo_surface_destroy (intermediate); - - effective_mask = &intermediate_pattern.base; - mask_x = extents.x; - mask_y = extents.y; - - } else { - effective_mask = mask; - mask_x = mask_y = 0; - } - - _cairo_pattern_init_copy (&pattern.base, gstate->source); - _cairo_gstate_pattern_transform (gstate, &pattern.base); - - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - effective_mask, - gstate->target, - extents.x, extents.y, - extents.x - mask_x, extents.y - mask_y, - extents.x, extents.y, - extents.width, extents.height); + _get_mask_extents (gstate, &mask_pattern.base, &extents); + + status = _cairo_gstate_clip_and_composite (&gstate->clip, gstate->operator, + &source_pattern.base, + _cairo_gstate_mask_draw_func, &mask_pattern.base, + gstate->target, + &extents); - if (gstate->clip.surface) - _cairo_pattern_fini (&intermediate_pattern.base); - _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&source_pattern.base); + _cairo_pattern_fini (&mask_pattern.base); return status; } @@ -968,7 +1052,7 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) if (gstate->line_width <= 0.0) return CAIRO_STATUS_SUCCESS; - status = _cairo_gstate_set_clip (gstate); + status = _cairo_surface_set_clip (gstate->target, &gstate->clip); if (status) return status; @@ -982,11 +1066,7 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) return status; } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->target, - &traps); + _cairo_gstate_clip_and_composite_trapezoids (gstate, &traps); _cairo_traps_fini (&traps); @@ -1035,7 +1115,7 @@ BAIL: * _cairo_rectangle_fixed_round. */ -static void +void _cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle) { rectangle->x = _cairo_fixed_integer_floor (box->p1.x); @@ -1044,7 +1124,7 @@ _cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle) rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; } -static void +void _cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src) { int x1, y1, x2, y2; @@ -1067,80 +1147,10 @@ _cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src) } } -static int -_cairo_rectangle_empty (cairo_rectangle_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -/* Given a region representing a set of trapezoids that will be - * drawn, clip the region according to the gstate and compute - * the overall extents. - */ -static cairo_status_t -_clip_and_compute_extents_region (cairo_gstate_t *gstate, - pixman_region16_t *trap_region, - cairo_rectangle_t *extents) -{ - cairo_status_t status; - - status = _cairo_gstate_intersect_clip (gstate, trap_region); - if (status) - return status; - - _region_rect_extents (trap_region, extents); - - return CAIRO_STATUS_SUCCESS; -} - -/* Given a a set of trapezoids to draw, find a bounding box (non-exact) - * of the trapezoids clipped by the gstate - */ -static cairo_status_t -_clip_and_compute_extents_arbitrary (cairo_gstate_t *gstate, - cairo_traps_t *traps, - cairo_rectangle_t *extents) -{ - cairo_box_t trap_extents; - - _cairo_traps_extents (traps, &trap_extents); - _cairo_box_round_to_rectangle (&trap_extents, extents); - - if (gstate->clip.region) { - pixman_region16_t *intersection; - cairo_status_t status; - - status = _region_new_from_rect (extents, &intersection); - if (status) - return status; - - if (pixman_region_intersect (intersection, - gstate->clip.region, - intersection) == PIXMAN_REGION_STATUS_SUCCESS) - { - _region_rect_extents (intersection, extents); - } - else - { - status = CAIRO_STATUS_NO_MEMORY; - } - - pixman_region_destroy (intersection); - - if (status) - return status; - } - - if (gstate->clip.surface) - _cairo_rectangle_intersect (extents, &gstate->clip.surface_rect); - - return CAIRO_STATUS_SUCCESS; -} - /* Composites a region representing a set of trapezoids. */ static cairo_status_t -_composite_trap_region (cairo_gstate_t *gstate, +_composite_trap_region (cairo_clip_t *clip, cairo_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, @@ -1148,7 +1158,6 @@ _composite_trap_region (cairo_gstate_t *gstate, cairo_rectangle_t *extents) { cairo_status_t status; - cairo_pattern_union_t pattern; cairo_pattern_union_t mask; int num_rects = pixman_region_num_rects (trap_region); unsigned int clip_serial; @@ -1157,146 +1166,42 @@ _composite_trap_region (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; if (num_rects > 1) { - - if (gstate->clip.mode != CAIRO_CLIP_MODE_REGION) + if (clip->mode != CAIRO_CLIP_MODE_REGION) return CAIRO_INT_STATUS_UNSUPPORTED; - clip_serial = _cairo_surface_allocate_clip_serial (gstate->target); - status = _cairo_surface_set_clip_region (gstate->target, + clip_serial = _cairo_surface_allocate_clip_serial (dst); + status = _cairo_surface_set_clip_region (dst, trap_region, clip_serial); if (status) return status; } - _cairo_pattern_init_copy (&pattern.base, src); - _cairo_gstate_pattern_transform (gstate, &pattern.base); - - if (gstate->clip.surface) - _cairo_pattern_init_for_surface (&mask.surface, gstate->clip.surface); + if (clip->surface) + _cairo_pattern_init_for_surface (&mask.surface, clip->surface); - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - gstate->clip.surface ? &mask.base : NULL, + status = _cairo_surface_composite (operator, + src, + clip->surface ? &mask.base : NULL, dst, extents->x, extents->y, - extents->x - (gstate->clip.surface ? gstate->clip.surface_rect.x : 0), - extents->y - (gstate->clip.surface ? gstate->clip.surface_rect.y : 0), + 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); - _cairo_pattern_fini (&pattern.base); - if (gstate->clip.surface) + if (clip->surface) _cairo_pattern_fini (&mask.base); return status; } -static void -translate_traps (cairo_traps_t *traps, int x, int y) -{ - cairo_fixed_t xoff, yoff; - cairo_trapezoid_t *t; - int i; - - /* Ugh. The cairo_composite/(Render) interface doesn't allow - an offset for the trapezoids. Need to manually shift all - the coordinates to align with the offset origin of the - intermediate surface. */ - - xoff = _cairo_fixed_from_int (x); - yoff = _cairo_fixed_from_int (y); - - for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { - t->top += yoff; - t->bottom += yoff; - t->left.p1.x += xoff; - t->left.p1.y += yoff; - t->left.p2.x += xoff; - t->left.p2.y += yoff; - t->right.p1.x += xoff; - t->right.p1.y += yoff; - t->right.p2.x += xoff; - t->right.p2.y += yoff; - } -} - -/* Composites a set of trapezoids in the case where we need to create - * an intermediate surface to handle gstate->clip.surface - * - * Warning: This call modifies the coordinates of traps - */ -static cairo_status_t -_composite_traps_intermediate_surface (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_rectangle_t *extents) -{ - cairo_pattern_union_t pattern; - cairo_surface_t *intermediate; - cairo_surface_pattern_t intermediate_pattern; - cairo_status_t status; - - translate_traps (traps, -extents->x, -extents->y); - - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height, - CAIRO_COLOR_TRANSPARENT); - if (intermediate->status) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); - - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - &pattern.base, - intermediate, - extents->x, extents->y, - 0, 0, - extents->width, - extents->height, - traps->traps, - traps->num_traps); - _cairo_pattern_fini (&pattern.base); - - if (status) - goto out; - - status = _cairo_gstate_combine_clip_surface (gstate, intermediate, extents); - if (status) - goto out; - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_pattern_init_copy (&pattern.base, src); - _cairo_gstate_pattern_transform (gstate, &pattern.base); - - status = _cairo_surface_composite (operator, - &pattern.base, - &intermediate_pattern.base, - dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - _cairo_pattern_fini (&pattern.base); - _cairo_pattern_fini (&intermediate_pattern.base); - - out: - cairo_surface_destroy (intermediate); - - return status; -} - /* Composites a region representing a set of trapezoids in the * case of a solid source (so we can use * _cairo_surface_fill_rectangles). */ static cairo_status_t -_composite_trap_region_solid (cairo_gstate_t *gstate, +_composite_trap_region_solid (cairo_clip_t *clip, cairo_solid_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, @@ -1330,48 +1235,69 @@ _composite_trap_region_solid (cairo_gstate_t *gstate, return status; } -/* Composites a set of trapezoids in the general case where - gstate->clip.surface == NULL - */ +typedef struct { + cairo_traps_t *traps; + cairo_antialias_t antialias; +} cairo_composite_traps_info_t; + static cairo_status_t -_composite_traps (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_rectangle_t *extents) -{ +_composite_traps_draw_func (void *closure, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_t *extents) +{ + cairo_composite_traps_info_t *info = closure; cairo_pattern_union_t pattern; cairo_status_t status; - - _cairo_pattern_init_copy (&pattern.base, src); - _cairo_gstate_pattern_transform (gstate, &pattern.base); - status = _cairo_surface_composite_trapezoids (gstate->operator, - &pattern.base, dst, - extents->x, extents->y, - extents->x, extents->y, - extents->width, - extents->height, - traps->traps, - traps->num_traps); + 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); + if (!src) + src = &pattern.base; + + status = _cairo_surface_composite_trapezoids (operator, + 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; } +/* Gets the bounding box of a region as a cairo_rectangle_t */ +static void +_region_rect_extents (pixman_region16_t *region, + cairo_rectangle_t *rect) +{ + pixman_box16_t *region_extents = pixman_region_extents (region); + + rect->x = region_extents->x1; + rect->y = region_extents->y1; + rect->width = region_extents->x2 - region_extents->x1; + rect->height = region_extents->y2 - region_extents->y1; +} + /* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps) +cairo_status_t +_cairo_surface_clip_and_composite_trapezoids (cairo_pattern_t *src, + cairo_operator_t operator, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_clip_t *clip, + cairo_antialias_t antialias) { cairo_status_t status; pixman_region16_t *trap_region; cairo_rectangle_t extents; + cairo_composite_traps_info_t traps_info; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; @@ -1380,60 +1306,65 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, if (status) return status; - if (trap_region) - status = _clip_and_compute_extents_region (gstate, trap_region, &extents); + if (_cairo_operator_bounded (operator)) + { + if (trap_region) { + status = _cairo_clip_intersect_to_region (clip, trap_region); + _region_rect_extents (trap_region, &extents); + } else { + cairo_box_t trap_extents; + _cairo_traps_extents (traps, &trap_extents); + _cairo_box_round_to_rectangle (&trap_extents, &extents); + status = _cairo_clip_intersect_to_rectangle (clip, &extents); + } + } else - status = _clip_and_compute_extents_arbitrary (gstate, traps, &extents); + { + status = _cairo_surface_get_extents (dst, &extents); + if (status) + return status; + status = _cairo_clip_intersect_to_rectangle (clip, &extents); + if (status) + return status; + } if (status) goto out; - if (_cairo_rectangle_empty (&extents)) - /* Nothing to do */ - goto out; - - if (gstate->clip.surface) { - if (trap_region) { - /* If we are compositing a set of rectangles, we can set them as the - * clip region for the destination surface and use the clip surface - * as the mask. A clip region might not be supported, in which case - * we fall through to the next method - */ - status = _composite_trap_region (gstate, src, operator, dst, - trap_region, &extents); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto out; - } - - /* Handle a clip surface by creating an intermediate surface. */ - status = _composite_traps_intermediate_surface (gstate, src, operator, - dst, traps, &extents); - } else { - /* No clip surface */ - if (trap_region && src->type == CAIRO_PATTERN_SOLID) { - /* Solid rectangles are handled specially */ - status = _composite_trap_region_solid (gstate, (cairo_solid_pattern_t *)src, + if (trap_region && _cairo_operator_bounded (operator)) + { + if (src->type == CAIRO_PATTERN_SOLID && !clip->surface) + { + /* Solid rectangles special case */ + status = _composite_trap_region_solid (clip, (cairo_solid_pattern_t *)src, operator, dst, trap_region); - } else { - if (trap_region) { - /* 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. - */ - status = _composite_trap_region (gstate, src, operator, dst, - trap_region, &extents); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto out; - - /* If a clip regions aren't supported, fall through */ - } - - status = _composite_traps (gstate, src, operator, - dst, traps, &extents); + goto out; } + + /* 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. + * + * 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, operator, dst, + trap_region, &extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto out; } + traps_info.traps = traps; + traps_info.antialias = antialias; + + status = _cairo_gstate_clip_and_composite (clip, operator, src, + _composite_traps_draw_func, &traps_info, + dst, &extents); + out: if (trap_region) pixman_region_destroy (trap_region); @@ -1441,6 +1372,28 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, return status; } +/* Warning: This call modifies the coordinates of traps */ +static cairo_status_t +_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, + cairo_traps_t *traps) +{ + cairo_pattern_union_t pattern; + cairo_status_t status; + + _cairo_gstate_copy_transformed_source (gstate, &pattern.base); + + status = _cairo_surface_clip_and_composite_trapezoids (&pattern.base, + gstate->operator, + gstate->target, + traps, + &gstate->clip, + gstate->antialias); + + _cairo_pattern_fini (&pattern.base); + + return status; +} + cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { @@ -1450,7 +1403,7 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) if (gstate->source->status) return gstate->source->status; - status = _cairo_gstate_set_clip (gstate); + status = _cairo_surface_set_clip (gstate->target, &gstate->clip); if (status) return status; @@ -1466,17 +1419,16 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); if (status) { _cairo_traps_fini (&traps); return status; } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->target, - &traps); + _cairo_gstate_clip_and_composite_trapezoids (gstate, &traps); _cairo_traps_fini (&traps); @@ -1497,7 +1449,10 @@ _cairo_gstate_in_fill (cairo_gstate_t *gstate, _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); if (status) goto BAIL; @@ -1567,7 +1522,10 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); if (status) goto BAIL; @@ -1590,226 +1548,19 @@ BAIL: cairo_status_t _cairo_gstate_reset_clip (cairo_gstate_t *gstate) { - /* destroy any existing clip-region artifacts */ - if (gstate->clip.surface) - cairo_surface_destroy (gstate->clip.surface); - gstate->clip.surface = NULL; - - if (gstate->clip.region) - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = NULL; - - if (gstate->clip.path) - _cairo_clip_path_destroy (gstate->clip.path); - gstate->clip.path = NULL; - - gstate->clip.serial = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gstate_intersect_clip_path (cairo_gstate_t *gstate, - cairo_path_fixed_t *path) -{ - cairo_clip_path_t *clip_path; - cairo_status_t status; - - if (gstate->clip.mode != CAIRO_CLIP_MODE_PATH) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clip_path = malloc (sizeof (cairo_clip_path_t)); - if (clip_path == NULL) - return CAIRO_STATUS_NO_MEMORY; - - status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (status) - return status; - - clip_path->ref_count = 1; - clip_path->fill_rule = gstate->fill_rule; - clip_path->tolerance = gstate->tolerance; - clip_path->prev = gstate->clip.path; - gstate->clip.path = clip_path; - gstate->clip.serial = _cairo_surface_allocate_clip_serial (gstate->target); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_clip_path_reference (cairo_clip_path_t *clip_path) -{ - if (clip_path == NULL) - return; - - clip_path->ref_count++; -} - -static void -_cairo_clip_path_destroy (cairo_clip_path_t *clip_path) -{ - if (clip_path == NULL) - return; - - clip_path->ref_count--; - if (clip_path->ref_count) - return; - - _cairo_path_fixed_fini (&clip_path->path); - _cairo_clip_path_destroy (clip_path->prev); - free (clip_path); -} - -static cairo_status_t -_cairo_gstate_intersect_clip_region (cairo_gstate_t *gstate, - cairo_traps_t *traps) -{ - pixman_region16_t *region; - cairo_status_t status; - - if (gstate->clip.mode != CAIRO_CLIP_MODE_REGION) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_traps_extract_region (traps, ®ion); - if (status) - return status; - - if (region == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = CAIRO_STATUS_SUCCESS; - if (gstate->clip.region == NULL) { - gstate->clip.region = region; - } else { - pixman_region16_t *intersection = pixman_region_create(); - - if (pixman_region_intersect (intersection, - gstate->clip.region, region) - == PIXMAN_REGION_STATUS_SUCCESS) { - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = intersection; - } else { - status = CAIRO_STATUS_NO_MEMORY; - } - pixman_region_destroy (region); - } - gstate->clip.serial = _cairo_surface_allocate_clip_serial (gstate->target); - return status; -} - -static cairo_status_t -_cairo_gstate_intersect_clip_mask (cairo_gstate_t *gstate, - cairo_traps_t *traps) -{ - cairo_pattern_union_t pattern; - cairo_box_t extents; - cairo_rectangle_t surface_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 (gstate->clip.surface != NULL) - _cairo_rectangle_intersect (&surface_rect, &gstate->clip.surface_rect); - - surface = _cairo_surface_create_similar_solid (gstate->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. */ - - translate_traps (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, - 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 (gstate->clip.surface != NULL) { - _cairo_pattern_init_for_surface (&pattern.surface, gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - surface, - surface_rect.x - gstate->clip.surface_rect.x, - surface_rect.y - gstate->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 (gstate->clip.surface); - } - - gstate->clip.surface = surface; - gstate->clip.surface_rect = surface_rect; - - return status; + return _cairo_clip_reset (&gstate->clip); } cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { - cairo_status_t status; - cairo_traps_t traps; - - status = _cairo_gstate_intersect_clip_path (gstate, path); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); - if (status) - goto bail; - - status = _cairo_gstate_intersect_clip_region (gstate, &traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto bail; - - status = _cairo_gstate_intersect_clip_mask (gstate, &traps); - - bail: - _cairo_traps_fini (&traps); - - return status; + return _cairo_clip_clip (&gstate->clip, + path, gstate->fill_rule, gstate->tolerance, + gstate->antialias, gstate->target); } static void -_cairo_gstate_unset_font (cairo_gstate_t *gstate) +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) { if (gstate->scaled_font) { cairo_scaled_font_destroy (gstate->scaled_font); @@ -1825,7 +1576,7 @@ _cairo_gstate_select_font_face (cairo_gstate_t *gstate, { cairo_font_face_t *font_face; - font_face = _cairo_simple_font_face_create (family, slant, weight); + font_face = _cairo_toy_font_face_create (family, slant, weight); if (font_face->status) return font_face->status; @@ -1839,7 +1590,7 @@ cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, double size) { - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_scale (&gstate->font_matrix, size, size); @@ -1850,7 +1601,7 @@ cairo_status_t _cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); gstate->font_matrix = *matrix; @@ -1868,7 +1619,7 @@ cairo_status_t _cairo_gstate_set_font_options (cairo_gstate_t *gstate, const cairo_font_options_t *options) { - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); gstate->font_options = *options; @@ -1979,9 +1730,9 @@ _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) if (!gstate->font_face) { cairo_font_face_t *font_face; - font_face = _cairo_simple_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); + font_face = _cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); if (font_face->status) return font_face->status; else @@ -1992,7 +1743,7 @@ _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) } static cairo_status_t -_cairo_gstate_ensure_font (cairo_gstate_t *gstate) +_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) { cairo_status_t status; cairo_font_options_t options; @@ -2022,7 +1773,7 @@ cairo_status_t _cairo_gstate_get_font_extents (cairo_gstate_t *gstate, cairo_font_extents_t *extents) { - cairo_status_t status = _cairo_gstate_ensure_font (gstate); + cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; @@ -2042,7 +1793,7 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, cairo_status_t status; int i; - status = _cairo_gstate_ensure_font (gstate); + status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; @@ -2071,18 +1822,15 @@ cairo_status_t _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face) { - if (font_face->status) + if (font_face && font_face->status) return font_face->status; if (font_face != gstate->font_face) { - if (gstate->font_face) - cairo_font_face_destroy (gstate->font_face); - gstate->font_face = font_face; - if (gstate->font_face) - cairo_font_face_reference (gstate->font_face); + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = cairo_font_face_reference (font_face); } - _cairo_gstate_unset_font (gstate); + _cairo_gstate_unset_scaled_font (gstate); return CAIRO_STATUS_SUCCESS; } @@ -2095,7 +1843,7 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, { cairo_status_t status; - status = _cairo_gstate_ensure_font (gstate); + status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; @@ -2106,6 +1854,58 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } +typedef struct { + cairo_scaled_font_t *font; + cairo_glyph_t *glyphs; + int num_glyphs; +} cairo_show_glyphs_info_t; + +static cairo_status_t +_cairo_gstate_show_glyphs_draw_func (void *closure, + cairo_operator_t operator, + cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_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) + { + glyph_info->glyphs[i].x -= dst_x; + glyph_info->glyphs[i].y -= dst_y; + } + } + + _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); + if (!src) + src = &pattern.base; + + status = _cairo_scaled_font_show_glyphs (glyph_info->font, + operator, + 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_status_t _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, @@ -2117,15 +1917,16 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_pattern_union_t pattern; cairo_box_t bbox; cairo_rectangle_t extents; + cairo_show_glyphs_info_t glyph_info; if (gstate->source->status) return gstate->source->status; - status = _cairo_gstate_set_clip (gstate); + status = _cairo_surface_set_clip (gstate->target, &gstate->clip); if (status) return status; - status = _cairo_gstate_ensure_font (gstate); + status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; @@ -2140,113 +1941,41 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, &transformed_glyphs[i].x, &transformed_glyphs[i].y); } - - status = _cairo_scaled_font_glyph_bbox (gstate->scaled_font, - transformed_glyphs, num_glyphs, - &bbox); - _cairo_box_round_to_rectangle (&bbox, &extents); - if (status) - goto CLEANUP_GLYPHS; - - if (gstate->clip.surface) + if (_cairo_operator_bounded (gstate->operator)) { - cairo_surface_t *intermediate; - cairo_surface_pattern_t intermediate_pattern; - - _cairo_rectangle_intersect (&extents, &gstate->clip.surface_rect); - - /* Shortcut if empty */ - if (_cairo_rectangle_empty (&extents)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_CONTENT_ALPHA, - extents.width, - extents.height, - CAIRO_COLOR_TRANSPARENT); - if (intermediate->status) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } - - /* move the glyphs again, from dev space to intermediate space */ - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i].x -= extents.x; - transformed_glyphs[i].y -= extents.y; - } - - _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); - - status = _cairo_scaled_font_show_glyphs (gstate->scaled_font, - CAIRO_OPERATOR_ADD, - &pattern.base, intermediate, - extents.x, extents.y, - 0, 0, - extents.width, extents.height, - transformed_glyphs, num_glyphs); - - _cairo_pattern_fini (&pattern.base); - + status = _cairo_scaled_font_glyph_bbox (gstate->scaled_font, + transformed_glyphs, num_glyphs, + &bbox); if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents.x - gstate->clip.surface_rect.x, - extents.y - gstate->clip.surface_rect.y, - 0, 0, - 0, 0, - extents.width, extents.height); - - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_pattern_init_copy (&pattern.base, gstate->source); - _cairo_gstate_pattern_transform (gstate, &pattern.base); - - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - &intermediate_pattern.base, - gstate->target, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - _cairo_pattern_fini (&pattern.base); - _cairo_pattern_fini (&intermediate_pattern.base); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: - ; + goto CLEANUP_GLYPHS; + + _cairo_box_round_to_rectangle (&bbox, &extents); } else { - _cairo_pattern_init_copy (&pattern.base, gstate->source); - _cairo_gstate_pattern_transform (gstate, &pattern.base); + status = _cairo_surface_get_extents (gstate->target, &extents); + if (status) + goto CLEANUP_GLYPHS; + } + + status = _cairo_clip_intersect_to_rectangle (&gstate->clip, &extents); + if (status) + goto CLEANUP_GLYPHS; + + _cairo_gstate_copy_transformed_source (gstate, &pattern.base); - status = _cairo_scaled_font_show_glyphs (gstate->scaled_font, - gstate->operator, &pattern.base, - gstate->target, - extents.x, extents.y, - extents.x, extents.y, - extents.width, extents.height, - transformed_glyphs, num_glyphs); + glyph_info.font = gstate->scaled_font; + glyph_info.glyphs = transformed_glyphs; + glyph_info.num_glyphs = num_glyphs; + + status = _cairo_gstate_clip_and_composite (&gstate->clip, gstate->operator, + &pattern.base, + _cairo_gstate_show_glyphs_draw_func, &glyph_info, + gstate->target, + &extents); - _cairo_pattern_fini (&pattern.base); - } + _cairo_pattern_fini (&pattern.base); CLEANUP_GLYPHS: free (transformed_glyphs); @@ -2264,7 +1993,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, int i; cairo_glyph_t *transformed_glyphs = NULL; - status = _cairo_gstate_ensure_font (gstate); + status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; @@ -2287,3 +2016,19 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, free (transformed_glyphs); return status; } + +cairo_private cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias) +{ + gstate->antialias = antialias; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_private cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate) +{ + return gstate->antialias; +} + |