diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2009-07-23 15:32:13 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2009-07-23 15:32:14 +0100 |
commit | bed2701e1c89095878d549cbca8f22d84f3dda3c (patch) | |
tree | 974807761b6d839839ecad9961eae8d567898dcc /src/cairo-surface-fallback.c | |
parent | f5a1cdf283a6aa1f4409ccbf3c2274fb587724fe (diff) |
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
Diffstat (limited to 'src/cairo-surface-fallback.c')
-rw-r--r-- | src/cairo-surface-fallback.c | 995 |
1 files changed, 555 insertions, 440 deletions
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c index 830c1b3d..9867eea8 100644 --- a/src/cairo-surface-fallback.c +++ b/src/cairo-surface-fallback.c @@ -40,6 +40,7 @@ #include "cairo-surface-fallback-private.h" #include "cairo-clip-private.h" +#include "cairo-region-private.h" typedef struct { cairo_surface_t *dst; @@ -82,13 +83,13 @@ _fallback_init (fallback_state_t *state, if (unlikely (status)) return status; + /* XXX: This NULL value tucked away in state->image is a rather * ugly interface. Cleaner would be to push the * CAIRO_INT_STATUS_NOTHING_TO_DO value down into * _cairo_surface_acquire_dest_image and its backend * counterparts. */ - if (state->image == NULL) - return CAIRO_INT_STATUS_NOTHING_TO_DO; + assert (state->image != NULL); return CAIRO_STATUS_SUCCESS; } @@ -101,46 +102,85 @@ _fallback_fini (fallback_state_t *state) state->image_extra); } -typedef cairo_status_t (*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents); +typedef cairo_status_t +(*cairo_draw_func_t) (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); static cairo_status_t _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, cairo_clip_t *clip, - cairo_draw_func_t draw_func, + cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, const cairo_rectangle_int_t *extents) { cairo_surface_t *mask; + cairo_region_t *clip_region = NULL; + cairo_solid_pattern_t solid; cairo_status_t status; + cairo_bool_t clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } - mask = cairo_surface_create_similar (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height); - if (mask->status) + /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with + * a mask (as called via _cairo_surface_mask) triggers assertion failures. + */ + mask = _cairo_surface_create_similar_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + if (unlikely (mask->status)) return mask->status; - status = (*draw_func) (draw_closure, CAIRO_OPERATOR_ADD, - NULL, mask, - extents->x, extents->y, - extents); + _cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA); + status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, + &solid.base, mask, + extents->x, extents->y, + extents, + clip_region); if (unlikely (status)) goto CLEANUP_SURFACE; - if (clip && clip->surface) - status = _cairo_clip_combine_to_surface (clip, CAIRO_OPERATOR_IN, - mask, - extents->x, extents->y, - extents); - if (unlikely (status)) - goto CLEANUP_SURFACE; + if (clip_surface) { + cairo_surface_pattern_t pattern; + cairo_surface_t *surface; + + surface = _cairo_clip_get_surface (clip, mask); + _cairo_pattern_init_for_surface (&pattern, surface); + cairo_surface_destroy (surface); + + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + &pattern.base, + NULL, + mask, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) + goto CLEANUP_SURFACE; + } _cairo_pattern_init_for_surface (mask_pattern, mask); @@ -169,17 +209,17 @@ _clip_and_composite_with_mask (cairo_clip_t *clip, clip, draw_func, draw_closure, dst, extents); - if (unlikely (status)) - return status; - - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - _cairo_pattern_fini (&mask_pattern.base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_surface_composite (op, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + + _cairo_pattern_fini (&mask_pattern.base); + } return status; } @@ -197,77 +237,95 @@ _clip_and_composite_combine (cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_surface_t *intermediate; - cairo_surface_pattern_t dst_pattern; - cairo_surface_pattern_t intermediate_pattern; + cairo_surface_pattern_t pattern; + cairo_surface_pattern_t clip_pattern; + cairo_surface_t *clip_surface; 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. - * cairo_surface_create_similar() also unnecessarily clears the surface. */ - intermediate = cairo_surface_create_similar (dst, - CAIRO_CONTENT_COLOR_ALPHA, - extents->width, - extents->height); - if (intermediate->status) + intermediate = + _cairo_surface_create_similar_scratch (dst, + CAIRO_CONTENT_COLOR_ALPHA, + extents->width, + extents->height); + if (intermediate == NULL) { + intermediate = + _cairo_image_surface_create_with_content (CAIRO_CONTENT_COLOR_ALPHA, + extents->width, + extents->width); + } + if (unlikely (intermediate->status)) return intermediate->status; - /* Initialize the intermediate surface from the destination surface - */ - _cairo_pattern_init_for_surface (&dst_pattern, dst); - + /* Initialize the intermediate surface from the destination surface */ + _cairo_pattern_init_for_surface (&pattern, dst); status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &dst_pattern.base, NULL, intermediate, + &pattern.base, NULL, intermediate, extents->x, extents->y, 0, 0, 0, 0, - extents->width, extents->height); - - _cairo_pattern_fini (&dst_pattern.base); - + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto CLEANUP_SURFACE; status = (*draw_func) (draw_closure, op, src, intermediate, extents->x, extents->y, - extents); + extents, + NULL); if (unlikely (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 (unlikely (status)) + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, dst); + if (unlikely (clip_surface->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); + _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); + cairo_surface_destroy (clip_surface); + + /* Combine that with the clip */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, + &clip_pattern.base, NULL, intermediate, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); if (unlikely (status)) goto CLEANUP_SURFACE; - /* Now add the two results together - */ - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + /* Punch the clip out of the destination */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &clip_pattern.base, NULL, dst, + extents->x - clip->path->extents.x, + extents->y - clip->path->extents.y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + if (unlikely (status)) + goto CLEANUP_SURFACE; + /* Now add the two results together */ + _cairo_pattern_init_for_surface (&pattern, intermediate); status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &intermediate_pattern.base, NULL, dst, + &pattern.base, NULL, dst, 0, 0, 0, 0, extents->x, extents->y, - extents->width, extents->height); - - _cairo_pattern_fini (&intermediate_pattern.base); + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); CLEANUP_SURFACE: + _cairo_pattern_fini (&clip_pattern.base); cairo_surface_destroy (intermediate); return status; @@ -285,10 +343,10 @@ _clip_and_composite_source (cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_surface_pattern_t mask_pattern; + cairo_region_t *clip_region = NULL; cairo_status_t status; - /* Create a surface that is mask IN clip - */ + /* Create a surface that is mask IN clip */ status = _create_composite_mask_pattern (&mask_pattern, clip, draw_func, draw_closure, @@ -296,26 +354,35 @@ _clip_and_composite_source (cairo_clip_t *clip, if (unlikely (status)) return status; - /* Compute dest' = dest OUT (mask IN clip) - */ + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + /* a solitary clip rectangle is already accommodated by extents */ + if (clip_region && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + /* Compute dest' = dest OUT (mask IN clip) */ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, &mask_pattern.base, NULL, dst, 0, 0, 0, 0, extents->x, extents->y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); if (unlikely (status)) goto CLEANUP_MASK_PATTERN; - /* Now compute (src IN (mask IN clip)) ADD dest' - */ + /* Now compute (src IN (mask IN clip)) ADD dest' */ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, src, &mask_pattern.base, dst, extents->x, extents->y, 0, 0, extents->x, extents->y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); CLEANUP_MASK_PATTERN: _cairo_pattern_fini (&mask_pattern.base); @@ -372,30 +439,45 @@ _clip_and_composite (cairo_clip_t *clip, 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 (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, + src, + draw_func, draw_closure, + dst, extents); + } else { + cairo_bool_t clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_surface) { + 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 { + /* a solitary clip rectangle is already accommodated by extents */ + if (clip_region && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + status = draw_func (draw_closure, op, + src, dst, + 0, 0, + extents, + clip_region); + } } return status; @@ -409,59 +491,51 @@ _composite_trap_region (cairo_clip_t *clip, cairo_operator_t op, cairo_surface_t *dst, cairo_region_t *trap_region, - cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_solid_pattern_t solid_pattern; - cairo_surface_pattern_t mask; - int num_rects = cairo_region_num_rectangles (trap_region); - unsigned int clip_serial; - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + cairo_surface_pattern_t mask_pattern; + cairo_pattern_t *mask = NULL; + int mask_x = 0, mask_y =0; + + if (clip != NULL) { + cairo_surface_t *clip_surface = NULL; + const cairo_rectangle_int_t *clip_extents; + + clip_surface = _cairo_clip_get_surface (clip, dst); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + src = &solid_pattern.base; + op = CAIRO_OPERATOR_DEST_OUT; + } - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); + cairo_surface_destroy (clip_surface); - if (clip_surface && op == CAIRO_OPERATOR_CLEAR) { - _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &solid_pattern.base; - op = CAIRO_OPERATOR_DEST_OUT; + clip_extents = _cairo_clip_get_extents (clip); + mask_x = extents->x - clip_extents->x; + mask_y = extents->y - clip_extents->y; + mask = &mask_pattern.base; } - if (num_rects > 1) { - if (_cairo_surface_get_clip_mode (dst) != CAIRO_CLIP_MODE_REGION) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clip_serial = _cairo_surface_allocate_clip_serial (dst); - status = _cairo_surface_set_clip_region (dst, - trap_region, - clip_serial); - if (unlikely (status)) - return status; - } + /* reduce a solitary clipping region to the extents */ + if (cairo_region_num_rectangles (trap_region) == 1) + trap_region = NULL; - if (clip_surface) - _cairo_pattern_init_for_surface (&mask, clip_surface); - - status = _cairo_surface_composite (op, - src, - clip_surface ? &mask.base : NULL, - dst, + status = _cairo_surface_composite (op, src, mask, dst, extents->x, extents->y, - extents->x - (clip_surface ? clip->surface_rect.x : 0), - extents->y - (clip_surface ? clip->surface_rect.y : 0), + mask_x, mask_y, extents->x, extents->y, - extents->width, extents->height); - - /* Restore the original clip if we modified it temporarily. */ - if (num_rects > 1) { - cairo_status_t status2 = _cairo_surface_set_clip (dst, clip); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } + extents->width, extents->height, + trap_region); - if (clip_surface) - _cairo_pattern_fini (&mask.base); + if (mask != NULL) + _cairo_pattern_fini (mask); return status; } @@ -478,183 +552,211 @@ _composite_traps_draw_func (void *closure, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_composite_traps_info_t *info = closure; - cairo_solid_pattern_t pattern; if (dst_x != 0 || dst_y != 0) _cairo_traps_translate (info->traps, - dst_x, - dst_y); - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; - } - return _cairo_surface_composite_trapezoids (op, 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); + info->traps->num_traps, + clip_region); } -/* Warning: This call modifies the coordinates of traps */ +enum { + HAS_CLEAR_REGION = 0x1, +}; + static cairo_status_t -_clip_and_composite_trapezoids (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_clip_t *clip, - cairo_antialias_t antialias) +_clip_and_composite_region (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) { + cairo_region_t clear_region; + cairo_bool_t clip_surface = FALSE; + unsigned int has_region = 0; cairo_status_t status; - cairo_region_t *trap_region = NULL; - cairo_region_t *clear_region = NULL; - cairo_rectangle_int_t extents; - cairo_composite_traps_info_t traps_info; - if (_cairo_operator_bounded_by_mask (op) && traps->num_traps == 0) - return CAIRO_STATUS_SUCCESS; + if (clip != NULL) { + cairo_region_t *clip_region; - status = _cairo_surface_get_extents (dst, &extents); - if (unlikely (status)) - return status; + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); - status = _cairo_traps_extract_region (traps, &trap_region); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t trap_extents; - if (trap_region) { - status = _cairo_clip_intersect_to_region (clip, trap_region); + cairo_region_get_extents (trap_region, &trap_extents); + if (! _cairo_rectangle_intersect (extents, &trap_extents)) + return CAIRO_STATUS_SUCCESS; + } else { + if (! 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_rectangle (&clear_region, extents); + status = cairo_region_subtract (&clear_region, trap_region); if (unlikely (status)) - goto out; + return status; - 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); + if (! cairo_region_is_empty (&clear_region)) + has_region |= HAS_CLEAR_REGION; } + } - if (! _cairo_rectangle_intersect (&extents, &trap_extents)) { - status = CAIRO_STATUS_SUCCESS; - goto out; - } + if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && + ! clip_surface) + { + const cairo_color_t *color; - status = _cairo_clip_intersect_to_rectangle (clip, &extents); - if (unlikely (status)) - goto out; + 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); } else { - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + /* 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 its 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_surface ? clip : NULL, + src, op, dst, + trap_region, extents); + } - if (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. - */ - clear_region = cairo_region_create_rectangle (&extents); + if (has_region & HAS_CLEAR_REGION) { + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_fill_region (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + } + _cairo_region_fini (&clear_region); + } - status = cairo_region_status (clear_region); - if (unlikely (status)) - goto out; + return status; +} - status = _cairo_clip_intersect_to_region (clip, clear_region); - if (unlikely (status)) - goto out; +/* Warning: This call modifies the coordinates of traps */ +static cairo_status_t +_clip_and_composite_trapezoids (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_composite_traps_info_t traps_info; + cairo_region_t *clip_region = NULL; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; - cairo_region_get_extents (clear_region, &extents); + if (_cairo_operator_bounded_by_mask (op) && traps->num_traps == 0) + return CAIRO_STATUS_SUCCESS; - status = cairo_region_subtract (clear_region, trap_region); - if (unlikely (status)) - goto out; - - if (cairo_region_is_empty (clear_region)) { - cairo_region_destroy (clear_region); - clear_region = NULL; - } - } else { - status = _cairo_clip_intersect_to_rectangle (clip, &extents); - } - } + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status))) + return status; - if (unlikely (status)) - goto out; + /* The all-clipped state should not have been propagated this far. */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } - if (trap_region) { - cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (! clip_surface || + (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) + { + cairo_region_t *trap_region = NULL; - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || - op == CAIRO_OPERATOR_CLEAR) && !clip_surface) { - const cairo_color_t *color; + status = _cairo_traps_extract_region (traps, &trap_region); + if (unlikely (_cairo_status_is_error (status))) + return status; - if (op == CAIRO_OPERATOR_CLEAR) { - color = CAIRO_COLOR_TRANSPARENT; - } else { - color = &((cairo_solid_pattern_t *)src)->color; - } + if (trap_region != NULL) { + status = cairo_region_intersect_rectangle (trap_region, extents); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); + if (clip_region != NULL) { + status = cairo_region_intersect (trap_region, clip_region); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + } - if (!status && clear_region) { - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - clear_region); + if (cairo_region_is_empty (trap_region) && + _cairo_operator_bounded_by_mask (op)) + { + cairo_region_destroy (trap_region); + return CAIRO_STATUS_SUCCESS; } - goto out; - } + status = _clip_and_composite_region (src, op, dst, + trap_region, + antialias, + clip, extents); + cairo_region_destroy (trap_region); - 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 its 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 && clear_region) - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - clear_region); - goto out; - } - } + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + return status; + } } - traps_info.traps = traps; - traps_info.antialias = antialias; - - status = _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, &extents); + /* Otherwise we need to render the trapezoids to a mask and composite + * in the usual fashion. + */ + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t trap_extents; + cairo_box_t trap_box; -out: - if (trap_region) - cairo_region_destroy (trap_region); - if (clear_region) - cairo_region_destroy (clear_region); + _cairo_traps_extents (traps, &trap_box); + _cairo_box_round_to_rectangle (&trap_box, &trap_extents); + if (! _cairo_rectangle_intersect (extents, &trap_extents)) + return CAIRO_STATUS_SUCCESS; + } - return status; + traps_info.traps = traps; + traps_info.antialias = antialias; + return _clip_and_composite (clip, op, src, + _composite_traps_draw_func, + &traps_info, dst, extents); } typedef struct { @@ -671,11 +773,11 @@ _composite_spans_fill_func (void *closure, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_composite_rectangles_t rects; cairo_composite_spans_fill_info_t *info = closure; - cairo_solid_pattern_t pattern; _cairo_composite_rectangles_init ( &rects, extents->x, extents->y, @@ -687,60 +789,60 @@ _composite_spans_fill_func (void *closure, rects.dst.x -= dst_x; rects.dst.y -= dst_y; - /* We're called without a source pattern from - * _create_composite_mask_pattern(). */ - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; + return _cairo_path_fixed_fill_using_spans (op, src, info->path, dst, + info->fill_rule, + info->tolerance, + info->antialias, + &rects, + clip_region); +} + +static cairo_bool_t +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +{ + if (clip != NULL) { + const cairo_rectangle_int_t *clip_extents; + + clip_extents = _cairo_clip_get_extents (clip); + if (clip_extents != NULL) + return _cairo_rectangle_intersect (extents, clip_extents); } - return _cairo_path_fixed_fill_using_spans ( - op, src, info->path, dst, - info->fill_rule, info->tolerance, info->antialias, - &rects); + return ! _cairo_rectangle_empty (extents); } cairo_status_t _cairo_surface_fallback_paint (cairo_surface_t *surface, cairo_operator_t op, - const cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_clip_t *clip) { cairo_status_t status; cairo_rectangle_int_t extents; cairo_box_t box; cairo_traps_t traps; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); 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; - + _cairo_pattern_get_extents (source, &source_extents); 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 (! _rectangle_intersect_clip (&extents, clip)) + return CAIRO_STATUS_SUCCESS; _cairo_box_from_rectangle (&box, &extents); _cairo_traps_init_box (&traps, &box); - - status = _clip_and_composite_trapezoids (source, - op, - surface, - &traps, - surface->clip, - CAIRO_ANTIALIAS_NONE); - + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, CAIRO_ANTIALIAS_NONE, + clip, &extents); _cairo_traps_fini (&traps); return status; @@ -753,69 +855,67 @@ _cairo_surface_mask_draw_func (void *closure, cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_pattern_t *mask = closure; - if (src) + if (src) { return _cairo_surface_composite (op, src, mask, dst, extents->x, extents->y, extents->x, extents->y, extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - else + extents->width, extents->height, + clip_region); + } else { return _cairo_surface_composite (op, mask, NULL, dst, extents->x, extents->y, 0, 0, /* unused */ extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); + extents->width, extents->height, + clip_region); + } } + 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) + const cairo_pattern_t *mask, + cairo_clip_t *clip) { - cairo_status_t status; - cairo_rectangle_int_t extents, source_extents, mask_extents; + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); if (_cairo_operator_bounded_by_source (op)) { - status = _cairo_pattern_get_extents (source, &source_extents); - if (unlikely (status)) - return status; + cairo_rectangle_int_t source_extents; + _cairo_pattern_get_extents (source, &source_extents); 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; + cairo_rectangle_int_t mask_extents; + _cairo_pattern_get_extents (mask, &mask_extents); 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); + if (! _rectangle_intersect_clip (&extents, clip)) + return CAIRO_STATUS_SUCCESS; - return status; + return _clip_and_composite (clip, op, source, + _cairo_surface_mask_draw_func, + (void *) mask, + surface, &extents); } cairo_status_t @@ -827,32 +927,27 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_clip_t *clip) { cairo_status_t status; cairo_traps_t traps; cairo_box_t box; cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); 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; + _cairo_pattern_get_extents (source, &source_extents); 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) + if (! _rectangle_intersect_clip (&extents, clip)) return CAIRO_STATUS_SUCCESS; _cairo_box_from_rectangle (&box, &extents); @@ -868,14 +963,10 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, if (unlikely (status)) goto FAIL; - status = _clip_and_composite_trapezoids (source, - op, - surface, - &traps, - surface->clip, - antialias); - -FAIL: + status = _clip_and_composite_trapezoids (source, op, + surface, &traps, antialias, + clip, &extents); + FAIL: _cairo_traps_fini (&traps); return status; @@ -888,35 +979,41 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_clip_t *clip) { cairo_status_t status; cairo_traps_t traps; cairo_box_t box; cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); 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; - + _cairo_pattern_get_extents (source, &source_extents); 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) + if (! _rectangle_intersect_clip (&extents, clip)) return CAIRO_STATUS_SUCCESS; + /* XXX future direction: + status = _cairo_path_fixed_fill_to_region (path, fill_rule, ®ion); + if (status != CAIRO_STATUS_INT_UNSUPPORTED) { + if (unlikely (status)) + return status; + + status = _clip_and_composite_region (); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + */ + /* 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 @@ -927,9 +1024,7 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, /* 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) && + if (antialias != CAIRO_ANTIALIAS_NONE && ! path->is_rectilinear && _cairo_surface_check_span_renderer ( op, source, surface, antialias, NULL)) { @@ -948,12 +1043,11 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - return _clip_and_composite ( - surface->clip, op, source, - _composite_spans_fill_func, - &info, - surface, - &extents); + return _clip_and_composite (clip, op, source, + _composite_spans_fill_func, + &info, + surface, + &extents); } /* Fall back to trapezoid fills. */ @@ -965,18 +1059,13 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, 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); + if (unlikely (status)) + goto FAIL; + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, &extents); + FAIL: _cairo_traps_fini (&traps); return status; @@ -995,10 +1084,10 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure cairo_surface_t *dst, int dst_x, int dst_y, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) { cairo_show_glyphs_info_t *glyph_info = closure; - cairo_solid_pattern_t pattern; cairo_status_t status; /* Modifying the glyph array is fine because we know that this function @@ -1008,19 +1097,12 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure if (dst_x != 0 || dst_y != 0) { int i; - for (i = 0; i < glyph_info->num_glyphs; ++i) - { + for (i = 0; i < glyph_info->num_glyphs; ++i) { ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; } } - if (src == NULL) { - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - src = &pattern.base; - } - status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, dst, extents->x, extents->y, @@ -1029,7 +1111,8 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure extents->width, extents->height, glyph_info->glyphs, - glyph_info->num_glyphs); + glyph_info->num_glyphs, + clip_region); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; @@ -1041,7 +1124,8 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure extents->y - dst_y, extents->width, extents->height, glyph_info->glyphs, - glyph_info->num_glyphs); + glyph_info->num_glyphs, + clip_region); } cairo_status_t @@ -1050,15 +1134,16 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) { cairo_status_t status; cairo_rectangle_int_t extents; cairo_show_glyphs_info_t glyph_info; + cairo_bool_t is_bounded; - status = _cairo_surface_get_extents (surface, &extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded || clip); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t glyph_extents; @@ -1074,23 +1159,18 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (unlikely (status)) - return status; + if (! _rectangle_intersect_clip (&extents, clip)) + return CAIRO_STATUS_SUCCESS; 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; + return _clip_and_composite (clip, op, source, + _cairo_surface_old_show_glyphs_draw_func, + &glyph_info, + surface, + &extents); } cairo_surface_t * @@ -1124,15 +1204,10 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface) } _cairo_pattern_init_for_surface (&pattern, &image->base); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL, - snapshot, - 0, 0, - 0, 0, - 0, 0, - image->width, - image->height); + status = _cairo_surface_paint (snapshot, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); _cairo_pattern_fini (&pattern.base); _cairo_surface_release_source_image (surface, image, image_extra); if (unlikely (status)) { @@ -1155,15 +1230,17 @@ _cairo_surface_fallback_composite (cairo_operator_t op, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { fallback_state_t state; + cairo_region_t *fallback_region = NULL; cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1172,12 +1249,29 @@ _cairo_surface_fallback_composite (cairo_operator_t op, * _cairo_surface_composite so that we get the correct device * offset handling. */ + + if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } + status = _cairo_surface_composite (op, src, mask, &state.image->base, src_x, src_y, mask_x, mask_y, dst_x - state.image_rect.x, dst_y - state.image_rect.y, - width, height); + width, height, + clip_region); + FAIL: + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); _fallback_fini (&state); return status; @@ -1224,7 +1318,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1271,16 +1365,18 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, - int num_traps) + int num_traps, + cairo_region_t *clip_region) { fallback_state_t state; + cairo_region_t *fallback_region = NULL; cairo_trapezoid_t *offset_traps = NULL; cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + status = CAIRO_STATUS_SUCCESS; return status; } @@ -1288,15 +1384,28 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, if (state.image_rect.x != 0 || state.image_rect.y != 0) { offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); - if (!offset_traps) { + if (offset_traps == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; + goto FAIL; } _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, - state.image_rect.x, - state.image_rect.y, 1.0, 1.0); traps = offset_traps; + + /* similarly we need to adjust the region */ + if (clip_region != NULL) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } } status = _cairo_surface_composite_trapezoids (op, pattern, @@ -1306,11 +1415,14 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, dst_x - state.image_rect.x, dst_y - state.image_rect.y, width, height, - traps, num_traps); - if (offset_traps) + traps, num_traps, + clip_region); + if (offset_traps != NULL) free (offset_traps); - DONE: + FAIL: + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); _fallback_fini (&state); return status; @@ -1335,7 +1447,9 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, new_surface = _cairo_surface_create_similar_scratch (surface, src->content & content, width, height); - if (new_surface->status) + if (new_surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (new_surface->status)) return new_surface->status; /* We have to copy these here, so that the coordinate spaces are correct */ @@ -1348,7 +1462,8 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, status = _cairo_surface_paint (new_surface, CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); + &pattern.base, + NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { |