mesa/es: Improve support for GL_OES_compressed_paletted_texture.
authorChia-I Wu <olvaffe@gmail.com>
Tue, 24 Nov 2009 04:21:03 +0000 (12:21 +0800)
committerBrian Paul <brianp@vmware.com>
Mon, 4 Jan 2010 21:15:16 +0000 (14:15 -0700)
Add error checking and fix handling of level (it should be negative).
Besides, always use the palette entry format/type when calling
_mesa_TexImage2D.  It respects the base internal formats of the cpal
formats, and is simpler and faster, except for cases where the unpack
alignment needs to be changed.

Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
src/mesa/es/main/es_cpaltex.c

index 15b6ad361739a1d111ffb64f640de07693966bac..0c497774ff8b9ccd3a13a1ac4f27c1748d324f0a 100644 (file)
 
 
 /**
- * Code to convert compressed/paletted texture images to ordinary 4-byte RGBA.
+ * Code to convert compressed/paletted texture images to ordinary images.
  * See the GL_OES_compressed_paletted_texture spec at
  * http://khronos.org/registry/gles/extensions/OES/OES_compressed_paletted_texture.txt
+ *
+ * XXX this makes it impossible to add hardware support...
  */
 
 
-#include <stdlib.h>
-#include <assert.h>
 #include "GLES/gl.h"
 #include "GLES/glext.h"
 
+#include "main/compiler.h" /* for ASSERT */
+
 
 void GL_APIENTRY _es_CompressedTexImage2DARB(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
 
+void GL_APIENTRY _mesa_GetIntegerv(GLenum pname, GLint *params);
+void GL_APIENTRY _mesa_PixelStorei(GLenum pname, GLint param);
 void GL_APIENTRY _mesa_TexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
 void GL_APIENTRY _mesa_CompressedTexImage2DARB(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
 
+void *_mesa_get_current_context(void);
+void _mesa_error(void *ctx, GLenum error, const char *fmtString, ... );
+
 
-static const struct {
+static const struct cpal_format_info {
+   GLenum cpal_format;
    GLenum format;
+   GLenum type;
    GLuint palette_size;
    GLuint size;
 } formats[] = {
-   { GL_PALETTE4_RGB8_OES,      16, 3 },
-   { GL_PALETTE4_RGBA8_OES,     16, 4 },
-   { GL_PALETTE4_R5_G6_B5_OES,  16, 2 },
-   { GL_PALETTE4_RGBA4_OES,     16, 2 },
-   { GL_PALETTE4_RGB5_A1_OES,   16, 2 },
-   { GL_PALETTE8_RGB8_OES,     256, 3 },
-   { GL_PALETTE8_RGBA8_OES,    256, 4 },
-   { GL_PALETTE8_R5_G6_B5_OES, 256, 2 },
-   { GL_PALETTE8_RGBA4_OES,    256, 2 },
-   { GL_PALETTE8_RGB5_A1_OES,  256, 2 }
+   { GL_PALETTE4_RGB8_OES,     GL_RGB,  GL_UNSIGNED_BYTE,           16, 3 },
+   { GL_PALETTE4_RGBA8_OES,    GL_RGBA, GL_UNSIGNED_BYTE,           16, 4 },
+   { GL_PALETTE4_R5_G6_B5_OES, GL_RGB,  GL_UNSIGNED_SHORT_5_6_5,    16, 2 },
+   { GL_PALETTE4_RGBA4_OES,    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,  16, 2 },
+   { GL_PALETTE4_RGB5_A1_OES,  GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,  16, 2 },
+   { GL_PALETTE8_RGB8_OES,     GL_RGB,  GL_UNSIGNED_BYTE,          256, 3 },
+   { GL_PALETTE8_RGBA8_OES,    GL_RGBA, GL_UNSIGNED_BYTE,          256, 4 },
+   { GL_PALETTE8_R5_G6_B5_OES, GL_RGB,  GL_UNSIGNED_SHORT_5_6_5,   256, 2 },
+   { GL_PALETTE8_RGBA4_OES,    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 256, 2 },
+   { GL_PALETTE8_RGB5_A1_OES,  GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 256, 2 }
 };
 
 
 /**
- * Get a color/entry from the palette.  Convert to GLubyte/RGBA format.
+ * Get a color/entry from the palette.
  */
-static void
-get_palette_entry(GLenum format, const void *palette, GLuint index,
-                  GLubyte rgba[4])
+static GLuint
+get_palette_entry(const struct cpal_format_info *info, const GLubyte *palette,
+                  GLuint index, GLubyte *pixel)
 {
-   switch (format) {
-   case GL_PALETTE4_RGB8_OES:
-   case GL_PALETTE8_RGB8_OES:
-      {
-         const GLubyte *pal = (const GLubyte *) palette;
-         rgba[0] = pal[index * 3 + 0];
-         rgba[1] = pal[index * 3 + 1];
-         rgba[2] = pal[index * 3 + 2];
-         rgba[3] = 255;
-      }
-      break;
-   case GL_PALETTE4_RGBA8_OES:
-   case GL_PALETTE8_RGBA8_OES:
-      {
-         const GLubyte *pal = (const GLubyte *) palette;
-         rgba[0] = pal[index * 4 + 0];
-         rgba[1] = pal[index * 4 + 1];
-         rgba[2] = pal[index * 4 + 2];
-         rgba[3] = pal[index * 4 + 3];
-      }
-      break;
-   case GL_PALETTE4_R5_G6_B5_OES:
-   case GL_PALETTE8_R5_G6_B5_OES:
-      {
-         const GLushort *pal = (const GLushort *) palette;
-         const GLushort color = pal[index];
-         rgba[0] = ((color >> 8) & 0xf8) | ((color >> 11) & 0x3);
-         rgba[1] = ((color >> 3) & 0xfc) | ((color >> 1 ) & 0x3);
-         rgba[2] = ((color << 3) & 0xf8) | ((color      ) & 0x7);
-         rgba[3] = 255;
-      }
-      break;
-   case GL_PALETTE4_RGBA4_OES:
-   case GL_PALETTE8_RGBA4_OES:
-      {
-         const GLushort *pal = (const GLushort *) palette;
-         const GLushort color = pal[index];
-         rgba[0] = ((color & 0xf000) >> 8) | ((color & 0xf000) >> 12);
-         rgba[1] = ((color & 0x0f00) >> 4) | ((color & 0x0f00) >>  8);
-         rgba[2] = ((color & 0x00f0)     ) | ((color & 0x00f0) >>  4);
-         rgba[3] = ((color & 0x000f) << 4) | ((color & 0x000f)      );
-      }
-      break;
-   case GL_PALETTE4_RGB5_A1_OES:
-   case GL_PALETTE8_RGB5_A1_OES:
-      {
-         const GLushort *pal = (const GLushort *) palette;
-         const GLushort color = pal[index];
-         rgba[0] = ((color >> 8) & 0xf8) | ((color >> 11) & 0x7);
-         rgba[1] = ((color >> 3) & 0xf8) | ((color >>  6) & 0x7);
-         rgba[2] = ((color << 2) & 0xf8) | ((color >>  1) & 0x7);
-         rgba[3] = (color & 0x1) * 255;
-      }
-      break;
-   default:
-      assert(0);
-   }
+   memcpy(pixel, palette + info->size * index, info->size);
+   return info->size;
 }
 
 
 /**
- * Convert paletted texture to simple GLubyte/RGBA format.
+ * Convert paletted texture to color texture.
  */
 static void
-paletted_to_rgba(GLenum src_format,
-                 const void *palette,
-                 const void *indexes,
-                 GLsizei width, GLsizei height,
-                 GLubyte *rgba)
+paletted_to_color(const struct cpal_format_info *info, const GLubyte *palette,
+                  const void *indices, GLuint num_pixels, GLubyte *image)
 {
-   GLuint pal_ents, i;
-
-   assert(src_format >= GL_PALETTE4_RGB8_OES);
-   assert(src_format <= GL_PALETTE8_RGB5_A1_OES);
-   assert(formats[src_format - GL_PALETTE4_RGB8_OES].format == src_format);
+   GLubyte *pix = image;
+   GLuint remain, i;
 
-   pal_ents = formats[src_format - GL_PALETTE4_RGB8_OES].palette_size;
-
-   if (pal_ents == 16) {
+   if (info->palette_size == 16) {
       /* 4 bits per index */
-      const GLubyte *ind = (const GLubyte *) indexes;
+      const GLubyte *ind = (const GLubyte *) indices;
 
-      if (width * height == 1) {
-         /* special case the only odd-sized image */
-         GLuint index0 = ind[0] >> 4;
-         get_palette_entry(src_format, palette, index0, rgba);
-         return;
-      }
       /* two pixels per iteration */
-      for (i = 0; i < width * height / 2; i++) {
-         GLuint index0 = ind[i] >> 4;
-         GLuint index1 = ind[i] & 0xf;
-         get_palette_entry(src_format, palette, index0, rgba + i * 8);
-         get_palette_entry(src_format, palette, index1, rgba + i * 8 + 4);
+      remain = num_pixels % 2;
+      for (i = 0; i < num_pixels / 2; i++) {
+         pix += get_palette_entry(info, palette, (ind[i] >> 4) & 0xf, pix);
+         pix += get_palette_entry(info, palette, ind[i] & 0xf, pix);
+      }
+      if (remain) {
+         get_palette_entry(info, palette, (ind[i] >> 4) & 0xf, pix);
       }
    }
    else {
       /* 8 bits per index */
-      const GLubyte *ind = (const GLubyte *) indexes;
-      for (i = 0; i < width * height; i++) {
-         GLuint index = ind[i];
-         get_palette_entry(src_format, palette, index, rgba + i * 4);
-      }
+      const GLubyte *ind = (const GLubyte *) indices;
+      for (i = 0; i < num_pixels; i++)
+         pix += get_palette_entry(info, palette, ind[i], pix);
    }
 }
 
 
+static const struct cpal_format_info *
+cpal_get_info(GLint level, GLenum internalFormat,
+              GLsizei width, GLsizei height, GLsizei imageSize)
+{
+   const struct cpal_format_info *info;
+   GLint lvl, num_levels;
+   GLsizei w, h, expect_size;
+
+   info = &formats[internalFormat - GL_PALETTE4_RGB8_OES];
+   ASSERT(info->cpal_format == internalFormat);
+
+   if (level > 0) {
+      _mesa_error(_mesa_get_current_context(), GL_INVALID_VALUE,
+            "glCompressedTexImage2D(level=%d)", level);
+      return NULL;
+   }
+
+   num_levels = -level + 1;
+   expect_size = info->palette_size * info->size;
+   for (lvl = 0; lvl < num_levels; lvl++) {
+      w = width >> lvl;
+      if (!w)
+         w = 1;
+      h = height >> lvl;
+      if (!h)
+         h = 1;
+
+      if (info->palette_size == 16)
+         expect_size += (w * h  + 1) / 2;
+      else
+         expect_size += w * h;
+   }
+   if (expect_size > imageSize) {
+      _mesa_error(_mesa_get_current_context(), GL_INVALID_VALUE,
+            "glCompressedTexImage2D(imageSize=%d)", imageSize);
+      return NULL;
+   }
+   return info;
+}
+
 /**
  * Convert a call to glCompressedTexImage2D() where internalFormat is a
  *  compressed palette format into a regular GLubyte/RGBA glTexImage2D() call.
  */
 static void
-cpal_compressed_teximage2d(GLenum target, GLint level,
-                           GLenum internalFormat,
-                           GLsizei width, GLsizei height,
-                           const void *pixels)
+cpal_compressed_teximage2d(GLenum target, GLint level, GLenum internalFormat,
+                           GLsizei width, GLsizei height, GLsizei imageSize,
+                           const void *palette)
 {
-   GLuint pal_ents, pal_ent_size, pal_bytes;
-   const GLint num_levels = level + 1;
-   GLint lvl;
-   const GLubyte *indexes;
+   const struct cpal_format_info *info;
+   GLint lvl, num_levels;
+   const GLubyte *indices;
+   GLint saved_align, align;
 
-   assert(internalFormat >= GL_PALETTE4_RGB8_OES);
-   assert(internalFormat <= GL_PALETTE8_RGB5_A1_OES);
-   assert(formats[internalFormat - GL_PALETTE4_RGB8_OES].format == internalFormat);
+   info = cpal_get_info(level, internalFormat, width, height, imageSize);
+   if (!info)
+      return;
 
-   pal_ents = formats[internalFormat - GL_PALETTE4_RGB8_OES].palette_size;
-   pal_ent_size = formats[internalFormat - GL_PALETTE4_RGB8_OES].size;
-   pal_bytes = pal_ents * pal_ent_size;
+   info = &formats[internalFormat - GL_PALETTE4_RGB8_OES];
+   ASSERT(info->cpal_format == internalFormat);
+   num_levels = -level + 1;
 
    /* first image follows the palette */
-   indexes = (const GLubyte *) pixels + pal_bytes;
+   indices = (const GLubyte *) palette + info->palette_size * info->size;
 
-   /* No worries about glPixelStore state since the only supported parameter is
-    * GL_UNPACK_ALIGNMENT and it doesn't matter when unpacking GLubyte/RGBA.
-    */
+   _mesa_GetIntegerv(GL_UNPACK_ALIGNMENT, &saved_align);
+   align = saved_align;
 
    for (lvl = 0; lvl < num_levels; lvl++) {
-      /* Allocate GLubyte/RGBA dest image buffer */
-      GLubyte *rgba = (GLubyte *) malloc(width * height * 4);
-
-      if (pixels)
-         paletted_to_rgba(internalFormat, pixels, indexes, width, height, rgba);
+      GLsizei w, h;
+      GLuint num_texels;
+      GLubyte *image = NULL;
+
+      w = width >> lvl;
+      if (!w)
+         w = 1;
+      h = height >> lvl;
+      if (!h)
+         h = 1;
+      num_texels = w * h;
+      if (w * info->size % align) {
+         _mesa_PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+         align = 1;
+      }
 
-      _mesa_TexImage2D(target, lvl, GL_RGBA, width, height, 0,
-                       GL_RGBA, GL_UNSIGNED_BYTE, rgba);
+      /* allocate and fill dest image buffer */
+      if (palette) {
+         image = (GLubyte *) malloc(num_texels * info->size);
+         paletted_to_color(info, palette, indices, num_texels, image);
+      }
 
-      free(rgba);
+      _mesa_TexImage2D(target, lvl, info->format, w, h, 0,
+                       info->format, info->type, image);
+      if (image)
+         free(image);
 
       /* advance index pointer to point to next src mipmap */
-      if (pal_ents == 4)
-         indexes += width * height / 2;
+      if (info->palette_size == 16)
+         indices += (num_texels + 1) / 2;
       else
-         indexes += width * height;
-
-      /* next mipmap level size */
-      if (width > 1)
-         width /= 2;
-      if (height > 1)
-         height /= 2;
+         indices += num_texels;
    }
+
+   if (saved_align != align)
+      _mesa_PixelStorei(GL_UNPACK_ALIGNMENT, saved_align);
 }
 
 
@@ -231,7 +222,7 @@ _es_CompressedTexImage2DARB(GLenum target, GLint level, GLenum internalFormat,
    case GL_PALETTE8_RGBA4_OES:
    case GL_PALETTE8_RGB5_A1_OES:
       cpal_compressed_teximage2d(target, level, internalFormat,
-                                 width, height, data);
+                                 width, height, imageSize, data);
       break;
    default:
       _mesa_CompressedTexImage2DARB(target, level, internalFormat,