/**
- * 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);
}
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,