summaryrefslogtreecommitdiff
path: root/src/cairo-gl-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-surface.c')
-rw-r--r--src/cairo-gl-surface.c2031
1 files changed, 2031 insertions, 0 deletions
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
new file mode 100644
index 0000000..17bf4bc
--- /dev/null
+++ b/src/cairo-gl-surface.c
@@ -0,0 +1,2031 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+slim_hidden_proto (cairo_gl_context_reference);
+slim_hidden_proto (cairo_gl_context_destroy);
+
+#define BIAS .375
+
+static inline float
+int_as_float (uint32_t val)
+{
+ union fi {
+ float f;
+ uint32_t u;
+ } fi;
+
+ fi.u = val;
+ return fi.f;
+}
+
+static const cairo_gl_context_t _nil_context = {
+ CAIRO_REFERENCE_COUNT_INVALID,
+ CAIRO_STATUS_NO_MEMORY
+};
+
+static const cairo_gl_context_t _nil_context__invalid_format = {
+ CAIRO_REFERENCE_COUNT_INVALID,
+ CAIRO_STATUS_INVALID_FORMAT
+};
+
+static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
+{
+ return surface->backend == &_cairo_gl_surface_backend;
+}
+
+cairo_gl_context_t *
+_cairo_gl_context_create_in_error (cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_gl_context_t *) &_nil_context;
+
+ if (status == CAIRO_STATUS_INVALID_FORMAT)
+ return (cairo_gl_context_t *) &_nil_context__invalid_format;
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+cairo_status_t
+_cairo_gl_context_init (cairo_gl_context_t *ctx)
+{
+ int n;
+
+ ctx->status = CAIRO_STATUS_SUCCESS;
+ CAIRO_REFERENCE_COUNT_INIT (&ctx->ref_count, 1);
+ CAIRO_MUTEX_INIT (ctx->mutex);
+
+ memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
+
+ if (glewInit () != GLEW_OK)
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
+
+ if (! GLEW_EXT_framebuffer_object ||
+ ! GLEW_ARB_texture_env_combine ||
+ ! GLEW_ARB_texture_non_power_of_two)
+ {
+ fprintf (stderr,
+ "Required GL extensions not available:\n");
+ if (! GLEW_EXT_framebuffer_object)
+ fprintf (stderr, " GL_EXT_framebuffer_object\n");
+ if (! GLEW_ARB_texture_env_combine)
+ fprintf (stderr, " GL_ARB_texture_env_combine\n");
+ if (! GLEW_ARB_texture_non_power_of_two)
+ fprintf (stderr, " GL_ARB_texture_non_power_of_two\n");
+
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
+ }
+
+ /* Set up the dummy texture for tex_env_combine with constant color. */
+ glGenTextures (1, &ctx->dummy_tex);
+ glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ ctx->max_framebuffer_size = 0;
+ glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
+ ctx->max_texture_size = 0;
+ glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+ _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_gl_context_t *
+cairo_gl_context_reference (cairo_gl_context_t *context)
+{
+ if (context == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
+ {
+ return context;
+ }
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+ _cairo_reference_count_inc (&context->ref_count);
+
+ return context;
+}
+slim_hidden_def (cairo_gl_context_reference);
+
+void
+cairo_gl_context_destroy (cairo_gl_context_t *context)
+{
+ int n;
+
+ if (context == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
+ {
+ return;
+ }
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+ if (! _cairo_reference_count_dec_and_test (&context->ref_count))
+ return;
+
+ glDeleteTextures (1, &context->dummy_tex);
+
+ for (n = 0; n < ARRAY_LENGTH (context->glyph_cache); n++)
+ _cairo_gl_glyph_cache_fini (&context->glyph_cache[n]);
+
+ context->destroy (context);
+
+ free (context);
+}
+slim_hidden_def (cairo_gl_context_destroy);
+
+cairo_gl_context_t *
+_cairo_gl_context_acquire (cairo_gl_context_t *ctx)
+{
+ CAIRO_MUTEX_LOCK (ctx->mutex);
+ return ctx;
+}
+
+cairo_bool_t
+_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha)
+{
+ *has_alpha = TRUE;
+
+ switch (pixman_format) {
+ case PIXMAN_a8r8g8b8:
+ *internal_format = GL_RGBA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return TRUE;
+ case PIXMAN_x8r8g8b8:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a8b8g8r8:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return TRUE;
+ case PIXMAN_x8b8g8r8:
+ *internal_format = GL_RGB;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_b8g8r8a8:
+ *internal_format = GL_BGRA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8;
+ return TRUE;
+ case PIXMAN_b8g8r8x8:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_r8g8b8:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+ case PIXMAN_b8g8r8:
+ *internal_format = GL_RGB;
+ *format = GL_BGR;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+ case PIXMAN_r5g6b5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ return TRUE;
+ case PIXMAN_b5g6r5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5_REV;
+ return TRUE;
+ case PIXMAN_a1r5g5b5:
+ *internal_format = GL_RGBA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ return TRUE;
+ case PIXMAN_x1r5g5b5:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a1b5g5r5:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ return TRUE;
+ case PIXMAN_x1b5g5r5:
+ *internal_format = GL_RGB;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a8:
+ *internal_format = GL_ALPHA;
+ *format = GL_ALPHA;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+
+ case PIXMAN_a2b10g10r10:
+ case PIXMAN_x2b10g10r10:
+ case PIXMAN_a4r4g4b4:
+ case PIXMAN_x4r4g4b4:
+ case PIXMAN_a4b4g4r4:
+ case PIXMAN_x4b4g4r4:
+ case PIXMAN_r3g3b2:
+ case PIXMAN_b2g3r3:
+ case PIXMAN_a2r2g2b2:
+ case PIXMAN_a2b2g2r2:
+ case PIXMAN_c8:
+ case PIXMAN_x4a4:
+ /* case PIXMAN_x4c4: */
+ case PIXMAN_x4g4:
+ case PIXMAN_a4:
+ case PIXMAN_r1g2b1:
+ case PIXMAN_b1g2r1:
+ case PIXMAN_a1r1g1b1:
+ case PIXMAN_a1b1g1r1:
+ case PIXMAN_c4:
+ case PIXMAN_g4:
+ case PIXMAN_a1:
+ case PIXMAN_g1:
+ case PIXMAN_yuy2:
+ case PIXMAN_yv12:
+#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16)
+ case PIXMAN_x2r10g10b10:
+ case PIXMAN_a2r10g10b10:
+#endif
+ default:
+ return FALSE;
+ }
+}
+
+void
+_cairo_gl_context_release (cairo_gl_context_t *ctx)
+{
+ CAIRO_MUTEX_UNLOCK (ctx->mutex);
+}
+
+void
+_cairo_gl_set_destination (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx = surface->ctx;
+
+ if (ctx->current_target != surface) {
+ ctx->current_target = surface;
+
+ if (surface->fb) {
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ } else {
+ ctx->make_current (ctx, surface);
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
+ glDrawBuffer (GL_BACK_LEFT);
+ glReadBuffer (GL_BACK_LEFT);
+ }
+ }
+
+ glViewport (0, 0, surface->width, surface->height);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ if (surface->fb)
+ glOrtho (0, surface->width, 0, surface->height, -1.0, 1.0);
+ else
+ glOrtho (0, surface->width, surface->height, 0, -1.0, 1.0);
+
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+}
+
+cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op)
+{
+ return op < CAIRO_OPERATOR_SATURATE;
+}
+
+void
+_cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
+{
+ struct {
+ GLenum src;
+ GLenum dst;
+ } blend_factors[] = {
+ { GL_ZERO, GL_ZERO }, /* Clear */
+ { GL_ONE, GL_ZERO }, /* Source */
+ { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */
+ { GL_DST_ALPHA, GL_ZERO }, /* In */
+ { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */
+ { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */
+
+ { GL_ZERO, GL_ONE }, /* Dest */
+ { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */
+ { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */
+ { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */
+ { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */
+
+ { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */
+ { GL_ONE, GL_ONE }, /* Add */
+ };
+ GLenum src_factor, dst_factor;
+
+ assert (op < ARRAY_LENGTH (blend_factors));
+
+ src_factor = blend_factors[op].src;
+ dst_factor = blend_factors[op].dst;
+
+ /* We may have a visual with alpha bits despite the user requesting
+ * CAIRO_CONTENT_COLOR. So clear out those bits in that case.
+ */
+ if (dst->base.content == CAIRO_CONTENT_COLOR) {
+ if (src_factor == GL_ONE_MINUS_DST_ALPHA)
+ src_factor = GL_ZERO;
+ if (src_factor == GL_DST_ALPHA)
+ src_factor = GL_ONE;
+ }
+
+ glEnable (GL_BLEND);
+ glBlendFunc (src_factor, dst_factor);
+}
+
+static void
+_cairo_gl_set_texture_surface (int tex_unit, GLuint tex,
+ cairo_surface_attributes_t *attributes)
+{
+ glActiveTexture (GL_TEXTURE0 + tex_unit);
+ glBindTexture (GL_TEXTURE_2D, tex);
+ switch (attributes->extend) {
+ case CAIRO_EXTEND_NONE:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ case CAIRO_EXTEND_PAD:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+ break;
+ }
+ switch (attributes->filter) {
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ default:
+ case CAIRO_FILTER_GAUSSIAN:
+ ASSERT_NOT_REACHED;
+ }
+ glEnable (GL_TEXTURE_2D);
+}
+
+void
+_cairo_gl_surface_init (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_content_t content,
+ int width, int height)
+{
+ _cairo_surface_init (&surface->base,
+ &_cairo_gl_surface_backend,
+ content);
+
+ surface->ctx = cairo_gl_context_reference (ctx);
+ surface->width = width;
+ surface->height = height;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_gl_surface_t *surface;
+ GLenum err, format;
+ cairo_status_t status;
+
+ if (!CAIRO_CONTENT_VALID (content))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ if (ctx == NULL) {
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+ }
+ if (ctx->status)
+ return _cairo_surface_create_in_error (ctx->status);
+
+ if (width > ctx->max_framebuffer_size || height > ctx->max_framebuffer_size)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = calloc (1, sizeof (cairo_gl_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (ctx, surface, content, width, height);
+
+ switch (content) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ format = GL_RGBA;
+ break;
+ case CAIRO_CONTENT_ALPHA:
+ format = GL_RGBA;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ format = GL_RGB;
+ break;
+ }
+
+ /* Create the texture used to store the surface's data. */
+ glGenTextures (1, &surface->tex);
+ glBindTexture (GL_TEXTURE_2D, surface->tex);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0,
+ format, GL_UNSIGNED_BYTE, NULL);
+
+ /* Create a framebuffer object wrapping the texture so that we can render
+ * to it.
+ */
+ glGenFramebuffersEXT (1, &surface->fb);
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D,
+ surface->tex,
+ 0);
+
+ while ((err = glGetError ())) {
+ fprintf (stderr, "GL error in surface create: 0x%08x\n", err);
+ }
+
+ status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ fprintf (stderr, "destination is framebuffer incomplete\n");
+
+ /* Cairo surfaces start out initialized to transparent (black) */
+ ctx = _cairo_gl_context_acquire (surface->ctx);
+ _cairo_gl_set_destination (surface);
+ glClearColor (0.0, 0.0, 0.0, 0.0);
+ glClear (GL_COLOR_BUFFER_BIT);
+ _cairo_gl_context_release (ctx);
+
+ return &surface->base;
+}
+slim_hidden_def (cairo_gl_surface_create);
+
+void
+cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+ cairo_status_t status;
+
+ if (! _cairo_surface_is_gl (abstract_surface) || surface->fb) {
+ status = _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ surface->width = width;
+ surface->height = height;
+}
+
+int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_gl (abstract_surface))
+ return 0;
+
+ return surface->width;
+}
+
+int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_gl (abstract_surface))
+ return 0;
+
+ return surface->height;
+}
+
+
+void
+cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+ cairo_status_t status;
+
+ if (! _cairo_surface_is_gl (abstract_surface)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ if (! surface->fb)
+ surface->ctx->swap_buffers (surface->ctx, surface);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ assert (CAIRO_CONTENT_VALID (content));
+
+ if (width > surface->ctx->max_framebuffer_size ||
+ height > surface->ctx->max_framebuffer_size)
+ {
+ return NULL;
+ }
+
+ if (width < 1)
+ width = 1;
+ if (height < 1)
+ height = 1;
+
+ return cairo_gl_surface_create (surface->ctx, content, width, height);
+}
+
+cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y)
+{
+ GLenum internal_format, format, type;
+ cairo_bool_t has_alpha;
+ cairo_image_surface_t *clone = NULL;
+ int cpp;
+
+ if (! _cairo_gl_get_image_format_and_type (src->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha))
+ {
+ cairo_bool_t is_supported;
+
+ clone = _cairo_image_surface_coerce (src,
+ _cairo_format_from_content (src->base.content));
+ if (unlikely (clone->base.status))
+ return clone->base.status;
+
+ is_supported =
+ _cairo_gl_get_image_format_and_type (clone->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha);
+ assert (is_supported);
+ src = clone;
+ }
+
+ cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+
+ glBindTexture (GL_TEXTURE_2D, dst->tex);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+ glTexSubImage2D (GL_TEXTURE_2D, 0,
+ dst_x, dst_y, width, height,
+ format, GL_UNSIGNED_BYTE,
+ src->data + src_y * src->stride + src_x * cpp);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+ cairo_surface_destroy (&clone->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_surface_get_image (cairo_gl_surface_t *surface,
+ cairo_rectangle_int_t *interest,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *rect_out)
+{
+ cairo_image_surface_t *image;
+ GLenum err;
+ GLenum format, type;
+ cairo_format_t cairo_format;
+ unsigned int cpp;
+
+ /* Want to use a switch statement here but the compiler gets whiny. */
+ if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
+ format = GL_BGRA;
+ cairo_format = CAIRO_FORMAT_ARGB32;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_COLOR) {
+ format = GL_BGRA;
+ cairo_format = CAIRO_FORMAT_RGB24;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ format = GL_ALPHA;
+ cairo_format = CAIRO_FORMAT_A8;
+ type = GL_UNSIGNED_BYTE;
+ cpp = 1;
+ } else {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ image = (cairo_image_surface_t*)
+ cairo_image_surface_create (cairo_format,
+ interest->width, interest->height);
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ /* This is inefficient, as we'd rather just read the thing without making
+ * it the destination. But then, this is the fallback path, so let's not
+ * fall back instead.
+ */
+ _cairo_gl_set_destination (surface);
+
+ glPixelStorei (GL_PACK_ALIGNMENT, 1);
+ glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
+ glReadPixels (interest->x, interest->y,
+ interest->width, interest->height,
+ format, type, image->data);
+
+ while ((err = glGetError ()))
+ fprintf (stderr, "GL error 0x%08x\n", (int) err);
+
+ *image_out = image;
+ if (rect_out != NULL)
+ *rect_out = *interest;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_surface_finish (void *abstract_surface)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ glDeleteFramebuffersEXT (1, &surface->fb);
+ glDeleteTextures (1, &surface->tex);
+
+ if (surface->ctx->current_target == surface)
+ surface->ctx->current_target = NULL;
+
+ cairo_gl_context_destroy (surface->ctx);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ *image_extra = NULL;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+ return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL);
+}
+
+static void
+_cairo_gl_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_gl_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ *image_extra = NULL;
+ return _cairo_gl_surface_get_image (surface, interest_rect, image_out,
+ image_rect_out);
+}
+
+static void
+_cairo_gl_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_status_t status;
+
+ status = _cairo_gl_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image_rect->x, image_rect->y);
+ /* as we created the image, its format should be directly applicable */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_gl_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ if (src->backend == surface->base.backend) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ } else if (_cairo_surface_is_image (src)) {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+ cairo_gl_surface_t *clone;
+ cairo_status_t status;
+
+ clone = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_similar (&surface->base,
+ content,
+ width, height);
+ if (clone == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (clone->base.status)
+ return clone->base.status;
+
+ status = _cairo_gl_surface_draw_image (clone, image_src,
+ src_x, src_y,
+ width, height,
+ 0, 0);
+ if (status) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ *clone_out = &clone->base;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/** Creates a cairo-gl pattern surface for the given trapezoids */
+static cairo_status_t
+_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ int width, int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_antialias_t antialias,
+ cairo_surface_pattern_t *pattern)
+{
+ pixman_format_code_t pixman_format;
+ pixman_image_t *image;
+ cairo_surface_t *surface;
+ int i;
+
+ pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+ image = pixman_image_create_bits (pixman_format, width, height, NULL, 0);
+ if (unlikely (image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < num_traps; i++) {
+ pixman_trapezoid_t trap;
+
+ trap.top = _cairo_fixed_to_16_16 (traps[i].top);
+ trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
+
+ trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
+
+ trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+ }
+
+ surface = _cairo_image_surface_create_for_pixman_image (image,
+ pixman_format);
+ if (unlikely (surface->status)) {
+ pixman_image_unref (image);
+ return surface->status;
+ }
+
+ _cairo_pattern_init_for_surface (pattern, surface);
+ cairo_surface_destroy (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_pattern_surface_texture_setup (cairo_gl_composite_operand_t *operand,
+ const cairo_surface_pattern_t *src,
+ cairo_gl_surface_t *dst,
+ int src_x, int src_y,
+ int dst_x, int dst_y)
+{
+ cairo_surface_t *source = src->surface;
+ cairo_gl_surface_t *gl_surface;
+ cairo_status_t status;
+ cairo_matrix_t m;
+ cairo_surface_attributes_t *attributes;
+
+ if (source->backend != &_cairo_gl_surface_backend) {
+ gl_surface = (cairo_gl_surface_t *)
+ _cairo_surface_has_snapshot (source,
+ &_cairo_gl_surface_backend,
+ source->content);
+ if (gl_surface == NULL) {
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ status = _cairo_surface_acquire_source_image (source,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ gl_surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_similar (&dst->base,
+ image->base.content,
+ image->width, image->height);
+ if (gl_surface == NULL) {
+ _cairo_surface_release_source_image (source,
+ image, image_extra);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (unlikely (gl_surface->base.status)) {
+ _cairo_surface_release_source_image (source,
+ image, image_extra);
+ return gl_surface->base.status;
+ }
+
+ status = _cairo_gl_surface_draw_image (gl_surface, image,
+ 0, 0,
+ image->width, image->height,
+ 0, 0);
+ _cairo_surface_release_source_image (source, image, image_extra);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&gl_surface->base);
+ return status;
+ }
+
+ status = _cairo_surface_attach_snapshot (source,
+ &gl_surface->base,
+ cairo_surface_destroy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&gl_surface->base);
+ return status;
+ }
+
+ /* XXX consider placing a bound on snapshot caches */
+ }
+ } else {
+ gl_surface = (cairo_gl_surface_t *) source;
+ }
+
+ operand->operand.texture.surface = NULL;
+ operand->operand.texture.tex = gl_surface->tex;
+ switch (gl_surface->base.content) {
+ case CAIRO_CONTENT_ALPHA:
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ operand->operand.texture.has_alpha = TRUE;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ operand->operand.texture.has_alpha = FALSE;
+ break;
+ }
+
+ attributes = &operand->operand.texture.attributes;
+
+ operand->type = OPERAND_TEXTURE;
+ operand->operand.texture.tex = gl_surface->tex;
+ operand->operand.texture.surface = NULL;
+ attributes->matrix = src->base.matrix;
+ attributes->extend = src->base.extend;
+ attributes->filter = src->base.filter;
+ /* Demote the filter if we're doing a 1:1 mapping of pixels. */
+ if ((attributes->filter == CAIRO_FILTER_GOOD ||
+ attributes->filter == CAIRO_FILTER_BEST ||
+ attributes->filter == CAIRO_FILTER_BILINEAR) &&
+ _cairo_matrix_is_pixel_exact (&attributes->matrix))
+ {
+ attributes->filter = CAIRO_FILTER_NEAREST;
+ }
+
+ attributes->x_offset = 0;
+ attributes->y_offset = 0;
+
+ /* Set up translation matrix for
+ * (unnormalized dst -> unnormalized src)
+ */
+ cairo_matrix_init_translate (&m, src_x - dst_x, src_y - dst_y);
+ cairo_matrix_multiply (&attributes->matrix, &m, &attributes->matrix);
+
+ /* Translate the matrix from
+ * (unnormalized src -> unnormalized src) to
+ * (unnormalized dst -> normalized src)
+ */
+ cairo_matrix_init_scale (&m,
+ 1.0 / gl_surface->width,
+ 1.0 / gl_surface->height);
+ cairo_matrix_multiply (&attributes->matrix, &attributes->matrix, &m);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * Like cairo_pattern_acquire_surface(), but returns a matrix that transforms
+ * from dest to src coords.
+ */
+static cairo_status_t
+_cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
+ const cairo_pattern_t *src,
+ cairo_gl_surface_t *dst,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ int width, int height)
+{
+ cairo_status_t status;
+ cairo_matrix_t m;
+ cairo_gl_surface_t *surface;
+ cairo_surface_attributes_t *attributes;
+
+ attributes = &operand->operand.texture.attributes;
+
+ /* First, try to just upload it to a texture if it's a surface. */
+ if (src->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ status =
+ _cairo_gl_pattern_surface_texture_setup (operand,
+ (cairo_surface_pattern_t *) src,
+ dst,
+ src_x, src_y,
+ dst_x, dst_y);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_pattern_acquire_surface (src, &dst->base,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ src_x, src_y,
+ width, height,
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ (cairo_surface_t **)
+ &surface,
+ attributes);
+ if (unlikely (status))
+ return status;
+
+ assert (surface->base.backend == &_cairo_gl_surface_backend);
+
+ operand->operand.texture.surface = surface;
+ operand->operand.texture.tex = surface->tex;
+ switch (surface->base.content) {
+ case CAIRO_CONTENT_ALPHA:
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ operand->operand.texture.has_alpha = TRUE;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ operand->operand.texture.has_alpha = FALSE;
+ break;
+ }
+
+ /* Translate the matrix from
+ * (unnormalized src -> unnormalized src) to
+ * (unnormalized dst -> unnormalized src)
+ */
+ cairo_matrix_init_translate (&m,
+ src_x - dst_x + attributes->x_offset,
+ src_y - dst_y + attributes->y_offset);
+ cairo_matrix_multiply (&attributes->matrix,
+ &m,
+ &attributes->matrix);
+
+
+ /* Translate the matrix from
+ * (unnormalized src -> unnormalized src) to
+ * (unnormalized dst -> normalized src)
+ */
+ cairo_matrix_init_scale (&m,
+ 1.0 / surface->width,
+ 1.0 / surface->height);
+ cairo_matrix_multiply (&attributes->matrix,
+ &attributes->matrix,
+ &m);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand,
+ const cairo_color_t *color)
+{
+ operand->type = OPERAND_CONSTANT;
+ operand->operand.constant.color[0] = color->red * color->alpha;
+ operand->operand.constant.color[1] = color->green * color->alpha;
+ operand->operand.constant.color[2] = color->blue * color->alpha;
+ operand->operand.constant.color[3] = color->alpha;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
+ const cairo_pattern_t *pattern,
+ cairo_gl_surface_t *dst,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ int width, int height)
+{
+ operand->pattern = pattern;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_gl_solid_operand_init (operand,
+ &((cairo_solid_pattern_t *) pattern)->color);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ {
+ cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
+
+ /* Fast path for gradients with less than 2 color stops.
+ * Required to prevent _cairo_pattern_acquire_surface() returning
+ * a solid color which is cached beyond the life of the context.
+ */
+ if (src->n_stops < 2) {
+ if (src->n_stops) {
+ return _cairo_gl_solid_operand_init (operand,
+ &src->stops->color);
+ } else {
+ return _cairo_gl_solid_operand_init (operand,
+ CAIRO_COLOR_TRANSPARENT);
+ }
+ } else {
+ unsigned int i;
+
+ /* Is the gradient a uniform colour?
+ * Happens more often than you would believe.
+ */
+ for (i = 1; i < src->n_stops; i++) {
+ if (! _cairo_color_equal (&src->stops[0].color,
+ &src->stops[i].color))
+ {
+ break;
+ }
+ }
+ if (i == src->n_stops) {
+ return _cairo_gl_solid_operand_init (operand,
+ &src->stops->color);
+ }
+ }
+ }
+
+ /* fall through */
+
+ default:
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ operand->type = OPERAND_TEXTURE;
+ return _cairo_gl_pattern_texture_setup (operand,
+ pattern, dst,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height);
+ }
+}
+
+void
+_cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
+{
+ switch (operand->type) {
+ case OPERAND_CONSTANT:
+ break;
+ case OPERAND_TEXTURE:
+ if (operand->operand.texture.surface != NULL) {
+ cairo_gl_surface_t *surface = operand->operand.texture.surface;
+
+ _cairo_pattern_release_surface (operand->pattern,
+ &surface->base,
+ &operand->operand.texture.attributes);
+ }
+ break;
+ }
+}
+
+static void
+_cairo_gl_set_tex_combine_constant_color (cairo_gl_context_t *ctx, int tex_unit,
+ GLfloat *color)
+{
+ glActiveTexture (GL_TEXTURE0 + tex_unit);
+ /* Have to have a dummy texture bound in order to use the combiner unit. */
+ glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
+ glEnable (GL_TEXTURE_2D);
+
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ if (tex_unit == 0) {
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ } else {
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ }
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT);
+ if (tex_unit == 0) {
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ } else {
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ }
+}
+
+void
+_cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
+ cairo_gl_composite_setup_t *setup)
+{
+ cairo_surface_attributes_t *src_attributes;
+ GLfloat constant_color[4] = {0.0, 0.0, 0.0, 1.0};
+
+ src_attributes = &setup->src.operand.texture.attributes;
+
+ switch (setup->src.type) {
+ case OPERAND_CONSTANT:
+ _cairo_gl_set_tex_combine_constant_color (ctx, 0,
+ setup->src.operand.constant.color);
+ break;
+ case OPERAND_TEXTURE:
+ _cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex,
+ src_attributes);
+ /* Set up the constant color we use to set alpha to 1 if needed. */
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
+ /* Set up the combiner to just set color to the sampled texture. */
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
+ /* Wire the src alpha to 1 if the surface doesn't have it.
+ * We may have a teximage with alpha bits even though we didn't ask
+ * for it and we don't pay attention to setting alpha to 1 in a dest
+ * that has inadvertent alpha.
+ */
+ if (setup->src.operand.texture.has_alpha)
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
+ else
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ break;
+ }
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_surface_attributes_t *src_attributes, *mask_attributes = NULL;
+ cairo_gl_context_t *ctx;
+ struct gl_point {
+ GLfloat x, y;
+ } vertices_stack[8], texcoord_src_stack[8], texcoord_mask_stack[8];
+ struct gl_point *vertices = vertices_stack;
+ struct gl_point *texcoord_src = texcoord_src_stack;
+ struct gl_point *texcoord_mask = texcoord_mask_stack;
+ cairo_status_t status;
+ int num_vertices, i;
+ GLenum err;
+ cairo_gl_composite_setup_t setup;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ memset (&setup, 0, sizeof (setup));
+
+ status = _cairo_gl_operand_init (&setup.src, src, dst,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ return status;
+ src_attributes = &setup.src.operand.texture.attributes;
+
+ if (mask != NULL && _cairo_pattern_is_opaque (mask))
+ mask = NULL;
+
+ if (mask != NULL) {
+ status = _cairo_gl_operand_init (&setup.mask, mask, dst,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status)) {
+ _cairo_gl_operand_destroy (&setup.src);
+ return status;
+ }
+ mask_attributes = &setup.mask.operand.texture.attributes;
+ }
+
+ ctx = _cairo_gl_context_acquire (dst->ctx);
+ _cairo_gl_set_destination (dst);
+ _cairo_gl_set_operator (dst, op);
+
+ _cairo_gl_set_src_operand (ctx, &setup);
+
+ if (mask != NULL) {
+ switch (setup.mask.type) {
+ case OPERAND_CONSTANT:
+ _cairo_gl_set_tex_combine_constant_color (ctx, 1,
+ setup.mask.operand.constant.color);
+ break;
+ case OPERAND_TEXTURE:
+ _cairo_gl_set_texture_surface (1, setup.mask.operand.texture.tex,
+ mask_attributes);
+
+ /* IN: dst.argb = src.argb * mask.aaaa */
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE1);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE1);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ break;
+ }
+ }
+
+ if (clip_region != NULL) {
+ int num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ if (num_rectangles * 4 > ARRAY_LENGTH (vertices_stack)) {
+ vertices = _cairo_malloc_ab (num_rectangles,
+ 4*3*sizeof (vertices[0]));
+ if (unlikely (vertices == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CONTEXT_RELEASE;
+ }
+
+ texcoord_src = vertices + num_rectangles * 4;
+ texcoord_mask = texcoord_src + num_rectangles * 4;
+ }
+
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+ vertices[4*i + 0].x = rect.x;
+ vertices[4*i + 0].y = rect.y;
+ vertices[4*i + 1].x = rect.x + rect.width;
+ vertices[4*i + 1].y = rect.y;
+ vertices[4*i + 2].x = rect.x + rect.width;
+ vertices[4*i + 2].y = rect.y + rect.height;
+ vertices[4*i + 3].x = rect.x;
+ vertices[4*i + 3].y = rect.y + rect.height;
+ }
+
+ num_vertices = 4 * num_rectangles;
+ } else {
+ vertices[0].x = dst_x;
+ vertices[0].y = dst_y;
+ vertices[1].x = dst_x + width;
+ vertices[1].y = dst_y;
+ vertices[2].x = dst_x + width;
+ vertices[2].y = dst_y + height;
+ vertices[3].x = dst_x;
+ vertices[3].y = dst_y + height;
+
+ num_vertices = 4;
+ }
+
+ glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
+ glEnableClientState (GL_VERTEX_ARRAY);
+
+ if (setup.src.type == OPERAND_TEXTURE) {
+ for (i = 0; i < num_vertices; i++) {
+ double s, t;
+
+ s = vertices[i].x;
+ t = vertices[i].y;
+ cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+ texcoord_src[i].x = s;
+ texcoord_src[i].y = t;
+ }
+
+ glClientActiveTexture (GL_TEXTURE0);
+ glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_src);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+
+ if (mask != NULL) {
+ if (setup.mask.type == OPERAND_TEXTURE) {
+ for (i = 0; i < num_vertices; i++) {
+ double s, t;
+
+ s = vertices[i].x;
+ t = vertices[i].y;
+ cairo_matrix_transform_point (&mask_attributes->matrix, &s, &t);
+ texcoord_mask[i].x = s;
+ texcoord_mask[i].y = t;
+ }
+
+ glClientActiveTexture (GL_TEXTURE1);
+ glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat)*2, texcoord_mask);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ }
+
+ glDrawArrays (GL_QUADS, 0, num_vertices);
+
+ glDisable (GL_BLEND);
+
+ glDisableClientState (GL_VERTEX_ARRAY);
+
+ glClientActiveTexture (GL_TEXTURE0);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glActiveTexture (GL_TEXTURE0);
+ glDisable (GL_TEXTURE_2D);
+
+ glClientActiveTexture (GL_TEXTURE1);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glActiveTexture (GL_TEXTURE1);
+ glDisable (GL_TEXTURE_2D);
+
+ while ((err = glGetError ()))
+ fprintf (stderr, "GL error 0x%08x\n", (int) err);
+
+ CONTEXT_RELEASE:
+ _cairo_gl_context_release (ctx);
+
+ _cairo_gl_operand_destroy (&setup.src);
+ if (mask != NULL)
+ _cairo_gl_operand_destroy (&setup.mask);
+
+ if (vertices != vertices_stack)
+ free (vertices);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_surface_pattern_t traps_pattern;
+ cairo_int_status_t status;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_cairo_surface_check_span_renderer (op,pattern,&dst->base, antialias)) {
+ status =
+ _cairo_surface_composite_trapezoids_as_polygon (&dst->base,
+ op, pattern,
+ antialias,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height,
+ traps, num_traps,
+ clip_region);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_gl_get_traps_pattern (dst,
+ dst_x, dst_y, width, height,
+ traps, num_traps, antialias,
+ &traps_pattern);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_surface_composite (op,
+ pattern, &traps_pattern.base, dst,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+
+ _cairo_pattern_fini (&traps_pattern.base);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+#define N_STACK_RECTS 4
+ cairo_gl_surface_t *surface = abstract_surface;
+ GLfloat vertices_stack[N_STACK_RECTS*4*2];
+ GLfloat colors_stack[N_STACK_RECTS*4*4];
+ cairo_gl_context_t *ctx;
+ int i;
+ GLfloat *vertices;
+ GLfloat *colors;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ ctx = _cairo_gl_context_acquire (surface->ctx);
+
+ _cairo_gl_set_destination (surface);
+ _cairo_gl_set_operator (surface, op);
+
+ if (num_rects > N_STACK_RECTS) {
+ vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2);
+ colors = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 4);
+ if (!vertices || !colors) {
+ _cairo_gl_context_release (ctx);
+ free (vertices);
+ free (colors);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ } else {
+ vertices = vertices_stack;
+ colors = colors_stack;
+ }
+
+ /* This should be loaded in as either a blend constant and an operator
+ * setup specific to this, or better, a fragment shader constant.
+ */
+ colors[0] = color->red * color->alpha;
+ colors[1] = color->green * color->alpha;
+ colors[2] = color->blue * color->alpha;
+ colors[3] = color->alpha;
+ for (i = 1; i < num_rects * 4; i++) {
+ colors[i*4 + 0] = colors[0];
+ colors[i*4 + 1] = colors[1];
+ colors[i*4 + 2] = colors[2];
+ colors[i*4 + 3] = colors[3];
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ vertices[i * 8 + 0] = rects[i].x;
+ vertices[i * 8 + 1] = rects[i].y;
+ vertices[i * 8 + 2] = rects[i].x + rects[i].width;
+ vertices[i * 8 + 3] = rects[i].y;
+ vertices[i * 8 + 4] = rects[i].x + rects[i].width;
+ vertices[i * 8 + 5] = rects[i].y + rects[i].height;
+ vertices[i * 8 + 6] = rects[i].x;
+ vertices[i * 8 + 7] = rects[i].y + rects[i].height;
+ }
+
+ glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices);
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glColorPointer (4, GL_FLOAT, sizeof (GLfloat)*4, colors);
+ glEnableClientState (GL_COLOR_ARRAY);
+
+ glDrawArrays (GL_QUADS, 0, 4 * num_rects);
+
+ glDisableClientState (GL_COLOR_ARRAY);
+ glDisableClientState (GL_VERTEX_ARRAY);
+ glDisable (GL_BLEND);
+
+ _cairo_gl_context_release (ctx);
+ if (vertices != vertices_stack)
+ free (vertices);
+ if (colors != colors_stack)
+ free (colors);
+
+ return CAIRO_STATUS_SUCCESS;
+#undef N_STACK_RECTS
+}
+
+typedef struct _cairo_gl_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_gl_composite_setup_t setup;
+
+ cairo_operator_t op;
+ cairo_antialias_t antialias;
+
+ cairo_gl_surface_t *dst;
+ cairo_region_t *clip;
+
+ cairo_composite_rectangles_t composite_rectangles;
+ GLuint vbo;
+ void *vbo_base;
+ unsigned int vbo_size;
+ unsigned int vbo_offset;
+ unsigned int vertex_size;
+} cairo_gl_surface_span_renderer_t;
+
+static void
+_cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer)
+{
+ int count;
+
+ if (renderer->vbo_offset == 0)
+ return;
+
+ glUnmapBufferARB (GL_ARRAY_BUFFER_ARB);
+
+ count = renderer->vbo_offset / renderer->vertex_size;
+ renderer->vbo_offset = 0;
+
+ if (renderer->clip) {
+ int i, num_rectangles = cairo_region_num_rectangles (renderer->clip);
+
+ glEnable (GL_SCISSOR_TEST);
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (renderer->clip, i, &rect);
+
+ glScissor (rect.x, rect.y, rect.width, rect.height);
+ glDrawArrays (GL_LINES, 0, count);
+ }
+ glDisable (GL_SCISSOR_TEST);
+ } else {
+ glDrawArrays (GL_LINES, 0, count);
+ }
+}
+
+static void *
+_cairo_gl_span_renderer_get_vbo (cairo_gl_surface_span_renderer_t *renderer,
+ unsigned int num_vertices)
+{
+ unsigned int offset;
+
+ if (renderer->vbo == 0) {
+ renderer->vbo_size = 16384;
+ glGenBuffersARB (1, &renderer->vbo);
+ glBindBufferARB (GL_ARRAY_BUFFER_ARB, renderer->vbo);
+
+ if (renderer->setup.src.type == OPERAND_TEXTURE)
+ renderer->vertex_size = 4 * sizeof (float) + sizeof (uint32_t);
+ else
+ renderer->vertex_size = 2 * sizeof (float) + sizeof (uint32_t);
+
+ glVertexPointer (2, GL_FLOAT, renderer->vertex_size, 0);
+ glEnableClientState (GL_VERTEX_ARRAY);
+
+ glColorPointer (4, GL_UNSIGNED_BYTE, renderer->vertex_size,
+ (void *) (uintptr_t) (2 * sizeof (float)));
+ glEnableClientState (GL_COLOR_ARRAY);
+
+ if (renderer->setup.src.type == OPERAND_TEXTURE) {
+ glClientActiveTexture (GL_TEXTURE0);
+ glTexCoordPointer (2, GL_FLOAT, renderer->vertex_size,
+ (void *) (uintptr_t) (2 * sizeof (float) +
+ sizeof (uint32_t)));
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ }
+
+ if (renderer->vbo_offset + num_vertices * renderer->vertex_size >
+ renderer->vbo_size) {
+ _cairo_gl_span_renderer_flush (renderer);
+ }
+
+ if (renderer->vbo_offset == 0) {
+ /* We'll only be using these vertices once. */
+ glBufferDataARB (GL_ARRAY_BUFFER_ARB, renderer->vbo_size, NULL,
+ GL_STREAM_DRAW_ARB);
+ renderer->vbo_base = glMapBufferARB (GL_ARRAY_BUFFER_ARB,
+ GL_WRITE_ONLY_ARB);
+ }
+
+ offset = renderer->vbo_offset;
+ renderer->vbo_offset += num_vertices * renderer->vertex_size;
+
+ return (char *) renderer->vbo_base + offset;
+}
+
+static void
+_cairo_gl_emit_span_vertex (cairo_gl_surface_span_renderer_t *renderer,
+ int dst_x, int dst_y, uint8_t alpha,
+ float *vertices)
+{
+ cairo_surface_attributes_t *src_attributes;
+ int v = 0;
+
+ src_attributes = &renderer->setup.src.operand.texture.attributes;
+
+ vertices[v++] = dst_x + BIAS;
+ vertices[v++] = dst_y + BIAS;
+ vertices[v++] = int_as_float (alpha << 24);
+ if (renderer->setup.src.type == OPERAND_TEXTURE) {
+ double s, t;
+
+ s = dst_x + BIAS;
+ t = dst_y + BIAS;
+ cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+ vertices[v++] = s;
+ vertices[v++] = t;
+ }
+}
+
+static void
+_cairo_gl_emit_span (cairo_gl_surface_span_renderer_t *renderer,
+ int x1, int x2, int y, uint8_t alpha)
+{
+ float *vertices = _cairo_gl_span_renderer_get_vbo (renderer, 2);
+
+ _cairo_gl_emit_span_vertex (renderer, x1, y, alpha, vertices);
+ _cairo_gl_emit_span_vertex (renderer, x2, y, alpha,
+ vertices + renderer->vertex_size / 4);
+}
+
+/* Emits the contents of the span renderer rows as GL_LINES with the span's
+ * alpha.
+ *
+ * Unlike the image surface, which is compositing into a temporary, we emit
+ * coverage even for alpha == 0, in case we're using an unbounded operator.
+ * But it means we avoid having to do the fixup.
+ */
+static cairo_status_t
+_cairo_gl_surface_span_renderer_render_row (
+ void *abstract_renderer,
+ int y,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+ int xmin = renderer->composite_rectangles.mask.x;
+ int xmax = xmin + renderer->composite_rectangles.width;
+ int prev_x = xmin;
+ int prev_alpha = 0;
+ unsigned i;
+ int x_translate;
+
+ /* Make sure we're within y-range. */
+ if (y < renderer->composite_rectangles.mask.y ||
+ y >= renderer->composite_rectangles.mask.y +
+ renderer->composite_rectangles.height)
+ return CAIRO_STATUS_SUCCESS;
+
+ x_translate = renderer->composite_rectangles.dst.x -
+ renderer->composite_rectangles.mask.x;
+ y += renderer->composite_rectangles.dst.y -
+ renderer->composite_rectangles.mask.y;
+
+ /* Find the first span within x-range. */
+ for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
+ if (i>0)
+ prev_alpha = spans[i-1].coverage;
+
+ /* Set the intermediate spans. */
+ for (; i < num_spans; i++) {
+ int x = spans[i].x;
+
+ if (x >= xmax)
+ break;
+
+ _cairo_gl_emit_span (renderer,
+ prev_x + x_translate, x + x_translate, y,
+ prev_alpha);
+
+ prev_x = x;
+ prev_alpha = spans[i].coverage;
+ }
+
+ if (prev_x < xmax) {
+ _cairo_gl_emit_span (renderer,
+ prev_x + x_translate, xmax + x_translate, y,
+ prev_alpha);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (!renderer)
+ return;
+
+ _cairo_gl_operand_destroy (&renderer->setup.src);
+ _cairo_gl_context_release (renderer->dst->ctx);
+
+ free (renderer);
+}
+
+static cairo_status_t
+_cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ _cairo_gl_span_renderer_flush (renderer);
+
+ glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
+ glDeleteBuffersARB (1, &renderer->vbo);
+ glDisableClientState (GL_VERTEX_ARRAY);
+ glDisableClientState (GL_COLOR_ARRAY);
+
+ glClientActiveTexture (GL_TEXTURE0);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glActiveTexture (GL_TEXTURE0);
+ glDisable (GL_TEXTURE_2D);
+
+ glActiveTexture (GL_TEXTURE1);
+ glDisable (GL_TEXTURE_2D);
+
+ glDisable (GL_BLEND);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_gl_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias)
+{
+ if (! _cairo_gl_operator_is_supported (op))
+ return FALSE;
+
+ if (! GLEW_ARB_vertex_buffer_object)
+ return FALSE;
+
+ return TRUE;
+
+ (void) pattern;
+ (void) abstract_dst;
+ (void) antialias;
+}
+
+static cairo_span_renderer_t *
+_cairo_gl_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_surface_span_renderer_t *renderer;
+ cairo_status_t status;
+ int width = rects->width;
+ int height = rects->height;
+ cairo_surface_attributes_t *src_attributes;
+ GLenum err;
+
+ renderer = calloc (1, sizeof (*renderer));
+ if (unlikely (renderer == NULL))
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy;
+ renderer->base.finish = _cairo_gl_surface_span_renderer_finish;
+ renderer->base.render_row =
+ _cairo_gl_surface_span_renderer_render_row;
+ renderer->op = op;
+ renderer->antialias = antialias;
+ renderer->dst = dst;
+ renderer->clip = clip_region;
+
+ renderer->composite_rectangles = *rects;
+
+ status = _cairo_gl_operand_init (&renderer->setup.src, src, dst,
+ rects->src.x, rects->src.y,
+ rects->dst.x, rects->dst.y,
+ width, height);
+ if (unlikely (status)) {
+ _cairo_gl_context_acquire (dst->ctx);
+ _cairo_gl_surface_span_renderer_destroy (renderer);
+ return _cairo_span_renderer_create_in_error (status);
+ }
+
+ _cairo_gl_context_acquire (dst->ctx);
+ _cairo_gl_set_destination (dst);
+
+ src_attributes = &renderer->setup.src.operand.texture.attributes;
+
+ _cairo_gl_set_operator (dst, op);
+ _cairo_gl_set_src_operand (dst->ctx, &renderer->setup);
+
+ /* Set up the mask to source from the incoming vertex color. */
+ glActiveTexture (GL_TEXTURE1);
+ /* Have to have a dummy texture bound in order to use the combiner unit. */
+ glBindTexture (GL_TEXTURE_2D, dst->ctx->dummy_tex);
+ glEnable (GL_TEXTURE_2D);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
+ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ while ((err = glGetError ()))
+ fprintf (stderr, "GL error 0x%08x\n", (int) err);
+
+ return &renderer->base;
+}
+
+static cairo_bool_t
+_cairo_gl_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_gl_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+}
+
+
+static cairo_int_status_t
+_cairo_gl_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ /* simplify the common case of clearing the surface */
+ if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_gl_context_t *ctx;
+
+ ctx = _cairo_gl_context_acquire (surface->ctx);
+ _cairo_gl_set_destination (surface);
+ glClear (GL_COLOR_BUFFER_BIT);
+ _cairo_gl_context_release (ctx);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+const cairo_surface_backend_t _cairo_gl_surface_backend = {
+ CAIRO_SURFACE_TYPE_GL,
+ _cairo_gl_surface_create_similar,
+ _cairo_gl_surface_finish,
+
+ _cairo_gl_surface_acquire_source_image,
+ _cairo_gl_surface_release_source_image,
+ _cairo_gl_surface_acquire_dest_image,
+ _cairo_gl_surface_release_dest_image,
+
+ _cairo_gl_surface_clone_similar,
+ _cairo_gl_surface_composite,
+ _cairo_gl_surface_fill_rectangles,
+ _cairo_gl_surface_composite_trapezoids,
+ _cairo_gl_surface_create_span_renderer,
+ _cairo_gl_surface_check_span_renderer,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_gl_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_gl_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ _cairo_gl_surface_scaled_glyph_fini,
+ _cairo_gl_surface_paint,
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ _cairo_gl_surface_show_glyphs, /* show_glyphs */
+ NULL /* snapshot */
+};
+
+/** Call glFinish(), used for accurate performance testing. */
+cairo_status_t
+cairo_gl_surface_glfinish (cairo_surface_t *surface)
+{
+ glFinish ();
+
+ return CAIRO_STATUS_SUCCESS;
+}