From patchwork Mon Jul 25 13:56:39 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: alexandros.frantzis@linaro.org X-Patchwork-Id: 3087 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id BC92D23E54 for ; Mon, 25 Jul 2011 13:57:34 +0000 (UTC) Received: from mail-qw0-f52.google.com (mail-qw0-f52.google.com [209.85.216.52]) by fiordland.canonical.com (Postfix) with ESMTP id 7EFEDA183B4 for ; Mon, 25 Jul 2011 13:57:34 +0000 (UTC) Received: by mail-qw0-f52.google.com with SMTP id 8so3003702qwb.11 for ; Mon, 25 Jul 2011 06:57:34 -0700 (PDT) Received: by 10.229.1.217 with SMTP id 25mr1001422qcg.38.1311602254203; Mon, 25 Jul 2011 06:57:34 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.229.217.78 with SMTP id hl14cs77889qcb; Mon, 25 Jul 2011 06:57:33 -0700 (PDT) Received: by 10.204.11.11 with SMTP id r11mr1509581bkr.190.1311602252974; Mon, 25 Jul 2011 06:57:32 -0700 (PDT) Received: from mail-fx0-f44.google.com (mail-fx0-f44.google.com [209.85.161.44]) by mx.google.com with ESMTPS id 18si7042659fat.105.2011.07.25.06.57.32 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 25 Jul 2011 06:57:32 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.161.44 is neither permitted nor denied by best guess record for domain of alexandros.frantzis@linaro.org) client-ip=209.85.161.44; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.161.44 is neither permitted nor denied by best guess record for domain of alexandros.frantzis@linaro.org) smtp.mail=alexandros.frantzis@linaro.org Received: by mail-fx0-f44.google.com with SMTP id 6so6802455fxe.17 for ; Mon, 25 Jul 2011 06:57:32 -0700 (PDT) Received: by 10.223.13.198 with SMTP id d6mr6755461faa.119.1311602252534; Mon, 25 Jul 2011 06:57:32 -0700 (PDT) Received: from localhost (77.49.93.204.dsl.dyn.forthnet.gr [77.49.93.204]) by mx.google.com with ESMTPS id h9sm3803094faa.39.2011.07.25.06.57.31 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 25 Jul 2011 06:57:32 -0700 (PDT) From: alexandros.frantzis@linaro.org To: patches@linaro.org Subject: [PATCH 12/21] gl: Add GLES2 support for drawing image surfaces on gl surfaces Date: Mon, 25 Jul 2011 16:56:39 +0300 Message-Id: <1311602208-5973-12-git-send-email-alexandros.frantzis@linaro.org> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1311602208-5973-1-git-send-email-alexandros.frantzis@linaro.org> References: <1311602208-5973-1-git-send-email-alexandros.frantzis@linaro.org> From: Alexandros Frantzis Work around GLES2 limitations in texture pixel formats and options for pixel packing/unpacking. Depending on the endianness and the image pixel format, we may need to manually swap the elements in a pixel group. This is not currently implemented, but for little-endian architectures the common pixman BGRA formats don't need a swap. Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the image data ourselves in some cases, so we can provide it to glTexSubImage2D using a layout it can understand. 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 We use GL_PACK_ALIGNMENT 4 by default because that is the default pixman alignment value and in some cases it allows us to avoid the manual pixel extraction. However, when we extract image data manually we use an alignment of 1. Signed-off-by: Chris Wilson --- src/cairo-gl-ext-def-private.h | 20 ++++ src/cairo-gl-private.h | 6 +- src/cairo-gl-surface.c | 240 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 244 insertions(+), 22 deletions(-) diff --git a/src/cairo-gl-ext-def-private.h b/src/cairo-gl-ext-def-private.h index a4b7d34..97e42c1 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 6f1d410..433a40b 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 b67a1ea..813e354 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) &&