added GL_SGIX/SGIS_pixel_texture
[mesa.git] / src / mesa / main / texutil.c
index 3023113c1737c2f651af0c0caea7ce782401b5ed..67805c8d71b0f6a9d2d8378e6d7adbac36fd8100 100644 (file)
@@ -1,4 +1,3 @@
-/* $Id: texutil.c,v 1.1 2000/03/24 20:54:21 brianp Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -29,6 +28,7 @@
 #include "all.h"
 #else
 #include "glheader.h"
+#include "context.h"
 #include "image.h"
 #include "mem.h"
 #include "texutil.h"
 
 /*
  * Texture utilities which may be useful to device drivers.
+ * See the texutil.h for the list of supported internal formats.
+ * It's expected that new formats will be added for new hardware.
  */
 
 
+/*
+ * If the system is little endian and can do 4-byte word stores on
+ * non 4-byte-aligned addresses then we can use this optimization.
+ */
+#if defined(__i386__)
+#define DO_32BIT_STORES000
+#endif
+
 
 
 /*
@@ -48,7 +58,8 @@
  * Input:
  *   dstFormat - the destination internal format
  *   dstWidth, dstHeight - the destination image size
- *   destImage - pointer to destination image buffer
+ *   dstImage - pointer to destination image buffer
+ *   dstRowStride - bytes to jump between image rows
  *   srcWidth, srcHeight - size of texture image
  *   srcFormat, srcType - format and datatype of source image
  *   srcImage - pointer to user's texture image
@@ -63,7 +74,7 @@
  *   GL_LUMINANCE        GL_UNSIGNED_BYTE                 MESA_L8
  *   GL_ALPHA            GL_UNSIGNED_BYTE                 MESA_A8
  *   GL_COLOR_INDEX      GL_UNSIGNED_BYTE                 MESA_C8
- *   GL_LUMINANCE_ALPHA  GL_UNSIGNED_BYTE                 MESA_L8_A8
+ *   GL_LUMINANCE_ALPHA  GL_UNSIGNED_BYTE                 MESA_A8_L8
  *   GL_RGB              GL_UNSIGNED_BYTE                 MESA_R5_G6_B5
  *   GL_RGB              GL_UNSIGNED_SHORT_5_6_5          MESA_R5_G6_B5
  *   GL_RGBA             GL_UNSIGNED_BYTE                 MESA_A4_R4_G4_B4
@@ -81,7 +92,8 @@
 GLboolean
 _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                        GLint dstWidth, GLint dstHeight, GLvoid *dstImage,
-                       GLsizei srcWidth, GLsizei srcHeight,
+                       GLint dstRowStride,
+                       GLint srcWidth, GLint srcHeight,
                        GLenum srcFormat, GLenum srcType,
                        const GLvoid *srcImage,
                        const struct gl_pixelstore_attrib *packing)
@@ -119,7 +131,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   MEMCPY(dst, src, dstWidth * sizeof(GLubyte));
-                  dst += dstWidth;
+                  dst += dstRowStride;
                   src += srcStride;
                }
             }
@@ -135,13 +147,13 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                   for (col = 0; col < dstWidth; col++) {
                      dst[col] = src[col / wScale];
                   }
-                  dst += dstWidth;
+                  dst += dstRowStride;
                }
             }
          }
          break;
 
-      case MESA_L8_A8:
+      case MESA_A8_L8:
          if (srcType != GL_UNSIGNED_BYTE || srcFormat != GL_LUMINANCE_ALPHA) {
             return GL_FALSE;
          }
@@ -152,21 +164,21 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row, col;
                for (row = 0; row < dstHeight; row++) {
                   for (col = 0; col < dstWidth; col++) {
-                     GLubyte alpha = src[col * 2 + 0];
-                     GLubyte luminance = src[col * 2 + 1];
-                     dst[col] = ((GLushort) luminance << 8) | alpha;
+                     GLubyte luminance = src[col * 2 + 0];
+                     GLubyte alpha = src[col * 2 + 1];
+                     dst[col] = ((GLushort) alpha << 8) | luminance;
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                   src += srcStride;
                }
             }
             else {
                /* must rescale */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row, col;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -176,11 +188,11 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                                                  srcWidth, srcFormat, srcType);
                   for (col = 0; col < dstWidth; col++) {
                      GLint srcCol = col / wScale;
-                     GLubyte alpha = src[srcCol * 2 + 0];
-                     GLubyte luminance = src[srcCol * 2 + 1];
-                     dst[col] = ((GLushort) luminance << 8) | alpha;
+                     GLubyte luminance = src[srcCol * 2 + 0];
+                     GLubyte alpha = src[srcCol * 2 + 1];
+                     dst[col] = ((GLushort) alpha << 8) | luminance;
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                   src += srcStride;
                }
             }
@@ -195,17 +207,17 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   MEMCPY(dst, src, dstWidth * sizeof(GLushort));
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -215,7 +227,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                   for (col = 0; col < dstWidth; col++) {
                      dst[col] = src[col / wScale];
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -226,7 +238,32 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+#ifdef DO_32BIT_STORES
+               GLuint *dst = (GLuint *) dstImage;
+               GLint row;
+               for (row = 0; row < dstHeight; row++) {
+                  GLint col, col3;
+                  GLint halfDstWidth = dstWidth >> 1;
+                  for (col = col3 = 0; col < halfDstWidth; col++, col3 += 6) {
+                     GLubyte r0 = src[col3 + 0];
+                     GLubyte g0 = src[col3 + 1];
+                     GLubyte b0 = src[col3 + 2];
+                     GLubyte r1 = src[col3 + 3];
+                     GLubyte g1 = src[col3 + 4];
+                     GLubyte b1 = src[col3 + 5];
+                     GLuint d0 = ((r0 & 0xf8) << 8)
+                               | ((g0 & 0xfc) << 3)
+                               | ((b0 & 0xf8) >> 3);
+                     GLuint d1 = ((r1 & 0xf8) << 8)
+                               | ((g1 & 0xfc) << 3)
+                               | ((b1 & 0xf8) >> 3);
+                     dst[col] = (d1 << 16) | d0;
+                  }
+                  src += srcStride;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+#else /* 16-bit stores */
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint col, col3;
@@ -239,12 +276,13 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((b & 0xf8) >> 3);
                   }
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
+#endif
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -260,7 +298,78 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((g & 0xfc) << 3)
                               | ((b & 0xf8) >> 3);
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE) {
+            /* general case (used by Quake3) */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 srcWidth, srcFormat, srcType);
+#ifdef DO_32BIT_STORES
+               GLuint *dst = dstImage;
+               GLint row;
+               for (row = 0; row < dstHeight; row++) {
+                  GLint col, col4;
+                  GLint halfDstWidth = dstWidth >> 1;
+                  for (col = col4 = 0; col < halfDstWidth; col++, col4 += 8) {
+                     GLubyte r0 = src[col4 + 0];
+                     GLubyte g0 = src[col4 + 1];
+                     GLubyte b0 = src[col4 + 2];
+                     GLubyte r1 = src[col4 + 4];
+                     GLubyte g1 = src[col4 + 5];
+                     GLubyte b1 = src[col4 + 6];
+                     GLuint d0 = ((r0 & 0xf8) << 8)
+                               | ((g0 & 0xfc) << 3)
+                               | ((b0 & 0xf8) >> 3);
+                     GLuint d1 = ((r1 & 0xf8) << 8)
+                               | ((g1 & 0xfc) << 3)
+                               | ((b1 & 0xf8) >> 3);
+                     dst[col] = (d1 << 16) | d0;
+                  }
+                  src += srcStride;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+#else /* 16-bit stores */
+               GLushort *dst = (GLushort *) dstImage;
+               GLint row;
+               for (row = 0; row < dstHeight; row++) {
+                  GLint col, col4;
+                  for (col = col4 = 0; col < dstWidth; col++, col4 += 4) {
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+#endif
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) dstImage;
+               GLint row;
+               for (row = 0; row < dstHeight; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < dstWidth; col++) {
+                     GLint col4 = (col / wScale) * 4;
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -279,17 +388,17 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   MEMCPY(dst, src, dstWidth * sizeof(GLushort));
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -299,7 +408,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                   for (col = 0; col < dstWidth; col++) {
                      dst[col] = src[col / wScale];
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -310,7 +419,36 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+#ifdef DO_32BIT_STORES
+               GLuint *dst = dstImage;
+               GLint row;
+               for (row = 0; row < dstHeight; row++) {
+                  GLint col, col4;
+                  GLint halfDstWidth = dstWidth >> 1;
+                  for (col = col4 = 0; col < halfDstWidth; col++, col4 += 8) {
+                     GLubyte r0 = src[col4 + 0];
+                     GLubyte g0 = src[col4 + 1];
+                     GLubyte b0 = src[col4 + 2];
+                     GLubyte a0 = src[col4 + 3];
+                     GLubyte r1 = src[col4 + 4];
+                     GLubyte g1 = src[col4 + 5];
+                     GLubyte b1 = src[col4 + 6];
+                     GLubyte a1 = src[col4 + 7];
+                     GLuint d0 = ((a0 & 0xf0) << 8)
+                               | ((r0 & 0xf0) << 4)
+                               | ((g0 & 0xf0)     )
+                               | ((b0 & 0xf0) >> 4);
+                     GLuint d1 = ((a1 & 0xf0) << 8)
+                               | ((r1 & 0xf0) << 4)
+                               | ((g1 & 0xf0)     )
+                               | ((b1 & 0xf0) >> 4);
+                     dst[col] = (d1 << 16) | d0;
+                  }
+                  src += srcStride;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+#else /* 16-bit stores */
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint col, col4;
@@ -325,12 +463,13 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((b & 0xf0) >> 4);
                   }
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
+#endif
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -348,7 +487,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((g & 0xf0)     )
                               | ((b & 0xf0) >> 4);
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -367,17 +506,17 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   MEMCPY(dst, src, dstWidth * sizeof(GLushort));
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -387,7 +526,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                   for (col = 0; col < dstWidth; col++) {
                      dst[col] = src[col / wScale];
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -398,7 +537,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                              srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
                const GLint srcStride = _mesa_image_row_stride(packing,
                                                  srcWidth, srcFormat, srcType);
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint col, col4;
@@ -413,12 +552,12 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((b & 0xf8) >> 3);
                   }
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLushort *dst = (GLushort *) dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -436,7 +575,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                               | ((g & 0xf8) << 2)
                               | ((b & 0xf8) >> 3);
                   }
-                  dst += dstWidth;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -460,7 +599,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                for (row = 0; row < dstHeight; row++) {
                   MEMCPY(dst, src, dstWidth * sizeof(GLuint));
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
@@ -475,7 +614,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                   for (col = 0; col < dstWidth; col++) {
                      dst[col] = src[col / wScale];
                   }
-                  dst += dstWidth;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -498,12 +637,12 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                      dst[col] = (a << 24) | (r << 16) | (g << 8) | b;
                   }
                   src += srcStride;
-                  dst += dstWidth;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
                }
             }
             else {
                /* must rescale image */
-               GLushort *dst = dstImage;
+               GLuint *dst = dstImage;
                GLint row;
                for (row = 0; row < dstHeight; row++) {
                   GLint srcRow = row / hScale;
@@ -518,7 +657,7 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
                      GLubyte a = src[col4 + 3];
                      dst[col] = (a << 24) | (r << 16) | (g << 8) | b;
                   }
-                  dst += dstWidth;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
                }
             }
          }
@@ -535,3 +674,892 @@ _mesa_convert_teximage(MesaIntTexFormat dstFormat,
    }
    return GL_TRUE;
 }
+
+
+
+/*
+ * Replace a subregion of a texture image with new data.
+ * Input:
+ *   dstFormat - destination image format
+ *   dstXoffset, dstYoffset - destination for new subregion
+ *   dstWidth, dstHeight - total size of dest image
+ *   dstImage - pointer to dest image
+ *   dstRowStride - bytes to jump between image rows
+ *   width, height - size of region to copy/replace
+ *   srcWidth, srcHeight - size of the corresponding gl_texture_image
+ *   srcFormat, srcType - source image format and datatype
+ *   srcImage - source image
+ *   packing - source image packing information.
+ * Return:  GL_TRUE or GL_FALSE for success, failure
+ *
+ * Notes:
+ *   Like _mesa_convert_teximage(), we can do power-of-two image scaling
+ *   to accomodate hardware with texture image aspect ratio constraints.
+ *   dstWidth / srcWidth is used to compute the horizontal scaling factor and
+ *   dstHeight / srcHeight is used to compute the vertical scaling factor.
+ */
+GLboolean
+_mesa_convert_texsubimage(MesaIntTexFormat dstFormat,
+                          GLint dstXoffset, GLint dstYoffset,
+                          GLint dstWidth, GLint dstHeight, GLvoid *dstImage,
+                          GLint dstRowStride,
+                          GLint width, GLint height,
+                          GLint srcWidth, GLint srcHeight,
+                          GLenum srcFormat, GLenum srcType,
+                          const GLvoid *srcImage,
+                          const struct gl_pixelstore_attrib *packing)
+{
+   const GLint wScale = dstWidth / srcWidth;   /* must be power of two */
+   const GLint hScale = dstHeight / srcHeight; /* must be power of two */
+   ASSERT(dstWidth >= srcWidth);
+   ASSERT(dstHeight >= srcHeight);
+   ASSERT(dstImage);
+   ASSERT(srcImage);
+   ASSERT(packing);
+
+   width *= wScale;
+   height *= hScale;
+   dstXoffset *= wScale;
+   dstYoffset *= hScale;
+
+   /* XXX hscale != 1 and wscale != 1 not tested!!!! */
+   
+   switch (dstFormat) {
+      case MESA_I8:
+      case MESA_L8:
+      case MESA_A8:
+      case MESA_C8:
+         if (srcType != GL_UNSIGNED_BYTE ||
+             ((srcFormat != GL_INTENSITY) &&
+              (srcFormat != GL_LUMINANCE) &&
+              (srcFormat != GL_ALPHA) &&
+              (srcFormat != GL_COLOR_INDEX))) {
+            /* bad internal format / srcFormat combination */
+            return GL_FALSE;
+         }
+         else {
+            /* store as 8-bit texels */
+            if (wScale == 1 && hScale == 1) {
+               /* no scaling needed - fast case */
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                  width, srcFormat, srcType);
+               GLubyte *dst = (GLubyte *) dstImage
+                            + dstYoffset * dstRowStride + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  MEMCPY(dst, src, width * sizeof(GLubyte));
+                  dst += dstRowStride;
+                  src += srcStride;
+               }
+            }
+            else {
+               /* must rescale image */
+               GLubyte *dst = (GLubyte *) dstImage
+                            + dstYoffset * dstRowStride + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     dst[col] = src[col / wScale];
+                  }
+                  dst += dstRowStride;
+               }
+            }
+         }
+         break;
+
+      case MESA_A8_L8:
+         if (srcType != GL_UNSIGNED_BYTE || srcFormat != GL_LUMINANCE_ALPHA) {
+            return GL_FALSE;
+         }
+         else {
+            /* store as 16-bit texels */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row, col;
+               for (row = 0; row < height; row++) {
+                  for (col = 0; col < width; col++) {
+                     GLubyte luminance = src[col * 2 + 0];
+                     GLubyte alpha = src[col * 2 + 1];
+                     dst[col] = ((GLushort) alpha << 8) | luminance;
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+                  src += srcStride;
+               }
+            }
+            else {
+               /* must rescale */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row, col;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+                  for (col = 0; col < width; col++) {
+                     GLint srcCol = col / wScale;
+                     GLubyte luminance = src[srcCol * 2 + 0];
+                     GLubyte alpha = src[srcCol * 2 + 1];
+                     dst[col] = ((GLushort) alpha << 8) | luminance;
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+                  src += srcStride;
+               }
+            }
+         }
+         break;
+
+      case MESA_R5_G6_B5:
+         if (srcFormat == GL_RGB && srcType == GL_UNSIGNED_SHORT_5_6_5) {
+            /* special, optimized case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  MEMCPY(dst, src, width * sizeof(GLushort));
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLushort *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     dst[col] = src[col / wScale];
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGB && srcType == GL_UNSIGNED_BYTE) {
+            /* general case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint col, col3;
+                  for (col = col3 = 0; col < width; col++, col3 += 3) {
+                     GLubyte r = src[col3 + 0];
+                     GLubyte g = src[col3 + 1];
+                     GLubyte b = src[col3 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     GLint col3 = (col / wScale) * 3;
+                     GLubyte r = src[col3 + 0];
+                     GLubyte g = src[col3 + 1];
+                     GLubyte b = src[col3 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE) {
+            /* general case (used by Quake3) */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint col, col4;
+                  for (col = col4 = 0; col < width; col++, col4 += 4) {
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     GLint col4 = (col / wScale) * 4;
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     dst[col] = ((r & 0xf8) << 8)
+                              | ((g & 0xfc) << 3)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else {
+            /* can't handle this srcFormat/srcType combination */
+            return GL_FALSE;
+         }
+         break;
+
+      case MESA_A4_R4_G4_B4:
+         /* store as 16-bit texels (GR_TEXFMT_ARGB_4444) */
+         if (srcFormat == GL_BGRA && srcType == GL_UNSIGNED_SHORT_4_4_4_4_REV){
+            /* special, optimized case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  MEMCPY(dst, src, width * sizeof(GLushort));
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLushort *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     dst[col] = src[col / wScale];
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE) {
+            /* general case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint col, col4;
+                  for (col = col4 = 0; col < width; col++, col4 += 4) {
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = ((a & 0xf0) << 8)
+                              | ((r & 0xf0) << 4)
+                              | ((g & 0xf0)     )
+                              | ((b & 0xf0) >> 4);
+                  }
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     GLint col4 = (col / wScale) * 4;
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = ((a & 0xf0) << 8)
+                              | ((r & 0xf0) << 4)
+                              | ((g & 0xf0)     )
+                              | ((b & 0xf0) >> 4);
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else {
+            /* can't handle this format/srcType combination */
+            return GL_FALSE;
+         }
+         break;
+
+      case MESA_A1_R5_G5_B5:
+         /* store as 16-bit texels (GR_TEXFMT_ARGB_1555) */
+         if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_SHORT_1_5_5_5_REV){
+            /* special, optimized case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  MEMCPY(dst, src, width * sizeof(GLushort));
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLushort *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     dst[col] = src[col / wScale];
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE) {
+            /* general case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint col, col4;
+                  for (col = col4 = 0; col < width; col++, col4 += 4) {
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = ((a & 0x80) << 8)
+                              | ((r & 0xf8) << 7)
+                              | ((g & 0xf8) << 2)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  src += srcStride;
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLushort *dst = (GLushort *) ((GLubyte *) dstImage
+                             + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     GLint col4 = (col / wScale) * 4;
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = ((a & 0x80) << 8)
+                              | ((r & 0xf8) << 7)
+                              | ((g & 0xf8) << 2)
+                              | ((b & 0xf8) >> 3);
+                  }
+                  dst = (GLushort *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else {
+            /* can't handle this source format/type combination */
+            return GL_FALSE;
+         }
+         break;
+
+      case MESA_A8_R8_G8_B8:
+         /* 32-bit texels */
+         if (srcFormat == GL_BGRA && srcType == GL_UNSIGNED_INT_8_8_8_8_REV){
+            /* special, optimized case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLuint *dst = (GLuint *) ((GLubyte *) dstImage
+                           + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  MEMCPY(dst, src, width * sizeof(GLuint));
+                  src += srcStride;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLuint *dst = (GLuint *) ((GLubyte *) dstImage
+                           + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLuint *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     dst[col] = src[col / wScale];
+                  }
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else if (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE) {
+            /* general case */
+            if (wScale == 1 && hScale == 1) {
+               const GLubyte *src = _mesa_image_address(packing, srcImage,
+                             srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0);
+               const GLint srcStride = _mesa_image_row_stride(packing,
+                                                 width, srcFormat, srcType);
+               GLuint *dst = (GLuint *) ((GLubyte *) dstImage
+                           + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint col, col4;
+                  for (col = col4 = 0; col < width; col++, col4 += 4) {
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = (a << 24) | (r << 16) | (g << 8) | b;
+                  }
+                  src += srcStride;
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+            else {
+               /* must rescale image */
+               GLuint *dst = (GLuint *) ((GLubyte *) dstImage
+                           + dstYoffset * dstRowStride) + dstXoffset;
+               GLint row;
+               for (row = 0; row < height; row++) {
+                  GLint srcRow = row / hScale;
+                  const GLubyte *src = _mesa_image_address(packing, srcImage,
+                        srcWidth, srcHeight, srcFormat, srcType, 0, srcRow, 0);
+                  GLint col;
+                  for (col = 0; col < width; col++) {
+                     GLint col4 = (col / wScale) * 4;
+                     GLubyte r = src[col4 + 0];
+                     GLubyte g = src[col4 + 1];
+                     GLubyte b = src[col4 + 2];
+                     GLubyte a = src[col4 + 3];
+                     dst[col] = (a << 24) | (r << 16) | (g << 8) | b;
+                  }
+                  dst = (GLuint *) ((GLubyte *) dst + dstRowStride);
+               }
+            }
+         }
+         else {
+            /* can't handle this source format/type combination */
+            return GL_FALSE;
+         }
+         break;
+
+
+      default:
+         /* unexpected internal format! */
+         return GL_FALSE;
+   }
+   return GL_TRUE;
+}
+
+
+
+
+/*
+ * Used to convert 16-bit texels into GLubyte color components.
+ */
+static GLubyte R5G6B5toRed[0xffff];
+static GLubyte R5G6B5toGreen[0xffff];
+static GLubyte R5G6B5toBlue[0xffff];
+
+static GLubyte A4R4G4B4toRed[0xffff];
+static GLubyte A4R4G4B4toGreen[0xffff];
+static GLubyte A4R4G4B4toBlue[0xffff];
+static GLubyte A4R4G4B4toAlpha[0xffff];
+
+static GLubyte A1R5G5B5toRed[0xffff];
+static GLubyte A1R5G5B5toGreen[0xffff];
+static GLubyte A1R5G5B5toBlue[0xffff];
+static GLubyte A1R5G5B5toAlpha[0xffff];
+
+static void
+generate_lookup_tables(void)
+{
+   GLint i;
+   for (i = 0; i <= 0xffff; i++) {
+      GLint r = (i >> 8) & 0xf8;
+      GLint g = (i >> 3) & 0xfc;
+      GLint b = (i << 3) & 0xf8;
+      r = r * 255 / 0xf8;
+      g = g * 255 / 0xfc;
+      b = b * 255 / 0xf8;
+      R5G6B5toRed[i]   = r;
+      R5G6B5toGreen[i] = g;
+      R5G6B5toBlue[i]  = b;
+   }
+
+   for (i = 0; i <= 0xffff; i++) {
+      GLint r = (i >>  8) & 0xf;
+      GLint g = (i >>  4) & 0xf;
+      GLint b = (i      ) & 0xf;
+      GLint a = (i >> 12) & 0xf;
+      r = r * 255 / 0xf;
+      g = g * 255 / 0xf;
+      b = b * 255 / 0xf;
+      a = a * 255 / 0xf;
+      A4R4G4B4toRed[i]   = r;
+      A4R4G4B4toGreen[i] = g;
+      A4R4G4B4toBlue[i]  = b;
+      A4R4G4B4toAlpha[i] = a;
+   }
+
+   for (i = 0; i <= 0xffff; i++) {
+      GLint r = (i >> 10) & 0xf8;
+      GLint g = (i >>  5) & 0xf8;
+      GLint b = (i      ) & 0xf8;
+      GLint a = (i >> 15) & 0x1;
+      r = r * 255 / 0xf8;
+      g = g * 255 / 0xf8;
+      b = b * 255 / 0xf8;
+      a = a * 255;
+      A1R5G5B5toRed[i]   = r;
+      A1R5G5B5toGreen[i] = g;
+      A1R5G5B5toBlue[i]  = b;
+      A1R5G5B5toAlpha[i] = a;
+   }
+}
+
+
+
+/*
+ * Convert a texture image from an internal format to one of Mesa's
+ * core internal formats.  This is likely to be used by glGetTexImage
+ * and for fetching texture images when falling back to software rendering.
+ *
+ * Input:
+ *   srcFormat - source image format
+ *   srcWidth, srcHeight - source image size
+ *   srcImage - source image pointer
+ *   srcRowStride - bytes to jump between image rows
+ *   dstWidth, dstHeight - size of dest image
+ *   dstFormat - format of dest image (must be one of Mesa's IntFormat values)
+ *   dstImage - pointer to dest image
+ * Notes:
+ *   This function will do power of two image down-scaling to accomodate
+ *   drivers with limited texture image aspect ratios.
+ *   The implicit dest data type is GL_UNSIGNED_BYTE.
+ */
+void
+_mesa_unconvert_teximage(MesaIntTexFormat srcFormat,
+                         GLint srcWidth, GLint srcHeight,
+                         const GLvoid *srcImage, GLint srcRowStride,
+                         GLint dstWidth, GLint dstHeight,
+                         GLenum dstFormat, GLubyte *dstImage)
+{
+   static GLboolean firstCall = GL_TRUE;
+   const GLint wScale = srcWidth / dstWidth;   /* must be power of two */
+   const GLint hScale = srcHeight / dstHeight; /* must be power of two */
+   ASSERT(srcWidth >= dstWidth);
+   ASSERT(srcHeight >= dstHeight);
+   ASSERT(dstImage);
+   ASSERT(srcImage);
+
+   if (firstCall) {
+      generate_lookup_tables();
+      firstCall = GL_FALSE;
+   }
+
+   switch (srcFormat) {
+      case MESA_I8:
+      case MESA_L8:
+      case MESA_A8:
+      case MESA_C8:
+#ifdef DEBUG
+         if (srcFormat == MESA_I8) {
+            ASSERT(dstFormat == GL_INTENSITY);
+         }
+         else if (srcFormat == MESA_L8) {
+            ASSERT(dstFormat == GL_LUMINANCE);
+         }
+         else if (srcFormat == MESA_A8) {
+            ASSERT(dstFormat == GL_ALPHA);
+         }
+         else if (srcFormat == MESA_C8) {
+            ASSERT(dstFormat == GL_COLOR_INDEX);
+         }
+#endif
+         if (wScale == 1 && hScale == 1) {
+            /* easy! */
+            MEMCPY(dstImage, srcImage, dstWidth * dstHeight * sizeof(GLubyte));
+         }
+         else {
+            /* rescale */
+            const GLubyte *src8 = (const GLubyte *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  *dstImage++ = src8[srcRow * srcWidth + srcCol];
+               }
+            }
+         }
+         break;
+      case MESA_A8_L8:
+         ASSERT(dstFormat == GL_LUMINANCE_ALPHA);
+         if (wScale == 1 && hScale == 1) {
+            GLint i, n = dstWidth * dstHeight;
+            const GLushort *texel = (const GLushort *) srcImage;
+            for (i = 0; i < n; i++) {
+               const GLushort tex = *texel++;
+               *dstImage++ = (tex & 0xff); /* luminance */
+               *dstImage++ = (tex >> 8);   /* alpha */
+            }
+         }
+         else {
+            /* rescale */
+            const GLushort *src16 = (const GLushort *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  const GLushort tex = src16[srcRow * srcWidth + srcCol];
+                  *dstImage++ = (tex & 0xff); /* luminance */
+                  *dstImage++ = (tex >> 8);   /* alpha */
+               }
+            }
+         }
+         break;
+      case MESA_R5_G6_B5:
+         ASSERT(dstFormat == GL_RGB);
+         if (wScale == 1 && hScale == 1) {
+            GLint i, n = dstWidth * dstHeight;
+            const GLushort *texel = (const GLushort *) srcImage;
+            for (i = 0; i < n; i++) {
+               const GLushort tex = *texel++;
+               *dstImage++ = R5G6B5toRed[tex];
+               *dstImage++ = R5G6B5toGreen[tex];
+               *dstImage++ = R5G6B5toBlue[tex];
+            }
+         }
+         else {
+            /* rescale */
+            const GLushort *src16 = (const GLushort *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  const GLushort tex = src16[srcRow * srcWidth + srcCol];
+                  *dstImage++ = R5G6B5toRed[tex];
+                  *dstImage++ = R5G6B5toGreen[tex];
+                  *dstImage++ = R5G6B5toBlue[tex];
+               }
+            }
+         }
+         break;
+      case MESA_A4_R4_G4_B4:
+         ASSERT(dstFormat == GL_RGBA);
+         if (wScale == 1 && hScale == 1) {
+            GLint i, n = dstWidth * dstHeight;
+            const GLushort *texel = (const GLushort *) srcImage;
+            for (i = 0; i < n; i++) {
+               const GLushort tex = *texel++;
+               *dstImage++ = A4R4G4B4toRed[tex];
+               *dstImage++ = A4R4G4B4toGreen[tex];
+               *dstImage++ = A4R4G4B4toBlue[tex];
+               *dstImage++ = A4R4G4B4toAlpha[tex];
+            }
+         }
+         else {
+            /* rescale */
+            const GLushort *src16 = (const GLushort *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  const GLushort tex = src16[srcRow * srcWidth + srcCol];
+                  *dstImage++ = A4R4G4B4toRed[tex];
+                  *dstImage++ = A4R4G4B4toGreen[tex];
+                  *dstImage++ = A4R4G4B4toBlue[tex];
+                  *dstImage++ = A4R4G4B4toAlpha[tex];
+               }
+            }
+         }
+         break;
+      case MESA_A1_R5_G5_B5:
+         ASSERT(dstFormat == GL_RGBA);
+         if (wScale == 1 && hScale == 1) {
+            GLint i, n = dstWidth * dstHeight;
+            const GLushort *texel = (const GLushort *) srcImage;
+            for (i = 0; i < n; i++) {
+               const GLushort tex = *texel++;
+               *dstImage++ = A1R5G5B5toRed[tex];
+               *dstImage++ = A1R5G5B5toGreen[tex];
+               *dstImage++ = A1R5G5B5toBlue[tex];
+               *dstImage++ = A1R5G5B5toAlpha[tex];
+            }
+         }
+         else {
+            /* rescale */
+            const GLushort *src16 = (const GLushort *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  const GLushort tex = src16[srcRow * srcWidth + srcCol];
+                  *dstImage++ = A1R5G5B5toRed[tex];
+                  *dstImage++ = A1R5G5B5toGreen[tex];
+                  *dstImage++ = A1R5G5B5toBlue[tex];
+                  *dstImage++ = A1R5G5B5toAlpha[tex];
+               }
+            }
+         }
+         break;
+      case MESA_A8_R8_G8_B8:
+         ASSERT(dstFormat == GL_RGBA);
+         if (wScale == 1 && hScale == 1) {
+            GLint i, n = dstWidth * dstHeight;
+            const GLuint *texel = (const GLuint *) srcImage;
+            for (i = 0; i < n; i++) {
+               const GLuint tex = *texel++;
+               *dstImage++ = (tex >> 16) & 0xff; /* R */
+               *dstImage++ = (tex >>  8) & 0xff; /* G */
+               *dstImage++ = (tex      ) & 0xff; /* B */
+               *dstImage++ = (tex >> 24) & 0xff; /* A */
+            }
+         }
+         else {
+            /* rescale */
+            const GLuint *src = (const GLuint *) srcImage;
+            GLint row, col;
+            for (row = 0; row < dstHeight; row++) {
+               GLint srcRow = row * hScale;
+               for (col = 0; col < dstWidth; col++) {
+                  GLint srcCol = col * wScale;
+                  const GLuint tex = src[srcRow * srcWidth + srcCol];
+                  *dstImage++ = (tex >> 16) & 0xff; /* R */
+                  *dstImage++ = (tex >>  8) & 0xff; /* G */
+                  *dstImage++ = (tex      ) & 0xff; /* B */
+                  *dstImage++ = (tex >> 24) & 0xff; /* A */
+               }
+            }
+         }
+         break;
+      default:
+         gl_problem(NULL, "bad srcFormat in _mesa_uncovert_teximage()");
+   }
+}
+
+
+
+/*
+ * Given an internal Mesa driver texture format, fill in the component
+ * bit sizes in the given texture image struct.
+ */
+void
+_mesa_set_teximage_component_sizes(MesaIntTexFormat mesaFormat,
+                                   struct gl_texture_image *texImage)
+{
+   static const GLint bitSizes [][8] = {
+      /* format            R  G  B  A  I  L  C */
+      { MESA_I8,           0, 0, 0, 0, 8, 0, 0 },
+      { MESA_L8,           0, 0, 0, 0, 0, 8, 0 },
+      { MESA_A8,           0, 0, 0, 8, 0, 0, 0 },
+      { MESA_C8,           0, 0, 0, 0, 0, 0, 8 },
+      { MESA_A8_L8,        0, 0, 0, 8, 0, 8, 0 },
+      { MESA_R5_G6_B5,     5, 6, 5, 0, 0, 0, 0 },
+      { MESA_A4_R4_G4_B4,  4, 4, 4, 4, 0, 0, 0 },
+      { MESA_A1_R5_G5_B5,  5, 5, 5, 1, 0, 0, 0 },
+      { MESA_A8_R8_G8_B8,  8, 8, 8, 8, 0, 0, 0 },
+      { -1,                0, 0, 0, 0, 0, 0, 0 }
+   };
+   GLint i;
+   for (i = 0; i < bitSizes[i][0] >= 0; i++) {
+      if (bitSizes[i][0] == mesaFormat) {
+         texImage->RedBits       = bitSizes[i][1];
+         texImage->GreenBits     = bitSizes[i][2];
+         texImage->BlueBits      = bitSizes[i][3];
+         texImage->AlphaBits     = bitSizes[i][4];
+         texImage->IntensityBits = bitSizes[i][5];
+         texImage->LuminanceBits = bitSizes[i][6];
+         texImage->IndexBits     = bitSizes[i][7];
+         return;
+      }
+   }
+   gl_problem(NULL, "bad format in _mesa_set_teximage_component_sizes");
+}