Minor tweaks to help out at a driver level.
[mesa.git] / src / mesa / main / texstore.c
index 95f16b20768fb78b92264c7695f61fbde1ef41ad..899201e6c4ab80c278cab04401b8f969be63ae3e 100644 (file)
@@ -1,10 +1,8 @@
-/* $Id: texstore.c,v 1.22 2001/04/04 21:54:21 brianp Exp $ */
-
 /*
  * Mesa 3-D graphics library
- * Version:  3.5
+ * Version:  5.1
  *
- * Copyright (C) 1999-2001  Brian Paul   All Rights Reserved.
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  *   Brian Paul
  */
 
-
 /*
- * The functions in this file are mostly related to software texture fallbacks.
- * This includes texture image transfer/packing and texel fetching.
- * Hardware drivers will likely override most of this.
+ * The GL texture image functions in teximage.c basically just do
+ * error checking and data structure allocation.  They in turn call
+ * device driver functions which actually copy/convert/store the user's
+ * texture image data.
+ *
+ * However, most device drivers will be able to use the fallback functions
+ * in this file.  That is, most drivers will have the following bit of
+ * code:
+ *   ctx->Driver.TexImage1D = _mesa_store_teximage1d;
+ *   ctx->Driver.TexImage2D = _mesa_store_teximage2d;
+ *   ctx->Driver.TexImage3D = _mesa_store_teximage3d;
+ *   etc...
+ *
+ * Texture image processing is actually kind of complicated.  We have to do:
+ *    Format/type conversions
+ *    pixel unpacking
+ *    pixel transfer (scale, bais, lookup, convolution!, etc)
+ *
+ * These functions can handle most everything, including processing full
+ * images and sub-images.
  */
 
 
-
+#include "glheader.h"
 #include "colormac.h"
 #include "context.h"
 #include "convolve.h"
 #include "image.h"
 #include "macros.h"
-#include "mem.h"
+#include "imports.h"
+#include "texcompress.h"
 #include "texformat.h"
 #include "teximage.h"
 #include "texstore.h"
@@ -55,9 +70,6 @@
  * corresponding _base_ internal format:  GL_ALPHA, GL_LUMINANCE,
  * GL_LUMANCE_ALPHA, GL_INTENSITY, GL_RGB, or GL_RGBA.  Return the
  * number of components for the format.  Return -1 if invalid enum.
- *
- * GH: Do we really need this?  We have the number of bytes per texel
- * in the texture format structures, so why don't we just use that?
  */
 static GLint
 components_in_intformat( GLint format )
@@ -124,6 +136,8 @@ components_in_intformat( GLint format )
       case GL_DEPTH_COMPONENT24_SGIX:
       case GL_DEPTH_COMPONENT32_SGIX:
          return 1;
+      case GL_YCBCR_MESA:
+         return 2; /* Y + (Cb or Cr) */
       default:
          return -1;  /* error */
    }
@@ -136,16 +150,17 @@ components_in_intformat( GLint format )
  * We also take care of all image transfer operations here, including
  * convolution, scale/bias, colortables, etc.
  *
- * The destination texel channel type is always GLchan.
+ * The destination texel type is always GLchan.
+ * The destination texel format is one of the 6 basic types.
  *
  * A hardware driver may use this as a helper routine to unpack and
  * apply pixel transfer ops into a temporary image buffer.  Then,
  * convert the temporary image into the special hardware format.
  *
- * Input:
+ * \param
  *   dimensions - 1, 2, or 3
- *   texFormat - GL_LUMINANCE, GL_INTENSITY, GL_LUMINANCE_ALPHA, GL_ALPHA,
- *               GL_RGB or GL_RGBA
+ *   texDestFormat - GL_LUMINANCE, GL_INTENSITY, GL_LUMINANCE_ALPHA, GL_ALPHA,
+ *                   GL_RGB or GL_RGBA (the destination format)
  *   texDestAddr - destination image address
  *   srcWidth, srcHeight, srcDepth - size (in pixels) of src and dest images
  *   dstXoffset, dstYoffset, dstZoffset - position to store the image within
@@ -154,6 +169,7 @@ components_in_intformat( GLint format )
  *   srcFormat - source image format (GL_ALPHA, GL_RED, GL_RGB, etc)
  *   srcType - GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5, GL_FLOAT, etc
  *   srcPacking - describes packing of incoming image.
+ *   transferOps - mask of pixel transfer operations
  */
 static void
 transfer_teximage(GLcontext *ctx, GLuint dimensions,
@@ -163,16 +179,25 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                   GLint dstRowStride, GLint dstImageStride,
                   GLenum srcFormat, GLenum srcType,
                   const GLvoid *srcAddr,
-                  const struct gl_pixelstore_attrib *srcPacking)
+                  const struct gl_pixelstore_attrib *srcPacking,
+                  GLuint transferOps)
 {
    GLint texComponents;
 
    ASSERT(ctx);
    ASSERT(dimensions >= 1 && dimensions <= 3);
+   ASSERT(texDestFormat == GL_LUMINANCE ||
+          texDestFormat == GL_INTENSITY ||
+          texDestFormat == GL_LUMINANCE_ALPHA ||
+          texDestFormat == GL_ALPHA ||
+          texDestFormat == GL_RGB ||
+          texDestFormat == GL_RGBA ||
+          texDestFormat == GL_COLOR_INDEX ||
+          texDestFormat == GL_DEPTH_COMPONENT);
    ASSERT(texDestAddr);
-   ASSERT(srcWidth >= 1);
-   ASSERT(srcHeight >= 1);
-   ASSERT(srcDepth >= 1);
+   ASSERT(srcWidth >= 0);
+   ASSERT(srcHeight >= 0);
+   ASSERT(srcDepth >= 0);
    ASSERT(dstXoffset >= 0);
    ASSERT(dstYoffset >= 0);
    ASSERT(dstZoffset >= 0);
@@ -184,7 +209,7 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
    texComponents = components_in_intformat(texDestFormat);
 
    /* try common 2D texture cases first */
-   if (!ctx->_ImageTransferState && dimensions == 2 && srcType == CHAN_TYPE) {
+   if (!transferOps && dimensions == 2 && srcType == CHAN_TYPE) {
 
       if (srcFormat == texDestFormat) {
          /* This will cover the common GL_RGB, GL_RGBA, GL_ALPHA,
@@ -196,8 +221,9 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
          const GLint srcRowStride = _mesa_image_row_stride(srcPacking,
                                                srcWidth, srcFormat, srcType);
          const GLint widthInBytes = srcWidth * texComponents * sizeof(GLchan);
-         GLchan *dst = (GLchan *) texDestAddr + dstYoffset * dstRowStride
-                      + dstXoffset * texComponents;
+         GLchan *dst = (GLchan *) texDestAddr
+                     + dstYoffset * (dstRowStride / sizeof(GLchan))
+                     + dstXoffset * texComponents;
          if (srcRowStride == widthInBytes && dstRowStride == widthInBytes) {
             MEMCPY(dst, src, srcHeight * widthInBytes);
          }
@@ -205,8 +231,8 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
             GLint i;
             for (i = 0; i < srcHeight; i++) {
                MEMCPY(dst, src, widthInBytes);
-               src += srcRowStride;
-               dst += dstRowStride;
+               src += (srcRowStride / sizeof(GLchan));
+               dst += (dstRowStride / sizeof(GLchan));
             }
          }
          return;  /* all done */
@@ -218,8 +244,9 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                    srcFormat, srcType, 0, 0, 0);
          const GLint srcRowStride = _mesa_image_row_stride(srcPacking,
                                                srcWidth, srcFormat, srcType);
-         GLchan *dst = (GLchan *) texDestAddr + dstYoffset * dstRowStride
-                      + dstXoffset * texComponents;
+         GLchan *dst = (GLchan *) texDestAddr
+                     + dstYoffset * (dstRowStride / sizeof(GLchan))
+                     + dstXoffset * texComponents;
          GLint i, j;
          for (i = 0; i < srcHeight; i++) {
             const GLchan *s = src;
@@ -230,8 +257,8 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                *d++ = *s++;  /*blue*/
                s++;          /*alpha*/
             }
-            src += srcRowStride;
-            dst += dstRowStride;
+            src += (srcRowStride / sizeof(GLchan));
+            dst += (dstRowStride / sizeof(GLchan));
          }
          return;  /* all done */
       }
@@ -244,28 +271,48 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
       /* color index texture */
       const GLenum texType = CHAN_TYPE;
       GLint img, row;
-      GLchan *dest = (GLchan *) texDestAddr + dstZoffset * dstImageStride
-                    + dstYoffset * dstRowStride
-                    + dstXoffset * texComponents;
+      GLchan *dest = (GLchan *) texDestAddr
+                   + dstZoffset * (dstImageStride / sizeof(GLchan))
+                   + dstYoffset * (dstRowStride / sizeof(GLchan))
+                   + dstXoffset * texComponents;
       for (img = 0; img < srcDepth; img++) {
          GLchan *destRow = dest;
          for (row = 0; row < srcHeight; row++) {
             const GLvoid *src = _mesa_image_address(srcPacking,
                 srcAddr, srcWidth, srcHeight, srcFormat, srcType, img, row, 0);
             _mesa_unpack_index_span(ctx, srcWidth, texType, destRow,
-                                    srcType, src, srcPacking,
-                                    ctx->_ImageTransferState);
-            destRow += dstRowStride;
+                                    srcType, src, srcPacking, transferOps);
+            destRow += (dstRowStride / sizeof(GLchan));
          }
          dest += dstImageStride;
       }
    }
+   else if (texDestFormat == GL_YCBCR_MESA) {
+      /* YCbCr texture */
+      GLint img, row;
+      GLushort *dest = (GLushort *) texDestAddr
+                     + dstZoffset * (dstImageStride / sizeof(GLushort))
+                     + dstYoffset * (dstRowStride / sizeof(GLushort))
+                     + dstXoffset * texComponents;
+      ASSERT(ctx->Extensions.MESA_ycbcr_texture);
+      for (img = 0; img < srcDepth; img++) {
+         GLushort *destRow = dest;
+         for (row = 0; row < srcHeight; row++) {
+            const GLvoid *srcRow = _mesa_image_address(srcPacking,
+                                          srcAddr, srcWidth, srcHeight,
+                                          srcFormat, srcType, img, row, 0);
+            MEMCPY(destRow, srcRow, srcWidth * sizeof(GLushort));
+            destRow += (dstRowStride / sizeof(GLushort));
+         }
+         dest += dstImageStride / sizeof(GLushort);
+      }
+   }
    else if (texDestFormat == GL_DEPTH_COMPONENT) {
       /* Depth texture (shadow maps) */
       GLint img, row;
       GLubyte *dest = (GLubyte *) texDestAddr
                     + dstZoffset * dstImageStride
-                    + dstYoffset * dstRowStride
+                    + dstYoffset * (dstRowStride / sizeof(GLchan))
                     + dstXoffset * texComponents;
       for (img = 0; img < srcDepth; img++) {
          GLubyte *destRow = dest;
@@ -274,7 +321,7 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                 srcAddr, srcWidth, srcHeight, srcFormat, srcType, img, row, 0);
             _mesa_unpack_depth_span(ctx, srcWidth, (GLfloat *) destRow,
                                     srcType, src, srcPacking);
-            destRow += dstRowStride;
+            destRow += (dstRowStride / sizeof(GLchan));
          }
          dest += dstImageStride;
       }
@@ -314,7 +361,7 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                               srcFormat, srcType, img, row, 0);
                _mesa_unpack_float_color_span(ctx, srcWidth, GL_RGBA, dstf,
                          srcFormat, srcType, src, srcPacking,
-                         ctx->_ImageTransferState & IMAGE_PRE_CONVOLUTION_BITS,
+                         transferOps & IMAGE_PRE_CONVOLUTION_BITS,
                          GL_TRUE);
                dstf += srcWidth * 4;
             }
@@ -338,17 +385,18 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
 
             /* packing and transfer ops after convolution */
             srcf = convImage;
-            dest = (GLchan *) texDestAddr + (dstZoffset + img) * dstImageStride
-                 + dstYoffset * dstRowStride;
+            dest = (GLchan *) texDestAddr
+                 + (dstZoffset + img) * (dstImageStride / sizeof(GLchan))
+                 + dstYoffset * (dstRowStride / sizeof(GLchan));
             for (row = 0; row < convHeight; row++) {
                _mesa_pack_float_rgba_span(ctx, convWidth,
                                           (const GLfloat (*)[4]) srcf,
                                           texDestFormat, CHAN_TYPE,
                                           dest, &_mesa_native_packing,
-                                          ctx->_ImageTransferState
+                                          transferOps
                                           & IMAGE_POST_CONVOLUTION_BITS);
                srcf += convWidth * 4;
-               dest += dstRowStride;
+               dest += (dstRowStride / sizeof(GLchan));
             }
          }
 
@@ -360,9 +408,10 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
           * no convolution
           */
          GLint img, row;
-         GLchan *dest = (GLchan *) texDestAddr + dstZoffset * dstImageStride
-                       + dstYoffset * dstRowStride
-                       + dstXoffset * texComponents;
+         GLchan *dest = (GLchan *) texDestAddr
+                      + dstZoffset * (dstImageStride / sizeof(GLchan))
+                      + dstYoffset * (dstRowStride / sizeof(GLchan))
+                      + dstXoffset * texComponents;
          for (img = 0; img < srcDepth; img++) {
             GLchan *destRow = dest;
             for (row = 0; row < srcHeight; row++) {
@@ -371,10 +420,10 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                               srcFormat, srcType, img, row, 0);
                _mesa_unpack_chan_color_span(ctx, srcWidth, texDestFormat,
                                        destRow, srcFormat, srcType, srcRow,
-                                       srcPacking, ctx->_ImageTransferState);
-               destRow += dstRowStride;
+                                       srcPacking, transferOps);
+               destRow += (dstRowStride / sizeof(GLchan));
             }
-            dest += dstImageStride;
+            dest += dstImageStride / sizeof(GLchan);
          }
       }
    }
@@ -386,9 +435,19 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
  * Transfer a texture image from user space to <destAddr> applying all
  * needed image transfer operations and storing the result in the format
  * specified by <dstFormat>.  <dstFormat> may be any format from texformat.h.
- * Input:
+ * \param
+ *   dimensions - 1, 2 or 3
+ *   baseInternalFormat - base format of the internal texture format
+ *       specified by the user.  This is very important, see below.
+ *   dstFormat - destination image format
+ *   dstAddr - destination address
+ *   srcWidth, srcHeight, srcDepth - size of source iamge
+ *   dstX/Y/Zoffset - as specified by glTexSubImage
  *   dstRowStride - stride between dest rows in bytes
- *   dstImagetride - stride between dest images in bytes
+ *   dstImageStride - stride between dest images in bytes
+ *   srcFormat, srcType - incoming image format and data type
+ *   srcAddr - source image address
+ *   srcPacking - packing params of source image
  *
  * XXX this function is a bit more complicated than it should be.  If
  * _mesa_convert_texsubimage[123]d could handle any dest/source formats
@@ -397,6 +456,7 @@ transfer_teximage(GLcontext *ctx, GLuint dimensions,
  */
 void
 _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
+                        GLenum baseInternalFormat,
                         const struct gl_texture_format *dstFormat,
                         GLvoid *dstAddr,
                         GLint srcWidth, GLint srcHeight, GLint srcDepth,
@@ -409,10 +469,67 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
    const GLint dstRowStridePixels = dstRowStride / dstFormat->TexelBytes;
    const GLint dstImageStridePixels = dstImageStride / dstFormat->TexelBytes;
    GLboolean makeTemp;
+   GLuint transferOps = ctx->_ImageTransferState;
+   GLboolean freeSourceData = GL_FALSE;
+   GLint postConvWidth = srcWidth, postConvHeight = srcHeight;
+
+   assert(baseInternalFormat > 0);
+   ASSERT(baseInternalFormat == GL_LUMINANCE ||
+          baseInternalFormat == GL_INTENSITY ||
+          baseInternalFormat == GL_LUMINANCE_ALPHA ||
+          baseInternalFormat == GL_ALPHA ||
+          baseInternalFormat == GL_RGB ||
+          baseInternalFormat == GL_RGBA ||
+          baseInternalFormat == GL_YCBCR_MESA ||
+          baseInternalFormat == GL_COLOR_INDEX ||
+          baseInternalFormat == GL_DEPTH_COMPONENT);
+
+   if (transferOps & IMAGE_CONVOLUTION_BIT) {
+      _mesa_adjust_image_for_convolution(ctx, dimensions, &postConvWidth,
+                                         &postConvHeight);
+   }
+
+   /*
+    * Consider this scenario:  The user's source image is GL_RGB and the
+    * requested internal format is GL_LUMINANCE.  Now suppose the device
+    * driver doesn't support GL_LUMINANCE and instead uses RGB16 as the
+    * texture format.  In that case we still need to do an intermediate
+    * conversion to luminance format so that the incoming red channel gets
+    * replicated into the dest red, green and blue channels.  The following
+    * code takes care of that.
+    */
+   if (dstFormat->BaseFormat != baseInternalFormat) {
+      /* Allocate storage for temporary image in the baseInternalFormat */
+      const GLint texelSize = _mesa_components_in_format(baseInternalFormat)
+         * sizeof(GLchan);
+      const GLint bytes = texelSize * postConvWidth * postConvHeight *srcDepth;
+      const GLint tmpRowStride = texelSize * postConvWidth;
+      const GLint tmpImgStride = texelSize * postConvWidth * postConvHeight;
+      GLvoid *tmpImage = MALLOC(bytes);
+      if (!tmpImage)
+         return;
+      transfer_teximage(ctx, dimensions, baseInternalFormat, tmpImage,
+                        srcWidth, srcHeight, srcDepth,
+                        0, 0, 0, /* x/y/zoffset */
+                        tmpRowStride, tmpImgStride,
+                        srcFormat, srcType, srcAddr, srcPacking, transferOps);
+
+      /* this is our new source image */
+      srcWidth = postConvWidth;
+      srcHeight = postConvHeight;
+      srcFormat = baseInternalFormat;
+      srcType = CHAN_TYPE;
+      srcAddr = tmpImage;
+      srcPacking = &_mesa_native_packing;
+      freeSourceData = GL_TRUE;
+      transferOps = 0;  /* image transfer ops were completed */
+   }
 
-   /* First, determine if need to make a temporary, intermediate image */
+   /* Let the optimized tex conversion functions take a crack at the
+    * image conversion if the dest format is a h/w format.
+    */
    if (_mesa_is_hardware_tex_format(dstFormat)) {
-      if (ctx->_ImageTransferState) {
+      if (transferOps) {
          makeTemp = GL_TRUE;
       }
       else {
@@ -444,6 +561,8 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
          }
          if (!makeTemp) {
             /* all done! */
+            if (freeSourceData)
+               FREE((void *) srcAddr);
             return;
          }
       }
@@ -453,7 +572,6 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
       makeTemp = GL_FALSE;
    }
 
-
    if (makeTemp) {
       GLint postConvWidth = srcWidth, postConvHeight = srcHeight;
       GLenum tmpFormat;
@@ -461,26 +579,32 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
       GLint tmpRowStride, tmpImageStride;
       GLubyte *tmpImage;
 
-      if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) {
+      if (transferOps & IMAGE_CONVOLUTION_BIT) {
          _mesa_adjust_image_for_convolution(ctx, dimensions, &postConvWidth,
                                             &postConvHeight);
       }
 
-      tmpFormat = _mesa_base_tex_format(ctx, dstFormat->IntFormat);
+      tmpFormat = dstFormat->BaseFormat;
       tmpComps = _mesa_components_in_format(tmpFormat);
-      tmpTexelSize = tmpComps * sizeof(CHAN_TYPE);
+      tmpTexelSize = tmpComps * sizeof(GLchan);
       tmpRowStride = postConvWidth * tmpTexelSize;
       tmpImageStride = postConvWidth * postConvHeight * tmpTexelSize;
       tmpImage = (GLubyte *) MALLOC(postConvWidth * postConvHeight *
                                     srcDepth * tmpTexelSize);
-      if (!tmpImage)
+      if (!tmpImage) {
+         if (freeSourceData)
+            FREE((void *) srcAddr);
          return;
+      }
 
       transfer_teximage(ctx, dimensions, tmpFormat, tmpImage,
                         srcWidth, srcHeight, srcDepth,
                         0, 0, 0, /* x/y/zoffset */
                         tmpRowStride, tmpImageStride,
-                        srcFormat, srcType, srcAddr, srcPacking);
+                        srcFormat, srcType, srcAddr, srcPacking, transferOps);
+
+      if (freeSourceData)
+         FREE((void *) srcAddr);
 
       /* the temp image is our new source image */
       srcWidth = postConvWidth;
@@ -489,6 +613,7 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
       srcType = CHAN_TYPE;
       srcAddr = tmpImage;
       srcPacking = &_mesa_native_packing;
+      freeSourceData = GL_TRUE;
    }
 
    if (_mesa_is_hardware_tex_format(dstFormat)) {
@@ -502,6 +627,7 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                          srcPacking, srcAddr,
                                          dstAddr);
          assert(b);
+         (void) b;
       }
       else if (dimensions == 2) {
          GLboolean b;
@@ -513,6 +639,7 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                          srcPacking, srcAddr,
                                          dstAddr);
          assert(b);
+         (void) b;
       }
       else {
          GLboolean b;
@@ -523,28 +650,104 @@ _mesa_transfer_teximage(GLcontext *ctx, GLuint dimensions,
                                       srcFormat, srcType,
                                       srcPacking, srcAddr, dstAddr);
          assert(b);
+         (void) b;
       }
-      FREE((void *) srcAddr);  /* the temp image */
    }
    else {
       /* software format */
-      GLenum dstBaseFormat = _mesa_base_tex_format(ctx, dstFormat->IntFormat);
       assert(!makeTemp);
-      transfer_teximage(ctx, dimensions, dstBaseFormat, dstAddr,
+      transfer_teximage(ctx, dimensions, dstFormat->BaseFormat, dstAddr,
                         srcWidth, srcHeight, srcDepth,
                         dstXoffset, dstYoffset, dstZoffset,
                         dstRowStride, dstImageStride,
-                        srcFormat, srcType, srcAddr, srcPacking);
+                        srcFormat, srcType, srcAddr, srcPacking, transferOps);
+   }
+
+   if (freeSourceData)
+      FREE((void *) srcAddr);  /* the temp image */
+}
+
+
+
+/**
+ * Given a user's uncompressed texture image, this function takes care of
+ * pixel unpacking, pixel transfer, format conversion and compression.
+ */
+static void
+transfer_compressed_teximage(GLcontext *ctx, GLuint dimensions,
+                             GLsizei width, GLsizei height, GLsizei depth,
+                             GLenum srcFormat, GLenum srcType,
+                             const struct gl_pixelstore_attrib *unpacking,
+                             const GLvoid *source,
+                             const struct gl_texture_format *dstFormat,
+                             GLubyte *dest,
+                             GLint dstRowStride)
+{
+   GLchan *tempImage = NULL;
+   GLint srcRowStride;
+   GLenum baseFormat;
+
+   ASSERT(dimensions == 2);
+   /* TexelBytes is zero if and only if it's a compressed format */
+   ASSERT(dstFormat->TexelBytes == 0);
+
+   baseFormat = dstFormat->BaseFormat;
+
+   if (srcFormat != baseFormat || srcType != CHAN_TYPE ||
+       ctx->_ImageTransferState != 0 || unpacking->SwapBytes) {
+      /* need to convert user's image to texImage->Format, GLchan */
+      GLint comps = components_in_intformat(baseFormat);
+      GLint postConvWidth = width, postConvHeight = height;
+
+      /* XXX convolution untested */
+      if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) {
+         _mesa_adjust_image_for_convolution(ctx, dimensions, &postConvWidth,
+                                            &postConvHeight);
+      }
+
+      tempImage = (GLchan*) MALLOC(width * height * comps * sizeof(GLchan));
+      if (!tempImage) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
+         return;
+      }
+      transfer_teximage(ctx, dimensions,
+                        baseFormat,             /* dest format */
+                        tempImage,              /* dst address */
+                        width, height, depth,   /* src size */
+                        0, 0, 0,                /* x/y/zoffset */
+                        comps * width,          /* dst row stride */
+                        comps * width * height, /* dst image stride */
+                        srcFormat, srcType,     /* src format, type */
+                        source, unpacking,      /* src and src packing */
+                        ctx->_ImageTransferState);
+      source = tempImage;
+      width = postConvWidth;
+      height = postConvHeight;
+      srcRowStride = width;
+   }
+   else {
+      if (unpacking->RowLength)
+         srcRowStride = unpacking->RowLength;
+      else
+         srcRowStride = width;
+   }
+
+   _mesa_compress_teximage(ctx, width, height, baseFormat,
+                           (const GLchan *) source, srcRowStride,
+                           dstFormat, dest, dstRowStride);
+   if (tempImage) {
+      FREE(tempImage);
    }
 }
 
 
+
 /*
- * This is the software fallback for Driver.TexImage1D().
+ * This is the software fallback for Driver.TexImage1D()
+ * and Driver.CopyTexImage2D().
  * The texture image type will be GLchan.
  * The texture image format will be GL_COLOR_INDEX, GL_INTENSITY,
  * GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA, GL_RGB or GL_RGBA.
- *
  */
 void
 _mesa_store_teximage1d(GLcontext *ctx, GLenum target, GLint level,
@@ -556,7 +759,7 @@ _mesa_store_teximage1d(GLcontext *ctx, GLenum target, GLint level,
                        struct gl_texture_image *texImage)
 {
    GLint postConvWidth = width;
-   GLint texelBytes;
+   GLint texelBytes, sizeInBytes;
 
    if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) {
       _mesa_adjust_image_for_convolution(ctx, 1, &postConvWidth, NULL);
@@ -567,31 +770,59 @@ _mesa_store_teximage1d(GLcontext *ctx, GLenum target, GLint level,
    texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
                                           internalFormat, format, type);
    assert(texImage->TexFormat);
+   texImage->FetchTexel = texImage->TexFormat->FetchTexel1D;
 
    texelBytes = texImage->TexFormat->TexelBytes;
 
    /* allocate memory */
-   texImage->Data = MALLOC(postConvWidth * texelBytes);
+   if (texImage->IsCompressed)
+      sizeInBytes = texImage->CompressedSize;
+   else
+      sizeInBytes = postConvWidth * texelBytes;
+   texImage->Data = MESA_PBUFFER_ALLOC(sizeInBytes);
    if (!texImage->Data) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D");
       return;
    }
 
+   if (!pixels)
+      return;
+
    /* unpack image, apply transfer ops and store in texImage->Data */
-   _mesa_transfer_teximage(ctx, 1, texImage->TexFormat, texImage->Data,
-                           width, 1, 1, 0, 0, 0,
-                           0, /* dstRowStride */
-                           0, /* dstImageStride */
-                           format, type, pixels, packing);
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       width);
+      transfer_compressed_teximage(ctx, 1, width, 1, 1,
+                                   format, type, packing,
+                                   pixels, texImage->TexFormat,
+                                   (GLubyte *) texImage->Data, dstRowStride);
+   }
+   else {
+      _mesa_transfer_teximage(ctx, 1,
+                              texImage->Format, /* base format */
+                              texImage->TexFormat, texImage->Data,
+                              width, 1, 1,  /* src size */
+                              0, 0, 0,      /* dstX/Y/Zoffset */
+                              0, /* dstRowStride */
+                              0, /* dstImageStride */
+                              format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
 /*
- * This is the software fallback for Driver.TexImage2D().
+ * This is the software fallback for Driver.TexImage2D()
+ * and Driver.CopyTexImage2D().
  * The texture image type will be GLchan.
  * The texture image format will be GL_COLOR_INDEX, GL_INTENSITY,
  * GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA, GL_RGB or GL_RGBA.
- *
  */
 void
 _mesa_store_teximage2d(GLcontext *ctx, GLenum target, GLint level,
@@ -603,7 +834,7 @@ _mesa_store_teximage2d(GLcontext *ctx, GLenum target, GLint level,
                        struct gl_texture_image *texImage)
 {
    GLint postConvWidth = width, postConvHeight = height;
-   GLint texelBytes;
+   GLint texelBytes, sizeInBytes;
 
    if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) {
       _mesa_adjust_image_for_convolution(ctx, 2, &postConvWidth,
@@ -615,32 +846,60 @@ _mesa_store_teximage2d(GLcontext *ctx, GLenum target, GLint level,
    texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
                                           internalFormat, format, type);
    assert(texImage->TexFormat);
+   texImage->FetchTexel = texImage->TexFormat->FetchTexel2D;
 
    texelBytes = texImage->TexFormat->TexelBytes;
 
    /* allocate memory */
-   texImage->Data = MALLOC(postConvWidth * postConvHeight * texelBytes);
+   if (texImage->IsCompressed)
+      sizeInBytes = texImage->CompressedSize;
+   else
+      sizeInBytes = postConvWidth * postConvHeight * texelBytes;
+   texImage->Data = MESA_PBUFFER_ALLOC(sizeInBytes);
    if (!texImage->Data) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
       return;
    }
 
+   if (!pixels)
+      return;
+
    /* unpack image, apply transfer ops and store in texImage->Data */
-   _mesa_transfer_teximage(ctx, 2, texImage->TexFormat, texImage->Data,
-                           width, height, 1, 0, 0, 0,
-                           texImage->Width * texelBytes,
-                           0, /* dstImageStride */
-                           format, type, pixels, packing);
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       width);
+      transfer_compressed_teximage(ctx, 2, width, height, 1,
+                                   format, type, packing,
+                                   pixels, texImage->TexFormat,
+                                   (GLubyte *) texImage->Data, dstRowStride);
+   }
+   else {
+      _mesa_transfer_teximage(ctx, 2,
+                              texImage->Format,
+                              texImage->TexFormat, texImage->Data,
+                              width, height, 1,  /* src size */
+                              0, 0, 0,           /* dstX/Y/Zoffset */
+                              texImage->Width * texelBytes, /* dstRowStride */
+                              0, /* dstImageStride */
+                              format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
 
 /*
- * This is the software fallback for Driver.TexImage3D().
+ * This is the software fallback for Driver.TexImage3D()
+ * and Driver.CopyTexImage3D().
  * The texture image type will be GLchan.
  * The texture image format will be GL_COLOR_INDEX, GL_INTENSITY,
  * GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA, GL_RGB or GL_RGBA.
- *
  */
 void
 _mesa_store_teximage3d(GLcontext *ctx, GLenum target, GLint level,
@@ -651,36 +910,65 @@ _mesa_store_teximage3d(GLcontext *ctx, GLenum target, GLint level,
                        struct gl_texture_object *texObj,
                        struct gl_texture_image *texImage)
 {
-   GLint texelBytes;
+   GLint texelBytes, sizeInBytes;
 
    /* choose the texture format */
    assert(ctx->Driver.ChooseTextureFormat);
    texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
                                           internalFormat, format, type);
    assert(texImage->TexFormat);
+   texImage->FetchTexel = texImage->TexFormat->FetchTexel3D;
 
    texelBytes = texImage->TexFormat->TexelBytes;
 
    /* allocate memory */
-   texImage->Data = MALLOC(width * height * depth * texelBytes);
+   if (texImage->IsCompressed)
+      sizeInBytes = texImage->CompressedSize;
+   else
+      sizeInBytes = width * height * depth * texelBytes;
+   texImage->Data = MESA_PBUFFER_ALLOC(sizeInBytes);
    if (!texImage->Data) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage3D");
       return;
    }
 
+   if (!pixels)
+      return;
+
    /* unpack image, apply transfer ops and store in texImage->Data */
-   _mesa_transfer_teximage(ctx, 3, texImage->TexFormat, texImage->Data,
-                           width, height, depth, 0, 0, 0,
-                           texImage->Width * texelBytes,
-                           texImage->Width * texImage->Height * texelBytes,
-                           format, type, pixels, packing);
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       width);
+      transfer_compressed_teximage(ctx, 3, width, height, depth,
+                                   format, type, packing,
+                                   pixels, texImage->TexFormat,
+                                   (GLubyte *) texImage->Data, dstRowStride);
+   }
+   else {
+      _mesa_transfer_teximage(ctx, 3,
+                              texImage->Format,
+                              texImage->TexFormat, texImage->Data,
+                              width, height, depth, /* src size */
+                              0, 0, 0,  /* dstX/Y/Zoffset */
+                              texImage->Width * texelBytes, /* dstRowStride */
+                              texImage->Width * texImage->Height * texelBytes,
+                              format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
 
 
 /*
- * This is the software fallback for Driver.TexSubImage1D().
+ * This is the software fallback for Driver.TexSubImage1D()
+ * and Driver.CopyTexSubImage1D().
  */
 void
 _mesa_store_texsubimage1d(GLcontext *ctx, GLenum target, GLint level,
@@ -690,17 +978,45 @@ _mesa_store_texsubimage1d(GLcontext *ctx, GLenum target, GLint level,
                           struct gl_texture_object *texObj,
                           struct gl_texture_image *texImage)
 {
-   _mesa_transfer_teximage(ctx, 1, texImage->TexFormat, texImage->Data,
-                           width, 1, 1, /* src size */
-                           xoffset, 0, 0, /* dest offsets */
-                           0, /* dstRowStride */
-                           0, /* dstImageStride */
-                           format, type, pixels, packing);
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       texImage->Width);
+      GLubyte *dest = _mesa_compressed_image_address(xoffset, 0, 0,
+                                                     texImage->IntFormat,
+                                                     texImage->Width,
+                                          (GLubyte*) texImage->Data);
+      transfer_compressed_teximage(ctx, 1,             /* dimensions */
+                                   width, 1, 1,        /* size to replace */
+                                   format, type,       /* source format/type */
+                                   packing,            /* source packing */
+                                   pixels,             /* source data */
+                                   texImage->TexFormat,/* dest format */
+                                   dest, dstRowStride);
+   }
+   else {
+      _mesa_transfer_teximage(ctx, 1,
+                              texImage->Format,
+                              texImage->TexFormat, texImage->Data,
+                              width, 1, 1, /* src size */
+                              xoffset, 0, 0, /* dest offsets */
+                              0, /* dstRowStride */
+                              0, /* dstImageStride */
+                              format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
+
 /*
- * This is the software fallback for Driver.TexSubImage2D().
+ * This is the software fallback for Driver.TexSubImage2D()
+ * and Driver.CopyTexSubImage2D().
  */
 void
 _mesa_store_texsubimage2d(GLcontext *ctx, GLenum target, GLint level,
@@ -711,17 +1027,44 @@ _mesa_store_texsubimage2d(GLcontext *ctx, GLenum target, GLint level,
                           struct gl_texture_object *texObj,
                           struct gl_texture_image *texImage)
 {
-   _mesa_transfer_teximage(ctx, 2, texImage->TexFormat, texImage->Data,
-                           width, height, 1, /* src size */
-                           xoffset, yoffset, 0, /* dest offsets */
-                           texImage->Width * texImage->TexFormat->TexelBytes,
-                           0, /* dstImageStride */
-                           format, type, pixels, packing);
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       texImage->Width);
+      GLubyte *dest = _mesa_compressed_image_address(xoffset, yoffset, 0,
+                                                     texImage->IntFormat,
+                                                     texImage->Width,
+                                          (GLubyte*) texImage->Data);
+      transfer_compressed_teximage(ctx, 2,             /* dimensions */
+                                   width, height, 1,   /* size to replace */
+                                   format, type,       /* source format/type */
+                                   packing,            /* source packing */
+                                   pixels,             /* source data */
+                                   texImage->TexFormat,/* dest format */
+                                   dest, dstRowStride);
+   }
+   else {
+      _mesa_transfer_teximage(ctx, 2,
+                              texImage->Format,
+                              texImage->TexFormat, texImage->Data,
+                              width, height, 1, /* src size */
+                              xoffset, yoffset, 0, /* dest offsets */
+                              texImage->Width *texImage->TexFormat->TexelBytes,
+                              0, /* dstImageStride */
+                              format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
 /*
  * This is the software fallback for Driver.TexSubImage3D().
+ * and Driver.CopyTexSubImage3D().
  */
 void
 _mesa_store_texsubimage3d(GLcontext *ctx, GLenum target, GLint level,
@@ -732,13 +1075,39 @@ _mesa_store_texsubimage3d(GLcontext *ctx, GLenum target, GLint level,
                           struct gl_texture_object *texObj,
                           struct gl_texture_image *texImage)
 {
-   const GLint texelBytes = texImage->TexFormat->TexelBytes;
-   _mesa_transfer_teximage(ctx, 3, texImage->TexFormat, texImage->Data,
+   if (texImage->IsCompressed) {
+      GLint dstRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                                       texImage->Width);
+      GLubyte *dest = _mesa_compressed_image_address(xoffset, yoffset, zoffset,
+                                                     texImage->IntFormat,
+                                                     texImage->Width,
+                                          (GLubyte*) texImage->Data);
+      transfer_compressed_teximage(ctx, 3,              /* dimensions */
+                                   width, height, depth,/* size to replace */
+                                   format, type,       /* source format/type */
+                                   packing,            /* source packing */
+                                   pixels,             /* source data */
+                                   texImage->TexFormat,/* dest format */
+                                   dest, dstRowStride);
+   }
+   else {
+      const GLint texelBytes = texImage->TexFormat->TexelBytes;
+      _mesa_transfer_teximage(ctx, 3,
+                           texImage->Format,
+                           texImage->TexFormat, texImage->Data,
                            width, height, depth, /* src size */
-                           xoffset, yoffset, xoffset, /* dest offsets */
-                           texImage->Width * texelBytes,
+                           xoffset, yoffset, zoffset, /* dest offsets */
+                           texImage->Width * texelBytes,  /* dst row stride */
                            texImage->Width * texImage->Height * texelBytes,
                            format, type, pixels, packing);
+   }
+
+   /* GL_SGIS_generate_mipmap */
+   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
+      _mesa_generate_mipmap(ctx, target,
+                            &ctx->Texture.Unit[ctx->Texture.CurrentUnit],
+                            texObj);
+   }
 }
 
 
@@ -755,9 +1124,7 @@ _mesa_store_compressed_teximage1d(GLcontext *ctx, GLenum target, GLint level,
                                   struct gl_texture_object *texObj,
                                   struct gl_texture_image *texImage)
 {
-   /* Nothing here.
-    * The device driver has to do it all.
-    */
+   /* this space intentionally left blank */
 }
 
 
@@ -773,9 +1140,33 @@ _mesa_store_compressed_teximage2d(GLcontext *ctx, GLenum target, GLint level,
                                   struct gl_texture_object *texObj,
                                   struct gl_texture_image *texImage)
 {
-   /* Nothing here.
-    * The device driver has to do it all.
+   /* This is pretty simple, basically just do a memcpy without worrying
+    * about the usual image unpacking or image transfer operations.
     */
+   ASSERT(texObj);
+   ASSERT(texImage);
+   ASSERT(texImage->Width > 0);
+   ASSERT(texImage->Height > 0);
+   ASSERT(texImage->Depth == 1);
+   ASSERT(texImage->Data == NULL); /* was freed in glCompressedTexImage2DARB */
+
+   /* choose the texture format */
+   assert(ctx->Driver.ChooseTextureFormat);
+   texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
+                                          internalFormat, 0, 0);
+   assert(texImage->TexFormat);
+   texImage->FetchTexel = texImage->TexFormat->FetchTexel2D;
+
+   /* allocate storage */
+   texImage->Data = MESA_PBUFFER_ALLOC(imageSize);
+   if (!texImage->Data) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2DARB");
+      return;
+   }
+
+   /* copy the data */
+   ASSERT(texImage->CompressedSize == (GLuint) imageSize);
+   MEMCPY(texImage->Data, data, imageSize);
 }
 
 
@@ -792,42 +1183,901 @@ _mesa_store_compressed_teximage3d(GLcontext *ctx, GLenum target, GLint level,
                                   struct gl_texture_object *texObj,
                                   struct gl_texture_image *texImage)
 {
-   /* Nothing here.
-    * The device driver has to do it all.
-    */
+   /* this space intentionally left blank */
+}
+
+
+
+/**
+ * Fallback for Driver.CompressedTexSubImage1D()
+ */
+void
+_mesa_store_compressed_texsubimage1d(GLcontext *ctx, GLenum target,
+                                     GLint level,
+                                     GLint xoffset, GLsizei width,
+                                     GLenum format,
+                                     GLsizei imageSize, const GLvoid *data,
+                                     struct gl_texture_object *texObj,
+                                     struct gl_texture_image *texImage)
+{
+   /* this space intentionally left blank */
+}
+
+
+/**
+ * Fallback for Driver.CompressedTexSubImage2D()
+ */
+void
+_mesa_store_compressed_texsubimage2d(GLcontext *ctx, GLenum target,
+                                     GLint level,
+                                     GLint xoffset, GLint yoffset,
+                                     GLsizei width, GLsizei height,
+                                     GLenum format,
+                                     GLsizei imageSize, const GLvoid *data,
+                                     struct gl_texture_object *texObj,
+                                     struct gl_texture_image *texImage)
+{
+   GLint bytesPerRow, destRowStride, srcRowStride;
+   GLint i, rows;
+   GLubyte *dest;
+   const GLubyte *src;
+
+   /* these should have been caught sooner */
+   ASSERT((width & 3) == 0 || width == 2 || width == 1);
+   ASSERT((height & 3) == 0 || height == 2 || height == 1);
+   ASSERT((xoffset & 3) == 0);
+   ASSERT((yoffset & 3) == 0);
+
+   srcRowStride = _mesa_compressed_row_stride(texImage->IntFormat, width);
+   src = (const GLubyte *) data;
+
+   destRowStride = _mesa_compressed_row_stride(texImage->IntFormat,
+                                               texImage->Width);
+   dest = _mesa_compressed_image_address(xoffset, yoffset, 0,
+                                         texImage->IntFormat,
+                                         texImage->Width,
+                              (GLubyte*) texImage->Data);
+
+   bytesPerRow = srcRowStride;
+   rows = height / 4;
+
+   for (i = 0; i < rows; i++) {
+      MEMCPY(dest, src, bytesPerRow);
+      dest += destRowStride;
+      src += srcRowStride;
+   }
+}
+
+
+/**
+ * Fallback for Driver.CompressedTexSubImage3D()
+ */
+void
+_mesa_store_compressed_texsubimage3d(GLcontext *ctx, GLenum target,
+                                GLint level,
+                                GLint xoffset, GLint yoffset, GLint zoffset,
+                                GLsizei width, GLsizei height, GLsizei depth,
+                                GLenum format,
+                                GLsizei imageSize, const GLvoid *data,
+                                struct gl_texture_object *texObj,
+                                struct gl_texture_image *texImage)
+{
+   /* this space intentionally left blank */
 }
 
 
+/*
+ * Average together two rows of a source image to produce a single new
+ * row in the dest image.  It's legal for the two source rows to point
+ * to the same data.  The source width must be equal to either the
+ * dest width or two times the dest width.
+ */
+static void
+do_row(const struct gl_texture_format *format, GLint srcWidth,
+       const GLvoid *srcRowA, const GLvoid *srcRowB,
+       GLint dstWidth, GLvoid *dstRow)
+{
+   const GLuint k0 = (srcWidth == dstWidth) ? 0 : 1;
+   const GLuint colStride = (srcWidth == dstWidth) ? 1 : 2;
+
+   /* This assertion is no longer valid with non-power-of-2 textures
+   assert(srcWidth == dstWidth || srcWidth == 2 * dstWidth);
+   */
+
+   switch (format->MesaFormat) {
+   case MESA_FORMAT_RGBA:
+      {
+         GLuint i, j, k;
+         const GLchan (*rowA)[4] = (const GLchan (*)[4]) srcRowA;
+         const GLchan (*rowB)[4] = (const GLchan (*)[4]) srcRowB;
+         GLchan (*dst)[4] = (GLchan (*)[4]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) / 4;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) / 4;
+            dst[i][2] = (rowA[j][2] + rowA[k][2] +
+                         rowB[j][2] + rowB[k][2]) / 4;
+            dst[i][3] = (rowA[j][3] + rowA[k][3] +
+                         rowB[j][3] + rowB[k][3]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_RGB:
+      {
+         GLuint i, j, k;
+         const GLchan (*rowA)[3] = (const GLchan (*)[3]) srcRowA;
+         const GLchan (*rowB)[3] = (const GLchan (*)[3]) srcRowB;
+         GLchan (*dst)[3] = (GLchan (*)[3]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) / 4;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) / 4;
+            dst[i][2] = (rowA[j][2] + rowA[k][2] +
+                         rowB[j][2] + rowB[k][2]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_ALPHA:
+   case MESA_FORMAT_LUMINANCE:
+   case MESA_FORMAT_INTENSITY:
+   case MESA_FORMAT_COLOR_INDEX:
+      {
+         GLuint i, j, k;
+         const GLchan *rowA = (const GLchan *) srcRowA;
+         const GLchan *rowB = (const GLchan *) srcRowB;
+         GLchan *dst = (GLchan *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i] = (rowA[j] + rowA[k] + rowB[j] + rowB[k]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_LUMINANCE_ALPHA:
+      {
+         GLuint i, j, k;
+         const GLchan (*rowA)[2] = (const GLchan (*)[2]) srcRowA;
+         const GLchan (*rowB)[2] = (const GLchan (*)[2]) srcRowB;
+         GLchan (*dst)[2] = (GLchan (*)[2]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) / 4;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_DEPTH_COMPONENT:
+      {
+         GLuint i, j, k;
+         const GLfloat *rowA = (const GLfloat *) srcRowA;
+         const GLfloat *rowB = (const GLfloat *) srcRowB;
+         GLfloat *dst = (GLfloat *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i] = (rowA[j] + rowA[k] + rowB[j] + rowB[k]) * 0.25F;
+         }
+      }
+      return;
+   /* Begin hardware formats */
+   case MESA_FORMAT_RGBA8888:
+   case MESA_FORMAT_ARGB8888:
+      {
+         GLuint i, j, k;
+         const GLubyte (*rowA)[4] = (const GLubyte (*)[4]) srcRowA;
+         const GLubyte (*rowB)[4] = (const GLubyte (*)[4]) srcRowB;
+         GLubyte (*dst)[4] = (GLubyte (*)[4]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) / 4;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) / 4;
+            dst[i][2] = (rowA[j][2] + rowA[k][2] +
+                         rowB[j][2] + rowB[k][2]) / 4;
+            dst[i][3] = (rowA[j][3] + rowA[k][3] +
+                         rowB[j][3] + rowB[k][3]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_RGB888:
+      {
+         GLuint i, j, k;
+         const GLubyte (*rowA)[3] = (const GLubyte (*)[3]) srcRowA;
+         const GLubyte (*rowB)[3] = (const GLubyte (*)[3]) srcRowB;
+         GLubyte (*dst)[3] = (GLubyte (*)[3]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) / 4;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) / 4;
+            dst[i][2] = (rowA[j][2] + rowA[k][2] +
+                         rowB[j][2] + rowB[k][2]) / 4;
+         }
+      }
+      return;
+   case MESA_FORMAT_RGB565:
+      {
+         GLuint i, j, k;
+         const GLushort *rowA = (const GLushort *) srcRowA;
+         const GLushort *rowB = (const GLushort *) srcRowB;
+         GLushort *dst = (GLushort *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            const GLint rowAr0 = rowA[j] & 0x1f;
+            const GLint rowAr1 = rowA[k] & 0x1f;
+            const GLint rowBr0 = rowB[j] & 0x1f;
+            const GLint rowBr1 = rowB[k] & 0x1f;
+            const GLint rowAg0 = (rowA[j] >> 5) & 0x3f;
+            const GLint rowAg1 = (rowA[k] >> 5) & 0x3f;
+            const GLint rowBg0 = (rowB[j] >> 5) & 0x3f;
+            const GLint rowBg1 = (rowB[k] >> 5) & 0x3f;
+            const GLint rowAb0 = (rowA[j] >> 11) & 0x1f;
+            const GLint rowAb1 = (rowA[k] >> 11) & 0x1f;
+            const GLint rowBb0 = (rowB[j] >> 11) & 0x1f;
+            const GLint rowBb1 = (rowB[k] >> 11) & 0x1f;
+            const GLint red   = (rowAr0 + rowAr1 + rowBr0 + rowBr1) >> 4;
+            const GLint green = (rowAg0 + rowAg1 + rowBg0 + rowBg1) >> 4;
+            const GLint blue  = (rowAb0 + rowAb1 + rowBb0 + rowBb1) >> 4;
+            dst[i] = (blue << 11) | (green << 5) | red;
+         }
+      }
+      return;
+   case MESA_FORMAT_ARGB4444:
+      {
+         GLuint i, j, k;
+         const GLushort *rowA = (const GLushort *) srcRowA;
+         const GLushort *rowB = (const GLushort *) srcRowB;
+         GLushort *dst = (GLushort *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            const GLint rowAr0 = rowA[j] & 0xf;
+            const GLint rowAr1 = rowA[k] & 0xf;
+            const GLint rowBr0 = rowB[j] & 0xf;
+            const GLint rowBr1 = rowB[k] & 0xf;
+            const GLint rowAg0 = (rowA[j] >> 4) & 0xf;
+            const GLint rowAg1 = (rowA[k] >> 4) & 0xf;
+            const GLint rowBg0 = (rowB[j] >> 4) & 0xf;
+            const GLint rowBg1 = (rowB[k] >> 4) & 0xf;
+            const GLint rowAb0 = (rowA[j] >> 8) & 0xf;
+            const GLint rowAb1 = (rowA[k] >> 8) & 0xf;
+            const GLint rowBb0 = (rowB[j] >> 8) & 0xf;
+            const GLint rowBb1 = (rowB[k] >> 8) & 0xf;
+            const GLint rowAa0 = (rowA[j] >> 12) & 0xf;
+            const GLint rowAa1 = (rowA[k] >> 12) & 0xf;
+            const GLint rowBa0 = (rowB[j] >> 12) & 0xf;
+            const GLint rowBa1 = (rowB[k] >> 12) & 0xf;
+            const GLint red   = (rowAr0 + rowAr1 + rowBr0 + rowBr1) >> 4;
+            const GLint green = (rowAg0 + rowAg1 + rowBg0 + rowBg1) >> 4;
+            const GLint blue  = (rowAb0 + rowAb1 + rowBb0 + rowBb1) >> 4;
+            const GLint alpha = (rowAa0 + rowAa1 + rowBa0 + rowBa1) >> 4;
+            dst[i] = (alpha << 12) | (blue << 8) | (green << 4) | red;
+         }
+      }
+      return;
+   case MESA_FORMAT_ARGB1555:
+      {
+         GLuint i, j, k;
+         const GLushort *rowA = (const GLushort *) srcRowA;
+         const GLushort *rowB = (const GLushort *) srcRowB;
+         GLushort *dst = (GLushort *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            const GLint rowAr0 = rowA[j] & 0x1f;
+            const GLint rowAr1 = rowA[k] & 0x1f;
+            const GLint rowBr0 = rowB[j] & 0x1f;
+            const GLint rowBr1 = rowB[k] & 0xf;
+            const GLint rowAg0 = (rowA[j] >> 5) & 0x1f;
+            const GLint rowAg1 = (rowA[k] >> 5) & 0x1f;
+            const GLint rowBg0 = (rowB[j] >> 5) & 0x1f;
+            const GLint rowBg1 = (rowB[k] >> 5) & 0x1f;
+            const GLint rowAb0 = (rowA[j] >> 10) & 0x1f;
+            const GLint rowAb1 = (rowA[k] >> 10) & 0x1f;
+            const GLint rowBb0 = (rowB[j] >> 10) & 0x1f;
+            const GLint rowBb1 = (rowB[k] >> 10) & 0x1f;
+            const GLint rowAa0 = (rowA[j] >> 15) & 0x1;
+            const GLint rowAa1 = (rowA[k] >> 15) & 0x1;
+            const GLint rowBa0 = (rowB[j] >> 15) & 0x1;
+            const GLint rowBa1 = (rowB[k] >> 15) & 0x1;
+            const GLint red   = (rowAr0 + rowAr1 + rowBr0 + rowBr1) >> 4;
+            const GLint green = (rowAg0 + rowAg1 + rowBg0 + rowBg1) >> 4;
+            const GLint blue  = (rowAb0 + rowAb1 + rowBb0 + rowBb1) >> 4;
+            const GLint alpha = (rowAa0 + rowAa1 + rowBa0 + rowBa1) >> 4;
+            dst[i] = (alpha << 15) | (blue << 10) | (green << 5) | red;
+         }
+      }
+      return;
+   case MESA_FORMAT_AL88:
+      {
+         GLuint i, j, k;
+         const GLubyte (*rowA)[2] = (const GLubyte (*)[2]) srcRowA;
+         const GLubyte (*rowB)[2] = (const GLubyte (*)[2]) srcRowB;
+         GLubyte (*dst)[2] = (GLubyte (*)[2]) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i][0] = (rowA[j][0] + rowA[k][0] +
+                         rowB[j][0] + rowB[k][0]) >> 2;
+            dst[i][1] = (rowA[j][1] + rowA[k][1] +
+                         rowB[j][1] + rowB[k][1]) >> 2;
+         }
+      }
+      return;
+   case MESA_FORMAT_RGB332:
+      {
+         GLuint i, j, k;
+         const GLubyte *rowA = (const GLubyte *) srcRowA;
+         const GLubyte *rowB = (const GLubyte *) srcRowB;
+         GLubyte *dst = (GLubyte *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            const GLint rowAr0 = rowA[j] & 0x3;
+            const GLint rowAr1 = rowA[k] & 0x3;
+            const GLint rowBr0 = rowB[j] & 0x3;
+            const GLint rowBr1 = rowB[k] & 0x3;
+            const GLint rowAg0 = (rowA[j] >> 2) & 0x7;
+            const GLint rowAg1 = (rowA[k] >> 2) & 0x7;
+            const GLint rowBg0 = (rowB[j] >> 2) & 0x7;
+            const GLint rowBg1 = (rowB[k] >> 2) & 0x7;
+            const GLint rowAb0 = (rowA[j] >> 5) & 0x7;
+            const GLint rowAb1 = (rowA[k] >> 5) & 0x7;
+            const GLint rowBb0 = (rowB[j] >> 5) & 0x7;
+            const GLint rowBb1 = (rowB[k] >> 5) & 0x7;
+            const GLint red   = (rowAr0 + rowAr1 + rowBr0 + rowBr1) >> 4;
+            const GLint green = (rowAg0 + rowAg1 + rowBg0 + rowBg1) >> 4;
+            const GLint blue  = (rowAb0 + rowAb1 + rowBb0 + rowBb1) >> 4;
+            dst[i] = (blue << 5) | (green << 2) | red;
+         }
+      }
+      return;
+   case MESA_FORMAT_A8:
+   case MESA_FORMAT_L8:
+   case MESA_FORMAT_I8:
+   case MESA_FORMAT_CI8:
+      {
+         GLuint i, j, k;
+         const GLubyte *rowA = (const GLubyte *) srcRowA;
+         const GLubyte *rowB = (const GLubyte *) srcRowB;
+         GLubyte *dst = (GLubyte *) dstRow;
+         for (i = j = 0, k = k0; i < (GLuint) dstWidth;
+              i++, j += colStride, k += colStride) {
+            dst[i] = (rowA[j] + rowA[k] + rowB[j] + rowB[k]) >> 2;
+         }
+      }
+      return;
+   default:
+      _mesa_problem(NULL, "bad format in do_row()");
+   }
+}
+
 
 /*
- * This is the fallback for Driver.TestProxyTexImage().
+ * These functions generate a 1/2-size mipmap image from a source image.
+ * Texture borders are handled by copying or averaging the source image's
+ * border texels, depending on the scale-down factor.
  */
-GLboolean
-_mesa_test_proxy_teximage(GLcontext *ctx, GLenum target, GLint level,
-                          GLint internalFormat, GLenum format, GLenum type,
-                          GLint width, GLint height, GLint depth, GLint border)
+
+static void
+make_1d_mipmap(const struct gl_texture_format *format, GLint border,
+               GLint srcWidth, const GLubyte *srcPtr,
+               GLint dstWidth, GLubyte *dstPtr)
+{
+   const GLint bpt = format->TexelBytes;
+   const GLubyte *src;
+   GLubyte *dst;
+
+   /* skip the border pixel, if any */
+   src = srcPtr + border * bpt;
+   dst = dstPtr + border * bpt;
+
+   /* we just duplicate the input row, kind of hack, saves code */
+   do_row(format, srcWidth - 2 * border, src, src,
+          dstWidth - 2 * border, dst);
+
+   if (border) {
+      /* copy left-most pixel from source */
+      MEMCPY(dstPtr, srcPtr, bpt);
+      /* copy right-most pixel from source */
+      MEMCPY(dstPtr + (dstWidth - 1) * bpt,
+             srcPtr + (srcWidth - 1) * bpt,
+             bpt);
+   }
+}
+
+
+static void
+make_2d_mipmap(const struct gl_texture_format *format, GLint border,
+               GLint srcWidth, GLint srcHeight, const GLubyte *srcPtr,
+               GLint dstWidth, GLint dstHeight, GLubyte *dstPtr)
+{
+   const GLint bpt = format->TexelBytes;
+   const GLint srcWidthNB = srcWidth - 2 * border;  /* sizes w/out border */
+   const GLint dstWidthNB = dstWidth - 2 * border;
+   const GLint dstHeightNB = dstHeight - 2 * border;
+   const GLint srcRowStride = bpt * srcWidth;
+   const GLint dstRowStride = bpt * dstWidth;
+   const GLubyte *srcA, *srcB;
+   GLubyte *dst;
+   GLint row, colStride;
+
+   colStride = (srcWidth == dstWidth) ? 1 : 2;
+
+   /* Compute src and dst pointers, skipping any border */
+   srcA = srcPtr + border * ((srcWidth + 1) * bpt);
+   if (srcHeight > 1)
+      srcB = srcA + srcRowStride;
+   else
+      srcB = srcA;
+   dst = dstPtr + border * ((dstWidth + 1) * bpt);
+
+   for (row = 0; row < dstHeightNB; row++) {
+      do_row(format, srcWidthNB, srcA, srcB,
+             dstWidthNB, dst);
+      srcA += 2 * srcRowStride;
+      srcB += 2 * srcRowStride;
+      dst += dstRowStride;
+   }
+
+   /* This is ugly but probably won't be used much */
+   if (border > 0) {
+      /* fill in dest border */
+      /* lower-left border pixel */
+      MEMCPY(dstPtr, srcPtr, bpt);
+      /* lower-right border pixel */
+      MEMCPY(dstPtr + (dstWidth - 1) * bpt,
+             srcPtr + (srcWidth - 1) * bpt, bpt);
+      /* upper-left border pixel */
+      MEMCPY(dstPtr + dstWidth * (dstHeight - 1) * bpt,
+             srcPtr + srcWidth * (srcHeight - 1) * bpt, bpt);
+      /* upper-right border pixel */
+      MEMCPY(dstPtr + (dstWidth * dstHeight - 1) * bpt,
+             srcPtr + (srcWidth * srcHeight - 1) * bpt, bpt);
+      /* lower border */
+      do_row(format, srcWidthNB,
+             srcPtr + bpt,
+             srcPtr + bpt,
+             dstWidthNB, dstPtr + bpt);
+      /* upper border */
+      do_row(format, srcWidthNB,
+             srcPtr + (srcWidth * (srcHeight - 1) + 1) * bpt,
+             srcPtr + (srcWidth * (srcHeight - 1) + 1) * bpt,
+             dstWidthNB,
+             dstPtr + (dstWidth * (dstHeight - 1) + 1) * bpt);
+      /* left and right borders */
+      if (srcHeight == dstHeight) {
+         /* copy border pixel from src to dst */
+         for (row = 1; row < srcHeight; row++) {
+            MEMCPY(dstPtr + dstWidth * row * bpt,
+                   srcPtr + srcWidth * row * bpt, bpt);
+            MEMCPY(dstPtr + (dstWidth * row + dstWidth - 1) * bpt,
+                   srcPtr + (srcWidth * row + srcWidth - 1) * bpt, bpt);
+         }
+      }
+      else {
+         /* average two src pixels each dest pixel */
+         for (row = 0; row < dstHeightNB; row += 2) {
+            do_row(format, 1,
+                   srcPtr + (srcWidth * (row * 2 + 1)) * bpt,
+                   srcPtr + (srcWidth * (row * 2 + 2)) * bpt,
+                   1, dstPtr + (dstWidth * row + 1) * bpt);
+            do_row(format, 1,
+                   srcPtr + (srcWidth * (row * 2 + 1) + srcWidth - 1) * bpt,
+                   srcPtr + (srcWidth * (row * 2 + 2) + srcWidth - 1) * bpt,
+                   1, dstPtr + (dstWidth * row + 1 + dstWidth - 1) * bpt);
+         }
+      }
+   }
+}
+
+
+static void
+make_3d_mipmap(const struct gl_texture_format *format, GLint border,
+               GLint srcWidth, GLint srcHeight, GLint srcDepth,
+               const GLubyte *srcPtr,
+               GLint dstWidth, GLint dstHeight, GLint dstDepth,
+               GLubyte *dstPtr)
 {
-   struct gl_texture_unit *texUnit;
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
+   const GLint bpt = format->TexelBytes;
+   const GLint srcWidthNB = srcWidth - 2 * border;  /* sizes w/out border */
+   const GLint srcDepthNB = srcDepth - 2 * border;
+   const GLint dstWidthNB = dstWidth - 2 * border;
+   const GLint dstHeightNB = dstHeight - 2 * border;
+   const GLint dstDepthNB = dstDepth - 2 * border;
+   GLvoid *tmpRowA, *tmpRowB;
+   GLint img, row;
+   GLint bytesPerSrcImage, bytesPerDstImage;
+   GLint bytesPerSrcRow, bytesPerDstRow;
+   GLint srcImageOffset, srcRowOffset;
+
+   (void) srcDepthNB; /* silence warnings */
+
+   /* Need two temporary row buffers */
+   tmpRowA = MALLOC(srcWidth * bpt);
+   if (!tmpRowA)
+      return;
+   tmpRowB = MALLOC(srcWidth * bpt);
+   if (!tmpRowB) {
+      FREE(tmpRowA);
+      return;
+   }
+
+   bytesPerSrcImage = srcWidth * srcHeight * bpt;
+   bytesPerDstImage = dstWidth * dstHeight * bpt;
 
-   (void) format;
-   (void) type;
+   bytesPerSrcRow = srcWidth * bpt;
+   bytesPerDstRow = dstWidth * bpt;
 
-   texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
-   texObj = _mesa_select_tex_object(ctx, texUnit, target);
-   texImage = _mesa_select_tex_image(ctx, texUnit, target, level);
+   /* Offset between adjacent src images to be averaged together */
+   srcImageOffset = (srcDepth == dstDepth) ? 0 : bytesPerSrcImage;
 
-   /* We always pass.
-    * The core Mesa code will have already tested the image size, etc.
-    * Drivers may have more stringent texture limits to enforce and will
-    * have to override this function.
+   /* Offset between adjacent src rows to be averaged together */
+   srcRowOffset = (srcHeight == dstHeight) ? 0 : srcWidth * bpt;
+
+   /*
+    * Need to average together up to 8 src pixels for each dest pixel.
+    * Break that down into 3 operations:
+    *   1. take two rows from source image and average them together.
+    *   2. take two rows from next source image and average them together.
+    *   3. take the two averaged rows and average them for the final dst row.
     */
-   /* choose the texture format */
-   assert(ctx->Driver.ChooseTextureFormat);
-   texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
-                                          internalFormat, format, type);
-   assert(texImage->TexFormat);
 
-   return GL_TRUE;
+   /*
+   _mesa_printf("mip3d %d x %d x %d  ->  %d x %d x %d\n",
+          srcWidth, srcHeight, srcDepth, dstWidth, dstHeight, dstDepth);
+   */
+
+   for (img = 0; img < dstDepthNB; img++) {
+      /* first source image pointer, skipping border */
+      const GLubyte *imgSrcA = srcPtr
+         + (bytesPerSrcImage + bytesPerSrcRow + border) * bpt * border
+         + img * (bytesPerSrcImage + srcImageOffset);
+      /* second source image pointer, skipping border */
+      const GLubyte *imgSrcB = imgSrcA + srcImageOffset;
+      /* address of the dest image, skipping border */
+      GLubyte *imgDst = dstPtr
+         + (bytesPerDstImage + bytesPerDstRow + border) * bpt * border
+         + img * bytesPerDstImage;
+
+      /* setup the four source row pointers and the dest row pointer */
+      const GLubyte *srcImgARowA = imgSrcA;
+      const GLubyte *srcImgARowB = imgSrcA + srcRowOffset;
+      const GLubyte *srcImgBRowA = imgSrcB;
+      const GLubyte *srcImgBRowB = imgSrcB + srcRowOffset;
+      GLubyte *dstImgRow = imgDst;
+
+      for (row = 0; row < dstHeightNB; row++) {
+         /* Average together two rows from first src image */
+         do_row(format, srcWidthNB, srcImgARowA, srcImgARowB,
+                srcWidthNB, tmpRowA);
+         /* Average together two rows from second src image */
+         do_row(format, srcWidthNB, srcImgBRowA, srcImgBRowB,
+                srcWidthNB, tmpRowB);
+         /* Average together the temp rows to make the final row */
+         do_row(format, srcWidthNB, tmpRowA, tmpRowB,
+                dstWidthNB, dstImgRow);
+         /* advance to next rows */
+         srcImgARowA += bytesPerSrcRow + srcRowOffset;
+         srcImgARowB += bytesPerSrcRow + srcRowOffset;
+         srcImgBRowA += bytesPerSrcRow + srcRowOffset;
+         srcImgBRowB += bytesPerSrcRow + srcRowOffset;
+         dstImgRow += bytesPerDstRow;
+      }
+   }
+
+   FREE(tmpRowA);
+   FREE(tmpRowB);
+
+   /* Luckily we can leverage the make_2d_mipmap() function here! */
+   if (border > 0) {
+      /* do front border image */
+      make_2d_mipmap(format, 1, srcWidth, srcHeight, srcPtr,
+                     dstWidth, dstHeight, dstPtr);
+      /* do back border image */
+      make_2d_mipmap(format, 1, srcWidth, srcHeight,
+                     srcPtr + bytesPerSrcImage * (srcDepth - 1),
+                     dstWidth, dstHeight,
+                     dstPtr + bytesPerDstImage * (dstDepth - 1));
+      /* do four remaining border edges that span the image slices */
+      if (srcDepth == dstDepth) {
+         /* just copy border pixels from src to dst */
+         for (img = 0; img < dstDepthNB; img++) {
+            const GLubyte *src;
+            GLubyte *dst;
+
+            /* do border along [img][row=0][col=0] */
+            src = srcPtr + (img + 1) * bytesPerSrcImage;
+            dst = dstPtr + (img + 1) * bytesPerDstImage;
+            MEMCPY(dst, src, bpt);
+
+            /* do border along [img][row=dstHeight-1][col=0] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (srcHeight - 1) * bytesPerSrcRow;
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (dstHeight - 1) * bytesPerDstRow;
+            MEMCPY(dst, src, bpt);
+
+            /* do border along [img][row=0][col=dstWidth-1] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (srcWidth - 1) * bpt;
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (dstWidth - 1) * bpt;
+            MEMCPY(dst, src, bpt);
+
+            /* do border along [img][row=dstHeight-1][col=dstWidth-1] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (bytesPerSrcImage - bpt);
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (bytesPerDstImage - bpt);
+            MEMCPY(dst, src, bpt);
+         }
+      }
+      else {
+         /* average border pixels from adjacent src image pairs */
+         ASSERT(srcDepthNB == 2 * dstDepthNB);
+         for (img = 0; img < dstDepthNB; img++) {
+            const GLubyte *src;
+            GLubyte *dst;
+
+            /* do border along [img][row=0][col=0] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage;
+            dst = dstPtr + (img + 1) * bytesPerDstImage;
+            do_row(format, 1, src, src + srcImageOffset, 1, dst);
+
+            /* do border along [img][row=dstHeight-1][col=0] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (srcHeight - 1) * bytesPerSrcRow;
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (dstHeight - 1) * bytesPerDstRow;
+            do_row(format, 1, src, src + srcImageOffset, 1, dst);
+
+            /* do border along [img][row=0][col=dstWidth-1] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (srcWidth - 1) * bpt;
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (dstWidth - 1) * bpt;
+            do_row(format, 1, src, src + srcImageOffset, 1, dst);
+
+            /* do border along [img][row=dstHeight-1][col=dstWidth-1] */
+            src = srcPtr + (img * 2 + 1) * bytesPerSrcImage
+                         + (bytesPerSrcImage - bpt);
+            dst = dstPtr + (img + 1) * bytesPerDstImage
+                         + (bytesPerDstImage - bpt);
+            do_row(format, 1, src, src + srcImageOffset, 1, dst);
+         }
+      }
+   }
+}
+
+
+/*
+ * For GL_SGIX_generate_mipmap:
+ * Generate a complete set of mipmaps from texObj's base-level image.
+ * Stop at texObj's MaxLevel or when we get to the 1x1 texture.
+ */
+void
+_mesa_generate_mipmap(GLcontext *ctx, GLenum target,
+                      const struct gl_texture_unit *texUnit,
+                      struct gl_texture_object *texObj)
+{
+   const struct gl_texture_image *srcImage;
+   const struct gl_texture_format *convertFormat;
+   const GLubyte *srcData = NULL;
+   GLubyte *dstData = NULL;
+   GLint level, maxLevels;
+
+   ASSERT(texObj);
+   srcImage = texObj->Image[texObj->BaseLevel];
+   ASSERT(srcImage);
+
+   maxLevels = _mesa_max_texture_levels(ctx, texObj->Target);
+   ASSERT(maxLevels > 0);  /* bad target */
+
+   /* Find convertFormat - the format that do_row() will process */
+   if (srcImage->IsCompressed) {
+      /* setup for compressed textures */
+      GLuint row;
+      GLint  components, size;
+      GLchan *dst;
+
+      assert(texObj->Target == GL_TEXTURE_2D);
+
+      if (srcImage->Format == GL_RGB) {
+         convertFormat = &_mesa_texformat_rgb;
+         components = 3;
+      }
+      else if (srcImage->Format == GL_RGBA) {
+         convertFormat = &_mesa_texformat_rgba;
+         components = 4;
+      }
+      else {
+         _mesa_problem(ctx, "bad srcImage->Format in _mesa_generate_mipmaps");
+         return;
+      }
+
+      /* allocate storage for uncompressed GL_RGB or GL_RGBA images */
+      size = _mesa_bytes_per_pixel(srcImage->Format, CHAN_TYPE)
+         * srcImage->Width * srcImage->Height * srcImage->Depth + 20;
+      /* 20 extra bytes, just be safe when calling last FetchTexel */
+      srcData = (GLubyte *) MALLOC(size);
+      if (!srcData) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps");
+         return;
+      }
+      dstData = (GLubyte *) MALLOC(size / 2);  /* 1/4 would probably be OK */
+      if (!dstData) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps");
+         FREE((void *) srcData);
+         return;
+      }
+
+      /* decompress base image here */
+      dst = (GLchan *) srcData;
+      for (row = 0; row < srcImage->Height; row++) {
+         GLuint col;
+         for (col = 0; col < srcImage->Width; col++) {
+            (*srcImage->FetchTexel)(srcImage, col, row, 0, (GLvoid *) dst);
+            dst += components;
+         }
+      }
+   }
+   else {
+      /* uncompressed */
+      convertFormat = srcImage->TexFormat;
+   }
+
+   for (level = texObj->BaseLevel; level < texObj->MaxLevel
+           && level < maxLevels - 1; level++) {
+      /* generate image[level+1] from image[level] */
+      const struct gl_texture_image *srcImage;
+      struct gl_texture_image *dstImage;
+      GLint srcWidth, srcHeight, srcDepth;
+      GLint dstWidth, dstHeight, dstDepth;
+      GLint border, bytesPerTexel;
+
+      /* get src image parameters */
+      srcImage = _mesa_select_tex_image(ctx, texUnit, target, level);
+      ASSERT(srcImage);
+      srcWidth = srcImage->Width;
+      srcHeight = srcImage->Height;
+      srcDepth = srcImage->Depth;
+      border = srcImage->Border;
+
+      /* compute next (level+1) image size */
+      if (srcWidth - 2 * border > 1) {
+         dstWidth = (srcWidth - 2 * border) / 2 + 2 * border;
+      }
+      else {
+         dstWidth = srcWidth; /* can't go smaller */
+      }
+      if (srcHeight - 2 * border > 1) {
+         dstHeight = (srcHeight - 2 * border) / 2 + 2 * border;
+      }
+      else {
+         dstHeight = srcHeight; /* can't go smaller */
+      }
+      if (srcDepth - 2 * border > 1) {
+         dstDepth = (srcDepth - 2 * border) / 2 + 2 * border;
+      }
+      else {
+         dstDepth = srcDepth; /* can't go smaller */
+      }
+
+      if (dstWidth == srcWidth &&
+          dstHeight == srcHeight &&
+          dstDepth == srcDepth) {
+         /* all done */
+         if (srcImage->IsCompressed) {
+            FREE((void *) srcData);
+            FREE(dstData);
+         }
+         return;
+      }
+
+      /* get dest gl_texture_image */
+      dstImage = _mesa_get_tex_image(ctx, texUnit, target, level + 1);
+      if (!dstImage) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps");
+         return;
+      }
+
+      /* Free old image data */
+      if (dstImage->Data)
+         MESA_PBUFFER_FREE(dstImage->Data);
+
+      /* initialize new image */
+      _mesa_init_teximage_fields(ctx, target, dstImage, dstWidth, dstHeight,
+                                 dstDepth, border, srcImage->IntFormat);
+      dstImage->DriverData = NULL;
+      dstImage->TexFormat = srcImage->TexFormat;
+      dstImage->FetchTexel = srcImage->FetchTexel;
+      ASSERT(dstImage->TexFormat);
+      ASSERT(dstImage->FetchTexel);
+
+      /* Alloc new teximage data buffer.
+       * Setup src and dest data pointers.
+       */
+      if (dstImage->IsCompressed) {
+         ASSERT(dstImage->CompressedSize > 0); /* set by init_teximage_fields*/
+         dstImage->Data = MESA_PBUFFER_ALLOC(dstImage->CompressedSize);
+         if (!dstImage->Data) {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps");
+            return;
+         }
+         /* srcData and dstData are already set */
+         ASSERT(srcData);
+         ASSERT(dstData);
+      }
+      else {
+         bytesPerTexel = srcImage->TexFormat->TexelBytes;
+         ASSERT(dstWidth * dstHeight * dstDepth * bytesPerTexel > 0);
+         dstImage->Data = MESA_PBUFFER_ALLOC(dstWidth * dstHeight * dstDepth
+                                             * bytesPerTexel);
+         if (!dstImage->Data) {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps");
+            return;
+         }
+         srcData = (const GLubyte *) srcImage->Data;
+         dstData = (GLubyte *) dstImage->Data;
+      }
+
+      /*
+       * We use simple 2x2 averaging to compute the next mipmap level.
+       */
+      switch (target) {
+         case GL_TEXTURE_1D:
+            make_1d_mipmap(convertFormat, border,
+                           srcWidth, srcData,
+                           dstWidth, dstData);
+            break;
+         case GL_TEXTURE_2D:
+         case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
+         case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
+         case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
+         case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
+         case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
+         case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
+            make_2d_mipmap(convertFormat, border,
+                           srcWidth, srcHeight, srcData,
+                           dstWidth, dstHeight, dstData);
+            break;
+         case GL_TEXTURE_3D:
+            make_3d_mipmap(convertFormat, border,
+                           srcWidth, srcHeight, srcDepth, srcData,
+                           dstWidth, dstHeight, dstDepth, dstData);
+            break;
+         case GL_TEXTURE_RECTANGLE_NV:
+            /* no mipmaps, do nothing */
+            break;
+         default:
+            _mesa_problem(ctx, "bad dimensions in _mesa_generate_mipmaps");
+            return;
+      }
+
+      if (dstImage->IsCompressed) {
+         GLubyte *temp;
+         /* compress image from dstData into dstImage->Data */
+         const GLenum srcFormat = convertFormat->BaseFormat;
+         GLint dstRowStride = _mesa_compressed_row_stride(srcImage->IntFormat,
+                                                          dstWidth);
+         ASSERT(srcFormat == GL_RGB || srcFormat == GL_RGBA);
+         _mesa_compress_teximage(ctx,
+                                 dstWidth, dstHeight, /* size */
+                                 srcFormat,           /* source format */
+                (const GLchan *) dstData,             /* source buffer */
+                                 dstWidth,            /* source row stride */
+                                 dstImage->TexFormat, /* dest format */
+                      (GLubyte*) dstImage->Data,      /* dest buffer */
+                                 dstRowStride );      /* dest row stride */
+
+         /* swap src and dest pointers */
+         temp = (GLubyte *) srcData;
+         srcData = dstData;
+         dstData = temp;
+      }
+
+   } /* loop over mipmap levels */
 }