static cairo_status_t _combine_region (cairo_surface_t *surface, const cairo_region_t *region, const cairo_rectangle_int_t *extents) { cairo_region_t clear_region; cairo_status_t status; _cairo_region_init_rectangle (&clear_region, extents); status = cairo_region_subtract (&clear_region, region); if (unlikely (status)) return status; if (! cairo_region_is_empty (&clear_region)) { cairo_region_translate (&clear_region, -extents->x, -extents->y); status = _cairo_surface_fill_region (surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear_region); } _cairo_region_fini (&clear_region); return status; }
/* Warning: This call modifies the coordinates of traps */ static cairo_status_t _clip_and_composite_trapezoids (cairo_pattern_t *src, cairo_operator_t op, cairo_surface_t *dst, cairo_traps_t *traps, cairo_clip_t *clip, cairo_antialias_t antialias) { cairo_status_t status; cairo_region_t trap_region; cairo_region_t clear_region; cairo_bool_t has_trap_region = FALSE; cairo_bool_t has_clear_region = FALSE; cairo_rectangle_int_t extents; cairo_composite_traps_info_t traps_info; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_get_extents (dst, &extents); if (status) return status; status = _cairo_traps_extract_region (traps, &trap_region); if (CAIRO_INT_STATUS_UNSUPPORTED == status) { has_trap_region = FALSE; } else if (status) { return status; } else { has_trap_region = TRUE; } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t trap_extents; if (has_trap_region) { status = _cairo_clip_intersect_to_region (clip, &trap_region); if (status) goto out; _cairo_region_get_extents (&trap_region, &trap_extents); } else { cairo_box_t trap_box; _cairo_traps_extents (traps, &trap_box); _cairo_box_round_to_rectangle (&trap_box, &trap_extents); } _cairo_rectangle_intersect (&extents, &trap_extents); status = _cairo_clip_intersect_to_rectangle (clip, &extents); if (status) goto out; } else { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if (has_trap_region && !clip_surface) { /* If we optimize drawing with an unbounded operator to * _cairo_surface_fill_rectangles() or to drawing with a * clip region, then we have an additional region to clear. */ _cairo_region_init_rect (&clear_region, &extents); has_clear_region = TRUE; status = _cairo_clip_intersect_to_region (clip, &clear_region); if (status) goto out; _cairo_region_get_extents (&clear_region, &extents); status = _cairo_region_subtract (&clear_region, &clear_region, &trap_region); if (status) goto out; if (!_cairo_region_not_empty (&clear_region)) { _cairo_region_fini (&clear_region); has_clear_region = FALSE; } } else { status = _cairo_clip_intersect_to_rectangle (clip, &extents); } } if (status) goto out; if (has_trap_region) { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && !clip_surface) { const cairo_color_t *color; if (op == CAIRO_OPERATOR_CLEAR) { color = CAIRO_COLOR_TRANSPARENT; } else { color = &((cairo_solid_pattern_t *)src)->color; } /* Solid rectangles special case */ status = _cairo_surface_fill_region (dst, op, color, &trap_region); if (!status && has_clear_region) status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear_region); goto out; } if ((_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE) || !clip_surface) { /* For a simple rectangle, we can just use composite(), for more * rectangles, we have to set a clip region. The cost of rasterizing * trapezoids is pretty high for most backends currently, so it's * worthwhile even if a region is needed. * * If we have a clip surface, we set it as the mask; this only works * for bounded operators other than SOURCE; for unbounded operators, * clip and mask cannot be interchanged. For SOURCE, the operator * as implemented by the backends is different in it's handling * of the mask then what we want. * * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has * more than rectangle and the destination doesn't support clip * regions. In that case, we fall through. */ status = _composite_trap_region (clip, src, op, dst, &trap_region, &extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { if (!status && has_clear_region) status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear_region); goto out; } } } traps_info.traps = traps; traps_info.antialias = antialias; status = _clip_and_composite (clip, op, src, _composite_traps_draw_func, &traps_info, dst, &extents); out: if (has_trap_region) _cairo_region_fini (&trap_region); if (has_clear_region) _cairo_region_fini (&clear_region); return status; }
static cairo_surface_t * _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path, cairo_surface_t *target) { cairo_surface_t *surface; cairo_pattern_union_t pattern; cairo_status_t status; const cairo_rectangle_int_t *clip_extents = &clip_path->extents; cairo_clip_path_t *prev; cairo_bool_t need_translate; if (clip_path->surface != NULL && clip_path->surface->backend == target->backend) { return cairo_surface_reference (clip_path->surface); } surface = _cairo_surface_create_similar_solid (target, CAIRO_CONTENT_ALPHA, clip_extents->width, clip_extents->height, CAIRO_COLOR_TRANSPARENT, FALSE); if (surface == NULL) { if (clip_path->surface != NULL && clip_path->surface->backend == &_cairo_image_surface_backend) { return cairo_surface_reference (clip_path->surface); } surface = _cairo_image_surface_create_with_content (CAIRO_CONTENT_ALPHA, clip_extents->width, clip_extents->height); } if (unlikely (surface->status)) return surface; _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_COLOR); status = _cairo_clip_path_to_region (clip_path); if (unlikely (_cairo_status_is_error (status))) goto BAIL; need_translate = clip_extents->x | clip_extents->y; if (status == CAIRO_STATUS_SUCCESS) { if (need_translate) { cairo_region_translate (clip_path->region, -clip_extents->x, -clip_extents->y); } status = _cairo_surface_fill_region (surface, CAIRO_OPERATOR_SOURCE, CAIRO_COLOR_WHITE, clip_path->region); if (need_translate) { cairo_region_translate (clip_path->region, clip_extents->x, clip_extents->y); } if (unlikely (status)) goto BAIL; goto DONE; } else { if (need_translate) { _cairo_path_fixed_translate (&clip_path->path, _cairo_fixed_from_int (-clip_extents->x), _cairo_fixed_from_int (-clip_extents->y)); } status = _cairo_surface_fill (surface, CAIRO_OPERATOR_OVER, &pattern.base, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias, NULL); if (need_translate) { _cairo_path_fixed_translate (&clip_path->path, _cairo_fixed_from_int (clip_extents->x), _cairo_fixed_from_int (clip_extents->y)); } if (unlikely (status)) goto BAIL; } prev = clip_path->prev; NEXT_PATH: if (prev != NULL) { status = _cairo_clip_path_to_region (prev); if (unlikely (_cairo_status_is_error (status))) goto BAIL; if (status == CAIRO_STATUS_SUCCESS) { status = _combine_region (surface, prev->region, clip_extents); if (unlikely (status)) goto BAIL; } else if (prev->flags & CAIRO_CLIP_PATH_IS_BOX) { /* a simple box only affects the extents */ } else if (prev->path.is_rectilinear) { if (need_translate) { _cairo_path_fixed_translate (&prev->path, _cairo_fixed_from_int (-clip_extents->x), _cairo_fixed_from_int (-clip_extents->y)); } status = _cairo_surface_fill (surface, CAIRO_OPERATOR_IN, &pattern.base, &prev->path, prev->fill_rule, prev->tolerance, prev->antialias, NULL); if (need_translate) { _cairo_path_fixed_translate (&prev->path, _cairo_fixed_from_int (clip_extents->x), _cairo_fixed_from_int (clip_extents->y)); } if (unlikely (status)) goto BAIL; prev = prev->prev; goto NEXT_PATH; } else { cairo_surface_t *prev_surface; prev_surface = _cairo_clip_path_get_surface (prev, target); _cairo_pattern_init_for_surface (&pattern.surface, prev_surface); cairo_surface_destroy (prev_surface); cairo_matrix_init_translate (&pattern.base.matrix, -prev->extents.x + clip_extents->x, -prev->extents.y + clip_extents->y); status = _cairo_surface_paint (surface, CAIRO_OPERATOR_IN, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto BAIL; } } DONE: cairo_surface_destroy (clip_path->surface); return clip_path->surface = cairo_surface_reference (surface); BAIL: cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); }