| /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
| /* cairo - a vector graphics library with display and print output |
| * |
| * Copyright © 2002 University of Southern California |
| * Copyright © 2005 Red Hat, Inc. |
| * Copyright © 2011 Intel Corporation |
| * Copyright © 2011 Samsung Electronics |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it either under the terms of the GNU Lesser General Public |
| * License version 2.1 as published by the Free Software Foundation |
| * (the "LGPL") or, at your option, under the terms of the Mozilla |
| * Public License Version 1.1 (the "MPL"). If you do not alter this |
| * notice, a recipient may use your version of this file under either |
| * the MPL or the LGPL. |
| * |
| * You should have received a copy of the LGPL along with this library |
| * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
| * You should have received a copy of the MPL along with this library |
| * in the file COPYING-MPL-1.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License |
| * Version 1.1 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| * the specific language governing rights and limitations. |
| * |
| * The Original Code is the cairo graphics library. |
| * |
| * The Initial Developer of the Original Code is University of Southern |
| * California. |
| * |
| * Contributor(s): |
| * Henry Song <hsong@sisa.samsung.com> |
| * Martin Robinson <mrobinson@igalia.com> |
| */ |
| |
| #include "cairoint.h" |
| |
| #include "cairo-clip-inline.h" |
| #include "cairo-composite-rectangles-private.h" |
| #include "cairo-compositor-private.h" |
| #include "cairo-gl-private.h" |
| #include "cairo-path-private.h" |
| #include "cairo-traps-private.h" |
| |
| static cairo_bool_t |
| can_use_msaa_compositor (cairo_gl_surface_t *surface, |
| cairo_antialias_t antialias); |
| |
| static void |
| query_surface_capabilities (cairo_gl_surface_t *surface); |
| |
| struct _tristrip_composite_info { |
| cairo_gl_composite_t setup; |
| cairo_gl_context_t *ctx; |
| }; |
| |
| static cairo_int_status_t |
| _draw_trap (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| cairo_trapezoid_t *trap) |
| { |
| cairo_point_t quad[4]; |
| |
| quad[0].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, |
| &trap->left.p2, |
| trap->top); |
| quad[0].y = trap->top; |
| |
| quad[1].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, |
| &trap->left.p2, |
| trap->bottom); |
| quad[1].y = trap->bottom; |
| |
| quad[2].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, |
| &trap->right.p2, |
| trap->bottom); |
| quad[2].y = trap->bottom; |
| |
| quad[3].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, |
| &trap->right.p2, |
| trap->top); |
| quad[3].y = trap->top; |
| return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); |
| } |
| |
| static cairo_int_status_t |
| _draw_traps (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| cairo_traps_t *traps) |
| { |
| cairo_int_status_t status = CAIRO_STATUS_SUCCESS; |
| int i; |
| |
| for (i = 0; i < traps->num_traps; i++) { |
| cairo_trapezoid_t *trap = traps->traps + i; |
| if (unlikely ((status = _draw_trap (ctx, setup, trap)))) |
| return status; |
| } |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _draw_int_rect (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| cairo_rectangle_int_t *rect) |
| { |
| cairo_box_t box; |
| cairo_point_t quad[4]; |
| |
| _cairo_box_from_rectangle (&box, rect); |
| quad[0].x = box.p1.x; |
| quad[0].y = box.p1.y; |
| quad[1].x = box.p1.x; |
| quad[1].y = box.p2.y; |
| quad[2].x = box.p2.x; |
| quad[2].y = box.p2.y; |
| quad[3].x = box.p2.x; |
| quad[3].y = box.p1.y; |
| |
| return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); |
| } |
| |
| static cairo_int_status_t |
| _draw_triangle_fan (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| const cairo_point_t *midpt, |
| const cairo_point_t *points, |
| int npoints) |
| { |
| int i; |
| |
| /* Our strategy here is to not even try to build a triangle fan, but to |
| draw each triangle as if it was an unconnected member of a triangle strip. */ |
| for (i = 1; i < npoints; i++) { |
| cairo_int_status_t status; |
| cairo_point_t triangle[3]; |
| |
| triangle[0] = *midpt; |
| triangle[1] = points[i - 1]; |
| triangle[2] = points[i]; |
| |
| status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); |
| if (unlikely (status)) |
| return status; |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| _clip_to_traps (cairo_clip_t *clip, |
| cairo_traps_t *traps) |
| { |
| cairo_int_status_t status; |
| cairo_polygon_t polygon; |
| cairo_antialias_t antialias; |
| cairo_fill_rule_t fill_rule; |
| |
| _cairo_traps_init (traps); |
| |
| if (clip->num_boxes == 1 && clip->path == NULL) { |
| cairo_boxes_t boxes; |
| _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); |
| return _cairo_traps_init_boxes (traps, &boxes); |
| } |
| |
| status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); |
| if (unlikely (status)) |
| return status; |
| |
| /* We ignore the antialias mode of the clip here, since the user requested |
| * unantialiased rendering of their path and we expect that this stencil |
| * based rendering of the clip to be a reasonable approximation to |
| * the intersection between that clip and the path. |
| * |
| * In other words, what the user expects when they try to perform |
| * a geometric intersection between an unantialiased polygon and an |
| * antialiased polygon is open to interpretation. And we choose the fast |
| * option. |
| */ |
| |
| _cairo_traps_init (traps); |
| status = _cairo_bentley_ottmann_tessellate_polygon (traps, |
| &polygon, |
| fill_rule); |
| _cairo_polygon_fini (&polygon); |
| |
| return status; |
| } |
| |
| cairo_int_status_t |
| _cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| cairo_clip_t *clip) |
| { |
| cairo_int_status_t status; |
| cairo_traps_t traps; |
| |
| status = _clip_to_traps (clip, &traps); |
| if (unlikely (status)) |
| return status; |
| status = _draw_traps (ctx, setup, &traps); |
| |
| _cairo_traps_fini (&traps); |
| return status; |
| } |
| |
| static cairo_bool_t |
| _should_use_unbounded_surface (cairo_composite_rectangles_t *composite) |
| { |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| cairo_rectangle_int_t *source = &composite->source; |
| |
| if (composite->is_bounded) |
| return FALSE; |
| |
| /* This isn't just an optimization. It also detects when painting is used |
| to paint back the unbounded surface, preventing infinite recursion. */ |
| return ! (source->x <= 0 && source->y <= 0 && |
| source->height + source->y >= dst->height && |
| source->width + source->x >= dst->width); |
| } |
| |
| static cairo_surface_t* |
| _prepare_unbounded_surface (cairo_gl_surface_t *dst) |
| { |
| |
| cairo_surface_t* surface = cairo_gl_surface_create (dst->base.device, |
| dst->base.content, |
| dst->width, |
| dst->height); |
| if (surface == NULL) |
| return NULL; |
| if (unlikely (surface->status)) { |
| cairo_surface_destroy (surface); |
| return NULL; |
| } |
| return surface; |
| } |
| |
| static cairo_int_status_t |
| _paint_back_unbounded_surface (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite, |
| cairo_surface_t *surface) |
| { |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| cairo_int_status_t status; |
| |
| cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); |
| if (unlikely (pattern->status)) { |
| status = pattern->status; |
| goto finish; |
| } |
| |
| status = _cairo_compositor_paint (compositor, &dst->base, |
| composite->op, pattern, |
| composite->clip); |
| |
| finish: |
| cairo_pattern_destroy (pattern); |
| cairo_surface_destroy (surface); |
| return status; |
| } |
| |
| static cairo_bool_t |
| can_use_msaa_compositor (cairo_gl_surface_t *surface, |
| cairo_antialias_t antialias) |
| { |
| query_surface_capabilities (surface); |
| if (! surface->supports_stencil) |
| return FALSE; |
| |
| /* Multisampling OpenGL ES surfaces only maintain one multisampling |
| framebuffer and thus must use the spans compositor to do non-antialiased |
| rendering. */ |
| if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES |
| && surface->supports_msaa |
| && antialias == CAIRO_ANTIALIAS_NONE) |
| return FALSE; |
| |
| /* The MSAA compositor has a single-sample mode, so we can |
| support non-antialiased rendering. */ |
| if (antialias == CAIRO_ANTIALIAS_NONE) |
| return TRUE; |
| |
| if (antialias == CAIRO_ANTIALIAS_FAST || antialias == CAIRO_ANTIALIAS_DEFAULT) |
| return surface->supports_msaa; |
| return FALSE; |
| } |
| |
| static void |
| _cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite, |
| cairo_gl_composite_t *setup) |
| { |
| if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip)) |
| return; |
| _cairo_gl_composite_set_clip (setup, composite->clip); |
| } |
| |
| /* Masking with the SOURCE operator requires two passes. In the first |
| * pass we use the mask as the source to get: |
| * result = (1 - ma) * dst |
| * In the second pass we use the add operator to achieve: |
| * result = (src * ma) + dst |
| * Combined this produces: |
| * result = (src * ma) + (1 - ma) * dst |
| */ |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_mask_source_operator (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite) |
| { |
| cairo_gl_composite_t setup; |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| cairo_gl_context_t *ctx = NULL; |
| cairo_int_status_t status; |
| |
| cairo_clip_t *clip = composite->clip; |
| cairo_traps_t traps; |
| |
| /* If we have a non-rectangular clip, we can avoid using the stencil buffer |
| * for clipping and just draw the clip polygon. */ |
| if (clip) { |
| status = _clip_to_traps (clip, &traps); |
| if (unlikely (status)) { |
| _cairo_traps_fini (&traps); |
| return status; |
| } |
| } |
| |
| status = _cairo_gl_composite_init (&setup, |
| CAIRO_OPERATOR_DEST_OUT, |
| dst, |
| FALSE /* assume_component_alpha */); |
| if (unlikely (status)) |
| return status; |
| status = _cairo_gl_composite_set_source (&setup, |
| &composite->mask_pattern.base, |
| &composite->mask_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto finish; |
| _cairo_gl_composite_set_multisample (&setup); |
| status = _cairo_gl_composite_begin (&setup, &ctx); |
| if (unlikely (status)) |
| goto finish; |
| |
| if (! clip) |
| status = _draw_int_rect (ctx, &setup, &composite->bounded); |
| else |
| status = _draw_traps (ctx, &setup, &traps); |
| if (unlikely (status)) |
| goto finish; |
| |
| /* Now draw the second pass. */ |
| status = _cairo_gl_composite_set_operator (&setup, CAIRO_OPERATOR_ADD, |
| FALSE /* assume_component_alpha */); |
| if (unlikely (status)) |
| goto finish; |
| status = _cairo_gl_composite_set_source (&setup, |
| &composite->source_pattern.base, |
| &composite->source_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto finish; |
| status = _cairo_gl_composite_set_mask (&setup, |
| &composite->mask_pattern.base, |
| &composite->source_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto finish; |
| status = _cairo_gl_set_operands_and_operator (&setup, ctx); |
| if (unlikely (status)) |
| goto finish; |
| |
| if (! clip) |
| status = _draw_int_rect (ctx, &setup, &composite->bounded); |
| else |
| status = _draw_traps (ctx, &setup, &traps); |
| |
| finish: |
| _cairo_gl_composite_fini (&setup); |
| if (ctx) |
| status = _cairo_gl_context_release (ctx, status); |
| if (clip) |
| _cairo_traps_fini (&traps); |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite) |
| { |
| cairo_gl_composite_t setup; |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| cairo_gl_context_t *ctx = NULL; |
| cairo_int_status_t status; |
| cairo_operator_t op = composite->op; |
| cairo_clip_t *clip = composite->clip; |
| |
| if (! can_use_msaa_compositor (dst, CAIRO_ANTIALIAS_DEFAULT)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (composite->op == CAIRO_OPERATOR_CLEAR && |
| composite->original_mask_pattern != NULL) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| /* GL compositing operators cannot properly represent a mask operation |
| using the SOURCE compositing operator in one pass. This only matters if |
| there actually is a mask (there isn't in a paint operation) and if the |
| mask isn't totally opaque. */ |
| if (op == CAIRO_OPERATOR_SOURCE && |
| composite->original_mask_pattern != NULL && |
| ! _cairo_pattern_is_opaque (&composite->mask_pattern.base, |
| &composite->mask_sample_area)) { |
| |
| if (! _cairo_pattern_is_opaque (&composite->source_pattern.base, |
| &composite->source_sample_area)) { |
| return _cairo_gl_msaa_compositor_mask_source_operator (compositor, composite); |
| } |
| |
| /* If the source is opaque the operation reduces to OVER. */ |
| op = CAIRO_OPERATOR_OVER; |
| } |
| |
| if (_should_use_unbounded_surface (composite)) { |
| cairo_surface_t* surface = _prepare_unbounded_surface (dst); |
| |
| if (unlikely (surface == NULL)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| /* This may be a paint operation. */ |
| if (composite->original_mask_pattern == NULL) { |
| status = _cairo_compositor_paint (compositor, surface, |
| CAIRO_OPERATOR_SOURCE, |
| &composite->source_pattern.base, |
| NULL); |
| } else { |
| status = _cairo_compositor_mask (compositor, surface, |
| CAIRO_OPERATOR_SOURCE, |
| &composite->source_pattern.base, |
| &composite->mask_pattern.base, |
| NULL); |
| } |
| |
| if (unlikely (status)) { |
| cairo_surface_destroy (surface); |
| return status; |
| } |
| |
| return _paint_back_unbounded_surface (compositor, composite, surface); |
| } |
| |
| status = _cairo_gl_composite_init (&setup, |
| op, |
| dst, |
| FALSE /* assume_component_alpha */); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_gl_composite_set_source (&setup, |
| &composite->source_pattern.base, |
| &composite->source_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto finish; |
| |
| if (composite->original_mask_pattern != NULL) { |
| status = _cairo_gl_composite_set_mask (&setup, |
| &composite->mask_pattern.base, |
| &composite->mask_sample_area, |
| &composite->bounded, |
| FALSE); |
| } |
| if (unlikely (status)) |
| goto finish; |
| |
| /* We always use multisampling here, because we do not yet have the smarts |
| to calculate when the clip or the source requires it. */ |
| _cairo_gl_composite_set_multisample (&setup); |
| |
| status = _cairo_gl_composite_begin (&setup, &ctx); |
| if (unlikely (status)) |
| goto finish; |
| |
| if (! clip) |
| status = _draw_int_rect (ctx, &setup, &composite->bounded); |
| else |
| status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip); |
| |
| finish: |
| _cairo_gl_composite_fini (&setup); |
| |
| if (ctx) |
| status = _cairo_gl_context_release (ctx, status); |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_paint (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite) |
| { |
| return _cairo_gl_msaa_compositor_mask (compositor, composite); |
| } |
| |
| static cairo_status_t |
| _stroke_shaper_add_triangle (void *closure, |
| const cairo_point_t triangle[3]) |
| { |
| struct _tristrip_composite_info *info = closure; |
| return _cairo_gl_composite_emit_triangle_as_tristrip (info->ctx, |
| &info->setup, |
| triangle); |
| } |
| |
| static cairo_status_t |
| _stroke_shaper_add_triangle_fan (void *closure, |
| const cairo_point_t *midpoint, |
| const cairo_point_t *points, |
| int npoints) |
| { |
| struct _tristrip_composite_info *info = closure; |
| return _draw_triangle_fan (info->ctx, &info->setup, |
| midpoint, points, npoints); |
| } |
| |
| static cairo_status_t |
| _stroke_shaper_add_quad (void *closure, |
| const cairo_point_t quad[4]) |
| { |
| struct _tristrip_composite_info *info = closure; |
| return _cairo_gl_composite_emit_quad_as_tristrip (info->ctx, &info->setup, |
| quad); |
| } |
| |
| static cairo_int_status_t |
| _prevent_overlapping_strokes (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| cairo_composite_rectangles_t *composite, |
| const cairo_path_fixed_t *path, |
| const cairo_stroke_style_t *style, |
| const cairo_matrix_t *ctm) |
| { |
| cairo_rectangle_int_t stroke_extents; |
| |
| if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (_cairo_pattern_is_opaque (&composite->source_pattern.base, |
| &composite->source_sample_area)) |
| return CAIRO_INT_STATUS_SUCCESS; |
| |
| if (glIsEnabled (GL_STENCIL_TEST) == FALSE) { |
| cairo_bool_t scissor_was_enabled; |
| |
| /* In case we have pending operations we have to flush before |
| adding the stencil buffer. */ |
| _cairo_gl_composite_flush (ctx); |
| |
| /* Enable the stencil buffer, even if we are not using it for clipping, |
| so we can use it below to prevent overlapping shapes. We initialize |
| it all to one here which represents infinite clip. */ |
| glDepthMask (GL_TRUE); |
| glEnable (GL_STENCIL_TEST); |
| |
| /* We scissor here so that we don't have to clear the entire stencil |
| * buffer. If the scissor test is already enabled, it was enabled |
| * for clipping. In that case, instead of calculating an intersection, |
| * we just reuse it, and risk clearing too much. */ |
| scissor_was_enabled = glIsEnabled (GL_SCISSOR_TEST); |
| if (! scissor_was_enabled) { |
| _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, |
| &stroke_extents); |
| _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents); |
| } |
| glClearStencil (1); |
| glClear (GL_STENCIL_BUFFER_BIT); |
| if (! scissor_was_enabled) |
| glDisable (GL_SCISSOR_TEST); |
| |
| glStencilFunc (GL_EQUAL, 1, 1); |
| } |
| |
| /* This means that once we draw to a particular pixel nothing else can |
| be drawn there until the stencil buffer is reset or the stencil test |
| is disabled. */ |
| glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO); |
| |
| _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); |
| setup->dst->clip_on_stencil_buffer = NULL; |
| |
| return CAIRO_INT_STATUS_SUCCESS; |
| } |
| |
| static void |
| query_surface_capabilities (cairo_gl_surface_t *surface) |
| { |
| GLint samples, stencil_bits; |
| cairo_gl_context_t *ctx; |
| cairo_int_status_t status; |
| |
| /* Texture surfaces are create in such a way that they always |
| have stencil and multisample bits if possible, so we don't |
| need to query their capabilities lazily. */ |
| if (_cairo_gl_surface_is_texture (surface)) |
| return; |
| if (surface->stencil_and_msaa_caps_initialized) |
| return; |
| |
| surface->stencil_and_msaa_caps_initialized = TRUE; |
| surface->supports_stencil = FALSE; |
| surface->supports_msaa = FALSE; |
| |
| status = _cairo_gl_context_acquire (surface->base.device, &ctx); |
| if (unlikely (status)) |
| return; |
| |
| _cairo_gl_context_set_destination (ctx, surface, FALSE); |
| |
| glGetIntegerv(GL_SAMPLES, &samples); |
| glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); |
| surface->supports_stencil = stencil_bits > 0; |
| surface->supports_msaa = samples > 1; |
| |
| status = _cairo_gl_context_release (ctx, status); |
| } |
| |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite, |
| const cairo_path_fixed_t *path, |
| const cairo_stroke_style_t *style, |
| const cairo_matrix_t *ctm, |
| const cairo_matrix_t *ctm_inverse, |
| double tolerance, |
| cairo_antialias_t antialias) |
| { |
| cairo_int_status_t status; |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| struct _tristrip_composite_info info; |
| |
| if (! can_use_msaa_compositor (dst, antialias)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (composite->is_bounded == FALSE) { |
| cairo_surface_t* surface = _prepare_unbounded_surface (dst); |
| |
| if (unlikely (surface == NULL)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| status = _cairo_compositor_stroke (compositor, surface, |
| CAIRO_OPERATOR_SOURCE, |
| &composite->source_pattern.base, |
| path, style, ctm, ctm_inverse, |
| tolerance, antialias, NULL); |
| if (unlikely (status)) { |
| cairo_surface_destroy (surface); |
| return status; |
| } |
| |
| return _paint_back_unbounded_surface (compositor, composite, surface); |
| } |
| |
| status = _cairo_gl_composite_init (&info.setup, |
| composite->op, |
| dst, |
| FALSE /* assume_component_alpha */); |
| if (unlikely (status)) |
| return status; |
| |
| info.ctx = NULL; |
| |
| status = _cairo_gl_composite_set_source (&info.setup, |
| &composite->source_pattern.base, |
| &composite->source_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto finish; |
| |
| _cairo_gl_msaa_compositor_set_clip (composite, &info.setup); |
| if (antialias != CAIRO_ANTIALIAS_NONE) |
| _cairo_gl_composite_set_multisample (&info.setup); |
| |
| status = _cairo_gl_composite_begin (&info.setup, &info.ctx); |
| if (unlikely (status)) |
| goto finish; |
| |
| status = _prevent_overlapping_strokes (info.ctx, &info.setup, |
| composite, path, style, ctm); |
| if (unlikely (status)) |
| goto finish; |
| |
| status = _cairo_path_fixed_stroke_to_shaper ((cairo_path_fixed_t *) path, |
| style, |
| ctm, |
| ctm_inverse, |
| tolerance, |
| _stroke_shaper_add_triangle, |
| _stroke_shaper_add_triangle_fan, |
| _stroke_shaper_add_quad, |
| &info); |
| if (unlikely (status)) |
| goto finish; |
| |
| finish: |
| _cairo_gl_composite_fini (&info.setup); |
| |
| if (info.ctx) |
| status = _cairo_gl_context_release (info.ctx, status); |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _draw_simple_quad_path (cairo_gl_context_t *ctx, |
| cairo_gl_composite_t *setup, |
| const cairo_path_fixed_t *path) |
| { |
| cairo_point_t triangle[3]; |
| cairo_int_status_t status; |
| const cairo_point_t *points; |
| |
| points = cairo_path_head (path)->points; |
| triangle[0] = points[0]; |
| triangle[1] = points[1]; |
| triangle[2] = points[2]; |
| status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); |
| if (status) |
| return status; |
| |
| triangle[0] = points[2]; |
| triangle[1] = points[3]; |
| triangle[2] = points[0]; |
| return _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); |
| } |
| |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite, |
| const cairo_path_fixed_t *path, |
| cairo_fill_rule_t fill_rule, |
| double tolerance, |
| cairo_antialias_t antialias) |
| { |
| cairo_gl_composite_t setup; |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| cairo_gl_context_t *ctx = NULL; |
| cairo_int_status_t status; |
| cairo_traps_t traps; |
| cairo_bool_t draw_path_with_traps; |
| |
| if (! can_use_msaa_compositor (dst, antialias)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (composite->is_bounded == FALSE) { |
| cairo_surface_t* surface = _prepare_unbounded_surface (dst); |
| |
| if (unlikely (surface == NULL)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| |
| status = _cairo_compositor_fill (compositor, surface, |
| CAIRO_OPERATOR_SOURCE, |
| &composite->source_pattern.base, |
| path, fill_rule, tolerance, |
| antialias, NULL); |
| |
| if (unlikely (status)) { |
| cairo_surface_destroy (surface); |
| return status; |
| } |
| |
| return _paint_back_unbounded_surface (compositor, composite, surface); |
| } |
| |
| draw_path_with_traps = ! _cairo_path_fixed_is_simple_quad (path); |
| |
| if (draw_path_with_traps) { |
| _cairo_traps_init (&traps); |
| status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps); |
| if (unlikely (status)) |
| goto cleanup_traps; |
| } |
| |
| status = _cairo_gl_composite_init (&setup, |
| composite->op, |
| dst, |
| FALSE /* assume_component_alpha */); |
| if (unlikely (status)) |
| goto cleanup_traps; |
| |
| status = _cairo_gl_composite_set_source (&setup, |
| &composite->source_pattern.base, |
| &composite->source_sample_area, |
| &composite->bounded, |
| FALSE); |
| if (unlikely (status)) |
| goto cleanup_setup; |
| |
| _cairo_gl_msaa_compositor_set_clip (composite, &setup); |
| if (antialias != CAIRO_ANTIALIAS_NONE) |
| _cairo_gl_composite_set_multisample (&setup); |
| |
| status = _cairo_gl_composite_begin (&setup, &ctx); |
| if (unlikely (status)) |
| goto cleanup_setup; |
| |
| if (! draw_path_with_traps) |
| status = _draw_simple_quad_path (ctx, &setup, path); |
| else |
| status = _draw_traps (ctx, &setup, &traps); |
| if (unlikely (status)) |
| goto cleanup_setup; |
| |
| cleanup_setup: |
| _cairo_gl_composite_fini (&setup); |
| |
| if (ctx) |
| status = _cairo_gl_context_release (ctx, status); |
| |
| cleanup_traps: |
| if (draw_path_with_traps) |
| _cairo_traps_fini (&traps); |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_gl_msaa_compositor_glyphs (const cairo_compositor_t *compositor, |
| cairo_composite_rectangles_t *composite, |
| cairo_scaled_font_t *scaled_font, |
| cairo_glyph_t *glyphs, |
| int num_glyphs, |
| cairo_bool_t overlap) |
| { |
| cairo_int_status_t status; |
| cairo_surface_t *src = NULL; |
| int src_x, src_y; |
| cairo_composite_glyphs_info_t info; |
| |
| cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; |
| |
| query_surface_capabilities (dst); |
| if (! dst->supports_stencil) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (composite->op == CAIRO_OPERATOR_CLEAR) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (composite->is_bounded == FALSE) { |
| cairo_surface_t* surface = _prepare_unbounded_surface (dst); |
| |
| if (unlikely (surface == NULL)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| status = _cairo_compositor_glyphs (compositor, surface, |
| CAIRO_OPERATOR_SOURCE, |
| &composite->source_pattern.base, |
| glyphs, num_glyphs, |
| scaled_font, composite->clip); |
| |
| if (unlikely (status)) { |
| cairo_surface_destroy (surface); |
| return status; |
| } |
| |
| return _paint_back_unbounded_surface (compositor, composite, surface); |
| } |
| |
| src = _cairo_gl_pattern_to_source (&dst->base, |
| &composite->source_pattern.base, |
| FALSE, |
| &composite->bounded, |
| &composite->source_sample_area, |
| &src_x, &src_y); |
| if (unlikely (src->status)) { |
| status = src->status; |
| goto finish; |
| } |
| |
| status = _cairo_gl_check_composite_glyphs (composite, |
| scaled_font, glyphs, |
| &num_glyphs); |
| if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) |
| goto finish; |
| |
| info.font = scaled_font; |
| info.glyphs = glyphs; |
| info.num_glyphs = num_glyphs; |
| info.use_mask = overlap || ! composite->is_bounded || |
| composite->op == CAIRO_OPERATOR_SOURCE; |
| info.extents = composite->bounded; |
| |
| _cairo_scaled_font_freeze_cache (scaled_font); |
| status = _cairo_gl_composite_glyphs_with_clip (dst, composite->op, |
| src, src_x, src_y, |
| 0, 0, &info, |
| composite->clip); |
| |
| _cairo_scaled_font_thaw_cache (scaled_font); |
| |
| finish: |
| if (src) |
| cairo_surface_destroy (src); |
| |
| return status; |
| } |
| |
| static void |
| _cairo_gl_msaa_compositor_init (cairo_compositor_t *compositor, |
| const cairo_compositor_t *delegate) |
| { |
| compositor->delegate = delegate; |
| |
| compositor->paint = _cairo_gl_msaa_compositor_paint; |
| compositor->mask = _cairo_gl_msaa_compositor_mask; |
| compositor->fill = _cairo_gl_msaa_compositor_fill; |
| compositor->stroke = _cairo_gl_msaa_compositor_stroke; |
| compositor->glyphs = _cairo_gl_msaa_compositor_glyphs; |
| } |
| |
| const cairo_compositor_t * |
| _cairo_gl_msaa_compositor_get (void) |
| { |
| static cairo_compositor_t compositor; |
| if (compositor.delegate == NULL) |
| _cairo_gl_msaa_compositor_init (&compositor, |
| _cairo_gl_span_compositor_get ()); |
| |
| return &compositor; |
| } |