summaryrefslogtreecommitdiff
path: root/src/cairo-gl-msaa-compositor.c
diff options
context:
space:
mode:
authorhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
committerhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
commit4078c98a5d481778482f52d3aaf7a1777ffe6088 (patch)
treec18ae9d21e2b29c349231c5b61d742e9fca9c1d9 /src/cairo-gl-msaa-compositor.c
parentcce6a0e298fac08c588204b085e7a807fa75813d (diff)
downloadcairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.gz
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.bz2
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.zip
Cairo 1.12.14
Change-Id: Ibc39e63896ec42cab29fbbbf615a46f2d58319a8 Signed-off-by: hk57.kim <hk57.kim@samsung.com>
Diffstat (limited to 'src/cairo-gl-msaa-compositor.c')
-rwxr-xr-xsrc/cairo-gl-msaa-compositor.c1284
1 files changed, 1284 insertions, 0 deletions
diff --git a/src/cairo-gl-msaa-compositor.c b/src/cairo-gl-msaa-compositor.c
new file mode 100755
index 000000000..7ce5c7a4b
--- /dev/null
+++ b/src/cairo-gl-msaa-compositor.c
@@ -0,0 +1,1284 @@
+/* -*- 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_bool_t
+_is_continuous_single_line (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style)
+{
+ return (_cairo_path_fixed_is_single_line (path) &&
+ style->dash == NULL);
+}
+
+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];
+
+ if (trap->left.p1.x == trap->left.p2.x) {
+ quad[0].x = trap->left.p1.x;
+ quad[1].x = trap->left.p1.x;
+ } else {
+ cairo_fixed_t x, dy;
+ x = trap->left.p1.x;
+ dy = trap->left.p2.y - trap->left.p1.y;
+
+ if (trap->top == trap->left.p1.y)
+ quad[0].x = x;
+ else if (trap->top == trap->left.p2.y)
+ quad[0].x = trap->left.p2.x;
+ else if (dy != 0)
+ quad[0].x = x + _cairo_fixed_mul_div_floor (trap->top - trap->left.p1.y,
+ trap->left.p2.x - trap->left.p1.x, dy);
+
+ if (trap->bottom == trap->left.p2.y)
+ quad[1].x = trap->left.p2.x;
+ else if (trap->bottom == trap->left.p1.y)
+ quad[1].x = x;
+ else if (dy != 0)
+ quad[1].x = x + _cairo_fixed_mul_div_floor (trap->bottom - trap->left.p1.y,
+ trap->left.p2.x - trap->left.p1.x, dy);
+ }
+ quad[0].y = trap->top;
+ quad[1].y = trap->bottom;
+
+ if (trap->right.p1.x == trap->right.p2.x) {
+ quad[2].x = trap->right.p1.x;
+ quad[3].x = trap->right.p1.x;
+ } else {
+ cairo_fixed_t x, dy;
+ x = trap->right.p1.x;
+ dy = trap->right.p2.y - trap->right.p1.y;
+
+ if (trap->bottom == trap->right.p2.y)
+ quad[2].x = trap->right.p2.x;
+ else if (trap->bottom == trap->right.p1.y)
+ quad[2].x = x;
+ else if (dy != 0)
+ quad[2].x = x + _cairo_fixed_mul_div_floor (trap->bottom - trap->right.p1.y,
+ trap->right.p2.x - trap->right.p1.x, dy);
+
+ if (trap->top == trap->right.p1.y)
+ quad[3].x = x;
+ else if (trap->top == trap->right.p2.y)
+ quad[3].x = trap->right.p2.x;
+ else if (dy != 0)
+ quad[3].x = x + _cairo_fixed_mul_div_floor (trap->top - trap->right.p1.y,
+ trap->right.p2.x - trap->right.p1.x, dy);
+ }
+ quad[2].y = trap->bottom;
+ 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)
+{
+ int quad[8];
+
+ quad[0] = quad[2] = rect->x;
+ quad[1] = quad[7] = rect->y;
+ quad[3] = quad[5] = rect->y + rect->height;
+ quad[4] = quad[6] = rect->x + rect->width;
+
+ return _cairo_gl_composite_emit_int_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_int_status_t
+_blit_texture_to_renderbuffer (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx = NULL;
+ cairo_gl_composite_t setup;
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t extents;
+ cairo_int_status_t status;
+ cairo_gl_flavor_t gl_flavor = ((cairo_gl_context_t *) surface->base.device)->gl_flavor;
+
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (! _cairo_gl_surface_is_texture (surface))
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (surface->msaa_active)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (surface->content_synced) {
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+ _cairo_gl_context_set_destination (ctx, surface, TRUE);
+ return _cairo_gl_context_release (ctx, status);
+ }
+
+ memset (&setup, 0, sizeof (cairo_gl_composite_t));
+
+ status = _cairo_gl_composite_set_operator (&setup,
+ CAIRO_OPERATOR_SOURCE,
+ FALSE);
+ if (status)
+ return status;
+
+ setup.dst = surface;
+ setup.clip_region = surface->clip_region;
+
+ _cairo_pattern_init_for_surface (&pattern, &surface->base);
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ status = _cairo_gl_composite_set_source (&setup, &pattern.base,
+ NULL, &extents, FALSE, FALSE);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _draw_int_rect (ctx, &setup, &extents);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ surface->content_synced = TRUE;
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+
+ if (ctx) {
+ _cairo_gl_composite_flush (ctx);
+ status = _cairo_gl_context_release (ctx, status);
+ }
+
+ 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)
+{
+ cairo_gl_flavor_t flavor = ((cairo_gl_context_t *) surface->base.device)->gl_flavor;
+ cairo_bool_t has_angle_multisampling = ((cairo_gl_context_t *) surface->base.device)->has_angle_multisampling;
+
+ 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 (! (flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (flavor == CAIRO_GL_FLAVOR_ES2 &&
+ has_angle_multisampling)) &&
+ 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_GRAY ||
+ antialias== CAIRO_ANTIALIAS_SUBPIXEL ||
+ antialias == CAIRO_ANTIALIAS_FAST ||
+ antialias == CAIRO_ANTIALIAS_DEFAULT) &&
+ surface->num_samples > 1)
+ return surface->supports_msaa;
+ return FALSE;
+}
+
+static void
+_cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite,
+ cairo_gl_composite_t *setup)
+{
+ uint32_t is_bounded;
+
+ /* We don't need to check CAIRO_OPERATOR_BOUND_BY_MASK in these
+ situations. */
+ is_bounded = composite->is_bounded;
+ composite->is_bounded = CAIRO_OPERATOR_BOUND_BY_SOURCE;
+ if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip))
+ return;
+
+ _cairo_gl_composite_set_clip (setup, composite->clip);
+
+ composite->is_bounded = is_bounded;
+}
+
+static cairo_bool_t
+_pattern_is_pixel_aligned (const cairo_pattern_t *pattern)
+{
+ long xoffset, yoffset;
+
+ if (!pattern)
+ return TRUE;
+
+ xoffset = pattern->matrix.x0;
+ yoffset = pattern->matrix.y0;
+
+ if (pattern->matrix.xx != 1.0 ||
+ pattern->matrix.xy != 0.0 ||
+ pattern->matrix.yy != 1.0 ||
+ pattern->matrix.yx != 1.0 ||
+ pattern->matrix.x0 != xoffset ||
+ pattern->matrix.y0 != yoffset)
+ return FALSE;
+ return TRUE;
+}
+
+static cairo_bool_t
+_clip_is_pixel_aligned (const cairo_clip_t *clip)
+{
+ if (!clip)
+ return TRUE;
+
+ if (clip->path || clip->num_boxes > 1)
+ return FALSE;
+
+ if (_cairo_fixed_is_integer (clip->boxes[0].p1.x) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p1.y) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p2.x) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p2.y))
+ return TRUE;
+ return FALSE;
+}
+
+/* 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;
+ cairo_bool_t is_pixel_aligned =
+ _pattern_is_pixel_aligned (composite->original_source_pattern) &&
+ _pattern_is_pixel_aligned (composite->original_mask_pattern) &&
+ _clip_is_pixel_aligned (clip);
+
+ /* 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;
+ }
+ }
+
+ if (! is_pixel_aligned) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ 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->original_mask_pattern,
+ &composite->mask_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ if (! is_pixel_aligned || dst->msaa_active)
+ _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);
+
+ /* 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->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+ status = _cairo_gl_composite_set_mask (&setup,
+ composite->original_mask_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ _cairo_gl_context_set_destination (ctx, dst, setup.multisample);
+
+ 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);
+
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+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;
+ cairo_bool_t is_pixel_aligned = FALSE;
+
+ 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);
+ }
+
+ if (_pattern_is_pixel_aligned (composite->original_source_pattern) &&
+ _pattern_is_pixel_aligned (composite->original_mask_pattern) &&
+ _clip_is_pixel_aligned (composite->clip))
+ is_pixel_aligned = TRUE;
+
+ if (! is_pixel_aligned) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ 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->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ if (composite->original_mask_pattern != NULL) {
+ status = _cairo_gl_composite_set_mask (&setup,
+ composite->original_mask_pattern,
+ &composite->mask_sample_area,
+ &composite->bounded,
+ FALSE);
+ }
+ if (unlikely (status))
+ goto finish;
+
+ /* if the source, mask and clip are pixel-aligned and
+ msaa is not active, we paint to texture directly */
+ if (! is_pixel_aligned || dst->msaa_active)
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+
+ if (unlikely (status))
+ goto finish;
+
+ if (op != CAIRO_OPERATOR_OVER) {
+ if (! clip)
+ status = _draw_int_rect (ctx, &setup, &composite->bounded);
+ else
+ status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip);
+ }
+ else {
+ /* fast path for CAIRO_OVER_OPERATOR */
+ cairo_rectangle_int_t rect, temp;
+
+ _cairo_surface_get_extents (&dst->base, &rect);
+ _cairo_pattern_get_extents (composite->original_source_pattern,
+ &temp);
+ _cairo_rectangle_intersect (&rect, &temp);
+ if (composite->original_mask_pattern) {
+ _cairo_pattern_get_extents (composite->original_mask_pattern,
+ &temp);
+ _cairo_rectangle_intersect (&rect, &temp);
+ }
+
+ if (clip) {
+ cairo_clip_t *clip_copy = _cairo_clip_copy (clip);
+
+ clip_copy = _cairo_clip_intersect_rectangle (clip_copy, &rect);
+ status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup,
+ clip_copy);
+ _cairo_clip_destroy (clip_copy);
+ }
+ else
+ status = _draw_int_rect (ctx, &setup, &rect);
+ }
+
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+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_bool_t
+_is_continuous_arc (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style)
+{
+ return (_cairo_path_fixed_is_single_arc (path) &&
+ style->dash == NULL);
+}
+
+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;
+ const cairo_pattern_t *pattern = composite->original_source_pattern;
+ cairo_pattern_type_t type = cairo_pattern_get_type ((cairo_pattern_t *) pattern);
+
+ if (! _cairo_gl_ensure_stencil (ctx, setup->dst))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+
+ /* XXX: improve me - since we have lazy init, we cannot use sample
+ area */
+ if (type == CAIRO_PATTERN_TYPE_SOLID &&
+ _cairo_pattern_is_opaque_solid (pattern))
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (ctx->states_cache.stencil_test_enabled == FALSE) {
+ /* 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. */
+ if (! ctx->states_cache.depth_mask) {
+ ctx->dispatch.DepthMask (GL_TRUE);
+ ctx->states_cache.depth_mask = TRUE;
+ }
+ ctx->dispatch.Enable (GL_STENCIL_TEST);
+ ctx->states_cache.stencil_test_enabled = TRUE;
+
+ /* 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. */
+ if (ctx->states_cache.scissor_test_enabled == FALSE) {
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
+ &stroke_extents);
+ _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents);
+ ctx->dispatch.Enable (GL_SCISSOR_TEST);
+ ctx->states_cache.scissor_test_enabled = TRUE;
+ }
+ ctx->dispatch.ClearStencil (1);
+ ctx->dispatch.Clear (GL_STENCIL_BUFFER_BIT);
+ _disable_scissor_buffer (ctx);
+
+ ctx->dispatch.StencilFunc (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. */
+ ctx->dispatch.StencilOp (GL_ZERO, GL_ZERO, GL_ZERO);
+
+ _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
+ setup->dst->clip_on_stencil_buffer = NULL;
+
+ /* we must let the next drawing know we have changed stencil buffer
+ * so that next drawing calls flush
+ */
+ setup->dst->needs_update = TRUE;
+
+ 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);
+
+ //ctx->dispatch.GetIntegerv(GL_SAMPLES, &samples);
+ //ctx->dispatch.GetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+ samples = 4;
+ stencil_bits = 4;
+ surface->supports_stencil = stencil_bits > 0;
+
+ surface->supports_msaa = samples > 1;
+
+ surface->num_samples = samples;
+
+ 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;
+ cairo_bool_t use_color_attribute;
+ cairo_rectangle_int_t stroke_extents;
+
+ if (! can_use_msaa_compositor (dst, antialias))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_path_fixed_stroke_is_rectilinear (path)) {
+ _cairo_path_fixed_approximate_fill_extents (path, &stroke_extents);
+
+ if (stroke_extents.width != 0 &&
+ stroke_extents.height != 0) {
+ if ((stroke_extents.width / stroke_extents.height > 10 &&
+ stroke_extents.height < 10) ||
+ (stroke_extents.height / stroke_extents.width > 10 &&
+ stroke_extents.width < 10)) {
+ 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);
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_gl_composite_init (&info.setup,
+ composite->op,
+ dst,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ return status;
+
+ info.ctx = NULL;
+ use_color_attribute = _cairo_gl_hairline_style_is_hairline (style, ctm);
+
+ status = _cairo_gl_composite_set_source (&info.setup,
+ composite->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, use_color_attribute);
+ 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;
+
+ if (_cairo_gl_hairline_style_is_hairline (style, ctm)) {
+ cairo_gl_hairline_closure_t closure;
+
+ if (! (_is_continuous_arc (path, style) ||
+ _is_continuous_single_line (path, style))) {
+ status = _prevent_overlapping_strokes (info.ctx, &info.setup,
+ composite, path,
+ style, ctm);
+ if (unlikely (status))
+ goto finish;
+ }
+
+ closure.ctx = info.ctx;
+
+ closure.tolerance = tolerance;
+
+ status = _cairo_gl_path_fixed_stroke_to_hairline (path, &closure,
+ style, ctm,
+ ctm_inverse,
+ _cairo_gl_hairline_move_to,
+ style->dash ?
+ _cairo_gl_hairline_line_to_dashed :
+ _cairo_gl_hairline_line_to,
+ _cairo_gl_hairline_curve_to,
+ _cairo_gl_hairline_close_path);
+ goto finish;
+ }
+
+ if (!_is_continuous_single_line (path, style)) {
+ 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;
+
+ dst->content_synced = FALSE;
+
+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;
+ cairo_rectangle_int_t fill_extents;
+
+ if (! can_use_msaa_compositor (dst, antialias))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_path_fixed_fill_is_rectilinear (path)) {
+ _cairo_path_fixed_approximate_fill_extents (path, &fill_extents);
+
+ if (fill_extents.width != 0 && fill_extents.height != 0) {
+ if ((fill_extents.width / fill_extents.height > 10 &&
+ fill_extents.height < 10) ||
+ (fill_extents.height / fill_extents.width > 10 &&
+ fill_extents.width < 10)) {
+ 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);
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ 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->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, ! draw_path_with_traps);
+ 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;
+
+ dst->content_synced = FALSE;
+
+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->original_source_pattern,
+ 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->source;
+
+ _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);
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+finish:
+ 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->lazy_init = TRUE;
+
+ 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;
+}