/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2009 Eric Anholt
 * Copyright © 2009 Chris Wilson
 * Copyright © 2005,2010 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., 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 Red Hat, Inc.
 *
 * Contributor(s):
 *	Benjamin Otte <otte@gnome.org>
 *	Carl Worth <cworth@cworth.org>
 *	Chris Wilson <chris@chris-wilson.co.uk>
 *	Eric Anholt <eric@anholt.net>
 */

#include "cairoint.h"

#include "cairo-gl-private.h"
#include "cairo-gl-ext.h"

#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-fallback-private.h"
#include <pthread.h>

static const cairo_surface_backend_t _cairo_gl_surface_backend;

static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags);

static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
{
    return surface->backend == &_cairo_gl_surface_backend;
}

static cairo_bool_t
_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format,
					   GLenum *internal_format, GLenum *format,
					   GLenum *type, cairo_bool_t *has_alpha,
					   cairo_bool_t *needs_swap)
{
    cairo_bool_t is_little_endian = _cairo_is_little_endian ();

    *has_alpha = TRUE;

    switch ((int) pixman_format) {
	case PIXMAN_a8r8g8b8:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = !is_little_endian;
		return TRUE;

	case PIXMAN_x8r8g8b8:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_BYTE;
		*has_alpha = FALSE;
		*needs_swap = !is_little_endian;
		return TRUE;

	case PIXMAN_a8b8g8r8:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = !is_little_endian;
		return TRUE;

	case PIXMAN_x8b8g8r8:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_BYTE;
		*has_alpha = FALSE;
		*needs_swap = !is_little_endian;
		return TRUE;

	case PIXMAN_b8g8r8a8:
		*internal_format = GL_BGRA;
		*format = GL_BGRA;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = is_little_endian;
		return TRUE;

	case PIXMAN_b8g8r8x8:
		*internal_format = GL_BGRA;
		*format = GL_BGRA;
		*type = GL_UNSIGNED_BYTE;
		*has_alpha = FALSE;
		*needs_swap = is_little_endian;
		return TRUE;

	case PIXMAN_r8g8b8:
		*internal_format = GL_RGB;
		*format = GL_RGB;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = is_little_endian;
		return TRUE;

	case PIXMAN_b8g8r8:
		*internal_format = GL_RGB;
		*format = GL_RGB;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = !is_little_endian;
		return TRUE;

	case PIXMAN_r5g6b5:
		*internal_format = GL_RGB;
		*format = GL_RGB;
		*type = GL_UNSIGNED_SHORT_5_6_5;
		*needs_swap = FALSE;
		return TRUE;

	case PIXMAN_b5g6r5:
		*internal_format = GL_RGB;
		*format = GL_RGB;
		*type = GL_UNSIGNED_SHORT_5_6_5;
		*needs_swap = TRUE;
		return TRUE;

	case PIXMAN_a1b5g5r5:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_SHORT_5_5_5_1;
		*needs_swap = TRUE;
		return TRUE;

	case PIXMAN_x1b5g5r5:
		*internal_format = GL_RGBA;
		*format = GL_RGBA;
		*type = GL_UNSIGNED_SHORT_5_5_5_1;
		*has_alpha = FALSE;
		*needs_swap = TRUE;
		return TRUE;

	case PIXMAN_a8:
		*internal_format = GL_ALPHA;
		*format = GL_ALPHA;
		*type = GL_UNSIGNED_BYTE;
		*needs_swap = FALSE;
		return TRUE;

	default:
		return FALSE;
    }
}

static cairo_bool_t
_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format,
					GLenum *internal_format, GLenum *format,
					GLenum *type, cairo_bool_t *has_alpha,
					cairo_bool_t *needs_swap)
{
    *has_alpha = TRUE;
    *needs_swap = FALSE;

    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_RGBA;
	*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;

#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,27,2)
    case PIXMAN_a8r8g8b8_sRGB:
#endif
    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:
    case PIXMAN_x2r10g10b10:
    case PIXMAN_a2r10g10b10:
    case PIXMAN_r8g8b8x8:
    case PIXMAN_r8g8b8a8:
    case PIXMAN_x14r6g6b6:
    default:
	return FALSE;
    }
}

/*
 * Extracts pixel data from an image surface.
 */
static cairo_status_t
_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image,
				      int x, int y,
				      int width, int height,
				      void **output)
{
    int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8;
    char *data = _cairo_malloc_ab (width * height, cpp);
    char *dst = data;
    unsigned char *src = image->data + y * image->stride + x * cpp;
    int i;

    if (unlikely (data == NULL))
	return CAIRO_STATUS_NO_MEMORY;

    for (i = 0; i < height; i++) {
	memcpy (dst, src, width * cpp);
	src += image->stride;
	dst += width * cpp;
    }

    *output = data;

    return CAIRO_STATUS_SUCCESS;
}

cairo_bool_t
_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
				     pixman_format_code_t pixman_format,
				     GLenum *internal_format, GLenum *format,
				     GLenum *type, cairo_bool_t *has_alpha,
				     cairo_bool_t *needs_swap)
{
    if (flavor == CAIRO_GL_FLAVOR_DESKTOP)
	return _cairo_gl_get_image_format_and_type_gl (pixman_format,
						       internal_format, format,
						       type, has_alpha,
						       needs_swap);
    else
	return _cairo_gl_get_image_format_and_type_gles2 (pixman_format,
							  internal_format, format,
							  type, has_alpha,
							  needs_swap);

}

cairo_bool_t
_cairo_gl_operator_is_supported (cairo_operator_t op)
{
    return op < CAIRO_OPERATOR_SATURATE;
}

static void
_cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface)
{
    cairo_gl_operand_t *operand = &surface->operand;
    cairo_surface_attributes_t *attributes = &operand->texture.attributes;

    memset (operand, 0, sizeof (cairo_gl_operand_t));

    operand->type = CAIRO_GL_OPERAND_TEXTURE;
    operand->texture.surface = surface;
    operand->texture.tex = surface->tex;

    if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) {
	cairo_matrix_init_identity (&attributes->matrix);
    } else {
	cairo_matrix_init_scale (&attributes->matrix,
				 1.0 / surface->width,
				 1.0 / surface->height);
    }

    attributes->extend = CAIRO_EXTEND_NONE;
    attributes->filter = CAIRO_FILTER_NEAREST;
}

void
_cairo_gl_surface_init (cairo_device_t *device,
			cairo_gl_surface_t *surface,
			cairo_content_t content,
			int width, int height)
{
    assert (width > 0 && height > 0);

    _cairo_surface_init (&surface->base,
			 &_cairo_gl_surface_backend,
			 device,
			 content);

    surface->width = width;
    surface->height = height;
    surface->needs_update = FALSE;

    _cairo_gl_surface_embedded_operand_init (surface);
}

static cairo_bool_t
_cairo_gl_surface_size_valid_for_context (cairo_gl_context_t *ctx,
					  int width, int height)
{
    return width > 0 && height > 0 &&
	width <= ctx->max_framebuffer_size &&
	height <= ctx->max_framebuffer_size;
}

static cairo_bool_t
_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface,
			      int width, int height)
{
    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
    return _cairo_gl_surface_size_valid_for_context (ctx, width, height);
}

static cairo_surface_t *
_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t   *ctx,
					      cairo_content_t	    content,
					      GLuint		    tex,
					      int		    width,
					      int		    height)
{
    cairo_gl_surface_t *surface;

    surface = calloc (1, sizeof (cairo_gl_surface_t));
    if (unlikely (surface == NULL))
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));

    if (width <= 0 || height <= 0)
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));

    surface->tex = tex;
    _cairo_gl_surface_init (&ctx->base, surface, content, width, height);

    surface->supports_msaa = ctx->supports_msaa;
    surface->supports_stencil = TRUE;

    /* Create the texture used to store the surface's data. */
    _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
    glBindTexture (ctx->tex_target, surface->tex);
    glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf (ctx->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf (ctx->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return &surface->base;
}

static pthread_mutex_t s_gl_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned s_gl_surface_memory_bytes;

unsigned cairo_gl_surface_get_memory_bytes()
{
	return s_gl_surface_memory_bytes;
}

#ifndef CAIRO_GL_MINIMUM_TEXTURE_WIDTH
#define CAIRO_GL_MINIMUM_TEXTURE_WIDTH 1
#endif

#ifndef CAIRO_GL_MINIMUM_TEXTURE_HEIGHT
#define CAIRO_GL_MINIMUM_TEXTURE_HEIGHT 1
#endif

static cairo_surface_t *
_create_scratch_internal (cairo_gl_context_t *ctx,
			  cairo_content_t content,
			  int width,
			  int height,
			  cairo_bool_t for_caching)
{
    cairo_gl_surface_t *surface;
    GLenum format;
    GLuint tex;

    glGenTextures (1, &tex);
    surface = (cairo_gl_surface_t *)
	_cairo_gl_surface_create_scratch_for_texture (ctx, content,
						      tex, width, height);
    if (unlikely (surface->base.status)){
    	glDeleteTextures(1, &tex);
    	return &surface->base;
    }

    surface->owns_tex = TRUE;

    /* adjust the texture size after setting our real extents */
    if (width < CAIRO_GL_MINIMUM_TEXTURE_WIDTH)
    	width = CAIRO_GL_MINIMUM_TEXTURE_WIDTH;
    if (height < CAIRO_GL_MINIMUM_TEXTURE_HEIGHT)
    	height = CAIRO_GL_MINIMUM_TEXTURE_HEIGHT;

    {
    	 pthread_mutex_lock(&s_gl_mutex);
    	 s_gl_surface_memory_bytes += 4 * width * height;
    	 pthread_mutex_unlock(&s_gl_mutex);
    }

    switch (content) {
		default:
		ASSERT_NOT_REACHED;
		case CAIRO_CONTENT_COLOR_ALPHA:
		format = GL_RGBA;
		break;
		case CAIRO_CONTENT_ALPHA:
		/* When using GL_ALPHA, compositing doesn't work properly, but for
		 * caching surfaces, we are just uploading pixel data, so it isn't
		 * an issue. */
		if (for_caching)
			format = GL_ALPHA;
		else
			format = GL_RGBA;
		break;
		case CAIRO_CONTENT_COLOR:
		/* GL_RGB is almost what we want here -- sampling 1 alpha when
		 * texturing, using 1 as destination alpha factor in blending,
		 * etc.  However, when filtering with GL_CLAMP_TO_BORDER, the
		 * alpha channel of the border color will also be clamped to
		 * 1, when we actually want the border color we explicitly
		 * specified.  So, we have to store RGBA, and fill the alpha
		 * channel with 1 when blending.
		 */
		format = GL_RGBA;
		break;
    }

    glTexImage2D (ctx->tex_target, 0, format, width, height, 0,
		  format, GL_UNSIGNED_BYTE, NULL);

    return &surface->base;
}

cairo_surface_t *
_cairo_gl_surface_create_scratch (cairo_gl_context_t   *ctx,
				  cairo_content_t	content,
				  int			width,
				  int			height)
{
    return _create_scratch_internal (ctx, content, width, height, FALSE);
}

cairo_surface_t *
_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx,
					      cairo_content_t content,
					      int width,
					      int height)
{
    return _create_scratch_internal (ctx, content, width, height, TRUE);
}

static cairo_status_t
_cairo_gl_surface_clear (cairo_gl_surface_t  *surface,
                         const cairo_color_t *color)
{
    cairo_gl_context_t *ctx;
    cairo_status_t status;
    double r, g, b, a;

    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
    if (unlikely (status))
    	return status;

    _cairo_gl_composite_flush (ctx);

    _cairo_gl_context_set_destination (ctx, surface, surface->msaa_active);
    if (surface->base.content & CAIRO_CONTENT_COLOR) {
        r = color->red   * color->alpha;
        g = color->green * color->alpha;
        b = color->blue  * color->alpha;
    } else {
        r = g = b = 0;
    }
    if (surface->base.content & CAIRO_CONTENT_ALPHA) {
        a = color->alpha;
    } else {
        a = 1.0;
    }

    glDisable (GL_SCISSOR_TEST);
    glClearColor (r, g, b, a);
    glClear (GL_COLOR_BUFFER_BIT);

    if (a == 0)
    	surface->base.is_clear = TRUE;

    return _cairo_gl_context_release (ctx, status);
}

static cairo_surface_t *
_cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx,
					    cairo_content_t content,
					    int width,
					    int height)
{
    cairo_gl_surface_t *surface;
    cairo_int_status_t status;

    surface = (cairo_gl_surface_t *)
	_cairo_gl_surface_create_scratch (ctx, content, width, height);
    if (unlikely (surface->base.status))
    	return &surface->base;

    /* Cairo surfaces start out initialized to transparent (black) */
    status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
    if (unlikely (status)) {
    	cairo_surface_destroy (&surface->base);
    	return _cairo_surface_create_in_error (status);
    }

    return &surface->base;
}

cairo_surface_t *
cairo_gl_surface_create (cairo_device_t		*abstract_device,
			 cairo_content_t	 content,
			 int			 width,
			 int			 height)
{
    cairo_gl_context_t *ctx;
    cairo_gl_surface_t *surface;
    cairo_status_t status;

    if (! CAIRO_CONTENT_VALID (content))
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));

    if (abstract_device == NULL)
	return _cairo_image_surface_create_with_content (content, width, height);

    if (abstract_device->status)
	return _cairo_surface_create_in_error (abstract_device->status);

    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));

    status = _cairo_gl_context_acquire (abstract_device, &ctx);
    if (unlikely (status))
	return _cairo_surface_create_in_error (status);

    if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) {
	status = _cairo_gl_context_release (ctx, status);
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
    }

    surface = (cairo_gl_surface_t *)
	_cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);
    if (unlikely (surface->base.status)) {
	status = _cairo_gl_context_release (ctx, surface->base.status);
	cairo_surface_destroy (&surface->base);
	return _cairo_surface_create_in_error (status);
    }

    status = _cairo_gl_context_release (ctx, status);
    if (unlikely (status)) {
	cairo_surface_destroy (&surface->base);
	return _cairo_surface_create_in_error (status);
    }

    return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create);

/**
 * cairo_gl_surface_create_for_texture:
 * @content: type of content in the surface
 * @tex: name of texture to use for storage of surface pixels
 * @width: width of the surface, in pixels
 * @height: height of the surface, in pixels
 *
 * Creates a GL surface for the specified texture with the specified
 * content and dimensions.  The texture must be kept around until the
 * #cairo_surface_t is destroyed or cairo_surface_finish() is called
 * on the surface.  The initial contents of @tex will be used as the
 * initial image contents; you must explicitly clear the buffer,
 * using, for example, cairo_rectangle() and cairo_fill() if you want
 * it cleared.  The format of @tex should be compatible with @content,
 * in the sense that it must have the color components required by
 * @content.
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: TBD
 **/
cairo_surface_t *
cairo_gl_surface_create_for_texture (cairo_device_t	*abstract_device,
				     cairo_content_t	 content,
				     unsigned int	 tex,
				     int		 width,
				     int		 height)
{
    cairo_gl_context_t *ctx;
    cairo_gl_surface_t *surface;
    cairo_status_t status;

    if (! CAIRO_CONTENT_VALID (content))
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));

    if (abstract_device == NULL)
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));

    if (abstract_device->status)
    	return _cairo_surface_create_in_error (abstract_device->status);

    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH));

    status = _cairo_gl_context_acquire (abstract_device, &ctx);
    if (unlikely (status))
    	return _cairo_surface_create_in_error (status);

    if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) {
    	status = _cairo_gl_context_release (ctx, status);
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
    }

    surface = (cairo_gl_surface_t *)
	_cairo_gl_surface_create_scratch_for_texture (ctx, content,
						      tex, width, height);
    status = _cairo_gl_context_release (ctx, status);

    return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create_for_texture);


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;

    if (unlikely (abstract_surface->status))
    	return;
    if (unlikely (abstract_surface->finished)) {
    	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
        return;
    }

    if (! _cairo_surface_is_gl (abstract_surface) ||
        _cairo_gl_surface_is_texture (surface)) {
    	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
    	return;
    }

    if (surface->width != width || surface->height != height) {
		surface->needs_update = TRUE;
		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;
}

cairo_status_t
cairo_gl_surface_get_size(cairo_surface_t* abstract_surface, int* ret_width, int* ret_height)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;

    *ret_width = surface->width;
    *ret_height = surface->height;

    return CAIRO_STATUS_SUCCESS;
}

cairo_format_t
cairo_gl_surface_get_format (cairo_surface_t *abstract_surface)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
    pixman_format_code_t pixman_format;
    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_FORMAT_INVALID;

    if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA)
    	return CAIRO_FORMAT_ARGB32;
	if (surface->base.content == CAIRO_CONTENT_COLOR)
		return CAIRO_FORMAT_RGB24;
	if (surface->base.content == CAIRO_CONTENT_ALPHA)
		return CAIRO_FORMAT_A8;
	return CAIRO_FORMAT_INVALID;
}

cairo_status_t
cairo_gl_surface_get_texture (cairo_surface_t *abstract_surface, GLuint* tex, GLuint* fb)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;

    *tex = surface->tex;
    *fb = surface->fb;

    return CAIRO_STATUS_SUCCESS;
}

cairo_status_t
cairo_gl_surface_framebuffer_complete(cairo_surface_t* abstract_surface)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;

    if(surface->fb_status == GL_FRAMEBUFFER_COMPLETE)
    	return CAIRO_STATUS_SUCCESS;

    return CAIRO_STATUS_NO_MEMORY;
}

void
cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
        return;
    }

    if (! _cairo_surface_is_gl (abstract_surface)) {
	_cairo_surface_set_error (abstract_surface,
				  CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
	return;
    }

    if (! _cairo_gl_surface_is_texture (surface)) {
	cairo_gl_context_t *ctx;
        cairo_status_t status;

        status = _cairo_gl_context_acquire (surface->base.device, &ctx);
        if (unlikely (status))
            return;

	/* For swapping on EGL, at least, we need a valid context/target. */
	_cairo_gl_context_set_destination (ctx, surface, FALSE);
	/* And in any case we should flush any pending operations. */
	_cairo_gl_composite_flush (ctx);

	ctx->swap_buffers (ctx, surface);

        status = _cairo_gl_context_release (ctx, status);
        if (status)
            status = _cairo_surface_set_error (abstract_surface, status);
    }
}

static cairo_surface_t *
_cairo_gl_surface_create_similar (void		 *abstract_surface,
				  cairo_content_t  content,
				  int		  width,
				  int		  height)
{
    cairo_surface_t *surface = abstract_surface;
    cairo_gl_context_t *ctx;
    cairo_status_t status;

    if (! _cairo_gl_surface_size_valid (abstract_surface, width, height))
        return _cairo_image_surface_create_with_content (content, width, height);

    status = _cairo_gl_context_acquire (surface->device, &ctx);
    if (unlikely (status))
	return _cairo_surface_create_in_error (status);

    surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);

    status = _cairo_gl_context_release (ctx, status);
    if (unlikely (status)) {
        cairo_surface_destroy (surface);
        return _cairo_surface_create_in_error (status);
    }

    return surface;
}

cairo_surface_t *
cairo_gl_surface_create_for_size (cairo_device_t *device,
				  cairo_content_t  content,
				  int		  width,
				  int		  height)
{
	cairo_surface_t *surface;
    cairo_gl_context_t *ctx;
    cairo_status_t status;

    status = _cairo_gl_context_acquire (device, &ctx);
    if (unlikely (status))
    	return NULL;

    surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);

    status = _cairo_gl_context_release (ctx, status);
    if (unlikely (status)) {
        cairo_surface_destroy (surface);
        return NULL;
    }

    return surface;
}

static cairo_int_status_t
_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst,
				      cairo_gl_context_t *ctx,
				      int x, int y,
				      int width, int height)
{
    cairo_gl_composite_t setup;
    cairo_status_t status;

    _cairo_gl_composite_flush (ctx);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);

    TRACE ((stderr, "%s.\n", __FUNCTION__));

    status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
				       dst, FALSE);
    if (unlikely (status))
        goto CLEANUP;

    _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK);

    status = _cairo_gl_composite_begin (&setup, &ctx);
    if (unlikely (status))
        goto CLEANUP;

    _cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height);

    status = _cairo_gl_context_release (ctx, status);

  CLEANUP:
    _cairo_gl_composite_fini (&setup);

    _cairo_gl_composite_flush (ctx);
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

    return status;
}

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,
			      cairo_bool_t force_flush)
{
    GLenum internal_format, format, type;
    cairo_bool_t has_alpha, needs_swap;
    cairo_image_surface_t *clone = NULL;
    cairo_gl_context_t *ctx;
    int cpp;
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    status = _cairo_gl_context_acquire (dst->base.device, &ctx);
    if (unlikely (status))
    	return status;

    if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
					       src->pixman_format,
					       &internal_format,
					       &format,
					       &type,
					       &has_alpha,
					       &needs_swap))
    {
		cairo_bool_t is_supported;

		clone = _cairo_image_surface_coerce (src);
		if (unlikely (status = clone->base.status))
			goto FAIL;

		is_supported =
			_cairo_gl_get_image_format_and_type (ctx->gl_flavor,
							 clone->pixman_format,
											 &internal_format,
							 &format,
							 &type,
							 &has_alpha,
							 &needs_swap);
		assert (is_supported);
		assert (!needs_swap);
		src = clone;
    }

    cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;

    if (force_flush) {
		status = _cairo_gl_surface_flush (&dst->base, 0);
		if (unlikely (status))
			goto FAIL;
    }

    if (_cairo_gl_surface_is_texture (dst)) {
		void *data_start = src->data + src_y * src->stride + src_x * cpp;
		void *data_start_gles2 = NULL;

		/*
		 * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the
		 * image data ourselves in some cases. In particular, we must extract
		 * the pixels if:
		 * a. we don't want full-length lines or
		 * b. the row stride cannot be handled by GL itself using a 4 byte
		 *     alignment constraint
		 */
		if (src->stride < 0 ||
			(ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
			 (src->width * cpp < src->stride - 3 ||
			  width != src->width)))
		{
			glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
			status = _cairo_gl_surface_extract_image_data (src, src_x, src_y,
								   width, height,
								   &data_start_gles2);
			if (unlikely (status))
				goto FAIL;

			data_start = data_start_gles2;
		}
		else
		{
			glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
			if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
				glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
		}

		_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
		glBindTexture (ctx->tex_target, dst->tex);
		glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf (ctx->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf (ctx->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexSubImage2D (ctx->tex_target, 0,
				 dst_x, dst_y, width, height,
				 format, type, data_start);

		free (data_start_gles2);

		/* If we just treated some rgb-only data as rgba, then we have to
		 * go back and fix up the alpha channel where we filled in this
		 * texture data.
		 */
		if (!has_alpha) {
			_cairo_gl_surface_fill_alpha_channel (dst, ctx,
							  dst_x, dst_y,
							  width, height);
		}
    } else {
        cairo_surface_t *tmp;

        tmp = _cairo_gl_surface_create_scratch (ctx,
                                                dst->base.content,
                                                width, height);
        if (unlikely (tmp->status))
            goto FAIL;

        status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
                                               src,
                                               src_x, src_y,
                                               width, height,
                                               0, 0, force_flush);
        if (status == CAIRO_INT_STATUS_SUCCESS) {
            cairo_surface_pattern_t tmp_pattern;
			cairo_rectangle_int_t r;
			cairo_clip_t *clip;

				_cairo_pattern_init_for_surface (&tmp_pattern, tmp);
			cairo_matrix_init_translate (&tmp_pattern.base.matrix,
						 -dst_x, -dst_y);
			tmp_pattern.base.filter = CAIRO_FILTER_NEAREST;
			tmp_pattern.base.extend = CAIRO_EXTEND_NONE;

			r.x = dst_x;
			r.y = dst_y;
			r.width = width;
			r.height = height;
			clip = _cairo_clip_intersect_rectangle (NULL, &r);
			status = _cairo_surface_paint (&dst->base,
						   CAIRO_OPERATOR_SOURCE,
						   &tmp_pattern.base,
						   clip);
			_cairo_clip_destroy (clip);
            _cairo_pattern_fini (&tmp_pattern.base);
        }

        cairo_surface_destroy (tmp);
    }

FAIL:
    status = _cairo_gl_context_release (ctx, status);

    if (clone)
        cairo_surface_destroy (&clone->base);

    TRACE ((stderr, "%s\n", __FUNCTION__));

    return status;
}

/* Limit on the width / height of an image surface in pixels.  This is
 * mainly determined by coordinates of things sent to pixman at the
 * moment being in 16.16 format. */
#define MAX_IMAGE_SIZE 32767

static cairo_bool_t
_cairo_image_surface_is_size_valid (int width, int height)
{
    return 0 <= width  &&  width <= MAX_IMAGE_SIZE &&
	   0 <= height && height <= MAX_IMAGE_SIZE;
}

static cairo_surface_t *
_cairo_gl_surface_create_for_data_BGRA (unsigned char     *data,
				     cairo_format_t	format,
				     int		width,
				     int		height,
				     int		stride)
{
    pixman_format_code_t pixman_format;
    int minstride;

    if (! CAIRO_FORMAT_VALID (format))
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));

    if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0)
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));

    if (! _cairo_image_surface_is_size_valid (width, height))
    	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));

    minstride = cairo_format_stride_for_width (format, width);
    if (stride < 0) {
		if (stride > -minstride) {
			return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
		}
    } else {
		if (stride < minstride) {
			return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
		}
    }

	switch (format) {
        case CAIRO_FORMAT_A1:
        	pixman_format = PIXMAN_a1;
    	break;
        case CAIRO_FORMAT_A8:
        	pixman_format = PIXMAN_a8;
    	break;
        case CAIRO_FORMAT_RGB24:
        	pixman_format = PIXMAN_x8b8g8r8;
    	break;
        case CAIRO_FORMAT_RGB30:
        	pixman_format = PIXMAN_x2r10g10b10;
    	break;
        case CAIRO_FORMAT_RGB16_565:
        	pixman_format = PIXMAN_r5g6b5;
    	break;
        case CAIRO_FORMAT_ARGB32:
        case CAIRO_FORMAT_INVALID:
        default:
        	pixman_format = PIXMAN_a8b8g8r8;
    	break;
	}

    return _cairo_image_surface_create_with_pixman_format (data,
							   pixman_format,
							   width, height,
							   stride);
}

cairo_surface_t *
cairo_gl_surface_create_for_data (cairo_device_t *device,
		unsigned char *data, cairo_format_t format, int width, int height, int stride)
{
	cairo_surface_t *tmp;
    cairo_gl_context_t *ctx;
    cairo_status_t status;
	cairo_image_surface_t *src;

	tmp = NULL;

	src = (cairo_image_surface_t *)_cairo_gl_surface_create_for_data_BGRA(data, format, width, height, stride);
	if (unlikely (src->base.status))
		return 0;

    status = _cairo_gl_context_acquire (device, &ctx);
    if (unlikely (status))
    	goto DONE;

    tmp = _cairo_gl_surface_create_scratch(ctx, src->base.content, width, height);
    if (unlikely (tmp->status))
        goto DONE;

    status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
                                           src,
                                           0, 0,
                                           width, height,
                                           0, 0, 0);
    if (status != CAIRO_INT_STATUS_SUCCESS) {
        cairo_surface_destroy (tmp);
        tmp = NULL;
    }

    status = _cairo_gl_context_release (ctx, status);
    if (unlikely (status)) {
        cairo_surface_destroy (tmp);
        tmp = NULL;
    }

DONE:
    cairo_surface_destroy (&src->base);
    return tmp;
}

static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface)
{
    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
    return ctx->gl_flavor;
}

static cairo_status_t
_cairo_gl_surface_finish (void *abstract_surface)
{
    cairo_gl_surface_t *surface = abstract_surface;
    cairo_status_t status;
    cairo_gl_context_t *ctx;

    TRACE ((stderr, "%s:%i surface size %ix%i\n", __FUNCTION__, __LINE__, surface->width, surface->height));

    if(surface->mapped_image)
    {
    	cairo_surface_destroy(&surface->mapped_image->base);
    	surface->mapped_image = NULL;
    }

	status = _cairo_gl_context_acquire (surface->base.device, &ctx);
	if (unlikely (status))
		return status;

	if(!_cairo_gl_context_is_flushed (ctx))
    	_cairo_gl_composite_flush (ctx);

    if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
        ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface)
    {
        ctx->dispatch.DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + CAIRO_GL_TEX_SOURCE);
        memset (&ctx->operands[CAIRO_GL_TEX_SOURCE], 0, sizeof (cairo_gl_operand_t));
    }

    if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
        ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface)
    {
    	ctx->dispatch.DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + CAIRO_GL_TEX_MASK);
        memset (&ctx->operands[CAIRO_GL_TEX_MASK], 0, sizeof (cairo_gl_operand_t));
    }

    if (ctx->current_target == surface) {
	    ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0);
    	ctx->current_target = NULL;
    }

    if (surface->fb)
        ctx->dispatch.DeleteFramebuffers (1, &surface->fb);

    if (surface->depth_stencil)
        ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil);
    if (surface->owns_tex)
    	glDeleteTextures (1, &surface->tex);

    if (surface->owns_tex) {
		 pthread_mutex_lock(&s_gl_mutex);
		 s_gl_surface_memory_bytes -= 4 * surface->width * surface->height;
		 pthread_mutex_unlock(&s_gl_mutex);
    }

    if (surface->msaa_depth_stencil)
    	ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil);

#if CAIRO_HAS_GL_SURFACE
    if (surface->msaa_fb)
    	ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb);
    if (surface->msaa_rb)
    	ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
#endif

    _cairo_clip_destroy (surface->clip_on_stencil_buffer);

    TRACE ((stderr, "%s:%i\n", __FUNCTION__, __LINE__));

   	return _cairo_gl_context_release (ctx, status);
}

static cairo_image_surface_t *
_cairo_gl_surface_map_to_image_internal (void      *abstract_surface,
				unsigned char* pixman_buffer, int pixman_stride,
				const cairo_rectangle_int_t   *extents,
				int overwrite)
{
    cairo_gl_surface_t *surface = abstract_surface;
    cairo_image_surface_t *image;
    cairo_gl_context_t *ctx;
    GLenum format, type;
    pixman_format_code_t pixman_format;
    unsigned int cpp;
    cairo_bool_t flipped, mesa_invert;
    cairo_status_t status;
    int y;

    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
    if (unlikely (status)) {
    	return _cairo_image_surface_create_in_error (status);
    }

    /* Want to use a switch statement here but the compiler gets whiny. */
    if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
		format = GL_BGRA;
		pixman_format = PIXMAN_a8r8g8b8;
		type = GL_UNSIGNED_INT_8_8_8_8_REV;
		cpp = 4;
    } else if (surface->base.content == CAIRO_CONTENT_COLOR) {
		format = GL_BGRA;
		pixman_format = PIXMAN_x8r8g8b8;
		type = GL_UNSIGNED_INT_8_8_8_8_REV;
		cpp = 4;
    } else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
		format = GL_ALPHA;
		pixman_format = PIXMAN_a8;
		type = GL_UNSIGNED_BYTE;
		cpp = 1;
    } else {
    	ASSERT_NOT_REACHED;
    	return NULL;
    }

    if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) {
		/* If only RGBA is supported, we must download data in a compatible
		 * format. This means that pixman will convert the data on the CPU when
		 * interacting with other image surfaces. For ALPHA, GLES2 does not
		 * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the
		 * pixman image that is created has row_stride = row_width * bpp. */
		if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) {
			cairo_bool_t little_endian = _cairo_is_little_endian ();
			format = GL_RGBA;

			if (surface->base.content == CAIRO_CONTENT_COLOR) {
				pixman_format = little_endian ? PIXMAN_x8b8g8r8 : PIXMAN_b8g8r8x8;
			} else {
				pixman_format = little_endian ? PIXMAN_a8b8g8r8 : PIXMAN_b8g8r8a8;
			}
		}

		/* GLES2 only supports GL_UNSIGNED_BYTE. */
		type = GL_UNSIGNED_BYTE;
		cpp = 4;
    }

	image = (cairo_image_surface_t*)
			_cairo_image_surface_create_with_pixman_format (pixman_buffer,
							pixman_format,
							extents->width,
							extents->height,
							pixman_stride);
	if (unlikely (image->base.status)) {
		status = _cairo_gl_context_release (ctx, status);
		return image;
	}

    cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);

#if 0
    /* If the original surface has not been modified or
     * is clear, we can avoid downloading data. */
    if (surface->base.is_clear || surface->base.serial == 0) {
		status = _cairo_gl_context_release (ctx, status);
		return image;
    }
#endif

	_cairo_gl_composite_flush (ctx);

    if(!overwrite){
		/* 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_context_set_destination (ctx, surface, FALSE);

		flipped = ! _cairo_gl_surface_is_texture (surface);
		mesa_invert = flipped && ctx->has_mesa_pack_invert;

		glPixelStorei (GL_PACK_ALIGNMENT, 4);
		if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
			glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
		if (mesa_invert)
			glPixelStorei (GL_PACK_INVERT_MESA, 1);

		y = extents->y;
		if (flipped)
			y = surface->height - extents->y - extents->height;

		glReadPixels (extents->x, y,
			  extents->width, extents->height,
			  format, type, image->data);
		if (mesa_invert)
			glPixelStorei (GL_PACK_INVERT_MESA, 0);
    }

    status = _cairo_gl_context_release (ctx, status);

    if (unlikely (status)) {
    	cairo_surface_destroy (&image->base);
    	return _cairo_image_surface_create_in_error (status);
    }

    /* We must invert the image manualy if we lack GL_MESA_pack_invert */
    if (!overwrite && flipped && ! mesa_invert) {
		uint8_t stack[1024], *row = stack;
		uint8_t *top = image->data;
		uint8_t *bot = image->data + (image->height-1)*image->stride;

		if (image->stride > (int)sizeof(stack)) {
			row = malloc (image->stride);
			if (unlikely (row == NULL)) {
				cairo_surface_destroy (&image->base);
				return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
			}
		}

		while (top < bot) {
			memcpy (row, top, image->stride);
			memcpy (top, bot, image->stride);
			memcpy (bot, row, image->stride);
			top += image->stride;
			bot -= image->stride;
		}

		if (row != stack)
			free(row);
    }

    image->base.is_clear = FALSE;
    return image;
}

static cairo_image_surface_t *
_cairo_gl_surface_map_to_image (void      *abstract_surface,
				const cairo_rectangle_int_t   *extents)
{
	return _cairo_gl_surface_map_to_image_internal(abstract_surface, 0, -1, extents, FALSE);
}

static cairo_surface_t *
_cairo_gl_surface_source (void		       *abstract_surface,
			  cairo_rectangle_int_t *extents)
{
    cairo_gl_surface_t *surface = abstract_surface;

    if (extents) {
	extents->x = extents->y = 0;
	extents->width  = surface->width;
	extents->height = surface->height;
    }

    return &surface->base;
}

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;

    *image_out = (cairo_image_surface_t *)
	_cairo_gl_surface_map_to_image (surface, &extents);
    return (*image_out)->base.status;
}

cairo_surface_t *
cairo_gl_surface_map_to_image(cairo_surface_t *abstract_surface, int overwrite)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
    cairo_rectangle_int_t extents;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return _cairo_image_surface_create_in_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH);

    if (unlikely (surface->mapped_image)){
    	cairo_surface_destroy(&surface->mapped_image->base);
    	surface->mapped_image = 0;
    }

    extents.x = extents.y = 0;
    extents.width = surface->width;
    extents.height = surface->height;

    surface->mapped_image = _cairo_gl_surface_map_to_image_internal(surface, 0, -1, &extents, overwrite);

    if (unlikely (surface->mapped_image->base.status)){
    	cairo_surface_destroy(&surface->mapped_image->base);
    	surface->mapped_image = 0;
    	return 0;
    }

    return &surface->mapped_image->base;
}

cairo_status_t
cairo_gl_surface_extract_data(cairo_surface_t* abstract_surface, unsigned char* buffer, int buffer_len)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
    cairo_rectangle_int_t extents;
    cairo_image_surface_t *dst_image;
    cairo_status_t status;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;

    if (surface->base.content != CAIRO_CONTENT_COLOR_ALPHA)
    	return CAIRO_INT_STATUS_UNSUPPORTED;

    if(buffer_len < surface->width * surface->height * 4)
    	return CAIRO_INT_STATUS_UNSUPPORTED;

    extents.x = extents.y = 0;
    extents.width = surface->width;
    extents.height = surface->height;

    dst_image = _cairo_gl_surface_map_to_image_internal(surface, buffer, surface->width * 4, &extents, FALSE);

    status = dst_image->base.status;

    cairo_surface_destroy(&dst_image->base);

    return status;
}

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_int_status_t
_cairo_gl_surface_unmap_image_internal (void		      *abstract_surface,
			       cairo_image_surface_t *image,
			       int dirty)
{
    cairo_int_status_t status;

    if(dirty){
    	status = _cairo_gl_surface_draw_image (abstract_surface, image,
					   0, 0,
					   image->width, image->height,
					   image->base.device_transform_inverse.x0,
					   image->base.device_transform_inverse.y0,
					   TRUE);
    }
    else
    	status = CAIRO_STATUS_SUCCESS;

    cairo_surface_finish (&image->base);
    cairo_surface_destroy (&image->base);

    return status;
}

static cairo_int_status_t
_cairo_gl_surface_unmap_image (void		      *abstract_surface,
			       cairo_image_surface_t *image)
{
	return _cairo_gl_surface_unmap_image_internal(abstract_surface, image, TRUE);
}

cairo_status_t
cairo_gl_surface_unmap_image(cairo_surface_t *abstract_surface, int dirty)
{
	cairo_int_status_t status;
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (! _cairo_surface_is_gl (abstract_surface))
    	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;

    status = _cairo_gl_surface_unmap_image_internal(surface, surface->mapped_image, dirty);
    surface->mapped_image = 0;

    return status;
}

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 cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags)
{
    cairo_gl_surface_t *surface = abstract_surface;
    cairo_status_t status;
    cairo_gl_context_t *ctx;

    if (flags)
    	return CAIRO_STATUS_SUCCESS;

    ctx = (cairo_gl_context_t *) surface->base.device;
    if(ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && ctx->make_current){
		if(_cairo_gl_context_is_flushed(ctx))
			return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
    if (unlikely (status))
        return status;

    if(ctx->make_current)
        _cairo_gl_composite_flush (ctx);
    else if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
         ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) ||
        (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
         ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) ||
        (ctx->current_target == surface))
      _cairo_gl_composite_flush (ctx);

    status = _cairo_gl_surface_resolve_multisampling (surface);

    return _cairo_gl_context_release (ctx, status);
}

cairo_int_status_t
_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface)
{
    cairo_gl_context_t *ctx;
    cairo_int_status_t status;

    if (! surface->msaa_active)
    	return CAIRO_INT_STATUS_SUCCESS;

    if (surface->base.device == NULL)
    	return CAIRO_INT_STATUS_SUCCESS;

    /* GLES surfaces do not need explicit resolution. */
    if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES)
    	return CAIRO_INT_STATUS_SUCCESS;

    if (! _cairo_gl_surface_is_texture (surface))
    	return CAIRO_INT_STATUS_SUCCESS;

    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
    if (unlikely (status))
    	return status;

    ctx->current_target = surface;

#if CAIRO_HAS_GL_SURFACE
    _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE);
#endif

    status = _cairo_gl_context_release (ctx, status);
    return status;
}

static const cairo_compositor_t *
get_compositor (cairo_gl_surface_t *surface)
{
    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
    return ctx->compositor;
}

static cairo_int_status_t
_cairo_gl_surface_paint (void			*surface,
			 cairo_operator_t	 op,
			 const cairo_pattern_t	*source,
			 const cairo_clip_t	*clip)
{
    /* simplify the common case of clearing the surface */
    if (clip == NULL) {
        if (op == CAIRO_OPERATOR_CLEAR)
            return _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
       else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
                (op == CAIRO_OPERATOR_SOURCE ||
                 (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
            return _cairo_gl_surface_clear (surface,
                                            &((cairo_solid_pattern_t *) source)->color);
        }
    }

    return _cairo_compositor_paint (get_compositor (surface), surface,
				    op, source, clip);
}

static cairo_int_status_t
_cairo_gl_surface_mask (void			 *surface,
			cairo_operator_t	  op,
			const cairo_pattern_t	*source,
			const cairo_pattern_t	*mask,
			const cairo_clip_t	*clip)
{
    return _cairo_compositor_mask (get_compositor (surface), surface,
				   op, source, mask, clip);
}

static cairo_int_status_t
_cairo_gl_surface_stroke (void			        *surface,
                          cairo_operator_t		 op,
                          const cairo_pattern_t	        *source,
                          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,
                          const cairo_clip_t		*clip)
{
    return _cairo_compositor_stroke (get_compositor (surface), surface,
				     op, source, path, style,
				     ctm, ctm_inverse, tolerance, antialias,
				     clip);
}

static cairo_int_status_t
_cairo_gl_surface_fill (void			*surface,
                        cairo_operator_t	 op,
                        const cairo_pattern_t	*source,
                        const cairo_path_fixed_t*path,
                        cairo_fill_rule_t	 fill_rule,
                        double			 tolerance,
                        cairo_antialias_t	 antialias,
                        const cairo_clip_t	*clip)
{
    return _cairo_compositor_fill (get_compositor (surface), surface,
				   op, source, path,
				   fill_rule, tolerance, antialias,
				   clip);
}

static cairo_int_status_t
_cairo_gl_surface_glyphs (void			*surface,
			  cairo_operator_t	 op,
			  const cairo_pattern_t	*source,
			  cairo_glyph_t		*glyphs,
			  int			 num_glyphs,
			  cairo_scaled_font_t	*font,
			  const cairo_clip_t	*clip)
{
    return _cairo_compositor_glyphs (get_compositor (surface), surface,
				     op, source, glyphs, num_glyphs, font,
				     clip);
}

static const cairo_surface_backend_t _cairo_gl_surface_backend = {
    CAIRO_SURFACE_TYPE_GL,
    _cairo_gl_surface_finish,
    _cairo_default_context_create,

    _cairo_gl_surface_create_similar,
    NULL, /* similar image */
    _cairo_gl_surface_map_to_image,
    _cairo_gl_surface_unmap_image,

    _cairo_gl_surface_source,
    _cairo_gl_surface_acquire_source_image,
    _cairo_gl_surface_release_source_image,
    NULL, /* snapshot */

    NULL, /* copy_page */
    NULL, /* show_page */

    _cairo_gl_surface_get_extents,
    _cairo_image_surface_get_font_options,

    _cairo_gl_surface_flush,
    NULL, /* mark_dirty_rectangle */

    _cairo_gl_surface_paint,
    _cairo_gl_surface_mask,
    _cairo_gl_surface_stroke,
    _cairo_gl_surface_fill,
    NULL, /* fill/stroke */
    _cairo_gl_surface_glyphs,
};
