diff --git a/src/cairo-gl-ext-def-private.h b/src/cairo-gl-ext-def-private.h index a4b7d3464..97e42c165 100644 --- a/src/cairo-gl-ext-def-private.h +++ b/src/cairo-gl-ext-def-private.h @@ -100,6 +100,10 @@ #define GL_CLAMP_TO_BORDER 0x812D #endif +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif + #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif @@ -108,6 +112,18 @@ #define GL_RGBA8 0x8058 #endif +#ifndef GL_UNSIGNED_INT_8_8_8_8 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#endif + +#ifndef GL_UNSIGNED_SHORT_5_6_5_REV +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#endif + +#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#endif + #ifndef GL_UNSIGNED_INT_8_8_8_8_REV #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #endif @@ -116,4 +132,8 @@ #define GL_PACK_ROW_LENGTH 0x0D02 #endif +#ifndef GL_UNPACK_ROW_LENGTH +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#endif + #endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */ diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 6f1d410e2..433a40bb4 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -466,9 +466,11 @@ _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit); cairo_private cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, +_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); + GLenum *type, cairo_bool_t *has_alpha, + cairo_bool_t *needs_swap); cairo_private void _cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font); diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index b67a1eab2..813e3542b 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -74,12 +74,125 @@ static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) return surface->backend == &_cairo_gl_surface_backend; } -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) +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_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_x8r8g8b8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *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: @@ -195,6 +308,55 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, } } +/* + * 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) { @@ -578,47 +740,79 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, int dst_x, int dst_y) { GLenum internal_format, format, type; - cairo_bool_t has_alpha; + cairo_bool_t has_alpha, needs_swap; cairo_image_surface_t *clone = NULL; cairo_gl_context_t *ctx; int cpp; cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (! _cairo_gl_get_image_format_and_type (src->pixman_format, + 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)) + &has_alpha, + &needs_swap)) { cairo_bool_t is_supported; clone = _cairo_image_surface_coerce (src); - if (unlikely (clone->base.status)) - return clone->base.status; + if (unlikely (status = clone->base.status)) + goto FAIL; is_supported = - _cairo_gl_get_image_format_and_type (clone->pixman_format, + _cairo_gl_get_image_format_and_type (ctx->gl_flavor, + clone->pixman_format, &internal_format, &format, &type, - &has_alpha); + &has_alpha, + &needs_swap); assert (is_supported); + assert (!needs_swap); src = clone; } cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - status = _cairo_gl_surface_flush (&dst->base); if (unlikely (status)) goto FAIL; - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) + glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); 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 (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; + } + else + { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); glBindTexture (ctx->tex_target, dst->tex); glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -626,7 +820,12 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, glTexSubImage2D (ctx->tex_target, 0, dst_x, dst_y, width, height, format, type, - src->data + src_y * src->stride + src_x * cpp); + data_start_gles2 != NULL ? data_start_gles2 : + data_start); + + + if (data_start_gles2) + 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 @@ -684,7 +883,8 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, } FAIL: - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); status = _cairo_gl_context_release (ctx, status); @@ -775,7 +975,7 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t *surface, _cairo_gl_composite_flush (ctx); _cairo_gl_context_set_destination (ctx, surface); - glPixelStorei (GL_PACK_ALIGNMENT, 1); + glPixelStorei (GL_PACK_ALIGNMENT, 4); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); if (! _cairo_gl_surface_is_texture (surface) &&