mesa: Allow glTexImage2D with a depth component cube map.
[mesa.git] / src / mesa / main / teximage.c
index 2e9410f27175de6416038d018ee145f08133a8fe..8a002b6755c6c1d742ea22e5d2e3d86ca8b3a868 100644 (file)
@@ -1,6 +1,5 @@
 /*
- * mesa 3-D graphics library
- * Version:  7.6
+ * Mesa 3-D graphics library
  *
  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
@@ -40,9 +39,9 @@
 #include "image.h"
 #include "imports.h"
 #include "macros.h"
+#include "mfeatures.h"
 #include "state.h"
 #include "texcompress.h"
-#include "texfetch.h"
 #include "teximage.h"
 #include "texstate.h"
 #include "texpal.h"
  * In particular, we care about pixel transfer state and buffer state
  * (such as glReadBuffer to make sure we read from the right renderbuffer).
  */
-#define NEW_COPY_TEX_STATE (_MESA_NEW_TRANSFER_STATE | \
-                            _NEW_BUFFERS | \
-                            _NEW_PIXEL)
-
-
-
-/**
- * We allocate texture memory on 512-byte boundaries so we can use MMX/SSE
- * elsewhere.
- */
-void *
-_mesa_alloc_texmemory(GLsizei bytes)
-{
-   return _mesa_align_malloc(bytes, 512);
-}
-
-
-/**
- * Free texture memory allocated with _mesa_alloc_texmemory()
- */
-void
-_mesa_free_texmemory(void *m)
-{
-   _mesa_align_free(m);
-}
-
-
-/*
- * Compute floor(log_base_2(n)).
- * If n < 0 return -1.
- */
-static int
-logbase2( int n )
-{
-   GLint i = 1;
-   GLint log2 = 0;
-
-   if (n < 0)
-      return -1;
-
-   if (n == 0)
-      return 0;
-
-   while ( n > i ) {
-      i *= 2;
-      log2++;
-   }
-   if (i != n) {
-      return log2 - 1;
-   }
-   else {
-      return log2;
-   }
-}
+#define NEW_COPY_TEX_STATE (_NEW_BUFFERS | _NEW_PIXEL)
 
 
 
@@ -182,16 +128,12 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
          ; /* fallthrough */
    }
 
-   if (ctx->Extensions.EXT_paletted_texture) {
+   /* GL_BGRA can be an internal format *only* in OpenGL ES (1.x or 2.0).
+    */
+   if (ctx->API != API_OPENGL) {
       switch (internalFormat) {
-         case GL_COLOR_INDEX:
-         case GL_COLOR_INDEX1_EXT:
-         case GL_COLOR_INDEX2_EXT:
-         case GL_COLOR_INDEX4_EXT:
-         case GL_COLOR_INDEX8_EXT:
-         case GL_COLOR_INDEX12_EXT:
-         case GL_COLOR_INDEX16_EXT:
-            return GL_COLOR_INDEX;
+         case GL_BGRA:
+            return GL_RGBA;
          default:
             ; /* fallthrough */
       }
@@ -303,11 +245,40 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
       }
    }
 
-   if (ctx->Extensions.MESA_texture_signed_rgba) {
+   if (ctx->Extensions.EXT_texture_snorm) {
       switch (internalFormat) {
+         case GL_RED_SNORM:
+         case GL_R8_SNORM:
+         case GL_R16_SNORM:
+            return GL_RED;
+         case GL_RG_SNORM:
+         case GL_RG8_SNORM:
+         case GL_RG16_SNORM:
+            return GL_RG;
+         case GL_RGB_SNORM:
+         case GL_RGB8_SNORM:
+         case GL_RGB16_SNORM:
+            return GL_RGB;
          case GL_RGBA_SNORM:
          case GL_RGBA8_SNORM:
+         case GL_RGBA16_SNORM:
             return GL_RGBA;
+         case GL_ALPHA_SNORM:
+         case GL_ALPHA8_SNORM:
+         case GL_ALPHA16_SNORM:
+            return GL_ALPHA;
+         case GL_LUMINANCE_SNORM:
+         case GL_LUMINANCE8_SNORM:
+         case GL_LUMINANCE16_SNORM:
+            return GL_LUMINANCE;
+         case GL_LUMINANCE_ALPHA_SNORM:
+         case GL_LUMINANCE8_ALPHA8_SNORM:
+         case GL_LUMINANCE16_ALPHA16_SNORM:
+            return GL_LUMINANCE_ALPHA;
+         case GL_INTENSITY_SNORM:
+         case GL_INTENSITY8_SNORM:
+         case GL_INTENSITY16_SNORM:
+            return GL_INTENSITY;
          default:
             ; /* fallthrough */
       }
@@ -340,11 +311,11 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
          return GL_RGBA;
       case GL_SLUMINANCE_ALPHA_EXT:
       case GL_SLUMINANCE8_ALPHA8_EXT:
-      case GL_COMPRESSED_SLUMINANCE_EXT:
       case GL_COMPRESSED_SLUMINANCE_ALPHA_EXT:
          return GL_LUMINANCE_ALPHA;
       case GL_SLUMINANCE_EXT:
       case GL_SLUMINANCE8_EXT:
+      case GL_COMPRESSED_SLUMINANCE_EXT:
          return GL_LUMINANCE;
       default:
          ; /* fallthrough */
@@ -360,6 +331,7 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
       case GL_RGBA8I_EXT:
       case GL_RGBA16I_EXT:
       case GL_RGBA32I_EXT:
+      case GL_RGB10_A2UI:
          return GL_RGBA;
       case GL_RGB8UI_EXT:
       case GL_RGB16UI_EXT:
@@ -499,6 +471,56 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
       }
    }
 
+   if (ctx->Extensions.EXT_texture_compression_latc) {
+      switch (internalFormat) {
+      case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
+      case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
+         return GL_LUMINANCE;
+      case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
+      case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
+         return GL_LUMINANCE_ALPHA;
+      default:
+         ; /* fallthrough */
+      }
+   }
+
+   if (ctx->Extensions.ATI_texture_compression_3dc) {
+      switch (internalFormat) {
+      case GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI:
+         return GL_LUMINANCE_ALPHA;
+      default:
+         ; /* fallthrough */
+      }
+   }
+
+   if (ctx->Extensions.OES_compressed_ETC1_RGB8_texture) {
+      switch (internalFormat) {
+      case GL_ETC1_RGB8_OES:
+         return GL_RGB;
+      default:
+         ; /* fallthrough */
+      }
+   }
+
+   if (ctx->API == API_OPENGLES) {
+      switch (internalFormat) {
+      case GL_PALETTE4_RGB8_OES:
+      case GL_PALETTE4_R5_G6_B5_OES:
+      case GL_PALETTE8_RGB8_OES:
+      case GL_PALETTE8_R5_G6_B5_OES:
+        return GL_RGB;
+      case GL_PALETTE4_RGBA8_OES:
+      case GL_PALETTE8_RGB5_A1_OES:
+      case GL_PALETTE4_RGBA4_OES:
+      case GL_PALETTE4_RGB5_A1_OES:
+      case GL_PALETTE8_RGBA8_OES:
+      case GL_PALETTE8_RGBA4_OES:
+        return GL_RGBA;
+      default:
+         ; /* fallthrough */
+      }
+   }
+
    return -1; /* error */
 }
 
@@ -510,8 +532,7 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
 GLuint
 _mesa_tex_target_to_face(GLenum target)
 {
-   if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-       target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)
+   if (_mesa_is_cube_face(target))
       return (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
    else
       return 0;
@@ -527,8 +548,6 @@ _mesa_tex_target_to_face(GLenum target)
  * \param target texture target.
  * \param level image level.
  * \param texImage texture image.
- * 
- * This was basically prompted by the introduction of cube maps.
  */
 void
 _mesa_set_tex_image(struct gl_texture_object *tObj,
@@ -539,12 +558,15 @@ _mesa_set_tex_image(struct gl_texture_object *tObj,
 
    ASSERT(tObj);
    ASSERT(texImage);
-   ASSERT(target != GL_TEXTURE_RECTANGLE_NV || level == 0);
+   if (target == GL_TEXTURE_RECTANGLE_NV || target == GL_TEXTURE_EXTERNAL_OES)
+      assert(level == 0);
 
    tObj->Image[face][level] = texImage;
 
    /* Set the 'back' pointer */
    texImage->TexObject = tObj;
+   texImage->Level = level;
+   texImage->Face = face;
 }
 
 
@@ -566,48 +588,22 @@ _mesa_new_texture_image( struct gl_context *ctx )
 
 
 /**
- * Free texture image data.
- * This function is a fallback called via ctx->Driver.FreeTexImageData().
- *
- * \param texImage texture image.
- *
- * Free the texture image data if it's not marked as client data.
- */
-void
-_mesa_free_texture_image_data(struct gl_context *ctx,
-                              struct gl_texture_image *texImage)
-{
-   (void) ctx;
-
-   if (texImage->Data && !texImage->IsClientData) {
-      /* free the old texture data */
-      _mesa_free_texmemory(texImage->Data);
-   }
-
-   texImage->Data = NULL;
-}
-
-
-/**
- * Free texture image.
+ * Free a gl_texture_image and associated data.
+ * This function is a fallback called via ctx->Driver.DeleteTextureImage().
  *
  * \param texImage texture image.
  *
  * Free the texture image structure and the associated image data.
  */
 void
-_mesa_delete_texture_image( struct gl_context *ctx, struct gl_texture_image *texImage )
+_mesa_delete_texture_image(struct gl_context *ctx,
+                           struct gl_texture_image *texImage)
 {
    /* Free texImage->Data and/or any other driver-specific texture
     * image storage.
     */
-   ASSERT(ctx->Driver.FreeTexImageData);
-   ctx->Driver.FreeTexImageData( ctx, texImage );
-
-   ASSERT(texImage->Data == NULL);
-   if (texImage->ImageOffsets)
-      free(texImage->ImageOffsets);
-   free(texImage);
+   ASSERT(ctx->Driver.FreeTextureImageBuffer);
+   ctx->Driver.FreeTextureImageBuffer( ctx, texImage );
 }
 
 
@@ -621,8 +617,11 @@ _mesa_delete_texture_image( struct gl_context *ctx, struct gl_texture_image *tex
 GLboolean
 _mesa_is_proxy_texture(GLenum target)
 {
-   /* NUM_TEXTURE_TARGETS should match number of terms below */
-   assert(NUM_TEXTURE_TARGETS == 7);
+   /*
+    * NUM_TEXTURE_TARGETS should match number of terms below, except there's no
+    * proxy for GL_TEXTURE_BUFFER and GL_TEXTURE_EXTERNAL_OES.
+    */
+   assert(NUM_TEXTURE_TARGETS == 7 + 2);
 
    return (target == GL_PROXY_TEXTURE_1D ||
            target == GL_PROXY_TEXTURE_2D ||
@@ -635,20 +634,64 @@ _mesa_is_proxy_texture(GLenum target)
 
 
 /**
- * Get the texture object that corresponds to the target of the given texture unit.
+ * Return the proxy target which corresponds to the given texture target
+ */
+static GLenum
+get_proxy_target(GLenum target)
+{
+   switch (target) {
+   case GL_TEXTURE_1D:
+   case GL_PROXY_TEXTURE_1D:
+      return GL_PROXY_TEXTURE_1D;
+   case GL_TEXTURE_2D:
+   case GL_PROXY_TEXTURE_2D:
+      return GL_PROXY_TEXTURE_2D;
+   case GL_TEXTURE_3D:
+   case GL_PROXY_TEXTURE_3D:
+      return GL_PROXY_TEXTURE_3D;
+   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:
+   case GL_TEXTURE_CUBE_MAP_ARB:
+   case GL_PROXY_TEXTURE_CUBE_MAP_ARB:
+      return GL_PROXY_TEXTURE_CUBE_MAP_ARB;
+   case GL_TEXTURE_RECTANGLE_NV:
+   case GL_PROXY_TEXTURE_RECTANGLE_NV:
+      return GL_PROXY_TEXTURE_RECTANGLE_NV;
+   case GL_TEXTURE_1D_ARRAY_EXT:
+   case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
+      return GL_PROXY_TEXTURE_1D_ARRAY_EXT;
+   case GL_TEXTURE_2D_ARRAY_EXT:
+   case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
+      return GL_PROXY_TEXTURE_2D_ARRAY_EXT;
+   default:
+      _mesa_problem(NULL, "unexpected target in get_proxy_target()");
+      return 0;
+   }
+}
+
+
+/**
+ * Get the texture object that corresponds to the target of the given
+ * texture unit.  The target should have already been checked for validity.
  *
  * \param ctx GL context.
  * \param texUnit texture unit.
  * \param target texture target.
  *
  * \return pointer to the texture object on success, or NULL on failure.
- * 
- * \sa gl_texture_unit.
  */
 struct gl_texture_object *
-_mesa_select_tex_object(struct gl_context *ctx, const struct gl_texture_unit *texUnit,
+_mesa_select_tex_object(struct gl_context *ctx,
+                        const struct gl_texture_unit *texUnit,
                         GLenum target)
 {
+   const GLboolean arrayTex = (ctx->Extensions.MESA_texture_array ||
+                               ctx->Extensions.EXT_texture_array);
+
    switch (target) {
       case GL_TEXTURE_1D:
          return texUnit->CurrentTex[TEXTURE_1D_INDEX];
@@ -681,17 +724,19 @@ _mesa_select_tex_object(struct gl_context *ctx, const struct gl_texture_unit *te
          return ctx->Extensions.NV_texture_rectangle
                 ? ctx->Texture.ProxyTex[TEXTURE_RECT_INDEX] : NULL;
       case GL_TEXTURE_1D_ARRAY_EXT:
-         return ctx->Extensions.MESA_texture_array
-                ? texUnit->CurrentTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+         return arrayTex ? texUnit->CurrentTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
       case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
-         return ctx->Extensions.MESA_texture_array
-                ? ctx->Texture.ProxyTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+         return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
       case GL_TEXTURE_2D_ARRAY_EXT:
-         return ctx->Extensions.MESA_texture_array
-                ? texUnit->CurrentTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+         return arrayTex ? texUnit->CurrentTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
       case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
-         return ctx->Extensions.MESA_texture_array
-                ? ctx->Texture.ProxyTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+         return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+      case GL_TEXTURE_BUFFER:
+         return ctx->Extensions.ARB_texture_buffer_object
+            ? texUnit->CurrentTex[TEXTURE_BUFFER_INDEX] : NULL;
+      case GL_TEXTURE_EXTERNAL_OES:
+         return ctx->Extensions.OES_EGL_image_external
+            ? texUnit->CurrentTex[TEXTURE_EXTERNAL_INDEX] : NULL;
       default:
          _mesa_problem(NULL, "bad target in _mesa_select_tex_object()");
          return NULL;
@@ -723,7 +768,8 @@ _mesa_get_current_tex_object(struct gl_context *ctx, GLenum target)
  * \return pointer to the texture image structure, or NULL on failure.
  */
 struct gl_texture_image *
-_mesa_select_tex_image(struct gl_context *ctx, const struct gl_texture_object *texObj,
+_mesa_select_tex_image(struct gl_context *ctx,
+                       const struct gl_texture_object *texObj,
                       GLenum target, GLint level)
 {
    const GLuint face = _mesa_tex_target_to_face(target);
@@ -875,14 +921,60 @@ _mesa_max_texture_levels(struct gl_context *ctx, GLenum target)
    case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
    case GL_TEXTURE_2D_ARRAY_EXT:
    case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
-      return ctx->Extensions.MESA_texture_array
+      return (ctx->Extensions.MESA_texture_array ||
+              ctx->Extensions.EXT_texture_array)
          ? ctx->Const.MaxTextureLevels : 0;
+   case GL_TEXTURE_BUFFER:
+   case GL_TEXTURE_EXTERNAL_OES:
+      /* fall-through */
    default:
       return 0; /* bad target */
    }
 }
 
 
+/**
+ * Return number of dimensions per mipmap level for the given texture target.
+ */
+GLint
+_mesa_get_texture_dimensions(GLenum target)
+{
+   switch (target) {
+   case GL_TEXTURE_1D:
+   case GL_PROXY_TEXTURE_1D:
+      return 1;
+   case GL_TEXTURE_2D:
+   case GL_TEXTURE_RECTANGLE:
+   case GL_TEXTURE_CUBE_MAP:
+   case GL_PROXY_TEXTURE_2D:
+   case GL_PROXY_TEXTURE_RECTANGLE:
+   case GL_PROXY_TEXTURE_CUBE_MAP:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+   case GL_TEXTURE_1D_ARRAY:
+   case GL_PROXY_TEXTURE_1D_ARRAY:
+   case GL_TEXTURE_EXTERNAL_OES:
+      return 2;
+   case GL_TEXTURE_3D:
+   case GL_PROXY_TEXTURE_3D:
+   case GL_TEXTURE_2D_ARRAY:
+   case GL_PROXY_TEXTURE_2D_ARRAY:
+      return 3;
+   case GL_TEXTURE_BUFFER:
+      /* fall-through */
+   default:
+      _mesa_problem(NULL, "invalid target 0x%x in get_texture_dimensions()",
+                    target);
+      return 2;
+   }
+}
+
+
+
 
 #if 000 /* not used anymore */
 /*
@@ -939,15 +1031,8 @@ make_null_texture(GLint width, GLint height, GLint depth, GLenum format)
 
 
 /**
- * Reset the fields of a gl_texture_image struct to zero.
- * 
- * \param img texture image structure.
- *
- * This is called when a proxy texture test fails, we set all the
- * image members (except DriverData) to zero.
- * It's also used in glTexImage[123]D as a safeguard to be sure all
- * required fields get initialized properly by the Driver.TexImage[123]D
- * functions.
+ * Set the size and format-related fields of a gl_texture_image struct
+ * to zero.  This is used when a proxy texture test fails.
  */
 static void
 clear_teximage_fields(struct gl_texture_image *img)
@@ -959,21 +1044,13 @@ clear_teximage_fields(struct gl_texture_image *img)
    img->Width = 0;
    img->Height = 0;
    img->Depth = 0;
-   img->RowStride = 0;
-   if (img->ImageOffsets) {
-      free(img->ImageOffsets);
-      img->ImageOffsets = NULL;
-   }
    img->Width2 = 0;
    img->Height2 = 0;
    img->Depth2 = 0;
    img->WidthLog2 = 0;
    img->HeightLog2 = 0;
    img->DepthLog2 = 0;
-   img->Data = NULL;
    img->TexFormat = MESA_FORMAT_NONE;
-   img->FetchTexelc = NULL;
-   img->FetchTexelf = NULL;
 }
 
 
@@ -988,6 +1065,7 @@ clear_teximage_fields(struct gl_texture_image *img)
  * \param depth image depth.
  * \param border image border.
  * \param internalFormat internal format.
+ * \param format  the actual hardware format (one of MESA_FORMAT_*)
  *
  * Fills in the fields of \p img with the given information.
  * Note: width, height and depth include the border.
@@ -996,10 +1074,9 @@ void
 _mesa_init_teximage_fields(struct gl_context *ctx, GLenum target,
                            struct gl_texture_image *img,
                            GLsizei width, GLsizei height, GLsizei depth,
-                           GLint border, GLenum internalFormat)
+                           GLint border, GLenum internalFormat,
+                           gl_format format)
 {
-   GLint i;
-
    ASSERT(img);
    ASSERT(width >= 0);
    ASSERT(height >= 0);
@@ -1014,7 +1091,7 @@ _mesa_init_teximage_fields(struct gl_context *ctx, GLenum target,
    img->Depth = depth;
 
    img->Width2 = width - 2 * border;   /* == 1 << img->WidthLog2; */
-   img->WidthLog2 = logbase2(img->Width2);
+   img->WidthLog2 = _mesa_logbase2(img->Width2);
 
    if (height == 1) { /* 1-D texture */
       img->Height2 = 1;
@@ -1022,7 +1099,7 @@ _mesa_init_teximage_fields(struct gl_context *ctx, GLenum target,
    }
    else {
       img->Height2 = height - 2 * border; /* == 1 << img->HeightLog2; */
-      img->HeightLog2 = logbase2(img->Height2);
+      img->HeightLog2 = _mesa_logbase2(img->Height2);
    }
 
    if (depth == 1) {  /* 2-D texture */
@@ -1031,46 +1108,12 @@ _mesa_init_teximage_fields(struct gl_context *ctx, GLenum target,
    }
    else {
       img->Depth2 = depth - 2 * border;   /* == 1 << img->DepthLog2; */
-      img->DepthLog2 = logbase2(img->Depth2);
+      img->DepthLog2 = _mesa_logbase2(img->Depth2);
    }
 
    img->MaxLog2 = MAX2(img->WidthLog2, img->HeightLog2);
 
-   if ((width == 1 || _mesa_is_pow_two(img->Width2)) &&
-       (height == 1 || _mesa_is_pow_two(img->Height2)) &&
-       (depth == 1 || _mesa_is_pow_two(img->Depth2)))
-      img->_IsPowerOfTwo = GL_TRUE;
-   else
-      img->_IsPowerOfTwo = GL_FALSE;
-
-   /* RowStride and ImageOffsets[] describe how to address texels in 'Data' */
-   img->RowStride = width;
-   /* Allocate the ImageOffsets array and initialize to typical values.
-    * We allocate the array for 1D/2D textures too in order to avoid special-
-    * case code in the texstore routines.
-    */
-   if (img->ImageOffsets)
-      free(img->ImageOffsets);
-   img->ImageOffsets = (GLuint *) malloc(depth * sizeof(GLuint));
-   for (i = 0; i < depth; i++) {
-      img->ImageOffsets[i] = i * width * height;
-   }
-
-   /* Compute Width/Height/DepthScale for mipmap lod computation */
-   if (target == GL_TEXTURE_RECTANGLE_NV) {
-      /* scale = 1.0 since texture coords directly map to texels */
-      img->WidthScale = 1.0;
-      img->HeightScale = 1.0;
-      img->DepthScale = 1.0;
-   }
-   else {
-      img->WidthScale = (GLfloat) img->Width;
-      img->HeightScale = (GLfloat) img->Height;
-      img->DepthScale = (GLfloat) img->Depth;
-   }
-
-   img->FetchTexelc = NULL;
-   img->FetchTexelf = NULL;
+   img->TexFormat = format;
 }
 
 
@@ -1084,9 +1127,10 @@ _mesa_init_teximage_fields(struct gl_context *ctx, GLenum target,
  * fields are cleared so that its parent object will test incomplete.
  */
 void
-_mesa_clear_texture_image(struct gl_context *ctx, struct gl_texture_image *texImage)
+_mesa_clear_texture_image(struct gl_context *ctx,
+                          struct gl_texture_image *texImage)
 {
-   ctx->Driver.FreeTexImageData(ctx, texImage);
+   ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
    clear_teximage_fields(texImage);
 }
 
@@ -1129,94 +1173,110 @@ _mesa_test_proxy_teximage(struct gl_context *ctx, GLenum target, GLint level,
    switch (target) {
    case GL_PROXY_TEXTURE_1D:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width >0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          level >= ctx->Const.MaxTextureLevels) {
-         /* bad width or level */
+      if (width < 2 * border || width > 2 + maxSize)
+         return GL_FALSE;
+      if (level >= ctx->Const.MaxTextureLevels)
          return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_2D:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width > 0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          height < 2 * border || height > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           height > 0 && !_mesa_is_pow_two(height - 2 * border)) ||
-          level >= ctx->Const.MaxTextureLevels) {
-         /* bad width or height or level */
+      if (width < 2 * border || width > 2 + maxSize)
          return GL_FALSE;
+      if (height < 2 * border || height > 2 + maxSize)
+         return GL_FALSE;
+      if (level >= ctx->Const.MaxTextureLevels)
+         return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
+         if (height > 0 && !_mesa_is_pow_two(height - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_3D:
       maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width > 0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          height < 2 * border || height > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           height > 0 && !_mesa_is_pow_two(height - 2 * border)) ||
-          depth < 2 * border || depth > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           depth > 0 && !_mesa_is_pow_two(depth - 2 * border)) ||
-          level >= ctx->Const.Max3DTextureLevels) {
-         /* bad width or height or depth or level */
+      if (width < 2 * border || width > 2 + maxSize)
          return GL_FALSE;
+      if (height < 2 * border || height > 2 + maxSize)
+         return GL_FALSE;
+      if (depth < 2 * border || depth > 2 + maxSize)
+         return GL_FALSE;
+      if (level >= ctx->Const.Max3DTextureLevels)
+         return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
+         if (height > 0 && !_mesa_is_pow_two(height - 2 * border))
+            return GL_FALSE;
+         if (depth > 0 && !_mesa_is_pow_two(depth - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_RECTANGLE_NV:
-      if (width < 0 || width > ctx->Const.MaxTextureRectSize ||
-          height < 0 || height > ctx->Const.MaxTextureRectSize ||
-          level != 0) {
-         /* bad width or height or level */
+      maxSize = ctx->Const.MaxTextureRectSize;
+      if (width < 0 || width > maxSize)
+         return GL_FALSE;
+      if (height < 0 || height > maxSize)
+         return GL_FALSE;
+      if (level != 0)
          return GL_FALSE;
-      }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_CUBE_MAP_ARB:
       maxSize = 1 << (ctx->Const.MaxCubeTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width > 0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          height < 2 * border || height > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           height > 0 && !_mesa_is_pow_two(height - 2 * border)) ||
-          level >= ctx->Const.MaxCubeTextureLevels) {
-         /* bad width or height */
+      if (width < 2 * border || width > 2 + maxSize)
+         return GL_FALSE;
+      if (height < 2 * border || height > 2 + maxSize)
+         return GL_FALSE;
+      if (level >= ctx->Const.MaxCubeTextureLevels)
          return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
+         if (height > 0 && !_mesa_is_pow_two(height - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width > 0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          level >= ctx->Const.MaxTextureLevels) {
-         /* bad width or level */
+      if (width < 2 * border || width > 2 + maxSize)
          return GL_FALSE;
-      }
-
-      if (height < 1 || height > ctx->Const.MaxArrayTextureLayers) {
+      if (height < 1 || height > ctx->Const.MaxArrayTextureLayers)
+         return GL_FALSE;
+      if (level >= ctx->Const.MaxTextureLevels)
          return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
-      if (width < 2 * border || width > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           width > 0 && !_mesa_is_pow_two(width - 2 * border)) ||
-          height < 2 * border || height > 2 + maxSize ||
-          (!ctx->Extensions.ARB_texture_non_power_of_two &&
-           height > 0 && !_mesa_is_pow_two(height - 2 * border)) ||
-          level >= ctx->Const.MaxTextureLevels) {
-         /* bad width or height or level */
+      if (width < 2 * border || width > 2 + maxSize)
          return GL_FALSE;
-      }
-      if (depth < 1 || depth > ctx->Const.MaxArrayTextureLayers) {
+      if (height < 2 * border || height > 2 + maxSize)
+         return GL_FALSE;
+      if (depth < 1 || depth > ctx->Const.MaxArrayTextureLayers)
          return GL_FALSE;
+      if (level >= ctx->Const.MaxTextureLevels)
+         return GL_FALSE;
+      if (!ctx->Extensions.ARB_texture_non_power_of_two) {
+         if (width > 0 && !_mesa_is_pow_two(width - 2 * border))
+            return GL_FALSE;
+         if (height > 0 && !_mesa_is_pow_two(height - 2 * border))
+            return GL_FALSE;
       }
       return GL_TRUE;
+
    default:
       _mesa_problem(ctx, "Invalid target in _mesa_test_proxy_teximage");
       return GL_FALSE;
@@ -1225,15 +1285,51 @@ _mesa_test_proxy_teximage(struct gl_context *ctx, GLenum target, GLint level,
 
 
 /**
- * Helper function to determine whether a target supports compressed textures
+ * Check if the memory used by the texture would exceed the driver's limit.
+ * This lets us support a max 3D texture size of 8K (for example) but
+ * prevents allocating a full 8K x 8K x 8K texture.
+ * XXX this could be rolled into the proxy texture size test (above) but
+ * we don't have the actual texture internal format at that point.
+ */
+static GLboolean
+legal_texture_size(struct gl_context *ctx, gl_format format,
+                   GLint width, GLint height, GLint depth)
+{
+   uint64_t bytes = _mesa_format_image_size64(format, width, height, depth);
+   uint64_t mbytes = bytes / (1024 * 1024); /* convert to MB */
+   return mbytes <= (uint64_t) ctx->Const.MaxTextureMbytes;
+}
+
+
+/**
+ * Return true if the format is only valid for glCompressedTexImage.
+ */
+static GLboolean
+compressedteximage_only_format(const struct gl_context *ctx, GLenum format)
+{
+   switch (format) {
+   case GL_ETC1_RGB8_OES:
+      return GL_TRUE;
+   default:
+      return GL_FALSE;
+   }
+}
+
+
+/**
+ * Helper function to determine whether a target and specific compression
+ * format are supported.
  */
 static GLboolean
-target_can_be_compressed(struct gl_context *ctx, GLenum target)
+target_can_be_compressed(const struct gl_context *ctx, GLenum target,
+                         GLenum intFormat)
 {
+   (void) intFormat;  /* not used yet */
+
    switch (target) {
    case GL_TEXTURE_2D:
    case GL_PROXY_TEXTURE_2D:
-      return GL_TRUE;
+      return GL_TRUE; /* true for any compressed format so far */
    case GL_PROXY_TEXTURE_CUBE_MAP:
    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
@@ -1244,7 +1340,8 @@ target_can_be_compressed(struct gl_context *ctx, GLenum target)
       return ctx->Extensions.ARB_texture_cube_map;
    case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
    case GL_TEXTURE_2D_ARRAY_EXT:
-      return ctx->Extensions.MESA_texture_array;
+      return (ctx->Extensions.MESA_texture_array ||
+              ctx->Extensions.EXT_texture_array);
    default:
       return GL_FALSE;
    }      
@@ -1252,132 +1349,207 @@ target_can_be_compressed(struct gl_context *ctx, GLenum target)
 
 
 /**
- * Test the glTexImage[123]D() parameters for errors.
- * 
- * \param ctx GL context.
- * \param target texture target given by the user.
- * \param level image level given by the user.
- * \param internalFormat internal format given by the user.
- * \param format pixel data format given by the user.
- * \param type pixel data type given by the user.
- * \param dimensions texture image dimensions (must be 1, 2 or 3).
- * \param width image width given by the user.
- * \param height image height given by the user.
- * \param depth image depth given by the user.
- * \param border image border given by the user.
- * 
- * \return GL_TRUE if an error was detected, or GL_FALSE if no errors.
- *
- * Verifies each of the parameters against the constants specified in
- * __struct gl_contextRec::Const and the supported extensions, and according to the
- * OpenGL specification.
+ * Check if the given texture target value is legal for a
+ * glTexImage1/2/3D call.
  */
 static GLboolean
-texture_error_check( struct gl_context *ctx, GLenum target,
-                     GLint level, GLint internalFormat,
-                     GLenum format, GLenum type,
-                     GLuint dimensions,
-                     GLint width, GLint height,
-                     GLint depth, GLint border )
+legal_teximage_target(struct gl_context *ctx, GLuint dims, GLenum target)
 {
-   const GLboolean isProxy = _mesa_is_proxy_texture(target);
-   GLboolean sizeOK = GL_TRUE;
-   GLboolean colorFormat, indexFormat;
-   GLenum proxy_target;
-
-   /* Basic level check (more checking in ctx->Driver.TestProxyTexImage) */
-   if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(level=%d)", dimensions, level);
+   switch (dims) {
+   case 1:
+      switch (target) {
+      case GL_TEXTURE_1D:
+      case GL_PROXY_TEXTURE_1D:
+         return GL_TRUE;
+      default:
+         return GL_FALSE;
       }
-      return GL_TRUE;
-   }
-
-   /* Check border */
-   if (border < 0 || border > 1 ||
-       ((target == GL_TEXTURE_RECTANGLE_NV ||
-         target == GL_PROXY_TEXTURE_RECTANGLE_NV) && border != 0)) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(border=%d)", dimensions, border);
+   case 2:
+      switch (target) {
+      case GL_TEXTURE_2D:
+      case GL_PROXY_TEXTURE_2D:
+         return GL_TRUE;
+      case GL_PROXY_TEXTURE_CUBE_MAP:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+         return ctx->Extensions.ARB_texture_cube_map;
+      case GL_TEXTURE_RECTANGLE_NV:
+      case GL_PROXY_TEXTURE_RECTANGLE_NV:
+         return ctx->Extensions.NV_texture_rectangle;
+      case GL_TEXTURE_1D_ARRAY_EXT:
+      case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
+         return (ctx->Extensions.MESA_texture_array ||
+                 ctx->Extensions.EXT_texture_array);
+      default:
+         return GL_FALSE;
       }
-      return GL_TRUE;
-   }
-
-   if (width < 0 || height < 0 || depth < 0) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(width, height or depth < 0)", dimensions);
+   case 3:
+      switch (target) {
+      case GL_TEXTURE_3D:
+      case GL_PROXY_TEXTURE_3D:
+         return GL_TRUE;
+      case GL_TEXTURE_2D_ARRAY_EXT:
+      case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
+         return (ctx->Extensions.MESA_texture_array ||
+                 ctx->Extensions.EXT_texture_array);
+      default:
+         return GL_FALSE;
       }
-      return GL_TRUE;
+   default:
+      _mesa_problem(ctx, "invalid dims=%u in legal_teximage_target()", dims);
+      return GL_FALSE;
    }
+}
 
-   /* Check target and call ctx->Driver.TestProxyTexImage() to check the
-    * level, width, height and depth.
-    */
-   if (dimensions == 1) {
-      if (target == GL_PROXY_TEXTURE_1D || target == GL_TEXTURE_1D) {
-         proxy_target = GL_PROXY_TEXTURE_1D;
-         height = 1;
-         depth = 1;
-      }
-      else {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glTexImage1D(target)" );
+
+/**
+ * Check if the given texture target value is legal for a
+ * glTexSubImage, glCopyTexSubImage or glCopyTexImage call.
+ * The difference compared to legal_teximage_target() above is that
+ * proxy targets are not supported.
+ */
+static GLboolean
+legal_texsubimage_target(struct gl_context *ctx, GLuint dims, GLenum target)
+{
+   switch (dims) {
+   case 1:
+      return target == GL_TEXTURE_1D;
+   case 2:
+      switch (target) {
+      case GL_TEXTURE_2D:
          return GL_TRUE;
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+         return ctx->Extensions.ARB_texture_cube_map;
+      case GL_TEXTURE_RECTANGLE_NV:
+         return ctx->Extensions.NV_texture_rectangle;
+      case GL_TEXTURE_1D_ARRAY_EXT:
+         return (ctx->Extensions.MESA_texture_array ||
+                 ctx->Extensions.EXT_texture_array);
+      default:
+         return GL_FALSE;
       }
-   }
-   else if (dimensions == 2) {
-      depth = 1;
-      if (target == GL_PROXY_TEXTURE_2D || target == GL_TEXTURE_2D) {
-         proxy_target = GL_PROXY_TEXTURE_2D;
-      }
-      else if (target == GL_PROXY_TEXTURE_CUBE_MAP_ARB ||
-               (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-                target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) {
-         if (!ctx->Extensions.ARB_texture_cube_map) {
-            _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage2D(target)");
-            return GL_TRUE;
-         }
-         proxy_target = GL_PROXY_TEXTURE_CUBE_MAP_ARB;
-         sizeOK = (width == height);
-      }
-      else if (target == GL_PROXY_TEXTURE_RECTANGLE_NV ||
-               target == GL_TEXTURE_RECTANGLE_NV) {
-         if (!ctx->Extensions.NV_texture_rectangle) {
-            _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage2D(target)");
-            return GL_TRUE;
-         }
-         proxy_target = GL_PROXY_TEXTURE_RECTANGLE_NV;
-      }
-      else if (target == GL_PROXY_TEXTURE_1D_ARRAY_EXT ||
-               target == GL_TEXTURE_1D_ARRAY_EXT) {
-         proxy_target = GL_PROXY_TEXTURE_1D_ARRAY_EXT;
-      }
-      else {
-         _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage2D(target)");
+   case 3:
+      switch (target) {
+      case GL_TEXTURE_3D:
          return GL_TRUE;
+      case GL_TEXTURE_2D_ARRAY_EXT:
+         return (ctx->Extensions.MESA_texture_array ||
+                 ctx->Extensions.EXT_texture_array);
+      default:
+         return GL_FALSE;
       }
+   default:
+      _mesa_problem(ctx, "invalid dims=%u in legal_texsubimage_target()",
+                    dims);
+      return GL_FALSE;
    }
-   else if (dimensions == 3) {
-      if (target == GL_PROXY_TEXTURE_3D || target == GL_TEXTURE_3D) {
-         proxy_target = GL_PROXY_TEXTURE_3D;
-      }
-      else if (target == GL_PROXY_TEXTURE_2D_ARRAY_EXT ||
-               target == GL_TEXTURE_2D_ARRAY_EXT) {
-         proxy_target = GL_PROXY_TEXTURE_2D_ARRAY_EXT;
+}
+
+
+/**
+ * Helper function to determine if a texture object is mutable (in terms
+ * of GL_ARB_texture_storage).
+ */
+static GLboolean
+mutable_tex_object(struct gl_context *ctx, GLenum target)
+{
+   if (ctx->Extensions.ARB_texture_storage) {
+      struct gl_texture_object *texObj =
+         _mesa_get_current_tex_object(ctx, target);
+      return !texObj->Immutable;
+   }
+   return GL_TRUE;
+}
+
+
+
+/**
+ * Test the glTexImage[123]D() parameters for errors.
+ * 
+ * \param ctx GL context.
+ * \param dimensions texture image dimensions (must be 1, 2 or 3).
+ * \param target texture target given by the user.
+ * \param level image level given by the user.
+ * \param internalFormat internal format given by the user.
+ * \param format pixel data format given by the user.
+ * \param type pixel data type given by the user.
+ * \param width image width given by the user.
+ * \param height image height given by the user.
+ * \param depth image depth given by the user.
+ * \param border image border given by the user.
+ * 
+ * \return GL_TRUE if an error was detected, or GL_FALSE if no errors.
+ *
+ * Verifies each of the parameters against the constants specified in
+ * __struct gl_contextRec::Const and the supported extensions, and according
+ * to the OpenGL specification.
+ */
+static GLboolean
+texture_error_check( struct gl_context *ctx,
+                     GLuint dimensions, GLenum target,
+                     GLint level, GLint internalFormat,
+                     GLenum format, GLenum type,
+                     GLint width, GLint height,
+                     GLint depth, GLint border )
+{
+   const GLenum proxyTarget = get_proxy_target(target);
+   const GLboolean isProxy = target == proxyTarget;
+   GLboolean sizeOK = GL_TRUE;
+   GLboolean colorFormat;
+
+   /* Even though there are no color-index textures, we still have to support
+    * uploading color-index data and remapping it to RGB via the
+    * GL_PIXEL_MAP_I_TO_[RGBA] tables.
+    */
+   const GLboolean indexFormat = (format == GL_COLOR_INDEX);
+
+   /* Basic level check (more checking in ctx->Driver.TestProxyTexImage) */
+   if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
+      if (!isProxy) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glTexImage%dD(level=%d)", dimensions, level);
       }
-      else {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glTexImage3D(target)" );
-         return GL_TRUE;
+      return GL_TRUE;
+   }
+
+   /* Check border */
+   if (border < 0 || border > 1 ||
+       ((target == GL_TEXTURE_RECTANGLE_NV ||
+         target == GL_PROXY_TEXTURE_RECTANGLE_NV) && border != 0)) {
+      if (!isProxy) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glTexImage%dD(border=%d)", dimensions, border);
       }
+      return GL_TRUE;
    }
-   else {
-      _mesa_problem( ctx, "bad dims in texture_error_check" );
+
+   if (width < 0 || height < 0 || depth < 0) {
+      if (!isProxy) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glTexImage%dD(width, height or depth < 0)", dimensions);
+      }
       return GL_TRUE;
    }
 
-   sizeOK = sizeOK && ctx->Driver.TestProxyTexImage(ctx, proxy_target, level,
+   /* Do this simple check before calling the TestProxyTexImage() function */
+   if (proxyTarget == GL_PROXY_TEXTURE_CUBE_MAP_ARB) {
+      sizeOK = (width == height);
+   }
+
+   /*
+    * Use the proxy texture driver hook to see if the size/level/etc are
+    * legal.
+    */
+   sizeOK = sizeOK && ctx->Driver.TestProxyTexImage(ctx, proxyTarget, level,
                                                     internalFormat, format,
                                                     type, width, height,
                                                     depth, border);
@@ -1419,9 +1591,7 @@ texture_error_check( struct gl_context *ctx, GLenum target,
 
    /* make sure internal format and format basically agree */
    colorFormat = _mesa_is_color_format(format);
-   indexFormat = _mesa_is_index_format(format);
    if ((_mesa_is_color_format(internalFormat) && !colorFormat && !indexFormat) ||
-       (_mesa_is_index_format(internalFormat) && !indexFormat) ||
        (_mesa_is_depth_format(internalFormat) != _mesa_is_depth_format(format)) ||
        (_mesa_is_ycbcr_format(internalFormat) != _mesa_is_ycbcr_format(format)) ||
        (_mesa_is_depthstencil_format(internalFormat) != _mesa_is_depthstencil_format(format)) ||
@@ -1466,13 +1636,20 @@ texture_error_check( struct gl_context *ctx, GLenum target,
 
    /* additional checks for depth textures */
    if (_mesa_base_tex_format(ctx, internalFormat) == GL_DEPTH_COMPONENT) {
-      /* Only 1D, 2D and rectangular textures supported, not 3D or cubes */
+      /* Only 1D, 2D, rect, array and cube textures supported, not 3D
+       * Cubemaps are only supported for GL version > 3.0 or with EXT_gpu_shader4 */
       if (target != GL_TEXTURE_1D &&
           target != GL_PROXY_TEXTURE_1D &&
           target != GL_TEXTURE_2D &&
           target != GL_PROXY_TEXTURE_2D &&
+          target != GL_TEXTURE_1D_ARRAY &&
+          target != GL_PROXY_TEXTURE_1D_ARRAY &&
+          target != GL_TEXTURE_2D_ARRAY &&
+          target != GL_PROXY_TEXTURE_2D_ARRAY &&
           target != GL_TEXTURE_RECTANGLE_ARB &&
-          target != GL_PROXY_TEXTURE_RECTANGLE_ARB) {
+          target != GL_PROXY_TEXTURE_RECTANGLE_ARB &&
+         !((_mesa_is_cube_face(target) || target == GL_PROXY_TEXTURE_CUBE_MAP) &&
+           (ctx->VersionMajor >= 3 || ctx->Extensions.EXT_gpu_shader4))) {
          if (!isProxy)
             _mesa_error(ctx, GL_INVALID_ENUM,
                         "glTexImage(target/internalFormat)");
@@ -1482,9 +1659,15 @@ texture_error_check( struct gl_context *ctx, GLenum target,
 
    /* additional checks for compressed textures */
    if (_mesa_is_compressed_format(ctx, internalFormat)) {
-      if (!target_can_be_compressed(ctx, target) && !isProxy) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glTexImage%dD(target)", dimensions);
+      if (!target_can_be_compressed(ctx, target, internalFormat)) {
+         if (!isProxy)
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glTexImage%dD(target)", dimensions);
+         return GL_TRUE;
+      }
+      if (compressedteximage_only_format(ctx, internalFormat)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glTexImage%dD(no compression for format)", dimensions);
          return GL_TRUE;
       }
       if (border != 0) {
@@ -1508,6 +1691,12 @@ texture_error_check( struct gl_context *ctx, GLenum target,
       return GL_TRUE;
    }
 
+   if (!mutable_tex_object(ctx, target)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glTexImage%dD(immutable texture)", dimensions);
+      return GL_TRUE;
+   }
+
    /* if we get here, the parameters are OK */
    return GL_FALSE;
 }
@@ -1532,8 +1721,8 @@ texture_error_check( struct gl_context *ctx, GLenum target,
  * \return GL_TRUE if an error was detected, or GL_FALSE if no errors.
  *
  * Verifies each of the parameters against the constants specified in
- * __struct gl_contextRec::Const and the supported extensions, and according to the
- * OpenGL specification.
+ * __struct gl_contextRec::Const and the supported extensions, and according
+ * to the OpenGL specification.
  */
 static GLboolean
 subtexture_error_check( struct gl_context *ctx, GLuint dimensions,
@@ -1542,61 +1731,13 @@ subtexture_error_check( struct gl_context *ctx, GLuint dimensions,
                         GLint width, GLint height, GLint depth,
                         GLenum format, GLenum type )
 {
-   /* Check target */
-   if (dimensions == 1) {
-      if (target != GL_TEXTURE_1D) {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage1D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else if (dimensions == 2) {
-      if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-          target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) {
-         if (!ctx->Extensions.ARB_texture_cube_map) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage2D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target == GL_TEXTURE_RECTANGLE_NV) {
-         if (!ctx->Extensions.NV_texture_rectangle) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage2D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target == GL_TEXTURE_1D_ARRAY_EXT) {
-        if (!ctx->Extensions.MESA_texture_array) {
-           _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage2D(target)" );
-           return GL_TRUE;
-        }
-      }
-      else if (target != GL_TEXTURE_2D) {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage2D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else if (dimensions == 3) {
-      if (target == GL_TEXTURE_2D_ARRAY_EXT) {
-         if (!ctx->Extensions.MESA_texture_array) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage3D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target != GL_TEXTURE_3D) {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glTexSubImage3D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else {
-      _mesa_problem( ctx, "invalid dims in texture_error_check" );
-      return GL_TRUE;
-   }
-
    /* Basic level check */
    if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
       _mesa_error(ctx, GL_INVALID_ENUM, "glTexSubImage2D(level=%d)", level);
       return GL_TRUE;
    }
 
+   /* Check for negative sizes */
    if (width < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glTexSubImage%dD(width=%d)", dimensions, width);
@@ -1683,10 +1824,9 @@ subtexture_error_check2( struct gl_context *ctx, GLuint dimensions,
    if (_mesa_is_format_compressed(destTex->TexFormat)) {
       GLuint bw, bh;
 
-      if (!target_can_be_compressed(ctx, target)) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glTexSubImage%dD(target=%s)", dimensions,
-                     _mesa_lookup_enum_by_nr(target));
+      if (compressedteximage_only_format(ctx, destTex->InternalFormat)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glTexSubImage%dD(no compression for format)", dimensions);
          return GL_TRUE;
       }
 
@@ -1732,17 +1872,25 @@ subtexture_error_check2( struct gl_context *ctx, GLuint dimensions,
  * \return GL_TRUE if an error was detected, or GL_FALSE if no errors.
  * 
  * Verifies each of the parameters against the constants specified in
- * __struct gl_contextRec::Const and the supported extensions, and according to the
- * OpenGL specification.
+ * __struct gl_contextRec::Const and the supported extensions, and according
+ * to the OpenGL specification.
  */
 static GLboolean
 copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
                          GLenum target, GLint level, GLint internalFormat,
                          GLint width, GLint height, GLint border )
 {
-   GLenum type;
+   const GLenum proxyTarget = get_proxy_target(target);
+   const GLenum type = GL_FLOAT;
    GLboolean sizeOK;
-   GLint format;
+   GLint baseFormat;
+
+   /* check target */
+   if (!legal_texsubimage_target(ctx, dimensions, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyTexImage%uD(target=%s)",
+                  dimensions, _mesa_lookup_enum_by_nr(target));
+      return GL_TRUE;
+   }       
 
    /* Basic level check (more checking in ctx->Driver.TestProxyTexImage) */
    if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
@@ -1753,7 +1901,9 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
 
    /* Check that the source buffer is complete */
    if (ctx->ReadBuffer->Name) {
-      _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer);
+      if (ctx->ReadBuffer->_Status == 0) {
+         _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer);
+      }
       if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
          _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
                      "glCopyTexImage%dD(invalid readbuffer)", dimensions);
@@ -1768,88 +1918,27 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
       return GL_TRUE;
    }
 
-   format = _mesa_base_tex_format(ctx, internalFormat);
-   if (format < 0) {
+   baseFormat = _mesa_base_tex_format(ctx, internalFormat);
+   if (baseFormat < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glCopyTexImage%dD(internalFormat)", dimensions);
       return GL_TRUE;
    }
 
-   if (!_mesa_source_buffer_exists(ctx, format)) {
+   if (!_mesa_source_buffer_exists(ctx, baseFormat)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glCopyTexImage%dD(missing readbuffer)", dimensions);
       return GL_TRUE;
    }
 
-   /* NOTE: the format and type aren't really significant for
-    * TestProxyTexImage().  Only the internalformat really matters.
-    */
-   type = GL_FLOAT;
+   /* Do size, level checking */
+   sizeOK = (proxyTarget == GL_PROXY_TEXTURE_CUBE_MAP_ARB)
+      ? (width == height) : 1;
 
-   /* Check target and call ctx->Driver.TestProxyTexImage() to check the
-    * level, width, height and depth.
-    */
-   if (dimensions == 1) {
-      if (target == GL_TEXTURE_1D) {
-         sizeOK = ctx->Driver.TestProxyTexImage(ctx, GL_PROXY_TEXTURE_1D,
-                                                level, internalFormat,
-                                                format, type,
-                                                width, 1, 1, border);
-      }
-      else {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexImage1D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else if (dimensions == 2) {
-      if (target == GL_TEXTURE_2D) {
-         sizeOK = ctx->Driver.TestProxyTexImage(ctx, GL_PROXY_TEXTURE_2D,
-                                                level, internalFormat,
-                                                format, type,
-                                                width, height, 1, border);
-      }
-      else if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-               target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) {
-         if (!ctx->Extensions.ARB_texture_cube_map) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexImage2D(target)" );
-            return GL_TRUE;
-         }
-         sizeOK = (width == height) &&
-            ctx->Driver.TestProxyTexImage(ctx, GL_PROXY_TEXTURE_CUBE_MAP_ARB,
-                                          level, internalFormat, format, type,
-                                          width, height, 1, border);
-      }
-      else if (target == GL_TEXTURE_RECTANGLE_NV) {
-         if (!ctx->Extensions.NV_texture_rectangle) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexImage2D(target)" );
-            return GL_TRUE;
-         }
-         sizeOK = ctx->Driver.TestProxyTexImage(ctx,
-                                                GL_PROXY_TEXTURE_RECTANGLE_NV,
-                                                level, internalFormat,
-                                                format, type,
-                                                width, height, 1, border);
-      }
-      else if (target == GL_TEXTURE_1D_ARRAY_EXT) {
-         if (!ctx->Extensions.MESA_texture_array) {
-            _mesa_error(ctx, GL_INVALID_ENUM, "glCopyTexImage2D(target)");
-            return GL_TRUE;
-         }
-         sizeOK = ctx->Driver.TestProxyTexImage(ctx,
-                                                GL_PROXY_TEXTURE_1D_ARRAY_EXT,
-                                                level, internalFormat,
-                                                format, type,
-                                                width, height, 1, border);
-      }
-      else {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexImage2D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else {
-      _mesa_problem(ctx, "invalid dimensions in copytexture_error_check");
-      return GL_TRUE;
-   }
+   sizeOK = sizeOK && ctx->Driver.TestProxyTexImage(ctx, proxyTarget, level,
+                                                    internalFormat, baseFormat,
+                                                    type, width, height,
+                                                    1, border);
 
    if (!sizeOK) {
       if (dimensions == 1) {
@@ -1865,32 +1954,27 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
    }
 
    if (_mesa_is_compressed_format(ctx, internalFormat)) {
-      if (!target_can_be_compressed(ctx, target)) {
+      if (!target_can_be_compressed(ctx, target, internalFormat)) {
          _mesa_error(ctx, GL_INVALID_ENUM,
                      "glCopyTexImage%dD(target)", dimensions);
          return GL_TRUE;
       }
-      if (border != 0) {
+      if (compressedteximage_only_format(ctx, internalFormat)) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glCopyTexImage%dD(border!=0)", dimensions);
+               "glCopyTexImage%dD(no compression for format)", dimensions);
          return GL_TRUE;
       }
-   }
-   else if (_mesa_is_depth_format(internalFormat)) {
-      /* make sure we have depth/stencil buffers */
-      if (!ctx->ReadBuffer->_DepthBuffer) {
+      if (border != 0) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glCopyTexImage%dD(no depth)", dimensions);
+                     "glCopyTexImage%dD(border!=0)", dimensions);
          return GL_TRUE;
       }
    }
-   else if (_mesa_is_depthstencil_format(internalFormat)) {
-      /* make sure we have depth/stencil buffers */
-      if (!ctx->ReadBuffer->_DepthBuffer || !ctx->ReadBuffer->_StencilBuffer) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glCopyTexImage%dD(no depth/stencil buffer)", dimensions);
-         return GL_TRUE;
-      }
+
+   if (!mutable_tex_object(ctx, target)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glCopyTexImage%dD(immutable texture)", dimensions);
+      return GL_TRUE;
    }
 
    /* if we get here, the parameters are OK */
@@ -1916,7 +2000,9 @@ copytexsubimage_error_check1( struct gl_context *ctx, GLuint dimensions,
 {
    /* Check that the source buffer is complete */
    if (ctx->ReadBuffer->Name) {
-      _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer);
+      if (ctx->ReadBuffer->_Status == 0) {
+         _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer);
+      }
       if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
          _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
                      "glCopyTexImage%dD(invalid readbuffer)", dimensions);
@@ -1924,45 +2010,11 @@ copytexsubimage_error_check1( struct gl_context *ctx, GLuint dimensions,
       }
    }
 
-   /* Check target */
-   if (dimensions == 1) {
-      if (target != GL_TEXTURE_1D) {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage1D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else if (dimensions == 2) {
-      if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-          target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) {
-         if (!ctx->Extensions.ARB_texture_cube_map) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage2D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target == GL_TEXTURE_RECTANGLE_NV) {
-         if (!ctx->Extensions.NV_texture_rectangle) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage2D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target == GL_TEXTURE_1D_ARRAY_EXT) {
-         if (!ctx->Extensions.MESA_texture_array) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage2D(target)" );
-            return GL_TRUE;
-         }
-      }
-      else if (target != GL_TEXTURE_2D) {
-         _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage2D(target)" );
-         return GL_TRUE;
-      }
-   }
-   else if (dimensions == 3) {
-      if (((target != GL_TEXTURE_2D_ARRAY_EXT) ||
-          (!ctx->Extensions.MESA_texture_array))
-         && (target != GL_TEXTURE_3D)) {
-        _mesa_error( ctx, GL_INVALID_ENUM, "glCopyTexSubImage3D(target)" );
-        return GL_TRUE;
-      }
+   /* check target (proxies not allowed) */
+   if (!legal_texsubimage_target(ctx, dimensions, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyTexSubImage%uD(target=%s)",
+                  dimensions, _mesa_lookup_enum_by_nr(target));
+      return GL_TRUE;
    }
 
    /* Check level */
@@ -2051,9 +2103,9 @@ copytexsubimage_error_check2( struct gl_context *ctx, GLuint dimensions,
    }
 
    if (_mesa_is_format_compressed(teximage->TexFormat)) {
-      if (!target_can_be_compressed(ctx, target)) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glCopyTexSubImage%dD(target)", dimensions);
+      if (compressedteximage_only_format(ctx, teximage->InternalFormat)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glCopyTexSubImage%dD(no compression for format)", dimensions);
          return GL_TRUE;
       }
       /* offset must be multiple of 4 */
@@ -2087,23 +2139,6 @@ copytexsubimage_error_check2( struct gl_context *ctx, GLuint dimensions,
       return GL_TRUE;
    }
 
-   if (teximage->_BaseFormat == GL_DEPTH_COMPONENT) {
-      if (!ctx->ReadBuffer->_DepthBuffer) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glCopyTexSubImage%dD(no depth buffer)",
-                     dimensions);
-         return GL_TRUE;
-      }
-   }
-   else if (teximage->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
-      if (!ctx->ReadBuffer->_DepthBuffer || !ctx->ReadBuffer->_StencilBuffer) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glCopyTexSubImage%dD(no depth/stencil buffer)",
-                     dimensions);
-         return GL_TRUE;
-      }
-   }
-
    /* If copying into an integer texture, the source buffer must also be
     * integer-valued.
     */
@@ -2153,7 +2188,7 @@ check_rtt_cb(GLuint key, void *data, void *userData)
              att->Texture == texObj &&
              att->TextureLevel == level &&
              att->CubeMapFace == face) {
-            ASSERT(att->Texture->Image[att->CubeMapFace][att->TextureLevel]);
+            ASSERT(_mesa_get_attachment_teximage(att));
             /* Tell driver about the new renderbuffer texture */
             ctx->Driver.RenderTexture(ctx, ctx->DrawBuffer, att);
             /* Mark fb status as indeterminate to force re-validation */
@@ -2191,7 +2226,7 @@ update_fbo_texture(struct gl_context *ctx, struct gl_texture_object *texObj,
  * changed the texture base level image, regenerate the rest of the
  * mipmap levels now.
  */
-static INLINE void
+static inline void
 check_gen_mipmap(struct gl_context *ctx, GLenum target,
                  struct gl_texture_object *texObj, GLint level)
 {
@@ -2257,13 +2292,14 @@ override_internal_format(GLenum internalFormat, GLint width, GLint height)
  * for efficient texture memory layout/allocation.  In particular, this
  * comes up during automatic mipmap generation.
  */
-void
+gl_format
 _mesa_choose_texture_format(struct gl_context *ctx,
                             struct gl_texture_object *texObj,
-                            struct gl_texture_image *texImage,
                             GLenum target, GLint level,
                             GLenum internalFormat, GLenum format, GLenum type)
 {
+   gl_format f;
+
    /* see if we've already chosen a format for the previous level */
    if (level > 0) {
       struct gl_texture_image *prevImage =
@@ -2275,102 +2311,101 @@ _mesa_choose_texture_format(struct gl_context *ctx,
           prevImage->Width > 0 &&
           prevImage->InternalFormat == internalFormat) {
          /* use the same format */
-         texImage->TexFormat = prevImage->TexFormat;
-         return;
+         ASSERT(prevImage->TexFormat != MESA_FORMAT_NONE);
+         return prevImage->TexFormat;
       }
    }
 
    /* choose format from scratch */
-   texImage->TexFormat = ctx->Driver.ChooseTextureFormat(ctx, internalFormat,
-                                                         format, type);
-   ASSERT(texImage->TexFormat != MESA_FORMAT_NONE);
+   f = ctx->Driver.ChooseTextureFormat(ctx, internalFormat, format, type);
+   ASSERT(f != MESA_FORMAT_NONE);
+   return f;
 }
 
+/**
+ * Adjust pixel unpack params and image dimensions to strip off the
+ * texture border.
+ *
+ * Gallium and intel don't support texture borders.  They've seldem been used
+ * and seldom been implemented correctly anyway.
+ *
+ * \param unpackNew returns the new pixel unpack parameters
+ */
+static void
+strip_texture_border(GLint *border,
+                     GLint *width, GLint *height, GLint *depth,
+                     const struct gl_pixelstore_attrib *unpack,
+                     struct gl_pixelstore_attrib *unpackNew)
+{
+   assert(*border > 0);  /* sanity check */
+
+   *unpackNew = *unpack;
 
+   if (unpackNew->RowLength == 0)
+      unpackNew->RowLength = *width;
 
-/*
- * Called from the API.  Note that width includes the border.
+   if (depth && unpackNew->ImageHeight == 0)
+      unpackNew->ImageHeight = *height;
+
+   unpackNew->SkipPixels += *border;
+   if (height)
+      unpackNew->SkipRows += *border;
+   if (depth)
+      unpackNew->SkipImages += *border;
+
+   assert(*width >= 3);
+   *width = *width - 2 * *border;
+   if (height && *height >= 3)
+      *height = *height - 2 * *border;
+   if (depth && *depth >= 3)
+      *depth = *depth - 2 * *border;
+   *border = 0;
+}
+
+/**
+ * Common code to implement all the glTexImage1D/2D/3D functions.
  */
-void GLAPIENTRY
-_mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
-                  GLsizei width, GLint border, GLenum format,
-                  GLenum type, const GLvoid *pixels )
+static void
+teximage(struct gl_context *ctx, GLuint dims,
+         GLenum target, GLint level, GLint internalFormat,
+         GLsizei width, GLsizei height, GLsizei depth,
+         GLint border, GLenum format, GLenum type,
+         const GLvoid *pixels)
 {
-   GET_CURRENT_CONTEXT(ctx);
+   GLboolean error;
+   struct gl_pixelstore_attrib unpack_no_border;
+   const struct gl_pixelstore_attrib *unpack = &ctx->Unpack;
+
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexImage1D %s %d %s %d %d %s %s %p\n",
+      _mesa_debug(ctx, "glTexImage%uD %s %d %s %d %d %d %d %s %s %p\n",
+                  dims,
                   _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat), width, border,
+                  _mesa_lookup_enum_by_nr(internalFormat),
+                  width, height, depth, border,
                   _mesa_lookup_enum_by_nr(format),
                   _mesa_lookup_enum_by_nr(type), pixels);
 
-   internalFormat = override_internal_format(internalFormat, width, 1);
+   internalFormat = override_internal_format(internalFormat, width, height);
 
-   if (target == GL_TEXTURE_1D) {
-      /* non-proxy target */
-      struct gl_texture_object *texObj;
-      struct gl_texture_image *texImage;
-      const GLuint face = _mesa_tex_target_to_face(target);
+   /* target error checking */
+   if (!legal_teximage_target(ctx, dims, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage%uD(target=%s)",
+                  dims, _mesa_lookup_enum_by_nr(target));
+      return;
+   }
 
-      if (texture_error_check(ctx, target, level, internalFormat,
-                              format, type, 1, width, 1, 1, border)) {
-         return;   /* error was recorded */
-      }
+   /* general error checking */
+   error = texture_error_check(ctx, dims, target, level, internalFormat,
+                               format, type, width, height, depth, border);
 
-      if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
-        _mesa_update_state(ctx);
+   if (_mesa_is_proxy_texture(target)) {
+      /* Proxy texture: just clear or set state depending on error checking */
+      struct gl_texture_image *texImage =
+         _mesa_get_proxy_tex_image(ctx, target, level);
 
-      texObj = _mesa_get_current_tex_object(ctx, target);
-      _mesa_lock_texture(ctx, texObj);
-      {
-        texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-        if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D");
-        }
-         else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
-            }
-
-            ASSERT(texImage->Data == NULL);
-
-            clear_teximage_fields(texImage); /* not really needed, but helpful */
-            _mesa_init_teximage_fields(ctx, target, texImage,
-                                       width, 1, 1,
-                                       border, internalFormat);
-
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, format, type);
-
-            /* Give the texture to the driver.  <pixels> may be null. */
-            ASSERT(ctx->Driver.TexImage1D);
-            ctx->Driver.TexImage1D(ctx, target, level, internalFormat,
-                                   width, border, format, type, pixels,
-                                   &ctx->Unpack, texObj, texImage);
-
-            ASSERT(texImage->TexFormat);
-
-            _mesa_set_fetch_functions(texImage, 1);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            update_fbo_texture(ctx, texObj, face, level);
-
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-   else if (target == GL_PROXY_TEXTURE_1D) {
-      /* Proxy texture: check for errors and update proxy state */
-      struct gl_texture_image *texImage;
-      texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-      if (texture_error_check(ctx, target, level, internalFormat,
-                              format, type, 1, width, 1, 1, border)) {
+      if (error) {
          /* when error, clear all proxy texture image parameters */
          if (texImage)
             clear_teximage_fields(texImage);
@@ -2379,133 +2414,133 @@ _mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
          /* no error, set the tex image parameters */
          struct gl_texture_object *texObj =
             _mesa_get_current_tex_object(ctx, target);
-         ASSERT(texImage);
-         _mesa_init_teximage_fields(ctx, target, texImage,
-                                    width, 1, 1,
-                                    border, internalFormat);
-         _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                     internalFormat, format, type);
+         gl_format texFormat = _mesa_choose_texture_format(ctx, texObj,
+                                                           target, level,
+                                                           internalFormat,
+                                                           format, type);
+
+         if (legal_texture_size(ctx, texFormat, width, height, depth)) {
+            _mesa_init_teximage_fields(ctx, target, texImage, width, height,
+                                       depth, border, internalFormat,
+                                       texFormat);
+         }
+         else if (texImage) {
+            clear_teximage_fields(texImage);
+         }
       }
    }
    else {
-      _mesa_error( ctx, GL_INVALID_ENUM, "glTexImage1D(target)" );
-      return;
-   }
-}
-
-
-void GLAPIENTRY
-_mesa_TexImage2D( GLenum target, GLint level, GLint internalFormat,
-                  GLsizei width, GLsizei height, GLint border,
-                  GLenum format, GLenum type,
-                  const GLvoid *pixels )
-{
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexImage2D %s %d %s %d %d %d %s %s %p\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat), width, height,
-                  border, _mesa_lookup_enum_by_nr(format),
-                  _mesa_lookup_enum_by_nr(type), pixels);
-
-   internalFormat = override_internal_format(internalFormat, width, height);
-
-   if (target == GL_TEXTURE_2D ||
-       (ctx->Extensions.ARB_texture_cube_map &&
-        target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-        target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) ||
-       (ctx->Extensions.NV_texture_rectangle &&
-        target == GL_TEXTURE_RECTANGLE_NV) ||
-       (ctx->Extensions.MESA_texture_array &&
-        target == GL_TEXTURE_1D_ARRAY_EXT)) {
       /* non-proxy target */
+      const GLuint face = _mesa_tex_target_to_face(target);
       struct gl_texture_object *texObj;
       struct gl_texture_image *texImage;
-      const GLuint face = _mesa_tex_target_to_face(target);
 
-      if (texture_error_check(ctx, target, level, internalFormat,
-                              format, type, 2, width, height, 1, border)) {
+      if (error) {
          return;   /* error was recorded */
       }
 
-      if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
+      /* Allow a hardware driver to just strip out the border, to provide
+       * reliable but slightly incorrect hardware rendering instead of
+       * rarely-tested software fallback rendering.
+       */
+      if (border && ctx->Const.StripTextureBorder) {
+        strip_texture_border(&border, &width, &height, &depth, unpack,
+                             &unpack_no_border);
+        unpack = &unpack_no_border;
+      }
+
+      if (ctx->NewState & _NEW_PIXEL)
         _mesa_update_state(ctx);
 
       texObj = _mesa_get_current_tex_object(ctx, target);
+
       _mesa_lock_texture(ctx, texObj);
       {
         texImage = _mesa_get_tex_image(ctx, texObj, target, level);
+
         if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
+           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
         }
          else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
-            }
-
-            ASSERT(texImage->Data == NULL);
-            clear_teximage_fields(texImage); /* not really needed, but helpful */
-            _mesa_init_teximage_fields(ctx, target, texImage,
-                                       width, height, 1,
-                                       border, internalFormat);
-
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, format, type);
-
-            /* Give the texture to the driver.  <pixels> may be null. */
-            ASSERT(ctx->Driver.TexImage2D);
-            ctx->Driver.TexImage2D(ctx, target, level, internalFormat,
-                                   width, height, border, format, type,
-                                   pixels, &ctx->Unpack, texObj, texImage);
+            gl_format texFormat;
 
-            ASSERT(texImage->TexFormat);
+            ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
 
-            _mesa_set_fetch_functions(texImage, 2);
+            texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
+                                                    internalFormat, format,
+                                                    type);
+
+            if (legal_texture_size(ctx, texFormat, width, height, depth)) {
+               _mesa_init_teximage_fields(ctx, target, texImage,
+                                          width, height, depth,
+                                          border, internalFormat, texFormat);
+
+               /* Give the texture to the driver.  <pixels> may be null. */
+               ASSERT(ctx->Driver.TexImage3D);
+               switch (dims) {
+               case 1:
+                  ctx->Driver.TexImage1D(ctx, target, level, internalFormat,
+                                         width, border, format,
+                                         type, pixels, unpack, texObj,
+                                         texImage);
+                  break;
+               case 2:
+                  ctx->Driver.TexImage2D(ctx, target, level, internalFormat,
+                                         width, height, border, format,
+                                         type, pixels, unpack, texObj,
+                                         texImage);
+                  break;
+               case 3:
+                  ctx->Driver.TexImage3D(ctx, target, level, internalFormat,
+                                         width, height, depth, border, format,
+                                         type, pixels, unpack, texObj,
+                                         texImage);
+                  break;
+               default:
+                  _mesa_problem(ctx, "invalid dims=%u in teximage()", dims);
+               }
 
-            check_gen_mipmap(ctx, target, texObj, level);
+               check_gen_mipmap(ctx, target, texObj, level);
 
-            update_fbo_texture(ctx, texObj, face, level);
+               update_fbo_texture(ctx, texObj, face, level);
 
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
+               /* state update */
+               texObj->_Complete = GL_FALSE;
+               ctx->NewState |= _NEW_TEXTURE;
+            }
+            else {
+               _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
+            }
          }
       }
       _mesa_unlock_texture(ctx, texObj);
    }
-   else if (target == GL_PROXY_TEXTURE_2D ||
-            (target == GL_PROXY_TEXTURE_CUBE_MAP_ARB &&
-             ctx->Extensions.ARB_texture_cube_map) ||
-            (target == GL_PROXY_TEXTURE_RECTANGLE_NV &&
-             ctx->Extensions.NV_texture_rectangle) ||
-            (ctx->Extensions.MESA_texture_array &&
-             target == GL_PROXY_TEXTURE_1D_ARRAY_EXT)) {
-      /* Proxy texture: check for errors and update proxy state */
-      struct gl_texture_image *texImage;
-      texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-      if (texture_error_check(ctx, target, level, internalFormat,
-                              format, type, 2, width, height, 1, border)) {
-         /* when error, clear all proxy texture image parameters */
-         if (texImage)
-            clear_teximage_fields(texImage);
-      }
-      else {
-         /* no error, set the tex image parameters */
-         struct gl_texture_object *texObj =
-            _mesa_get_current_tex_object(ctx, target);
-         _mesa_init_teximage_fields(ctx, target, texImage,
-                                    width, height, 1,
-                                    border, internalFormat);
-         _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                     internalFormat, format, type);
-      }
-   }
-   else {
-      _mesa_error( ctx, GL_INVALID_ENUM, "glTexImage2D(target)" );
-      return;
-   }
+}
+
+
+/*
+ * Called from the API.  Note that width includes the border.
+ */
+void GLAPIENTRY
+_mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
+                  GLsizei width, GLint border, GLenum format,
+                  GLenum type, const GLvoid *pixels )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   teximage(ctx, 1, target, level, internalFormat, width, 1, 1,
+            border, format, type, pixels);
+}
+
+
+void GLAPIENTRY
+_mesa_TexImage2D( GLenum target, GLint level, GLint internalFormat,
+                  GLsizei width, GLsizei height, GLint border,
+                  GLenum format, GLenum type,
+                  const GLvoid *pixels )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   teximage(ctx, 2, target, level, internalFormat, width, height, 1,
+            border, format, type, pixels);
 }
 
 
@@ -2520,101 +2555,8 @@ _mesa_TexImage3D( GLenum target, GLint level, GLint internalFormat,
                   const GLvoid *pixels )
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexImage3D %s %d %s %d %d %d %d %s %s %p\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat), width, height,
-                  depth, border, _mesa_lookup_enum_by_nr(format),
-                  _mesa_lookup_enum_by_nr(type), pixels);
-
-   internalFormat = override_internal_format(internalFormat, width, height);
-
-   if (target == GL_TEXTURE_3D ||
-       (ctx->Extensions.MESA_texture_array &&
-        target == GL_TEXTURE_2D_ARRAY_EXT)) {
-      /* non-proxy target */
-      struct gl_texture_object *texObj;
-      struct gl_texture_image *texImage;
-      const GLuint face = _mesa_tex_target_to_face(target);
-
-      if (texture_error_check(ctx, target, level, (GLint) internalFormat,
-                              format, type, 3, width, height, depth, border)) {
-         return;   /* error was recorded */
-      }
-
-      if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
-        _mesa_update_state(ctx);
-
-      texObj = _mesa_get_current_tex_object(ctx, target);
-      _mesa_lock_texture(ctx, texObj);
-      {
-        texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-        if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage3D");
-        }
-         else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
-            }
-
-            ASSERT(texImage->Data == NULL);
-            clear_teximage_fields(texImage); /* not really needed, but helpful */
-            _mesa_init_teximage_fields(ctx, target, texImage,
-                                       width, height, depth,
-                                       border, internalFormat);
-
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, format, type);
-
-            /* Give the texture to the driver.  <pixels> may be null. */
-            ASSERT(ctx->Driver.TexImage3D);
-            ctx->Driver.TexImage3D(ctx, target, level, internalFormat,
-                                   width, height, depth, border, format, type,
-                                   pixels, &ctx->Unpack, texObj, texImage);
-
-            ASSERT(texImage->TexFormat);
-
-            _mesa_set_fetch_functions(texImage, 3);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            update_fbo_texture(ctx, texObj, face, level);
-
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-   else if (target == GL_PROXY_TEXTURE_3D ||
-       (ctx->Extensions.MESA_texture_array &&
-        target == GL_PROXY_TEXTURE_2D_ARRAY_EXT)) {
-      /* Proxy texture: check for errors and update proxy state */
-      struct gl_texture_image *texImage;
-      texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-      if (texture_error_check(ctx, target, level, internalFormat,
-                              format, type, 3, width, height, depth, border)) {
-         /* when error, clear all proxy texture image parameters */
-         if (texImage)
-            clear_teximage_fields(texImage);
-      }
-      else {
-         /* no error, set the tex image parameters */
-         struct gl_texture_object *texObj =
-            _mesa_get_current_tex_object(ctx, target);
-         _mesa_init_teximage_fields(ctx, target, texImage, width, height,
-                                    depth, border, internalFormat);
-         _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                     internalFormat, format, type);
-      }
-   }
-   else {
-      _mesa_error( ctx, GL_INVALID_ENUM, "glTexImage3D(target)" );
-      return;
-   }
+   teximage(ctx, 3, target, level, internalFormat, width, height, depth,
+            border, format, type, pixels);
 }
 
 
@@ -2638,19 +2580,16 @@ _mesa_EGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image)
    GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (!ctx->Extensions.OES_EGL_image) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glEGLImageTargetTexture2DOES(unsupported)");
-      return;
-   }
-
-   if (target != GL_TEXTURE_2D) {
+   if ((target == GL_TEXTURE_2D &&
+        !ctx->Extensions.OES_EGL_image) ||
+       (target == GL_TEXTURE_EXTERNAL_OES &&
+        !ctx->Extensions.OES_EGL_image_external)) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                  "glEGLImageTargetTexture2D(target=%d)", target);
       return;
    }
 
-   if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
+   if (ctx->NewState & _NEW_PIXEL)
       _mesa_update_state(ctx);
 
    texObj = _mesa_get_current_tex_object(ctx, target);
@@ -2660,10 +2599,8 @@ _mesa_EGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image)
    if (!texImage) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEGLImageTargetTexture2D");
    } else {
-      if (texImage->Data)
-        ctx->Driver.FreeTexImageData( ctx, texImage );
+      ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
 
-      ASSERT(texImage->Data == NULL);
       ctx->Driver.EGLImageTargetTexture2D(ctx, target,
                                          texObj, texImage, image);
 
@@ -2677,51 +2614,92 @@ _mesa_EGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image)
 #endif
 
 
-void GLAPIENTRY
-_mesa_TexSubImage1D( GLenum target, GLint level,
-                     GLint xoffset, GLsizei width,
-                     GLenum format, GLenum type,
-                     const GLvoid *pixels )
+
+/**
+ * Implement all the glTexSubImage1/2/3D() functions.
+ */
+static void
+texsubimage(struct gl_context *ctx, GLuint dims, GLenum target, GLint level,
+            GLint xoffset, GLint yoffset, GLint zoffset,
+            GLsizei width, GLsizei height, GLsizei depth,
+            GLenum format, GLenum type, const GLvoid *pixels )
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
-   GET_CURRENT_CONTEXT(ctx);
+
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexSubImage1D %s %d %d %d %s %s %p\n",
+      _mesa_debug(ctx, "glTexSubImage%uD %s %d %d %d %d %d %d %d %s %s %p\n",
+                  dims,
                   _mesa_lookup_enum_by_nr(target), level,
-                  xoffset, width, _mesa_lookup_enum_by_nr(format),
+                  xoffset, yoffset, zoffset, width, height, depth,
+                  _mesa_lookup_enum_by_nr(format),
                   _mesa_lookup_enum_by_nr(type), pixels);
 
-   if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
+   /* check target (proxies not allowed) */
+   if (!legal_texsubimage_target(ctx, dims, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexSubImage%uD(target=%s)",
+                  dims, _mesa_lookup_enum_by_nr(target));
+      return;
+   }       
+
+   if (ctx->NewState & _NEW_PIXEL)
       _mesa_update_state(ctx);
 
-   if (subtexture_error_check(ctx, 1, target, level, xoffset, 0, 0,
-                              width, 1, 1, format, type)) {
+   if (subtexture_error_check(ctx, dims, target, level, xoffset, yoffset, zoffset,
+                              width, height, depth, format, type)) {
       return;   /* error was detected */
    }
 
-
    texObj = _mesa_get_current_tex_object(ctx, target);
-   assert(texObj);
 
    _mesa_lock_texture(ctx, texObj);
    {
       texImage = _mesa_select_tex_image(ctx, texObj, target, level);
 
-      if (subtexture_error_check2(ctx, 1, target, level, xoffset, 0, 0,
-                                 width, 1, 1, format, type, texImage)) {
+      if (subtexture_error_check2(ctx, dims, target, level,
+                                  xoffset, yoffset, zoffset,
+                                 width, height, depth,
+                                  format, type, texImage)) {
          /* error was recorded */
       }
-      else if (width > 0) {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
+      else if (width > 0 && height > 0 && depth > 0) {
+         /* If we have a border, offset=-1 is legal.  Bias by border width. */
+         switch (dims) {
+         case 3:
+            zoffset += texImage->Border;
+            /* fall-through */
+         case 2:
+            yoffset += texImage->Border;
+            /* fall-through */
+         case 1:
+            xoffset += texImage->Border;
+         }
 
-         ASSERT(ctx->Driver.TexSubImage1D);
-         ctx->Driver.TexSubImage1D(ctx, target, level, xoffset, width,
-                                   format, type, pixels, &ctx->Unpack,
-                                   texObj, texImage);
+         switch (dims) {
+         case 1:
+            ctx->Driver.TexSubImage1D(ctx, target, level,
+                                      xoffset, width,
+                                      format, type, pixels,
+                                      &ctx->Unpack, texObj, texImage );
+            break;
+         case 2:
+            ctx->Driver.TexSubImage2D(ctx, target, level,
+                                      xoffset, yoffset, width, height,
+                                      format, type, pixels,
+                                      &ctx->Unpack, texObj, texImage );
+            break;
+         case 3:
+            ctx->Driver.TexSubImage3D(ctx, target, level,
+                                      xoffset, yoffset, zoffset,
+                                      width, height, depth,
+                                      format, type, pixels,
+                                      &ctx->Unpack, texObj, texImage );
+            break;
+         default:
+            _mesa_problem(ctx, "unexpected dims in subteximage()");
+         }
 
          check_gen_mipmap(ctx, target, texObj, level);
 
@@ -2732,118 +2710,150 @@ _mesa_TexSubImage1D( GLenum target, GLint level,
 }
 
 
+void GLAPIENTRY
+_mesa_TexSubImage1D( GLenum target, GLint level,
+                     GLint xoffset, GLsizei width,
+                     GLenum format, GLenum type,
+                     const GLvoid *pixels )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   texsubimage(ctx, 1, target, level,
+               xoffset, 0, 0,
+               width, 1, 1,
+               format, type, pixels);
+}
+
+
 void GLAPIENTRY
 _mesa_TexSubImage2D( GLenum target, GLint level,
                      GLint xoffset, GLint yoffset,
                      GLsizei width, GLsizei height,
                      GLenum format, GLenum type,
                      const GLvoid *pixels )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   texsubimage(ctx, 2, target, level,
+               xoffset, yoffset, 0,
+               width, height, 1,
+               format, type, pixels);
+}
+
+
+
+void GLAPIENTRY
+_mesa_TexSubImage3D( GLenum target, GLint level,
+                     GLint xoffset, GLint yoffset, GLint zoffset,
+                     GLsizei width, GLsizei height, GLsizei depth,
+                     GLenum format, GLenum type,
+                     const GLvoid *pixels )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   texsubimage(ctx, 3, target, level,
+               xoffset, yoffset, zoffset,
+               width, height, depth,
+               format, type, pixels);
+}
+
+
+
+/**
+ * Implement the glCopyTexImage1/2D() functions.
+ */
+static void
+copyteximage(struct gl_context *ctx, GLuint dims,
+             GLenum target, GLint level, GLenum internalFormat,
+             GLint x, GLint y, GLsizei width, GLsizei height, GLint border )
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
-   GET_CURRENT_CONTEXT(ctx);
+   const GLuint face = _mesa_tex_target_to_face(target);
+
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexSubImage2D %s %d %d %d %d %d %s %s %p\n",
+      _mesa_debug(ctx, "glCopyTexImage%uD %s %d %s %d %d %d %d %d\n",
+                  dims,
                   _mesa_lookup_enum_by_nr(target), level,
-                  xoffset, yoffset, width, height,
-                  _mesa_lookup_enum_by_nr(format),
-                  _mesa_lookup_enum_by_nr(type), pixels);
+                  _mesa_lookup_enum_by_nr(internalFormat),
+                  x, y, width, height, border);
 
-   if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
+   if (ctx->NewState & NEW_COPY_TEX_STATE)
       _mesa_update_state(ctx);
 
-   if (subtexture_error_check(ctx, 2, target, level, xoffset, yoffset, 0,
-                             width, height, 1, format, type)) {
-      return;   /* error was detected */
-   }
+   if (copytexture_error_check(ctx, dims, target, level, internalFormat,
+                               width, height, border))
+      return;
 
    texObj = _mesa_get_current_tex_object(ctx, target);
 
+   if (border && ctx->Const.StripTextureBorder) {
+      x += border;
+      width -= border * 2;
+      if (dims == 2) {
+        y += border;
+        height -= border * 2;
+      }
+      border = 0;
+   }
+
    _mesa_lock_texture(ctx, texObj);
    {
-      texImage = _mesa_select_tex_image(ctx, texObj, target, level);
+      texImage = _mesa_get_tex_image(ctx, texObj, target, level);
 
-      if (subtexture_error_check2(ctx, 2, target, level, xoffset, yoffset, 0,
-                                 width, height, 1, format, type, texImage)) {
-        /* error was recorded */
+      if (!texImage) {
+        _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyTexImage%uD", dims);
       }
-      else if (width > 0 && height >= 0) {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
-         yoffset += texImage->Border;
+      else {
+         /* choose actual hw format */
+         gl_format texFormat = _mesa_choose_texture_format(ctx, texObj,
+                                                           target, level,
+                                                           internalFormat,
+                                                           GL_NONE, GL_NONE);
 
-         ASSERT(ctx->Driver.TexSubImage2D);
-         ctx->Driver.TexSubImage2D(ctx, target, level, xoffset, yoffset,
-                                   width, height, format, type, pixels,
-                                   &ctx->Unpack, texObj, texImage);
+         if (legal_texture_size(ctx, texFormat, width, height, 1)) {
+            GLint srcX = x, srcY = y, dstX = 0, dstY = 0;
 
-         check_gen_mipmap(ctx, target, texObj, level);
+            /* Free old texture image */
+            ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
 
-         ctx->NewState |= _NEW_TEXTURE;
-      }
-   }
-   _mesa_unlock_texture(ctx, texObj);
-}
-
-
-
-void GLAPIENTRY
-_mesa_TexSubImage3D( GLenum target, GLint level,
-                     GLint xoffset, GLint yoffset, GLint zoffset,
-                     GLsizei width, GLsizei height, GLsizei depth,
-                     GLenum format, GLenum type,
-                     const GLvoid *pixels )
-{
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexSubImage3D %s %d %d %d %d %d %d %d %s %s %p\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  xoffset, yoffset, zoffset, width, height, depth,
-                  _mesa_lookup_enum_by_nr(format),
-                  _mesa_lookup_enum_by_nr(type), pixels);
-
-   if (ctx->NewState & _MESA_NEW_TRANSFER_STATE)
-      _mesa_update_state(ctx);
-
-   if (subtexture_error_check(ctx, 3, target, level, xoffset, yoffset, zoffset,
-                              width, height, depth, format, type)) {
-      return;   /* error was detected */
-   }
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
+            _mesa_init_teximage_fields(ctx, target, texImage, width, height, 1,
+                                       border, internalFormat, texFormat);
 
-   _mesa_lock_texture(ctx, texObj);
-   {
-      texImage = _mesa_select_tex_image(ctx, texObj, target, level);
+            /* Allocate texture memory (no pixel data yet) */
+            if (dims == 1) {
+               ctx->Driver.TexImage1D(ctx, target, level, internalFormat,
+                                      width, border, GL_NONE, GL_NONE, NULL,
+                                      &ctx->Unpack, texObj, texImage);
+            }
+            else {
+               ctx->Driver.TexImage2D(ctx, target, level, internalFormat,
+                                      width, height, border, GL_NONE, GL_NONE,
+                                      NULL, &ctx->Unpack, texObj, texImage);
+            }
 
-      if (subtexture_error_check2(ctx, 3, target, level,
-                                  xoffset, yoffset, zoffset,
-                                 width, height, depth,
-                                  format, type, texImage)) {
-         /* error was recorded */
-      }
-      else if (width > 0 && height > 0 && height > 0) {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
-         yoffset += texImage->Border;
-         zoffset += texImage->Border;
+            if (_mesa_clip_copytexsubimage(ctx, &dstX, &dstY, &srcX, &srcY,
+                                           &width, &height)) {
+               if (dims == 1)
+                  ctx->Driver.CopyTexSubImage1D(ctx, target, level, dstX,
+                                                srcX, srcY, width);
+                                                
+               else
+                  ctx->Driver.CopyTexSubImage2D(ctx, target, level, dstX, dstY,
+                                                srcX, srcY, width, height);
+            }
 
-         ASSERT(ctx->Driver.TexSubImage3D);
-         ctx->Driver.TexSubImage3D(ctx, target, level,
-                                   xoffset, yoffset, zoffset,
-                                   width, height, depth,
-                                   format, type, pixels,
-                                   &ctx->Unpack, texObj, texImage );
+            check_gen_mipmap(ctx, target, texObj, level);
 
-         check_gen_mipmap(ctx, target, texObj, level);
+            update_fbo_texture(ctx, texObj, face, level);
 
-         ctx->NewState |= _NEW_TEXTURE;
+            /* state update */
+            texObj->_Complete = GL_FALSE;
+            ctx->NewState |= _NEW_TEXTURE;
+         }
+         else {
+            /* probably too large of image */
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyTexImage%uD", dims);
+         }
       }
    }
    _mesa_unlock_texture(ctx, texObj);
@@ -2857,65 +2867,8 @@ _mesa_CopyTexImage1D( GLenum target, GLint level,
                       GLint x, GLint y,
                       GLsizei width, GLint border )
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
-   const GLuint face = _mesa_tex_target_to_face(target);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCopyTexImage1D %s %d %s %d %d %d %d\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  x, y, width, border);
-
-   if (ctx->NewState & NEW_COPY_TEX_STATE)
-      _mesa_update_state(ctx);
-
-   if (copytexture_error_check(ctx, 1, target, level, internalFormat,
-                               width, 1, border))
-      return;
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-
-   _mesa_lock_texture(ctx, texObj);
-   {
-      texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-      if (!texImage) {
-        _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyTexImage1D");
-      }
-      else {
-         if (texImage->Data) {
-            ctx->Driver.FreeTexImageData( ctx, texImage );
-         }
-
-         ASSERT(texImage->Data == NULL);
-
-         clear_teximage_fields(texImage); /* not really needed, but helpful */
-         _mesa_init_teximage_fields(ctx, target, texImage, width, 1, 1,
-                                    border, internalFormat);
-
-         _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                     internalFormat, GL_NONE, GL_NONE);
-
-         ASSERT(ctx->Driver.CopyTexImage1D);
-         ctx->Driver.CopyTexImage1D(ctx, target, level, internalFormat,
-                                    x, y, width, border);
-
-         ASSERT(texImage->TexFormat);
-
-         _mesa_set_fetch_functions(texImage, 1);
-
-         check_gen_mipmap(ctx, target, texObj, level);
-
-         update_fbo_texture(ctx, texObj, face, level);
-
-         /* state update */
-         texObj->_Complete = GL_FALSE;
-         ctx->NewState |= _NEW_TEXTURE;
-      }
-   }
-   _mesa_unlock_texture(ctx, texObj);
+   copyteximage(ctx, 1, target, level, internalFormat, x, y, width, 1, border);
 }
 
 
@@ -2925,91 +2878,36 @@ _mesa_CopyTexImage2D( GLenum target, GLint level, GLenum internalFormat,
                       GLint x, GLint y, GLsizei width, GLsizei height,
                       GLint border )
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
-   const GLuint face = _mesa_tex_target_to_face(target);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCopyTexImage2D %s %d %s %d %d %d %d %d\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  x, y, width, height, border);
-
-   if (ctx->NewState & NEW_COPY_TEX_STATE)
-      _mesa_update_state(ctx);
-
-   if (copytexture_error_check(ctx, 2, target, level, internalFormat,
-                               width, height, border))
-      return;
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-
-   _mesa_lock_texture(ctx, texObj);
-   {
-      texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-
-      if (!texImage) {
-        _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyTexImage2D");
-      }
-      else {
-         if (texImage->Data) {
-            ctx->Driver.FreeTexImageData( ctx, texImage );
-         }
-
-         ASSERT(texImage->Data == NULL);
-
-         clear_teximage_fields(texImage); /* not really needed, but helpful */
-         _mesa_init_teximage_fields(ctx, target, texImage,
-                                    width, height, 1,
-                                    border, internalFormat);
-
-         _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                     internalFormat, GL_NONE, GL_NONE);
-
-         ASSERT(ctx->Driver.CopyTexImage2D);
-         ctx->Driver.CopyTexImage2D(ctx, target, level, internalFormat,
-                                    x, y, width, height, border);
-
-         ASSERT(texImage->TexFormat);
-
-         _mesa_set_fetch_functions(texImage, 2);
-
-         check_gen_mipmap(ctx, target, texObj, level);
-
-         update_fbo_texture(ctx, texObj, face, level);
-
-         /* state update */
-         texObj->_Complete = GL_FALSE;
-         ctx->NewState |= _NEW_TEXTURE;
-      }
-   }
-   _mesa_unlock_texture(ctx, texObj);
+   copyteximage(ctx, 2, target, level, internalFormat,
+                x, y, width, height, border);
 }
 
 
-void GLAPIENTRY
-_mesa_CopyTexSubImage1D( GLenum target, GLint level,
-                         GLint xoffset, GLint x, GLint y, GLsizei width )
+
+/**
+ * Implementation for glCopyTexSubImage1/2/3D() functions.
+ */
+static void
+copytexsubimage(struct gl_context *ctx, GLuint dims, GLenum target, GLint level,
+                GLint xoffset, GLint yoffset, GLint zoffset,
+                GLint x, GLint y, GLsizei width, GLsizei height)
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
-   GLint yoffset = 0;
-   GLsizei height = 1;
 
-   GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCopyTexSubImage1D %s %d %d %d %d %d\n",
+      _mesa_debug(ctx, "glCopyTexSubImage%uD %s %d %d %d %d %d %d %d %d\n",
+                  dims,
                   _mesa_lookup_enum_by_nr(target),
-                  level, xoffset, x, y, width);
+                  level, xoffset, yoffset, zoffset, x, y, width, height);
 
    if (ctx->NewState & NEW_COPY_TEX_STATE)
       _mesa_update_state(ctx);
 
-   if (copytexsubimage_error_check1(ctx, 1, target, level))
+   if (copytexsubimage_error_check1(ctx, dims, target, level))
       return;
 
    texObj = _mesa_get_current_tex_object(ctx, target);
@@ -3018,19 +2916,43 @@ _mesa_CopyTexSubImage1D( GLenum target, GLint level,
    {
       texImage = _mesa_select_tex_image(ctx, texObj, target, level);
 
-      if (copytexsubimage_error_check2(ctx, 1, target, level,
-                                      xoffset, 0, 0, width, 1, texImage)) {
-         /* error was recorded */
+      if (copytexsubimage_error_check2(ctx, dims, target, level, xoffset, yoffset,
+                                      zoffset, width, height, texImage)) {
+         /* error was recored */
       }
       else {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
+         /* If we have a border, offset=-1 is legal.  Bias by border width. */
+         switch (dims) {
+         case 3:
+            zoffset += texImage->Border;
+            /* fall-through */
+         case 2:
+            yoffset += texImage->Border;
+            /* fall-through */
+         case 1:
+            xoffset += texImage->Border;
+         }
 
          if (_mesa_clip_copytexsubimage(ctx, &xoffset, &yoffset, &x, &y,
                                         &width, &height)) {
-            ASSERT(ctx->Driver.CopyTexSubImage1D);
-            ctx->Driver.CopyTexSubImage1D(ctx, target, level,
-                                          xoffset, x, y, width);
+            switch (dims) {
+            case 1:
+               ctx->Driver.CopyTexSubImage1D(ctx, target, level,
+                                             xoffset, x, y, width);
+               break;
+            case 2:
+               ctx->Driver.CopyTexSubImage2D(ctx, target, level,
+                                             xoffset, yoffset,
+                                             x, y, width, height);
+               break;
+            case 3:
+               ctx->Driver.CopyTexSubImage3D(ctx, target, level,
+                                             xoffset, yoffset, zoffset,
+                                             x, y, width, height);
+               break;
+            default:
+               _mesa_problem(ctx, "bad dims in copytexsubimage()");
+            }
 
             check_gen_mipmap(ctx, target, texObj, level);
 
@@ -3042,57 +2964,24 @@ _mesa_CopyTexSubImage1D( GLenum target, GLint level,
 }
 
 
+void GLAPIENTRY
+_mesa_CopyTexSubImage1D( GLenum target, GLint level,
+                         GLint xoffset, GLint x, GLint y, GLsizei width )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   copytexsubimage(ctx, 1, target, level, xoffset, 0, 0, x, y, width, 1);
+}
+
+
 
 void GLAPIENTRY
 _mesa_CopyTexSubImage2D( GLenum target, GLint level,
                          GLint xoffset, GLint yoffset,
                          GLint x, GLint y, GLsizei width, GLsizei height )
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCopyTexSubImage2D %s %d %d %d %d %d %d %d\n",
-                  _mesa_lookup_enum_by_nr(target),
-                  level, xoffset, yoffset, x, y, width, height);
-
-   if (ctx->NewState & NEW_COPY_TEX_STATE)
-      _mesa_update_state(ctx);
-
-   if (copytexsubimage_error_check1(ctx, 2, target, level))
-      return;
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-
-   _mesa_lock_texture(ctx, texObj);
-   {
-      texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-
-      if (copytexsubimage_error_check2(ctx, 2, target, level,
-                                       xoffset, yoffset, 0,
-                                      width, height, texImage)) {
-         /* error was recorded */
-      }
-      else {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
-         yoffset += texImage->Border;
-
-         if (_mesa_clip_copytexsubimage(ctx, &xoffset, &yoffset, &x, &y,
-                                        &width, &height)) {
-            ASSERT(ctx->Driver.CopyTexSubImage2D);
-            ctx->Driver.CopyTexSubImage2D(ctx, target, level, xoffset, yoffset,
-                                          x, y, width, height);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-   }
-   _mesa_unlock_texture(ctx, texObj);
+   copytexsubimage(ctx, 2, target, level, xoffset, yoffset, 0, x, y,
+                   width, height);
 }
 
 
@@ -3102,52 +2991,9 @@ _mesa_CopyTexSubImage3D( GLenum target, GLint level,
                          GLint xoffset, GLint yoffset, GLint zoffset,
                          GLint x, GLint y, GLsizei width, GLsizei height )
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCopyTexSubImage3D %s %d %d %d %d %d %d %d %d\n",
-                  _mesa_lookup_enum_by_nr(target),
-                  level, xoffset, yoffset, zoffset, x, y, width, height);
-
-   if (ctx->NewState & NEW_COPY_TEX_STATE)
-      _mesa_update_state(ctx);
-
-   if (copytexsubimage_error_check1(ctx, 3, target, level))
-      return;
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-
-   _mesa_lock_texture(ctx, texObj);
-   {
-      texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-
-      if (copytexsubimage_error_check2(ctx, 3, target, level, xoffset, yoffset,
-                                      zoffset, width, height, texImage)) {
-         /* error was recored */
-      }
-      else {
-         /* If we have a border, xoffset=-1 is legal.  Bias by border width */
-         xoffset += texImage->Border;
-         yoffset += texImage->Border;
-         zoffset += texImage->Border;
-
-         if (_mesa_clip_copytexsubimage(ctx, &xoffset, &yoffset, &x, &y,
-                                        &width, &height)) {
-            ASSERT(ctx->Driver.CopyTexSubImage3D);
-            ctx->Driver.CopyTexSubImage3D(ctx, target, level,
-                                          xoffset, yoffset, zoffset,
-                                          x, y, width, height);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-   }
-   _mesa_unlock_texture(ctx, texObj);
+   copytexsubimage(ctx, 3, target, level, xoffset, yoffset, zoffset,
+                   x, y, width, height);
 }
 
 
@@ -3183,6 +3029,7 @@ get_compressed_block_size(GLenum glformat, GLuint *bw, GLuint *bh)
 
 /**
  * Error checking for glCompressedTexImage[123]D().
+ * \param reason  returns reason for error, if any
  * \return error code or GL_NO_ERROR.
  */
 static GLenum
@@ -3190,96 +3037,152 @@ compressed_texture_error_check(struct gl_context *ctx, GLint dimensions,
                                GLenum target, GLint level,
                                GLenum internalFormat, GLsizei width,
                                GLsizei height, GLsizei depth, GLint border,
-                               GLsizei imageSize)
+                               GLsizei imageSize, char **reason)
 {
-   GLint expectedSize, maxLevels = 0, maxTextureSize;
+   const GLenum proxyTarget = get_proxy_target(target);
+   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
+   GLint expectedSize;
+   GLenum choose_format;
+   GLenum choose_type;
+   GLenum proxy_format;
 
-   if (dimensions == 1) {
-      /* 1D compressed textures not allowed */
-      return GL_INVALID_ENUM;
-   }
-   else if (dimensions == 2) {
-      if (target == GL_PROXY_TEXTURE_2D) {
-         maxLevels = ctx->Const.MaxTextureLevels;
-      }
-      else if (target == GL_TEXTURE_2D) {
-         maxLevels = ctx->Const.MaxTextureLevels;
-      }
-      else if (target == GL_PROXY_TEXTURE_CUBE_MAP_ARB) {
-         if (!ctx->Extensions.ARB_texture_cube_map)
-            return GL_INVALID_ENUM; /*target*/
-         maxLevels = ctx->Const.MaxCubeTextureLevels;
-      }
-      else if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-               target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) {
-         if (!ctx->Extensions.ARB_texture_cube_map)
-            return GL_INVALID_ENUM; /*target*/
-         maxLevels = ctx->Const.MaxCubeTextureLevels;
-      }
-      else {
-         return GL_INVALID_ENUM; /*target*/
-      }
-   }
-   else if (dimensions == 3) {
-      /* 3D compressed textures not allowed */
+   *reason = ""; /* no error */
+
+   if (!target_can_be_compressed(ctx, target, internalFormat)) {
+      *reason = "target";
       return GL_INVALID_ENUM;
    }
-   else {
-      assert(0);
+
+   /* This will detect any invalid internalFormat value */
+   if (!_mesa_is_compressed_format(ctx, internalFormat)) {
+      *reason = "internalFormat";
       return GL_INVALID_ENUM;
    }
 
-   maxTextureSize = 1 << (maxLevels - 1);
+   switch (internalFormat) {
+#if FEATURE_ES
+   case GL_PALETTE4_RGB8_OES:
+   case GL_PALETTE4_RGBA8_OES:
+   case GL_PALETTE4_R5_G6_B5_OES:
+   case GL_PALETTE4_RGBA4_OES:
+   case GL_PALETTE4_RGB5_A1_OES:
+   case GL_PALETTE8_RGB8_OES:
+   case GL_PALETTE8_RGBA8_OES:
+   case GL_PALETTE8_R5_G6_B5_OES:
+   case GL_PALETTE8_RGBA4_OES:
+   case GL_PALETTE8_RGB5_A1_OES:
+      _mesa_cpal_compressed_format_type(internalFormat, &choose_format,
+                                       &choose_type);
+      proxy_format = choose_format;
 
-   /* This will detect any invalid internalFormat value */
-   if (!_mesa_is_compressed_format(ctx, internalFormat))
-      return GL_INVALID_ENUM;
+      /* check level */
+      if (level > 0 || level < -maxLevels) {
+        *reason = "level";
+        return GL_INVALID_VALUE;
+      }
 
-   /* This should really never fail */
-   if (_mesa_base_tex_format(ctx, internalFormat) < 0)
-      return GL_INVALID_ENUM;
+      if (dimensions != 2) {
+        *reason = "compressed paletted textures must be 2D";
+        return GL_INVALID_OPERATION;
+      }
 
-   if (border != 0)
-      return GL_INVALID_VALUE;
+      /* Figure out the expected texture size (in bytes).  This will be
+       * checked against the actual size later.
+       */
+      expectedSize = _mesa_cpal_compressed_size(level, internalFormat,
+                                               width, height);
+
+      /* This is for the benefit of the TestProxyTexImage below.  It expects
+       * level to be non-negative.  OES_compressed_paletted_texture uses a
+       * weird mechanism where the level specified to glCompressedTexImage2D
+       * is -(n-1) number of levels in the texture, and the data specifies the
+       * complete mipmap stack.  This is done to ensure the palette is the
+       * same for all levels.
+       */
+      level = -level;
+      break;
+#endif
 
-   /*
-    * XXX We should probably use the proxy texture error check function here.
-    */
-   if (width < 1 || width > maxTextureSize ||
-       (!ctx->Extensions.ARB_texture_non_power_of_two && !_mesa_is_pow_two(width)))
-      return GL_INVALID_VALUE;
+   default:
+      choose_format = GL_NONE;
+      choose_type = GL_NONE;
+      proxy_format = internalFormat;
 
-   if ((height < 1 || height > maxTextureSize ||
-       (!ctx->Extensions.ARB_texture_non_power_of_two && !_mesa_is_pow_two(height)))
-       && dimensions > 1)
-      return GL_INVALID_VALUE;
+      /* check level */
+      if (level < 0 || level >= maxLevels) {
+        *reason = "level";
+        return GL_INVALID_VALUE;
+      }
 
-   if ((depth < 1 || depth > maxTextureSize ||
-       (!ctx->Extensions.ARB_texture_non_power_of_two && !_mesa_is_pow_two(depth)))
-       && dimensions > 2)
+      /* Figure out the expected texture size (in bytes).  This will be
+       * checked against the actual size later.
+       */
+      expectedSize = compressed_tex_size(width, height, depth, internalFormat);
+      break;
+   }
+
+   /* This should really never fail */
+   if (_mesa_base_tex_format(ctx, internalFormat) < 0) {
+      *reason = "internalFormat";
+      return GL_INVALID_ENUM;
+   }
+
+   /* No compressed formats support borders at this time */
+   if (border != 0) {
+      *reason = "border != 0";
       return GL_INVALID_VALUE;
+   }
 
    /* For cube map, width must equal height */
-   if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-       target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB && width != height)
+   if (_mesa_is_cube_face(target) && width != height) {
+      *reason = "width != height";
       return GL_INVALID_VALUE;
+   }
 
-   if (level < 0 || level >= maxLevels)
-      return GL_INVALID_VALUE;
+   /* check image size against compression block size */
+   {
+      gl_format texFormat =
+         ctx->Driver.ChooseTextureFormat(ctx, proxy_format,
+                                        choose_format, choose_type);
+      GLuint bw, bh;
 
-   expectedSize = compressed_tex_size(width, height, depth, internalFormat);
-   if (expectedSize != imageSize)
+      _mesa_get_format_block_size(texFormat, &bw, &bh);
+      if ((width > bw && width % bw > 0) ||
+          (height > bh && height % bh > 0)) {
+         /*
+          * Per GL_ARB_texture_compression:  GL_INVALID_OPERATION is
+          * generated [...] if any parameter combinations are not
+          * supported by the specific compressed internal format. 
+          */
+         *reason = "invalid width or height for compression format";
+         return GL_INVALID_OPERATION;
+      }
+   }
+
+   /* check image sizes */
+   if (!ctx->Driver.TestProxyTexImage(ctx, proxyTarget, level,
+                                     proxy_format, choose_format,
+                                     choose_type,
+                                     width, height, depth, border)) {
+      /* See error comment above */
+      *reason = "invalid width, height or format";
+      return GL_INVALID_OPERATION;
+   }
+
+   /* check image size in bytes */
+   if (expectedSize != imageSize) {
+      /* Per GL_ARB_texture_compression:  GL_INVALID_VALUE is generated [...]
+       * if <imageSize> is not consistent with the format, dimensions, and
+       * contents of the specified image.
+       */
+      *reason = "imageSize inconsistant with width/height/format";
       return GL_INVALID_VALUE;
+   }
 
-#if FEATURE_EXT_texture_sRGB
-   if ((internalFormat == GL_COMPRESSED_SRGB_S3TC_DXT1_EXT ||
-        internalFormat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT ||
-        internalFormat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT ||
-        internalFormat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT)
-       && border != 0) {
+   if (!mutable_tex_object(ctx, target)) {
+      *reason = "immutable texture";
       return GL_INVALID_OPERATION;
    }
-#endif
 
    return GL_NO_ERROR;
 }
@@ -3319,8 +3222,7 @@ compressed_subtexture_error_check(struct gl_context *ctx, GLint dimensions,
             return GL_INVALID_ENUM; /*target*/
          maxLevels = ctx->Const.MaxCubeTextureLevels;
       }
-      else if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-               target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) {
+      else if (_mesa_is_cube_face(target)) {
          if (!ctx->Extensions.ARB_texture_cube_map)
             return GL_INVALID_ENUM; /*target*/
          maxLevels = ctx->Const.MaxCubeTextureLevels;
@@ -3389,6 +3291,13 @@ compressed_subtexture_error_check2(struct gl_context *ctx, GLuint dims,
       return GL_TRUE;
    }
 
+   if (compressedteximage_only_format(ctx, format)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glCompressedTexSubImage%uD(format=0x%x cannot be updated)"
+                  , dims, format);
+      return GL_TRUE;
+   }
+
    if (((width == 1 || width == 2) &&
         width != (GLsizei) texImage->Width) ||
        (width > (GLsizei) texImage->Width)) {
@@ -3407,277 +3316,111 @@ compressed_subtexture_error_check2(struct gl_context *ctx, GLuint dims,
       }
    }
 
-   if (dims >= 3) {
-      if (((depth == 1 || depth == 2) &&
-           depth != (GLsizei) texImage->Depth) ||
-          (depth > (GLsizei) texImage->Depth)) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glCompressedTexSubImage%uD(depth=%d)", dims, depth);
-         return GL_TRUE;
-      }
-   }
-
-   return GL_FALSE;
-}
-
-
-
-void GLAPIENTRY
-_mesa_CompressedTexImage1DARB(GLenum target, GLint level,
-                              GLenum internalFormat, GLsizei width,
-                              GLint border, GLsizei imageSize,
-                              const GLvoid *data)
-{
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCompressedTexImage1DARB %s %d %s %d %d %d %p\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  width, border, imageSize, data);
-
-   if (target == GL_TEXTURE_1D) {
-      /* non-proxy target */
-      struct gl_texture_object *texObj;
-      struct gl_texture_image *texImage;
-      GLenum error = compressed_texture_error_check(ctx, 1, target, level,
-                               internalFormat, width, 1, 1, border, imageSize);
-      if (error) {
-         _mesa_error(ctx, error, "glCompressedTexImage1D");
-         return;
-      }
-
-      texObj = _mesa_get_current_tex_object(ctx, target);
-
-      _mesa_lock_texture(ctx, texObj);
-      {
-        texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-        if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage1D");
-        }
-         else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
-            }
-            ASSERT(texImage->Data == NULL);
-
-            _mesa_init_teximage_fields(ctx, target, texImage, width, 1, 1,
-                                       border, internalFormat);
-
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
-
-            ASSERT(ctx->Driver.CompressedTexImage1D);
-            ctx->Driver.CompressedTexImage1D(ctx, target, level,
-                                             internalFormat, width, border,
-                                             imageSize, data,
-                                             texObj, texImage);
-
-            _mesa_set_fetch_functions(texImage, 1);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-   else if (target == GL_PROXY_TEXTURE_1D) {
-      /* Proxy texture: check for errors and update proxy state */
-      GLenum error = compressed_texture_error_check(ctx, 1, target, level,
-                               internalFormat, width, 1, 1, border, imageSize);
-      if (!error) {
-         ASSERT(ctx->Driver.TestProxyTexImage);
-         error = !(*ctx->Driver.TestProxyTexImage)(ctx, target, level,
-                                             internalFormat, GL_NONE, GL_NONE,
-                                             width, 1, 1, border);
-      }
-      if (error) {
-         /* if error, clear all proxy texture image parameters */
-         struct gl_texture_image *texImage;
-         texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-         if (texImage)
-            clear_teximage_fields(texImage);
-      }
-      else {
-         /* store the teximage parameters */
-         struct gl_texture_object *texObj;
-         struct gl_texture_image *texImage;
-
-         texObj = _mesa_get_current_tex_object(ctx, target);
-
-        _mesa_lock_texture(ctx, texObj);
-        {
-           texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-           _mesa_init_teximage_fields(ctx, target, texImage, width, 1, 1,
-                                      border, internalFormat);
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
-        }
-        _mesa_unlock_texture(ctx, texObj);
+   if (dims >= 3) {
+      if (((depth == 1 || depth == 2) &&
+           depth != (GLsizei) texImage->Depth) ||
+          (depth > (GLsizei) texImage->Depth)) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glCompressedTexSubImage%uD(depth=%d)", dims, depth);
+         return GL_TRUE;
       }
    }
-   else {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glCompressedTexImage1D(target)");
-      return;
-   }
+
+   return GL_FALSE;
 }
 
-void GLAPIENTRY
-_mesa_CompressedTexImage2DARB(GLenum target, GLint level,
-                              GLenum internalFormat, GLsizei width,
-                              GLsizei height, GLint border, GLsizei imageSize,
-                              const GLvoid *data)
+
+/**
+ * Implementation of the glCompressedTexImage1/2/3D() functions.
+ */
+static void
+compressedteximage(struct gl_context *ctx, GLuint dims,
+                   GLenum target, GLint level,
+                   GLenum internalFormat, GLsizei width,
+                   GLsizei height, GLsizei depth, GLint border,
+                   GLsizei imageSize, const GLvoid *data)
 {
-   GET_CURRENT_CONTEXT(ctx);
+   GLenum error;
+   char *reason = "";
+
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCompressedTexImage2DARB %s %d %s %d %d %d %d %p\n",
+      _mesa_debug(ctx,
+                  "glCompressedTexImage%uDARB %s %d %s %d %d %d %d %d %p\n",
+                  dims,
                   _mesa_lookup_enum_by_nr(target), level,
                   _mesa_lookup_enum_by_nr(internalFormat),
-                  width, height, border, imageSize, data);
+                  width, height, depth, border, imageSize, data);
 
-#if FEATURE_ES
-   switch (internalFormat) {
-   case GL_PALETTE4_RGB8_OES:
-   case GL_PALETTE4_RGBA8_OES:
-   case GL_PALETTE4_R5_G6_B5_OES:
-   case GL_PALETTE4_RGBA4_OES:
-   case GL_PALETTE4_RGB5_A1_OES:
-   case GL_PALETTE8_RGB8_OES:
-   case GL_PALETTE8_RGBA8_OES:
-   case GL_PALETTE8_R5_G6_B5_OES:
-   case GL_PALETTE8_RGBA4_OES:
-   case GL_PALETTE8_RGB5_A1_OES:
-      _mesa_cpal_compressed_teximage2d(target, level, internalFormat,
-                                      width, height, imageSize, data);
+   /* check target */
+   if (!legal_teximage_target(ctx, dims, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glCompressedTexImage%uD(target=%s)",
+                  dims, _mesa_lookup_enum_by_nr(target));
       return;
    }
-#endif
 
-   if (target == GL_TEXTURE_2D ||
-       (ctx->Extensions.ARB_texture_cube_map &&
-        target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
-        target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) {
-      /* non-proxy target */
-      struct gl_texture_object *texObj;
-      struct gl_texture_image *texImage;
+   error = compressed_texture_error_check(ctx, dims, target, level,
+                                          internalFormat, width, height, depth,
+                                          border, imageSize, &reason);
 
-      GLenum error = compressed_texture_error_check(ctx, 2, target, level,
-                          internalFormat, width, height, 1, border, imageSize);
-      if (error) {
-         _mesa_error(ctx, error, "glCompressedTexImage2D");
+#if FEATURE_ES
+   /* XXX this is kind of a hack */
+   if (!error && dims == 2) {
+      switch (internalFormat) {
+      case GL_PALETTE4_RGB8_OES:
+      case GL_PALETTE4_RGBA8_OES:
+      case GL_PALETTE4_R5_G6_B5_OES:
+      case GL_PALETTE4_RGBA4_OES:
+      case GL_PALETTE4_RGB5_A1_OES:
+      case GL_PALETTE8_RGB8_OES:
+      case GL_PALETTE8_RGBA8_OES:
+      case GL_PALETTE8_R5_G6_B5_OES:
+      case GL_PALETTE8_RGBA4_OES:
+      case GL_PALETTE8_RGB5_A1_OES:
+         _mesa_cpal_compressed_teximage2d(target, level, internalFormat,
+                                          width, height, imageSize, data);
          return;
       }
+   }
+#endif
 
-      texObj = _mesa_get_current_tex_object(ctx, target);
-
-      _mesa_lock_texture(ctx, texObj);
-      {
-        texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-        if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
-        }
-         else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
-            }
-            ASSERT(texImage->Data == NULL);
-
-            _mesa_init_teximage_fields(ctx, target, texImage, width, height, 1,
-                                       border, internalFormat);
+   if (_mesa_is_proxy_texture(target)) {
+      /* Proxy texture: just check for errors and update proxy state */
+      struct gl_texture_image *texImage;
 
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
+      if (!error) {
+         struct gl_texture_object *texObj =
+            _mesa_get_current_tex_object(ctx, target);
+         gl_format texFormat =
+            _mesa_choose_texture_format(ctx, texObj, target, level,
                                         internalFormat, GL_NONE, GL_NONE);
-
-            ASSERT(ctx->Driver.CompressedTexImage2D);
-            ctx->Driver.CompressedTexImage2D(ctx, target, level,
-                                             internalFormat, width, height,
-                                             border, imageSize, data,
-                                             texObj, texImage);
-
-            _mesa_set_fetch_functions(texImage, 2);
-
-            check_gen_mipmap(ctx, target, texObj, level);
-
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
+         if (!legal_texture_size(ctx, texFormat, width, height, depth)) {
+            error = GL_OUT_OF_MEMORY;
          }
       }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-   else if (target == GL_PROXY_TEXTURE_2D ||
-            (target == GL_PROXY_TEXTURE_CUBE_MAP_ARB &&
-             ctx->Extensions.ARB_texture_cube_map)) {
-      /* Proxy texture: check for errors and update proxy state */
-      GLenum error = compressed_texture_error_check(ctx, 2, target, level,
-                          internalFormat, width, height, 1, border, imageSize);
-      if (!error) {
-         ASSERT(ctx->Driver.TestProxyTexImage);
-         error = !(*ctx->Driver.TestProxyTexImage)(ctx, target, level,
-                                              internalFormat, GL_NONE, GL_NONE,
-                                              width, height, 1, border);
-      }
-      if (error) {
-         /* if error, clear all proxy texture image parameters */
-         struct gl_texture_image *texImage;
-         texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-         if (texImage)
+
+      texImage = _mesa_get_proxy_tex_image(ctx, target, level);
+      if (texImage) {
+         if (error) {
+            /* if error, clear all proxy texture image parameters */
             clear_teximage_fields(texImage);
-      }
-      else {
-         /* store the teximage parameters */
-         struct gl_texture_object *texObj;
-         struct gl_texture_image *texImage;
-
-         texObj = _mesa_get_current_tex_object(ctx, target);
-
-        _mesa_lock_texture(ctx, texObj);
-        {
-           texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-           _mesa_init_teximage_fields(ctx, target, texImage, width, height, 1,
-                                      border, internalFormat);
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
-        }
-        _mesa_unlock_texture(ctx, texObj);
+         }
+         else {
+            /* no error: store the teximage parameters */
+            _mesa_init_teximage_fields(ctx, target, texImage, width, height,
+                                       depth, border, internalFormat,
+                                       MESA_FORMAT_NONE);
+         }
       }
    }
    else {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glCompressedTexImage2D(target)");
-      return;
-   }
-}
-
-
-void GLAPIENTRY
-_mesa_CompressedTexImage3DARB(GLenum target, GLint level,
-                              GLenum internalFormat, GLsizei width,
-                              GLsizei height, GLsizei depth, GLint border,
-                              GLsizei imageSize, const GLvoid *data)
-{
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glCompressedTexImage3DARB %s %d %s %d %d %d %d %d %p\n",
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  width, height, depth, border, imageSize, data);
-
-   if (target == GL_TEXTURE_3D) {
       /* non-proxy target */
       struct gl_texture_object *texObj;
       struct gl_texture_image *texImage;
-      GLenum error = compressed_texture_error_check(ctx, 3, target, level,
-                      internalFormat, width, height, depth, border, imageSize);
+
       if (error) {
-         _mesa_error(ctx, error, "glCompressedTexImage3D");
+         _mesa_error(ctx, error, "glCompressedTexImage%uD(%s)", dims, reason);
          return;
       }
 
@@ -3687,79 +3430,102 @@ _mesa_CompressedTexImage3DARB(GLenum target, GLint level,
       {
         texImage = _mesa_get_tex_image(ctx, texObj, target, level);
         if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage3D");
+           _mesa_error(ctx, GL_OUT_OF_MEMORY,
+                        "glCompressedTexImage%uD", dims);
         }
          else {
-            if (texImage->Data) {
-               ctx->Driver.FreeTexImageData( ctx, texImage );
+            gl_format texFormat;
+
+            ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
+
+            texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
+                                                    internalFormat, GL_NONE,
+                                                    GL_NONE);
+
+            if (legal_texture_size(ctx, texFormat, width, height, depth)) {
+               _mesa_init_teximage_fields(ctx, target, texImage,
+                                          width, height, depth,
+                                          border, internalFormat, texFormat);
+
+               switch (dims) {
+               case 1:
+                  ASSERT(ctx->Driver.CompressedTexImage1D);
+                  ctx->Driver.CompressedTexImage1D(ctx, target, level,
+                                                   internalFormat,
+                                                   width,
+                                                   border, imageSize, data,
+                                                   texObj, texImage);
+                  break;
+               case 2:
+                  ASSERT(ctx->Driver.CompressedTexImage2D);
+                  ctx->Driver.CompressedTexImage2D(ctx, target, level,
+                                                   internalFormat,
+                                                   width, height,
+                                                   border, imageSize, data,
+                                                   texObj, texImage);
+                  break;
+               case 3:
+                  ASSERT(ctx->Driver.CompressedTexImage3D);
+                  ctx->Driver.CompressedTexImage3D(ctx, target, level,
+                                                   internalFormat,
+                                                   width, height, depth,
+                                                   border, imageSize, data,
+                                                   texObj, texImage);
+                  break;
+               default:
+                  _mesa_problem(ctx, "bad dims in compressedteximage");
+               }
+
+               check_gen_mipmap(ctx, target, texObj, level);
+
+               /* state update */
+               texObj->_Complete = GL_FALSE;
+               ctx->NewState |= _NEW_TEXTURE;
             }
-            ASSERT(texImage->Data == NULL);
+            else {
+               _mesa_error(ctx, GL_OUT_OF_MEMORY,
+                           "glCompressedTexImage%uD", dims);
+            }
+         }
+      }
+      _mesa_unlock_texture(ctx, texObj);
+   }
+}
 
-            _mesa_init_teximage_fields(ctx, target, texImage,
-                                       width, height, depth,
-                                       border, internalFormat);
 
-            /* Choose actual texture format */
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
+void GLAPIENTRY
+_mesa_CompressedTexImage1DARB(GLenum target, GLint level,
+                              GLenum internalFormat, GLsizei width,
+                              GLint border, GLsizei imageSize,
+                              const GLvoid *data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   compressedteximage(ctx, 1, target, level, internalFormat,
+                      width, 1, 1, border, imageSize, data);
+}
 
-            ASSERT(ctx->Driver.CompressedTexImage3D);
-            ctx->Driver.CompressedTexImage3D(ctx, target, level,
-                                             internalFormat,
-                                             width, height, depth,
-                                             border, imageSize, data,
-                                             texObj, texImage);
 
-            _mesa_set_fetch_functions(texImage, 3);
+void GLAPIENTRY
+_mesa_CompressedTexImage2DARB(GLenum target, GLint level,
+                              GLenum internalFormat, GLsizei width,
+                              GLsizei height, GLint border, GLsizei imageSize,
+                              const GLvoid *data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   compressedteximage(ctx, 2, target, level, internalFormat,
+                      width, height, 1, border, imageSize, data);
+}
 
-            check_gen_mipmap(ctx, target, texObj, level);
 
-            /* state update */
-            texObj->_Complete = GL_FALSE;
-            ctx->NewState |= _NEW_TEXTURE;
-         }
-      }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-   else if (target == GL_PROXY_TEXTURE_3D) {
-      /* Proxy texture: check for errors and update proxy state */
-      GLenum error = compressed_texture_error_check(ctx, 3, target, level,
-                      internalFormat, width, height, depth, border, imageSize);
-      if (!error) {
-         ASSERT(ctx->Driver.TestProxyTexImage);
-         error = !(*ctx->Driver.TestProxyTexImage)(ctx, target, level,
-                                             internalFormat, GL_NONE, GL_NONE,
-                                             width, height, depth, border);
-      }
-      if (error) {
-         /* if error, clear all proxy texture image parameters */
-         struct gl_texture_image *texImage;
-         texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-         if (texImage)
-            clear_teximage_fields(texImage);
-      }
-      else {
-         /* store the teximage parameters */
-         struct gl_texture_object *texObj;
-         struct gl_texture_image *texImage;
-
-         texObj = _mesa_get_current_tex_object(ctx, target);
-
-        _mesa_lock_texture(ctx, texObj);
-        {
-           texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-           _mesa_init_teximage_fields(ctx, target, texImage, width, height,
-                                      depth, border, internalFormat);
-            _mesa_choose_texture_format(ctx, texObj, texImage, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
-        }
-        _mesa_unlock_texture(ctx, texObj);
-      }
-   }
-   else {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glCompressedTexImage3D(target)");
-      return;
-   }
+void GLAPIENTRY
+_mesa_CompressedTexImage3DARB(GLenum target, GLint level,
+                              GLenum internalFormat, GLsizei width,
+                              GLsizei height, GLsizei depth, GLint border,
+                              GLsizei imageSize, const GLvoid *data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   compressedteximage(ctx, 3, target, level, internalFormat,
+                      width, height, depth, border, imageSize, data);
 }
 
 
@@ -3871,3 +3637,270 @@ _mesa_CompressedTexSubImage3DARB(GLenum target, GLint level, GLint xoffset,
 }
 
 
+/**
+ * Helper for glTexBuffer().  Check if internalFormat is legal.  If so,
+ * return the basic data type and number of components for the format.
+ * \param return  GL_TRUE if internalFormat is legal, GL_FALSE otherwise
+ */
+static GLboolean
+get_sized_format_info(const struct gl_context *ctx, GLenum internalFormat,
+                      GLenum *datatype, GLuint *components)
+{
+   switch (internalFormat) {
+   case GL_ALPHA8:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_ALPHA16:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_ALPHA16F_ARB:
+      *datatype = GL_HALF_FLOAT;
+      *components = 1;
+      break;
+   case GL_ALPHA32F_ARB:
+      *datatype = GL_FLOAT;
+      *components = 1;
+      break;
+   case GL_ALPHA8I_EXT:
+      *datatype = GL_BYTE;
+      *components = 1;
+      break;
+   case GL_ALPHA16I_EXT:
+      *datatype = GL_SHORT;
+      *components = 1;
+      break;
+   case GL_ALPHA32I_EXT:
+      *datatype = GL_INT;
+      *components = 1;
+      break;
+   case GL_ALPHA8UI_EXT:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_ALPHA16UI_EXT:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_ALPHA32UI_EXT:
+      *datatype = GL_UNSIGNED_INT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE8:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_LUMINANCE16:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE16F_ARB:
+      *datatype = GL_HALF_FLOAT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE32F_ARB:
+      *datatype = GL_FLOAT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE8I_EXT:
+      *datatype = GL_BYTE;
+      *components = 1;
+      break;
+   case GL_LUMINANCE16I_EXT:
+      *datatype = GL_SHORT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE32I_EXT:
+      *datatype = GL_INT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE8UI_EXT:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_LUMINANCE16UI_EXT:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE32UI_EXT:
+      *datatype = GL_UNSIGNED_INT;
+      *components = 1;
+      break;
+   case GL_LUMINANCE8_ALPHA8:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 2;
+      break;
+   case GL_LUMINANCE16_ALPHA16:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA16F_ARB:
+      *datatype = GL_HALF_FLOAT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA32F_ARB:
+      *datatype = GL_FLOAT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA8I_EXT:
+      *datatype = GL_BYTE;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA16I_EXT:
+      *datatype = GL_SHORT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA32I_EXT:
+      *datatype = GL_INT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA8UI_EXT:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA16UI_EXT:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 2;
+      break;
+   case GL_LUMINANCE_ALPHA32UI_EXT:
+      *datatype = GL_UNSIGNED_INT;
+      *components = 2;
+      break;
+   case GL_INTENSITY8:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_INTENSITY16:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_INTENSITY16F_ARB:
+      *datatype = GL_HALF_FLOAT;
+      *components = 1;
+      break;
+   case GL_INTENSITY32F_ARB:
+      *datatype = GL_FLOAT;
+      *components = 1;
+      break;
+   case GL_INTENSITY8I_EXT:
+      *datatype = GL_BYTE;
+      *components = 1;
+      break;
+   case GL_INTENSITY16I_EXT:
+      *datatype = GL_SHORT;
+      *components = 1;
+      break;
+   case GL_INTENSITY32I_EXT:
+      *datatype = GL_INT;
+      *components = 1;
+      break;
+   case GL_INTENSITY8UI_EXT:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 1;
+      break;
+   case GL_INTENSITY16UI_EXT:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 1;
+      break;
+   case GL_INTENSITY32UI_EXT:
+      *datatype = GL_UNSIGNED_INT;
+      *components = 1;
+      break;
+   case GL_RGBA8:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 4;
+      break;
+   case GL_RGBA16:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 4;
+      break;
+   case GL_RGBA16F_ARB:
+      *datatype = GL_HALF_FLOAT;
+      *components = 4;
+      break;
+   case GL_RGBA32F_ARB:
+      *datatype = GL_FLOAT;
+      *components = 4;
+      break;
+   case GL_RGBA8I_EXT:
+      *datatype = GL_BYTE;
+      *components = 4;
+      break;
+   case GL_RGBA16I_EXT:
+      *datatype = GL_SHORT;
+      *components = 4;
+      break;
+   case GL_RGBA32I_EXT:
+      *datatype = GL_INT;
+      *components = 4;
+      break;
+   case GL_RGBA8UI_EXT:
+      *datatype = GL_UNSIGNED_BYTE;
+      *components = 4;
+      break;
+   case GL_RGBA16UI_EXT:
+      *datatype = GL_UNSIGNED_SHORT;
+      *components = 4;
+      break;
+   case GL_RGBA32UI_EXT:
+      *datatype = GL_UNSIGNED_INT;
+      *components = 4;
+      break;
+   default:
+      return GL_FALSE;
+   }
+
+   if (*datatype == GL_FLOAT && !ctx->Extensions.ARB_texture_float)
+      return GL_FALSE;
+
+   if (*datatype == GL_HALF_FLOAT && !ctx->Extensions.ARB_half_float_pixel)
+      return GL_FALSE;
+
+   return GL_TRUE;
+}
+
+
+/** GL_ARB_texture_buffer_object */
+void GLAPIENTRY
+_mesa_TexBuffer(GLenum target, GLenum internalFormat, GLuint buffer)
+{
+   struct gl_texture_object *texObj;
+   struct gl_buffer_object *bufObj;
+   GLenum dataType;
+   GLuint comps;
+
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (!ctx->Extensions.ARB_texture_buffer_object) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer");
+      return;
+   }
+
+   if (target != GL_TEXTURE_BUFFER_ARB) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(target)");
+      return;
+   }
+
+   if (!get_sized_format_info(ctx, internalFormat, &dataType, &comps)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(internalFormat 0x%x)",
+                  internalFormat);
+      return;
+   }
+
+   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+   if (buffer && !bufObj) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer(buffer %u)", buffer);
+      return;
+   }
+
+   texObj = _mesa_get_current_tex_object(ctx, target);
+
+   _mesa_lock_texture(ctx, texObj);
+   {
+      _mesa_reference_buffer_object(ctx, &texObj->BufferObject, bufObj);
+      texObj->BufferObjectFormat = internalFormat;
+   }
+   _mesa_unlock_texture(ctx, texObj);
+}