mesa: Remove target parameter from dd_function_table::MapBuffer
[mesa.git] / src / mesa / main / texgetimage.c
index 66d01c15d04ffe657928dd00f6684941e45ed59d..a54da7160c71dd4f1d7ffeedbced7fea17a8c9a9 100644 (file)
 #include "context.h"
 #include "formats.h"
 #include "image.h"
+#include "mfeatures.h"
+#include "mtypes.h"
+#include "pack.h"
+#include "pbo.h"
 #include "texgetimage.h"
+#include "texfetch.h"
 #include "teximage.h"
 
 
@@ -63,13 +68,14 @@ type_with_negative_values(GLenum type)
  * glGetTexImage for color index pixels.
  */
 static void
-get_tex_color_index(GLcontext *ctx, GLuint dimensions,
+get_tex_color_index(struct gl_context *ctx, GLuint dimensions,
                     GLenum format, GLenum type, GLvoid *pixels,
                     const struct gl_texture_image *texImage)
 {
    const GLint width = texImage->Width;
    const GLint height = texImage->Height;
    const GLint depth = texImage->Depth;
+   const GLint rowstride = texImage->RowStride;
    const GLuint indexBits =
       _mesa_get_format_bits(texImage->TexFormat, GL_TEXTURE_INDEX_SIZE_EXT);
    const GLbitfield transferOps = 0x0;
@@ -85,14 +91,14 @@ get_tex_color_index(GLcontext *ctx, GLuint dimensions,
 
          if (indexBits == 8) {
             const GLubyte *src = (const GLubyte *) texImage->Data;
-            src += width * (img * texImage->Height + row);
+            src += rowstride * (img * height + row);
             for (col = 0; col < width; col++) {
                indexRow[col] = src[col];
             }
          }
          else if (indexBits == 16) {
             const GLushort *src = (const GLushort *) texImage->Data;
-            src += width * (img * texImage->Height + row);
+            src += rowstride * (img * height + row);
             for (col = 0; col < width; col++) {
                indexRow[col] = src[col];
             }
@@ -111,7 +117,7 @@ get_tex_color_index(GLcontext *ctx, GLuint dimensions,
  * glGetTexImage for depth/Z pixels.
  */
 static void
-get_tex_depth(GLcontext *ctx, GLuint dimensions,
+get_tex_depth(struct gl_context *ctx, GLuint dimensions,
               GLenum format, GLenum type, GLvoid *pixels,
               const struct gl_texture_image *texImage)
 {
@@ -119,10 +125,15 @@ get_tex_depth(GLcontext *ctx, GLuint dimensions,
    const GLint height = texImage->Height;
    const GLint depth = texImage->Depth;
    GLint img, row, col;
+   GLfloat *depthRow = (GLfloat *) malloc(width * sizeof(GLfloat));
+
+   if (!depthRow) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
+      return;
+   }
 
    for (img = 0; img < depth; img++) {
       for (row = 0; row < height; row++) {
-         GLfloat depthRow[MAX_WIDTH];
          void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
                                           width, height, format, type,
                                           img, row, 0);
@@ -134,6 +145,8 @@ get_tex_depth(GLcontext *ctx, GLuint dimensions,
          _mesa_pack_depth_span(ctx, width, dest, type, depthRow, &ctx->Pack);
       }
    }
+
+   free(depthRow);
 }
 
 
@@ -141,13 +154,14 @@ get_tex_depth(GLcontext *ctx, GLuint dimensions,
  * glGetTexImage for depth/stencil pixels.
  */
 static void
-get_tex_depth_stencil(GLcontext *ctx, GLuint dimensions,
+get_tex_depth_stencil(struct gl_context *ctx, GLuint dimensions,
                       GLenum format, GLenum type, GLvoid *pixels,
                       const struct gl_texture_image *texImage)
 {
    const GLint width = texImage->Width;
    const GLint height = texImage->Height;
    const GLint depth = texImage->Depth;
+   const GLint rowstride = texImage->RowStride;
    const GLuint *src = (const GLuint *) texImage->Data;
    GLint img, row;
 
@@ -156,12 +170,12 @@ get_tex_depth_stencil(GLcontext *ctx, GLuint dimensions,
          void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
                                           width, height, format, type,
                                           img, row, 0);
-         _mesa_memcpy(dest, src, width * sizeof(GLuint));
+         memcpy(dest, src, width * sizeof(GLuint));
          if (ctx->Pack.SwapBytes) {
             _mesa_swap4((GLuint *) dest, width);
          }
 
-         src += width * row + width * height * img;
+         src += rowstride;
       }
    }
 }
@@ -171,7 +185,7 @@ get_tex_depth_stencil(GLcontext *ctx, GLuint dimensions,
  * glGetTexImage for YCbCr pixels.
  */
 static void
-get_tex_ycbcr(GLcontext *ctx, GLuint dimensions,
+get_tex_ycbcr(struct gl_context *ctx, GLuint dimensions,
               GLenum format, GLenum type, GLvoid *pixels,
               const struct gl_texture_image *texImage)
 {
@@ -187,7 +201,7 @@ get_tex_ycbcr(GLcontext *ctx, GLuint dimensions,
          void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
                                           width, height, format, type,
                                           img, row, 0);
-         _mesa_memcpy(dest, src, width * sizeof(GLushort));
+         memcpy(dest, src, width * sizeof(GLushort));
 
          /* check for byte swapping */
          if ((texImage->TexFormat == MESA_FORMAT_YCBCR
@@ -207,134 +221,61 @@ get_tex_ycbcr(GLcontext *ctx, GLuint dimensions,
 }
 
 
-#if FEATURE_EXT_texture_sRGB
-
-
-/**
- * Convert a float value from linear space to a
- * non-linear sRGB value in [0, 255].
- * Not terribly efficient.
- */
-static INLINE GLfloat
-linear_to_nonlinear(GLfloat cl)
-{
-   /* can't have values outside [0, 1] */
-   GLfloat cs;
-   if (cl < 0.0031308f) {
-      cs = 12.92f * cl;
-   }
-   else {
-      cs = (GLfloat)(1.055 * _mesa_pow(cl, 0.41666) - 0.055);
-   }
-   return cs;
-}
-
-
 /**
- * glGetTexImagefor sRGB pixels;
- */
-static void
-get_tex_srgb(GLcontext *ctx, GLuint dimensions,
-             GLenum format, GLenum type, GLvoid *pixels,
-             const struct gl_texture_image *texImage)
-{
-   const GLint width = texImage->Width;
-   const GLint height = texImage->Height;
-   const GLint depth = texImage->Depth;
-   const GLbitfield transferOps = 0x0;
-   GLint img, row;
-
-   for (img = 0; img < depth; img++) {
-      for (row = 0; row < height; row++) {
-         void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
-                                          width, height, format, type,
-                                          img, row, 0);
-
-         GLfloat rgba[MAX_WIDTH][4];
-         GLint col;
-
-         /* convert row to RGBA format */
-         for (col = 0; col < width; col++) {
-            texImage->FetchTexelf(texImage, col, row, img, rgba[col]);
-            if (texImage->_BaseFormat == GL_LUMINANCE) {
-               rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
-               rgba[col][GCOMP] = 0.0;
-               rgba[col][BCOMP] = 0.0;
-            }
-            else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
-               rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
-               rgba[col][GCOMP] = 0.0;
-               rgba[col][BCOMP] = 0.0;
-            }
-            else if (texImage->_BaseFormat == GL_RGB ||
-                     texImage->_BaseFormat == GL_RGBA) {
-               rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
-               rgba[col][GCOMP] = linear_to_nonlinear(rgba[col][GCOMP]);
-               rgba[col][BCOMP] = linear_to_nonlinear(rgba[col][BCOMP]);
-            }
-         }
-         _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
-                                    format, type, dest,
-                                    &ctx->Pack, transferOps);
-      }
-   }
-}
-
-
-#else /* FEATURE_EXT_texture_sRGB */
-
-
-static INLINE void
-get_tex_srgb(GLcontext *ctx, GLuint dimensions,
-             GLenum format, GLenum type, GLvoid *pixels,
-             const struct gl_texture_image *texImage)
-{
-   ASSERT_NO_FEATURE();
-}
-
-
-#endif /* FEATURE_EXT_texture_sRGB */
-
-
-/**
- * glGetTexImagefor RGBA, Luminance, etc. pixels.
+ * glGetTexImage for (s)RGBA, Luminance, etc. pixels.
  * This is the slow way since we use texture sampling.
  */
 static void
-get_tex_rgba(GLcontext *ctx, GLuint dimensions,
+get_tex_rgba(struct gl_context *ctx, GLuint dimensions,
              GLenum format, GLenum type, GLvoid *pixels,
-             const struct gl_texture_image *texImage)
+             struct gl_texture_image *texImage)
 {
    const GLint width = texImage->Width;
    const GLint height = texImage->Height;
    const GLint depth = texImage->Depth;
+   const GLenum dataType = _mesa_get_format_datatype(texImage->TexFormat);
    /* Normally, no pixel transfer ops are performed during glGetTexImage.
     * The only possible exception is component clamping to [0,1].
     */
    GLbitfield transferOps = 0x0;
    GLint img, row;
+   GLfloat (*rgba)[4] = (GLfloat (*)[4]) malloc(4 * width * sizeof(GLfloat));
+   const GLboolean is_sampler_srgb_decode =
+       _mesa_get_format_color_encoding(texImage->TexFormat) == GL_SRGB &&
+       texImage->TexObject->Sampler.sRGBDecode == GL_DECODE_EXT;
+
+   if (!rgba) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
+      return;
+   }
+
+   /* Clamping does not apply to GetTexImage (final conversion)?
+    * Looks like we need clamp though when going from format
+    * containing negative values to unsigned format.
+    */
+   if (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA) {
+      transferOps |= IMAGE_CLAMP_BIT;
+   }
+   else if (!type_with_negative_values(type) &&
+            (dataType == GL_FLOAT ||
+             dataType == GL_SIGNED_NORMALIZED)) {
+      transferOps |= IMAGE_CLAMP_BIT;
+   }
+
+   /* glGetTexImage always returns sRGB data for sRGB textures. Make sure the
+    * fetch functions return sRGB data without linearizing it.
+    */
+   if (is_sampler_srgb_decode) {
+      texImage->TexObject->Sampler.sRGBDecode = GL_SKIP_DECODE_EXT;
+      _mesa_set_fetch_functions(texImage, dimensions);
+   }
 
    for (img = 0; img < depth; img++) {
       for (row = 0; row < height; row++) {
          void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
                                           width, height, format, type,
                                           img, row, 0);
-         GLfloat rgba[MAX_WIDTH][4];
          GLint col;
-         GLenum dataType = _mesa_get_format_datatype(texImage->TexFormat);
-
-         /* clamp does not apply to GetTexImage (final conversion)?
-          * Looks like we need clamp though when going from format
-          * containing negative values to unsigned format.
-          */
-         if (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA) {
-            transferOps |= IMAGE_CLAMP_BIT;
-         }
-         else if (!type_with_negative_values(type) &&
-                  (dataType == GL_FLOAT ||
-                   dataType == GL_SIGNED_NORMALIZED)) {
-            transferOps |= IMAGE_CLAMP_BIT;
-         }
 
          for (col = 0; col < width; col++) {
             texImage->FetchTexelf(texImage, col, row, img, rgba[col]);
@@ -363,6 +304,13 @@ get_tex_rgba(GLcontext *ctx, GLuint dimensions,
                                     &ctx->Pack, transferOps);
       }
    }
+
+   if (is_sampler_srgb_decode) {
+      texImage->TexObject->Sampler.sRGBDecode = GL_DECODE_EXT;
+      _mesa_set_fetch_functions(texImage, dimensions);
+   }
+
+   free(rgba);
 }
 
 
@@ -371,7 +319,7 @@ get_tex_rgba(GLcontext *ctx, GLuint dimensions,
  * \return GL_TRUE if done, GL_FALSE otherwise
  */
 static GLboolean
-get_tex_memcpy(GLcontext *ctx, GLenum format, GLenum type, GLvoid *pixels,
+get_tex_memcpy(struct gl_context *ctx, GLenum format, GLenum type, GLvoid *pixels,
                const struct gl_texture_object *texObj,
                const struct gl_texture_image *texImage)
 {
@@ -391,30 +339,43 @@ get_tex_memcpy(GLcontext *ctx, GLenum format, GLenum type, GLvoid *pixels,
         texObj->Target == GL_TEXTURE_RECTANGLE ||
         (texObj->Target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
          texObj->Target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))) {
-      if (texImage->TexFormat == MESA_FORMAT_ARGB8888 &&
+      if ((texImage->TexFormat == MESA_FORMAT_ARGB8888 ||
+             texImage->TexFormat == MESA_FORMAT_SARGB8) &&
           format == GL_BGRA &&
-          type == GL_UNSIGNED_BYTE &&
+          (type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) &&
           !ctx->Pack.SwapBytes &&
           _mesa_little_endian()) {
          memCopy = GL_TRUE;
       }
-      else if (texImage->TexFormat == MESA_FORMAT_AL88 &&
+      else if ((texImage->TexFormat == MESA_FORMAT_AL88 ||
+                  texImage->TexFormat == MESA_FORMAT_SLA8) &&
                format == GL_LUMINANCE_ALPHA &&
                type == GL_UNSIGNED_BYTE &&
                !ctx->Pack.SwapBytes &&
                _mesa_little_endian()) {
          memCopy = GL_TRUE;
       }
-      else if (texImage->TexFormat == MESA_FORMAT_L8 &&
+      else if ((texImage->TexFormat == MESA_FORMAT_L8 ||
+                  texImage->TexFormat == MESA_FORMAT_SL8) &&
                format == GL_LUMINANCE &&
                type == GL_UNSIGNED_BYTE) {
          memCopy = GL_TRUE;
       }
+      else if (texImage->TexFormat == MESA_FORMAT_L16 &&
+               format == GL_LUMINANCE &&
+               type == GL_UNSIGNED_SHORT) {
+         memCopy = GL_TRUE;
+      }
       else if (texImage->TexFormat == MESA_FORMAT_A8 &&
                format == GL_ALPHA &&
                type == GL_UNSIGNED_BYTE) {
          memCopy = GL_TRUE;
       }
+      else if (texImage->TexFormat == MESA_FORMAT_A16 &&
+               format == GL_ALPHA &&
+               type == GL_UNSIGNED_SHORT) {
+         memCopy = GL_TRUE;
+      }
    }
 
    if (memCopy) {
@@ -451,7 +412,7 @@ get_tex_memcpy(GLcontext *ctx, GLenum format, GLenum type, GLvoid *pixels,
  * The texture image must be mapped.
  */
 void
-_mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
+_mesa_get_teximage(struct gl_context *ctx, GLenum target, GLint level,
                    GLenum format, GLenum type, GLvoid *pixels,
                    struct gl_texture_object *texObj,
                    struct gl_texture_image *texImage)
@@ -480,8 +441,7 @@ _mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
        * texture data to the PBO if the PBO is in VRAM along with the texture.
        */
       GLubyte *buf = (GLubyte *)
-         ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
-                               GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
+         ctx->Driver.MapBuffer(ctx, GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
       if (!buf) {
          /* out of memory or other unexpected error */
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage(map PBO failed)");
@@ -508,16 +468,12 @@ _mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
    else if (format == GL_YCBCR_MESA) {
       get_tex_ycbcr(ctx, dimensions, format, type, pixels, texImage);
    }
-   else if (_mesa_get_format_color_encoding(texImage->TexFormat) == GL_SRGB) {
-      get_tex_srgb(ctx, dimensions, format, type, pixels, texImage);
-   }
    else {
       get_tex_rgba(ctx, dimensions, format, type, pixels, texImage);
    }
 
    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
-                              ctx->Pack.BufferObj);
+      ctx->Driver.UnmapBuffer(ctx, ctx->Pack.BufferObj);
    }
 }
 
@@ -528,7 +484,7 @@ _mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
  * All error checking will have been done before this routine is called.
  */
 void
-_mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
+_mesa_get_compressed_teximage(struct gl_context *ctx, GLenum target, GLint level,
                               GLvoid *img,
                               struct gl_texture_object *texObj,
                               struct gl_texture_image *texImage)
@@ -542,8 +498,7 @@ _mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
       /* pack texture image into a PBO */
       GLubyte *buf = (GLubyte *)
-         ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
-                               GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
+         ctx->Driver.MapBuffer(ctx, GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
       if (!buf) {
          /* out of memory or other unexpected error */
          _mesa_error(ctx, GL_OUT_OF_MEMORY,
@@ -560,7 +515,7 @@ _mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
                                                   texImage->Width,
                                                   texImage->Height,
                                                   texImage->Depth);
-      _mesa_memcpy(img, texImage->Data, size);
+      memcpy(img, texImage->Data, size);
    }
    else {
       GLuint bw, bh;
@@ -573,8 +528,7 @@ _mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
    }
 
    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
-                              ctx->Pack.BufferObj);
+      ctx->Driver.UnmapBuffer(ctx, ctx->Pack.BufferObj);
    }
 }
 
@@ -585,12 +539,14 @@ _mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
  * \return GL_TRUE if any error, GL_FALSE if no errors.
  */
 static GLboolean
-getteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
-                        GLenum format, GLenum type, GLvoid *pixels )
+getteximage_error_check(struct gl_context *ctx, GLenum target, GLint level,
+                        GLenum format, GLenum type, GLsizei clientMemSize,
+                        GLvoid *pixels )
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
-   const GLuint maxLevels = _mesa_max_texture_levels(ctx, target);
+   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
+   const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
    GLenum baseFormat;
 
    if (maxLevels == 0) {
@@ -693,17 +649,21 @@ getteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
       return GL_TRUE;
    }
 
-   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      /* packing texture image into a PBO */
-      const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
-      if (!_mesa_validate_pbo_access(dimensions, &ctx->Pack, texImage->Width,
-                                     texImage->Height, texImage->Depth,
-                                     format, type, pixels)) {
+   if (!_mesa_validate_pbo_access(dimensions, &ctx->Pack, texImage->Width,
+                                  texImage->Height, texImage->Depth,
+                                  format, type, clientMemSize, pixels)) {
+      if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetTexImage(out of bounds PBO write)");
-         return GL_TRUE;
+                     "glGetTexImage(out of bounds PBO access)");
+      } else {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glGetnTexImageARB(out of bounds access:"
+                     " bufSize (%d) is too small)", clientMemSize);
       }
+      return GL_TRUE;
+   }
 
+   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
       /* PBO should not be mapped */
       if (_mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
@@ -724,18 +684,20 @@ getteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
  * \param level image level.
  * \param format pixel data format for returned image.
  * \param type pixel data type for returned image.
+ * \param bufSize size of the pixels data buffer.
  * \param pixels returned pixel data.
  */
 void GLAPIENTRY
-_mesa_GetTexImage( GLenum target, GLint level, GLenum format,
-                   GLenum type, GLvoid *pixels )
+_mesa_GetnTexImageARB( GLenum target, GLint level, GLenum format,
+                       GLenum type, GLsizei bufSize, GLvoid *pixels )
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
    GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (getteximage_error_check(ctx, target, level, format, type, pixels)) {
+   if (getteximage_error_check(ctx, target, level, format, type,
+                               bufSize, pixels)) {
       return;
    }
 
@@ -765,18 +727,26 @@ _mesa_GetTexImage( GLenum target, GLint level, GLenum format,
 }
 
 
+void GLAPIENTRY
+_mesa_GetTexImage( GLenum target, GLint level, GLenum format,
+                   GLenum type, GLvoid *pixels )
+{
+   _mesa_GetnTexImageARB(target, level, format, type, INT_MAX, pixels);
+}
+
 
 /**
  * Do error checking for a glGetCompressedTexImage() call.
  * \return GL_TRUE if any error, GL_FALSE if no errors.
  */
 static GLboolean
-getcompressedteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
-                                  GLvoid *img)
+getcompressedteximage_error_check(struct gl_context *ctx, GLenum target,
+                                  GLint level, GLsizei clientMemSize, GLvoid *img)
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
-   const GLuint maxLevels = _mesa_max_texture_levels(ctx, target);
+   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
+   GLuint compressedSize;
 
    if (maxLevels == 0) {
       _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImage(target=0x%x)",
@@ -818,26 +788,31 @@ getcompressedteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
       return GL_TRUE;
    }
 
-   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      GLuint compressedSize;
+   compressedSize = _mesa_format_image_size(texImage->TexFormat,
+                                            texImage->Width,
+                                            texImage->Height,
+                                            texImage->Depth);
 
-      /* make sure PBO is not mapped */
-      if (_mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
+   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
+      /* do bounds checking on writing to client memory */
+      if (clientMemSize < compressedSize) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetCompressedTexImage(PBO is mapped)");
-         return GL_TRUE;
+                     "glGetnCompressedTexImageARB(out of bounds access:"
+                     " bufSize (%d) is too small)", clientMemSize);
       }
-
-      compressedSize = _mesa_format_image_size(texImage->TexFormat,
-                                               texImage->Width,
-                                               texImage->Height,
-                                               texImage->Depth);
-
+   } else {
       /* do bounds checking on PBO write */
       if ((const GLubyte *) img + compressedSize >
           (const GLubyte *) ctx->Pack.BufferObj->Size) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetCompressedTexImage(out of bounds PBO write)");
+                     "glGetCompressedTexImage(out of bounds PBO access)");
+         return GL_TRUE;
+      }
+
+      /* make sure PBO is not mapped */
+      if (_mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glGetCompressedTexImage(PBO is mapped)");
          return GL_TRUE;
       }
    }
@@ -847,14 +822,15 @@ getcompressedteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
 
 
 void GLAPIENTRY
-_mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
+_mesa_GetnCompressedTexImageARB(GLenum target, GLint level, GLsizei bufSize,
+                                GLvoid *img)
 {
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
    GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (getcompressedteximage_error_check(ctx, target, level, img)) {
+   if (getcompressedteximage_error_check(ctx, target, level, bufSize, img)) {
       return;
    }
 
@@ -881,3 +857,9 @@ _mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
    }
    _mesa_unlock_texture(ctx, texObj);
 }
+
+void GLAPIENTRY
+_mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
+{
+   _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
+}