diff --git a/src/cairo-compiler-private.h b/src/cairo-compiler-private.h index 96034d6e2..dfe114bfa 100644 --- a/src/cairo-compiler-private.h +++ b/src/cairo-compiler-private.h @@ -183,17 +183,8 @@ #endif #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -#define _CAIRO_BOOLEAN_EXPR(expr) \ - __extension__ ({ \ - int _cairo_boolean_var_; \ - if (expr) \ - _cairo_boolean_var_ = 1; \ - else \ - _cairo_boolean_var_ = 0; \ - _cairo_boolean_var_; \ -}) -#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1)) -#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0)) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) #else #define likely(expr) (expr) #define unlikely(expr) (expr) diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 27912b38c..c259fe912 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -42,6 +42,8 @@ #include "cairoint.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-gl-private.h" @@ -1226,3 +1228,332 @@ _cairo_gl_composite_init (cairo_gl_composite_t *setup, return CAIRO_STATUS_SUCCESS; } +static cairo_bool_t +cairo_boxes_for_each_box (cairo_boxes_t *boxes, + cairo_bool_t (*func) (cairo_box_t *box, + void *data), + void *data) +{ + struct _cairo_boxes_chunk *chunk; + int i; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) + if (! func (&chunk->base[i], data)) + return FALSE; + } + + return TRUE; +} + +struct image_contains_box { + int width, height; + int tx, ty; +}; + +static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure) +{ + struct image_contains_box *data = closure; + + return + _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 && + _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 && + _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width && + _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height; +} + +struct image_upload_box { + cairo_gl_surface_t *surface; + cairo_image_surface_t *image; + int tx, ty; +}; + +static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) +{ + const struct image_upload_box *iub = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int w = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int h = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + return _cairo_gl_surface_draw_image (iub->surface, + iub->image, + x + iub->tx, y + iub->ty, + w, h, + x, y) == CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_upload_image_inplace (cairo_gl_surface_t *surface, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct image_contains_box icb; + struct image_upload_box iub; + cairo_image_surface_t *image; + int tx, ty; + + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = (cairo_image_surface_t *) pattern->surface; + if (image->format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + icb.width = image->width; + icb.height = image->height; + icb.tx = tx; + icb.ty = ty; + if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + iub.surface = surface; + iub.image = image; + iub.tx = tx; + iub.ty = ty; + cairo_boxes_for_each_box (boxes, image_upload_box, &iub); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t composite_box (cairo_box_t *box, void *closure) +{ + _cairo_gl_composite_emit_rect (closure, + _cairo_fixed_integer_part (box->p1.x), + _cairo_fixed_integer_part (box->p1.y), + _cairo_fixed_integer_part (box->p2.x), + _cairo_fixed_integer_part (box->p2.y), + 0); + return TRUE; +} + +static cairo_status_t +_composite_boxes (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + cairo_bool_t need_clip_mask = FALSE; + cairo_gl_composite_t setup; + cairo_gl_context_t *ctx; + cairo_surface_pattern_t mask; + cairo_status_t status; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (extents->clip->path && + (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! extents->is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_gl_composite_init (&setup, op, dst, FALSE, + &extents->bounded); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_set_source (&setup, src, + extents->bounded.x, + extents->bounded.y, + extents->bounded.x, + extents->bounded.y, + extents->bounded.width, + extents->bounded.height); + if (unlikely (status)) + goto CLEANUP; + + need_clip_mask = extents->clip->path != NULL; + if (need_clip_mask) { + cairo_surface_t *clip_surface; + int clip_x, clip_y; + + clip_surface = _cairo_clip_get_surface (extents->clip, + &dst->base, + &clip_x, &clip_y); + if (unlikely (clip_surface->status)) { + status = clip_surface->status; + need_clip_mask = FALSE; + goto CLEANUP; + } + + _cairo_pattern_init_for_surface (&mask, clip_surface); + mask.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&mask.base.matrix, + -clip_x, + -clip_y); + cairo_surface_destroy (clip_surface); + + if (op == CAIRO_OPERATOR_CLEAR) { + src = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + + status = _cairo_gl_composite_set_mask (&setup, &mask.base, + extents->bounded.x, + extents->bounded.y, + extents->bounded.x, + extents->bounded.y, + extents->bounded.width, + extents->bounded.height); + if (unlikely (status)) + goto CLEANUP; + } + + status = _cairo_gl_composite_begin (&setup, &ctx); + if (unlikely (status)) + goto CLEANUP; + + cairo_boxes_for_each_box (boxes, composite_box, ctx); + + status = _cairo_gl_context_release (ctx, status); + + CLEANUP: + if (need_clip_mask) + _cairo_pattern_fini (&mask.base); + + _cairo_gl_composite_fini (&setup); + + return status; +} + +/* XXX _cairo_gl_clip_and_composite_polygon() */ +cairo_int_status_t +_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents) +{ + cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); + + if (! _cairo_clip_is_region (extents->clip)) + return UNSUPPORTED ("a clip surface would be required"); + + if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) + return UNSUPPORTED ("no span renderer"); + + if (op == CAIRO_OPERATOR_SOURCE) + return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + src = &_cairo_pattern_white.base; + } + + return _cairo_surface_composite_polygon (&dst->base, + op, + src, + fill_rule, + antialias, + extents, + polygon, + clip_region); +} + +cairo_int_status_t +_cairo_gl_clip_and_composite_boxes (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_composite_rectangles_t *extents) +{ + cairo_int_status_t status; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) && + (op == CAIRO_OPERATOR_SOURCE || + (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)))) + { + if (boxes->num_boxes == 1 && + extents->bounded.width == dst->width && + extents->bounded.height == dst->height) + { + op = CAIRO_OPERATOR_SOURCE; +#if 0 + dst->deferred_clear = FALSE; +#endif + } + + status = _upload_image_inplace (dst, src, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ + if (extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_clip_t *clip; + + clip = _cairo_clip_copy (extents->clip); + clip = _cairo_clip_intersect_boxes (clip, boxes); + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &antialias); + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + extents->clip = clip; + status = _cairo_gl_surface_polygon (dst, op, src, + &polygon, + fill_rule, + antialias, + extents); + if (extents->clip != clip) + clip = NULL; + extents->clip = saved_clip; + _cairo_polygon_fini (&polygon); + } + if (clip) + _cairo_clip_destroy (clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + +#if 0 + if (dst->deferred_clear) { + status = _cairo_gl_surface_clear (dst); + if (unlikely (status)) + return status; + } +#endif + + if (boxes->is_pixel_aligned && + _cairo_clip_is_region (extents->clip) && + op == CAIRO_OPERATOR_SOURCE) { + status = _upload_image_inplace (dst, src, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise XXX */ + return CAIRO_INT_STATUS_UNSUPPORTED; +} diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index c83d78f25..ee79b23ae 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -594,6 +594,26 @@ _cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand); cairo_private cairo_extend_t _cairo_gl_operand_get_extend (cairo_gl_operand_t *operand); +cairo_private cairo_bool_t +_cairo_gl_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +cairo_private cairo_int_status_t +_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents); + +cairo_private cairo_int_status_t +_cairo_gl_clip_and_composite_boxes (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_composite_rectangles_t *extents); + slim_hidden_proto (cairo_gl_surface_create); slim_hidden_proto (cairo_gl_surface_create_for_texture); diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index fde933579..09a158917 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -1581,7 +1581,7 @@ FAIL: return _cairo_span_renderer_create_in_error (status); } -cairo_private cairo_bool_t +cairo_bool_t _cairo_gl_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1632,6 +1632,12 @@ _cairo_gl_surface_paint (void *abstract_surface, const cairo_pattern_t *source, const cairo_clip_t *clip) { + cairo_gl_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t unbounded; + cairo_boxes_t boxes; + cairo_status_t status; + /* simplify the common case of clearing the surface */ if (clip == NULL) { if (op == CAIRO_OPERATOR_CLEAR) @@ -1644,41 +1650,22 @@ _cairo_gl_surface_paint (void *abstract_surface, } } - return CAIRO_INT_STATUS_UNSUPPORTED; -} + _cairo_gl_surface_get_extents (surface, &unbounded); + status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, + op, source, + clip); + if (unlikely (status)) + return status; -static cairo_int_status_t -_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *extents) -{ - cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); - - if (! _cairo_clip_is_region (extents->clip)) - return UNSUPPORTED ("a clip surface would be required"); - - if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) - return UNSUPPORTED ("no span renderer"); - - if (op == CAIRO_OPERATOR_SOURCE) - return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); - if (op == CAIRO_OPERATOR_CLEAR) { - op = CAIRO_OPERATOR_DEST_OUT; - src = &_cairo_pattern_white.base; + status = _cairo_clip_to_boxes(extents.clip, &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_gl_clip_and_composite_boxes (surface, op, source, + &boxes, &extents); } - return _cairo_surface_composite_polygon (&dst->base, - op, - src, - fill_rule, - antialias, - extents, - polygon, - clip_region); + _cairo_composite_rectangles_fini (&extents); + + return status; } static cairo_int_status_t @@ -1696,8 +1683,7 @@ _cairo_gl_surface_stroke (void *abstract_surface, cairo_gl_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_rectangle_int_t unbounded; - cairo_polygon_t polygon; - cairo_status_t status; + cairo_int_status_t status; _cairo_gl_surface_get_extents (surface, &unbounded); status = _cairo_composite_rectangles_init_for_stroke (&extents, @@ -1708,18 +1694,40 @@ _cairo_gl_surface_stroke (void *abstract_surface, if (unlikely (status)) return status; - _cairo_polygon_init_with_clip (&polygon, extents.clip); - status = _cairo_path_fixed_stroke_to_polygon (path, - style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents); + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents.clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_gl_clip_and_composite_boxes (surface, op, source, + &boxes, &extents); + } + _cairo_boxes_fini (&boxes); + } + + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + + _cairo_polygon_init_with_clip (&polygon, extents.clip); + status = _cairo_path_fixed_stroke_to_polygon (path, + style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_gl_surface_polygon (surface, op, source, &polygon, + CAIRO_FILL_RULE_WINDING, antialias, + &extents); + } + _cairo_polygon_fini (&polygon); } - _cairo_polygon_fini (&polygon); _cairo_composite_rectangles_fini (&extents); @@ -1739,8 +1747,7 @@ _cairo_gl_surface_fill (void *abstract_surface, cairo_gl_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_rectangle_int_t unbounded; - cairo_polygon_t polygon; - cairo_status_t status; + cairo_int_status_t status; _cairo_gl_surface_get_extents (surface, &unbounded); status = _cairo_composite_rectangles_init_for_fill (&extents, @@ -1750,14 +1757,34 @@ _cairo_gl_surface_fill (void *abstract_surface, if (unlikely (status)) return status; - _cairo_polygon_init_with_clip (&polygon, extents.clip); - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents); + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents.clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_gl_clip_and_composite_boxes (surface, op, source, + &boxes, &extents); + } + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + + _cairo_polygon_init_with_clip (&polygon, extents.clip); + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_gl_surface_polygon (surface, op, source, &polygon, + fill_rule, antialias, + &extents); + } + _cairo_polygon_fini (&polygon); } - _cairo_polygon_fini (&polygon); _cairo_composite_rectangles_fini (&extents); diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index bb9b2dd1b..478695f8c 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -585,8 +585,14 @@ _command_init (cairo_recording_surface_t *surface, command->index = surface->commands.num_elements; /* steal the clip */ - command->clip = composite->clip; - composite->clip = NULL; + command->clip = NULL; + if (! _cairo_clip_is_region (composite->clip) || + cairo_region_contains_rectangle (_cairo_clip_get_region (composite->clip), + &composite->unbounded) != CAIRO_REGION_OVERLAP_IN) + { + command->clip = composite->clip; + composite->clip = NULL; + } return status; }