Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  * Version:  7.7
      4  *
      5  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
      6  * Copyright (c) 2009 VMware, Inc.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be included
     16  * in all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     21  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     22  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 
     27 /**
     28  * Code for glGetTexImage() and glGetCompressedTexImage().
     29  */
     30 
     31 
     32 #include "glheader.h"
     33 #include "bufferobj.h"
     34 #include "enums.h"
     35 #include "context.h"
     36 #include "formats.h"
     37 #include "format_unpack.h"
     38 #include "glformats.h"
     39 #include "image.h"
     40 #include "mfeatures.h"
     41 #include "mtypes.h"
     42 #include "pack.h"
     43 #include "pbo.h"
     44 #include "texcompress.h"
     45 #include "texgetimage.h"
     46 #include "teximage.h"
     47 
     48 
     49 
     50 /**
     51  * Can the given type represent negative values?
     52  */
     53 static inline GLboolean
     54 type_needs_clamping(GLenum type)
     55 {
     56    switch (type) {
     57    case GL_BYTE:
     58    case GL_SHORT:
     59    case GL_INT:
     60    case GL_FLOAT:
     61    case GL_HALF_FLOAT_ARB:
     62    case GL_UNSIGNED_INT_10F_11F_11F_REV:
     63    case GL_UNSIGNED_INT_5_9_9_9_REV:
     64       return GL_FALSE;
     65    default:
     66       return GL_TRUE;
     67    }
     68 }
     69 
     70 
     71 /**
     72  * glGetTexImage for depth/Z pixels.
     73  */
     74 static void
     75 get_tex_depth(struct gl_context *ctx, GLuint dimensions,
     76               GLenum format, GLenum type, GLvoid *pixels,
     77               struct gl_texture_image *texImage)
     78 {
     79    const GLint width = texImage->Width;
     80    const GLint height = texImage->Height;
     81    const GLint depth = texImage->Depth;
     82    GLint img, row;
     83    GLfloat *depthRow = (GLfloat *) malloc(width * sizeof(GLfloat));
     84 
     85    if (!depthRow) {
     86       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
     87       return;
     88    }
     89 
     90    for (img = 0; img < depth; img++) {
     91       GLubyte *srcMap;
     92       GLint srcRowStride;
     93 
     94       /* map src texture buffer */
     95       ctx->Driver.MapTextureImage(ctx, texImage, img,
     96                                   0, 0, width, height, GL_MAP_READ_BIT,
     97                                   &srcMap, &srcRowStride);
     98 
     99       if (srcMap) {
    100          for (row = 0; row < height; row++) {
    101             void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
    102                                              width, height, format, type,
    103                                              img, row, 0);
    104             const GLubyte *src = srcMap + row * srcRowStride;
    105             _mesa_unpack_float_z_row(texImage->TexFormat, width, src, depthRow);
    106             _mesa_pack_depth_span(ctx, width, dest, type, depthRow, &ctx->Pack);
    107          }
    108 
    109          ctx->Driver.UnmapTextureImage(ctx, texImage, img);
    110       }
    111       else {
    112          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    113          break;
    114       }
    115    }
    116 
    117    free(depthRow);
    118 }
    119 
    120 
    121 /**
    122  * glGetTexImage for depth/stencil pixels.
    123  */
    124 static void
    125 get_tex_depth_stencil(struct gl_context *ctx, GLuint dimensions,
    126                       GLenum format, GLenum type, GLvoid *pixels,
    127                       struct gl_texture_image *texImage)
    128 {
    129    const GLint width = texImage->Width;
    130    const GLint height = texImage->Height;
    131    const GLint depth = texImage->Depth;
    132    GLint img, row;
    133 
    134    for (img = 0; img < depth; img++) {
    135       GLubyte *srcMap;
    136       GLint rowstride;
    137 
    138       /* map src texture buffer */
    139       ctx->Driver.MapTextureImage(ctx, texImage, img,
    140                                   0, 0, width, height, GL_MAP_READ_BIT,
    141                                   &srcMap, &rowstride);
    142 
    143       if (srcMap) {
    144          for (row = 0; row < height; row++) {
    145             const GLubyte *src = srcMap + row * rowstride;
    146             void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
    147                                              width, height, format, type,
    148                                              img, row, 0);
    149             /* XXX Z24_S8 vs. S8_Z24??? */
    150             memcpy(dest, src, width * sizeof(GLuint));
    151             if (ctx->Pack.SwapBytes) {
    152                _mesa_swap4((GLuint *) dest, width);
    153             }
    154          }
    155 
    156          ctx->Driver.UnmapTextureImage(ctx, texImage, img);
    157       }
    158       else {
    159          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    160          break;
    161       }
    162    }
    163 }
    164 
    165 
    166 /**
    167  * glGetTexImage for YCbCr pixels.
    168  */
    169 static void
    170 get_tex_ycbcr(struct gl_context *ctx, GLuint dimensions,
    171               GLenum format, GLenum type, GLvoid *pixels,
    172               struct gl_texture_image *texImage)
    173 {
    174    const GLint width = texImage->Width;
    175    const GLint height = texImage->Height;
    176    const GLint depth = texImage->Depth;
    177    GLint img, row;
    178 
    179    for (img = 0; img < depth; img++) {
    180       GLubyte *srcMap;
    181       GLint rowstride;
    182 
    183       /* map src texture buffer */
    184       ctx->Driver.MapTextureImage(ctx, texImage, img,
    185                                   0, 0, width, height, GL_MAP_READ_BIT,
    186                                   &srcMap, &rowstride);
    187 
    188       if (srcMap) {
    189          for (row = 0; row < height; row++) {
    190             const GLubyte *src = srcMap + row * rowstride;
    191             void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
    192                                              width, height, format, type,
    193                                              img, row, 0);
    194             memcpy(dest, src, width * sizeof(GLushort));
    195 
    196             /* check for byte swapping */
    197             if ((texImage->TexFormat == MESA_FORMAT_YCBCR
    198                  && type == GL_UNSIGNED_SHORT_8_8_REV_MESA) ||
    199                 (texImage->TexFormat == MESA_FORMAT_YCBCR_REV
    200                  && type == GL_UNSIGNED_SHORT_8_8_MESA)) {
    201                if (!ctx->Pack.SwapBytes)
    202                   _mesa_swap2((GLushort *) dest, width);
    203             }
    204             else if (ctx->Pack.SwapBytes) {
    205                _mesa_swap2((GLushort *) dest, width);
    206             }
    207          }
    208 
    209          ctx->Driver.UnmapTextureImage(ctx, texImage, img);
    210       }
    211       else {
    212          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    213          break;
    214       }
    215    }
    216 }
    217 
    218 
    219 /**
    220  * Get a color texture image with decompression.
    221  */
    222 static void
    223 get_tex_rgba_compressed(struct gl_context *ctx, GLuint dimensions,
    224                         GLenum format, GLenum type, GLvoid *pixels,
    225                         struct gl_texture_image *texImage,
    226                         GLbitfield transferOps)
    227 {
    228    /* don't want to apply sRGB -> RGB conversion here so override the format */
    229    const gl_format texFormat =
    230       _mesa_get_srgb_format_linear(texImage->TexFormat);
    231    const GLenum baseFormat = _mesa_get_format_base_format(texFormat);
    232    const GLenum destBaseFormat = _mesa_base_tex_format(ctx, format);
    233    GLenum rebaseFormat = GL_NONE;
    234    const GLuint width = texImage->Width;
    235    const GLuint height = texImage->Height;
    236    const GLuint depth = texImage->Depth;
    237    GLfloat *tempImage, *srcRow;
    238    GLuint row;
    239 
    240    /* Decompress into temp float buffer, then pack into user buffer */
    241    tempImage = (GLfloat *) malloc(width * height * depth
    242                                   * 4 * sizeof(GLfloat));
    243    if (!tempImage) {
    244       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()");
    245       return;
    246    }
    247 
    248    /* Decompress the texture image - results in 'tempImage' */
    249    {
    250       GLubyte *srcMap;
    251       GLint srcRowStride;
    252 
    253       ctx->Driver.MapTextureImage(ctx, texImage, 0,
    254                                   0, 0, width, height,
    255                                   GL_MAP_READ_BIT,
    256                                   &srcMap, &srcRowStride);
    257       if (srcMap) {
    258          _mesa_decompress_image(texFormat, width, height,
    259                                 srcMap, srcRowStride, tempImage);
    260 
    261          ctx->Driver.UnmapTextureImage(ctx, texImage, 0);
    262       }
    263       else {
    264          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    265          free(tempImage);
    266          return;
    267       }
    268    }
    269 
    270    if (baseFormat == GL_LUMINANCE ||
    271        baseFormat == GL_INTENSITY ||
    272        baseFormat == GL_LUMINANCE_ALPHA) {
    273       /* If a luminance (or intensity) texture is read back as RGB(A), the
    274        * returned value should be (L,0,0,1), not (L,L,L,1).  Set rebaseFormat
    275        * here to get G=B=0.
    276        */
    277       rebaseFormat = texImage->_BaseFormat;
    278    }
    279    else if ((baseFormat == GL_RGBA ||
    280              baseFormat == GL_RGB  ||
    281              baseFormat == GL_RG) &&
    282             (destBaseFormat == GL_LUMINANCE ||
    283              destBaseFormat == GL_LUMINANCE_ALPHA ||
    284              destBaseFormat == GL_LUMINANCE_INTEGER_EXT ||
    285              destBaseFormat == GL_LUMINANCE_ALPHA_INTEGER_EXT)) {
    286       /* If we're reading back an RGB(A) texture as luminance then we need
    287        * to return L=tex(R).  Note, that's different from glReadPixels which
    288        * returns L=R+G+B.
    289        */
    290       rebaseFormat = GL_LUMINANCE_ALPHA; /* this covers GL_LUMINANCE too */
    291    }
    292 
    293    if (rebaseFormat) {
    294       _mesa_rebase_rgba_float(width * height, (GLfloat (*)[4]) tempImage,
    295                               rebaseFormat);
    296    }
    297 
    298    srcRow = tempImage;
    299    for (row = 0; row < height; row++) {
    300       void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
    301                                        width, height, format, type,
    302                                        0, row, 0);
    303 
    304       _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) srcRow,
    305                                  format, type, dest, &ctx->Pack, transferOps);
    306       srcRow += width * 4;
    307    }
    308 
    309    free(tempImage);
    310 }
    311 
    312 
    313 /**
    314  * Get an uncompressed color texture image.
    315  */
    316 static void
    317 get_tex_rgba_uncompressed(struct gl_context *ctx, GLuint dimensions,
    318                           GLenum format, GLenum type, GLvoid *pixels,
    319                           struct gl_texture_image *texImage,
    320                           GLbitfield transferOps)
    321 {
    322    /* don't want to apply sRGB -> RGB conversion here so override the format */
    323    const gl_format texFormat =
    324       _mesa_get_srgb_format_linear(texImage->TexFormat);
    325    const GLuint width = texImage->Width;
    326    const GLenum destBaseFormat = _mesa_base_tex_format(ctx, format);
    327    GLenum rebaseFormat = GL_NONE;
    328    GLuint height = texImage->Height;
    329    GLuint depth = texImage->Depth;
    330    GLuint img, row;
    331    GLfloat (*rgba)[4];
    332    GLuint (*rgba_uint)[4];
    333    GLboolean tex_is_integer = _mesa_is_format_integer_color(texImage->TexFormat);
    334    GLboolean tex_is_uint = _mesa_is_format_unsigned(texImage->TexFormat);
    335 
    336    /* Allocate buffer for one row of texels */
    337    rgba = (GLfloat (*)[4]) malloc(4 * width * sizeof(GLfloat));
    338    rgba_uint = (GLuint (*)[4]) rgba;
    339    if (!rgba) {
    340       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()");
    341       return;
    342    }
    343 
    344    if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) {
    345       depth = height;
    346       height = 1;
    347    }
    348 
    349    if (texImage->_BaseFormat == GL_LUMINANCE ||
    350        texImage->_BaseFormat == GL_INTENSITY ||
    351        texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
    352       /* If a luminance (or intensity) texture is read back as RGB(A), the
    353        * returned value should be (L,0,0,1), not (L,L,L,1).  Set rebaseFormat
    354        * here to get G=B=0.
    355        */
    356       rebaseFormat = texImage->_BaseFormat;
    357    }
    358    else if ((texImage->_BaseFormat == GL_RGBA ||
    359              texImage->_BaseFormat == GL_RGB ||
    360              texImage->_BaseFormat == GL_RG) &&
    361             (destBaseFormat == GL_LUMINANCE ||
    362              destBaseFormat == GL_LUMINANCE_ALPHA ||
    363              destBaseFormat == GL_LUMINANCE_INTEGER_EXT ||
    364              destBaseFormat == GL_LUMINANCE_ALPHA_INTEGER_EXT)) {
    365       /* If we're reading back an RGB(A) texture as luminance then we need
    366        * to return L=tex(R).  Note, that's different from glReadPixels which
    367        * returns L=R+G+B.
    368        */
    369       rebaseFormat = GL_LUMINANCE_ALPHA; /* this covers GL_LUMINANCE too */
    370    }
    371 
    372    for (img = 0; img < depth; img++) {
    373       GLubyte *srcMap;
    374       GLint rowstride;
    375 
    376       /* map src texture buffer */
    377       ctx->Driver.MapTextureImage(ctx, texImage, img,
    378                                   0, 0, width, height, GL_MAP_READ_BIT,
    379                                   &srcMap, &rowstride);
    380       if (srcMap) {
    381          for (row = 0; row < height; row++) {
    382             const GLubyte *src = srcMap + row * rowstride;
    383             void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
    384                                              width, height, format, type,
    385                                              img, row, 0);
    386 
    387 	    if (tex_is_integer) {
    388 	       _mesa_unpack_uint_rgba_row(texFormat, width, src, rgba_uint);
    389                if (rebaseFormat)
    390                   _mesa_rebase_rgba_uint(width, rgba_uint, rebaseFormat);
    391                if (tex_is_uint) {
    392                   _mesa_pack_rgba_span_from_uints(ctx, width,
    393                                                   (GLuint (*)[4]) rgba_uint,
    394                                                   format, type, dest);
    395                } else {
    396                   _mesa_pack_rgba_span_from_ints(ctx, width,
    397                                                  (GLint (*)[4]) rgba_uint,
    398                                                  format, type, dest);
    399                }
    400 	    } else {
    401 	       _mesa_unpack_rgba_row(texFormat, width, src, rgba);
    402                if (rebaseFormat)
    403                   _mesa_rebase_rgba_float(width, rgba, rebaseFormat);
    404 	       _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
    405 					  format, type, dest,
    406 					  &ctx->Pack, transferOps);
    407 	    }
    408 	 }
    409 
    410          /* Unmap the src texture buffer */
    411          ctx->Driver.UnmapTextureImage(ctx, texImage, img);
    412       }
    413       else {
    414          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    415          break;
    416       }
    417    }
    418 
    419    free(rgba);
    420 }
    421 
    422 
    423 /**
    424  * glGetTexImage for color formats (RGBA, RGB, alpha, LA, etc).
    425  * Compressed textures are handled here as well.
    426  */
    427 static void
    428 get_tex_rgba(struct gl_context *ctx, GLuint dimensions,
    429              GLenum format, GLenum type, GLvoid *pixels,
    430              struct gl_texture_image *texImage)
    431 {
    432    const GLenum dataType = _mesa_get_format_datatype(texImage->TexFormat);
    433    GLbitfield transferOps = 0x0;
    434 
    435    /* In general, clamping does not apply to glGetTexImage, except when
    436     * the returned type of the image can't hold negative values.
    437     */
    438    if (type_needs_clamping(type)) {
    439       /* the returned image type can't have negative values */
    440       if (dataType == GL_FLOAT ||
    441           dataType == GL_SIGNED_NORMALIZED ||
    442           format == GL_LUMINANCE ||
    443           format == GL_LUMINANCE_ALPHA) {
    444          transferOps |= IMAGE_CLAMP_BIT;
    445       }
    446    }
    447 
    448    if (_mesa_is_format_compressed(texImage->TexFormat)) {
    449       get_tex_rgba_compressed(ctx, dimensions, format, type,
    450                               pixels, texImage, transferOps);
    451    }
    452    else {
    453       get_tex_rgba_uncompressed(ctx, dimensions, format, type,
    454                                 pixels, texImage, transferOps);
    455    }
    456 }
    457 
    458 
    459 /**
    460  * Try to do glGetTexImage() with simple memcpy().
    461  * \return GL_TRUE if done, GL_FALSE otherwise
    462  */
    463 static GLboolean
    464 get_tex_memcpy(struct gl_context *ctx, GLenum format, GLenum type,
    465                GLvoid *pixels,
    466                struct gl_texture_image *texImage)
    467 {
    468    const GLenum target = texImage->TexObject->Target;
    469    GLboolean memCopy = GL_FALSE;
    470 
    471    /*
    472     * Check if we can use memcpy to copy from the hardware texture
    473     * format to the user's format/type.
    474     * Note that GL's pixel transfer ops don't apply to glGetTexImage()
    475     */
    476    if (target == GL_TEXTURE_1D ||
    477        target == GL_TEXTURE_2D ||
    478        target == GL_TEXTURE_RECTANGLE ||
    479        _mesa_is_cube_face(target)) {
    480       memCopy = _mesa_format_matches_format_and_type(texImage->TexFormat,
    481                                                      format, type,
    482                                                      ctx->Pack.SwapBytes);
    483    }
    484 
    485    if (memCopy) {
    486       const GLuint bpp = _mesa_get_format_bytes(texImage->TexFormat);
    487       const GLuint bytesPerRow = texImage->Width * bpp;
    488       GLubyte *dst =
    489          _mesa_image_address2d(&ctx->Pack, pixels, texImage->Width,
    490                                texImage->Height, format, type, 0, 0);
    491       const GLint dstRowStride =
    492          _mesa_image_row_stride(&ctx->Pack, texImage->Width, format, type);
    493       GLubyte *src;
    494       GLint srcRowStride;
    495 
    496       /* map src texture buffer */
    497       ctx->Driver.MapTextureImage(ctx, texImage, 0,
    498                                   0, 0, texImage->Width, texImage->Height,
    499                                   GL_MAP_READ_BIT, &src, &srcRowStride);
    500 
    501       if (src) {
    502          if (bytesPerRow == dstRowStride && bytesPerRow == srcRowStride) {
    503             memcpy(dst, src, bytesPerRow * texImage->Height);
    504          }
    505          else {
    506             GLuint row;
    507             for (row = 0; row < texImage->Height; row++) {
    508                memcpy(dst, src, bytesPerRow);
    509                dst += dstRowStride;
    510                src += srcRowStride;
    511             }
    512          }
    513 
    514          /* unmap src texture buffer */
    515          ctx->Driver.UnmapTextureImage(ctx, texImage, 0);
    516       }
    517       else {
    518          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
    519       }
    520    }
    521 
    522    return memCopy;
    523 }
    524 
    525 
    526 /**
    527  * This is the software fallback for Driver.GetTexImage().
    528  * All error checking will have been done before this routine is called.
    529  * We'll call ctx->Driver.MapTextureImage() to access the data, then
    530  * unmap with ctx->Driver.UnmapTextureImage().
    531  */
    532 void
    533 _mesa_get_teximage(struct gl_context *ctx,
    534                    GLenum format, GLenum type, GLvoid *pixels,
    535                    struct gl_texture_image *texImage)
    536 {
    537    GLuint dimensions;
    538 
    539    switch (texImage->TexObject->Target) {
    540    case GL_TEXTURE_1D:
    541       dimensions = 1;
    542       break;
    543    case GL_TEXTURE_3D:
    544       dimensions = 3;
    545       break;
    546    default:
    547       dimensions = 2;
    548    }
    549 
    550    /* map dest buffer, if PBO */
    551    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    552       /* Packing texture image into a PBO.
    553        * Map the (potentially) VRAM-based buffer into our process space so
    554        * we can write into it with the code below.
    555        * A hardware driver might use a sophisticated blit to move the
    556        * texture data to the PBO if the PBO is in VRAM along with the texture.
    557        */
    558       GLubyte *buf = (GLubyte *)
    559          ctx->Driver.MapBufferRange(ctx, 0, ctx->Pack.BufferObj->Size,
    560 				    GL_MAP_WRITE_BIT, ctx->Pack.BufferObj);
    561       if (!buf) {
    562          /* out of memory or other unexpected error */
    563          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage(map PBO failed)");
    564          return;
    565       }
    566       /* <pixels> was an offset into the PBO.
    567        * Now make it a real, client-side pointer inside the mapped region.
    568        */
    569       pixels = ADD_POINTERS(buf, pixels);
    570    }
    571 
    572    if (get_tex_memcpy(ctx, format, type, pixels, texImage)) {
    573       /* all done */
    574    }
    575    else if (format == GL_DEPTH_COMPONENT) {
    576       get_tex_depth(ctx, dimensions, format, type, pixels, texImage);
    577    }
    578    else if (format == GL_DEPTH_STENCIL_EXT) {
    579       get_tex_depth_stencil(ctx, dimensions, format, type, pixels, texImage);
    580    }
    581    else if (format == GL_YCBCR_MESA) {
    582       get_tex_ycbcr(ctx, dimensions, format, type, pixels, texImage);
    583    }
    584    else {
    585       get_tex_rgba(ctx, dimensions, format, type, pixels, texImage);
    586    }
    587 
    588    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    589       ctx->Driver.UnmapBuffer(ctx, ctx->Pack.BufferObj);
    590    }
    591 }
    592 
    593 
    594 
    595 /**
    596  * This is the software fallback for Driver.GetCompressedTexImage().
    597  * All error checking will have been done before this routine is called.
    598  */
    599 void
    600 _mesa_get_compressed_teximage(struct gl_context *ctx,
    601                               struct gl_texture_image *texImage,
    602                               GLvoid *img)
    603 {
    604    const GLuint row_stride =
    605       _mesa_format_row_stride(texImage->TexFormat, texImage->Width);
    606    GLuint i;
    607    GLubyte *src;
    608    GLint srcRowStride;
    609 
    610    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    611       /* pack texture image into a PBO */
    612       GLubyte *buf = (GLubyte *)
    613          ctx->Driver.MapBufferRange(ctx, 0, ctx->Pack.BufferObj->Size,
    614 				    GL_MAP_WRITE_BIT, ctx->Pack.BufferObj);
    615       if (!buf) {
    616          /* out of memory or other unexpected error */
    617          _mesa_error(ctx, GL_OUT_OF_MEMORY,
    618                      "glGetCompresssedTexImage(map PBO failed)");
    619          return;
    620       }
    621       img = ADD_POINTERS(buf, img);
    622    }
    623 
    624    /* map src texture buffer */
    625    ctx->Driver.MapTextureImage(ctx, texImage, 0,
    626                                0, 0, texImage->Width, texImage->Height,
    627                                GL_MAP_READ_BIT, &src, &srcRowStride);
    628 
    629    if (src) {
    630       /* no pixelstore or pixel transfer, but respect stride */
    631 
    632       if (row_stride == srcRowStride) {
    633          const GLuint size = _mesa_format_image_size(texImage->TexFormat,
    634                                                      texImage->Width,
    635                                                      texImage->Height,
    636                                                      texImage->Depth);
    637          memcpy(img, src, size);
    638       }
    639       else {
    640          GLuint bw, bh;
    641          _mesa_get_format_block_size(texImage->TexFormat, &bw, &bh);
    642          for (i = 0; i < (texImage->Height + bh - 1) / bh; i++) {
    643             memcpy((GLubyte *)img + i * row_stride,
    644                    (GLubyte *)src + i * srcRowStride,
    645                    row_stride);
    646          }
    647       }
    648 
    649       ctx->Driver.UnmapTextureImage(ctx, texImage, 0);
    650    }
    651    else {
    652       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetCompresssedTexImage");
    653    }
    654 
    655    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    656       ctx->Driver.UnmapBuffer(ctx, ctx->Pack.BufferObj);
    657    }
    658 }
    659 
    660 
    661 /**
    662  * Validate the texture target enum supplied to glTexImage or
    663  * glCompressedTexImage.
    664  */
    665 static GLboolean
    666 legal_getteximage_target(struct gl_context *ctx, GLenum target)
    667 {
    668    switch (target) {
    669    case GL_TEXTURE_1D:
    670    case GL_TEXTURE_2D:
    671    case GL_TEXTURE_3D:
    672       return GL_TRUE;
    673    case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
    674    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
    675    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
    676    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
    677    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
    678    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
    679       return ctx->Extensions.ARB_texture_cube_map;
    680    case GL_TEXTURE_RECTANGLE_NV:
    681       return ctx->Extensions.NV_texture_rectangle;
    682    case GL_TEXTURE_1D_ARRAY_EXT:
    683    case GL_TEXTURE_2D_ARRAY_EXT:
    684       return (ctx->Extensions.MESA_texture_array ||
    685               ctx->Extensions.EXT_texture_array);
    686    default:
    687       return GL_FALSE;
    688    }
    689 }
    690 
    691 
    692 /**
    693  * Do error checking for a glGetTexImage() call.
    694  * \return GL_TRUE if any error, GL_FALSE if no errors.
    695  */
    696 static GLboolean
    697 getteximage_error_check(struct gl_context *ctx, GLenum target, GLint level,
    698                         GLenum format, GLenum type, GLsizei clientMemSize,
    699                         GLvoid *pixels )
    700 {
    701    struct gl_texture_object *texObj;
    702    struct gl_texture_image *texImage;
    703    const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
    704    const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
    705    GLenum baseFormat, err;
    706 
    707    if (!legal_getteximage_target(ctx, target)) {
    708       _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target=0x%x)", target);
    709       return GL_TRUE;
    710    }
    711 
    712    assert(maxLevels != 0);
    713    if (level < 0 || level >= maxLevels) {
    714       _mesa_error( ctx, GL_INVALID_VALUE, "glGetTexImage(level)" );
    715       return GL_TRUE;
    716    }
    717 
    718    err = _mesa_error_check_format_and_type(ctx, format, type);
    719    if (err != GL_NO_ERROR) {
    720       _mesa_error(ctx, err, "glGetTexImage(format/type)");
    721       return GL_TRUE;
    722    }
    723 
    724    texObj = _mesa_get_current_tex_object(ctx, target);
    725 
    726    if (!texObj) {
    727       _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target)");
    728       return GL_TRUE;
    729    }
    730 
    731    texImage = _mesa_select_tex_image(ctx, texObj, target, level);
    732    if (!texImage) {
    733       /* non-existant texture image */
    734       return GL_TRUE;
    735    }
    736 
    737    baseFormat = _mesa_get_format_base_format(texImage->TexFormat);
    738 
    739    /* Make sure the requested image format is compatible with the
    740     * texture's format.
    741     */
    742    if (_mesa_is_color_format(format)
    743        && !_mesa_is_color_format(baseFormat)) {
    744       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
    745       return GL_TRUE;
    746    }
    747    else if (_mesa_is_depth_format(format)
    748             && !_mesa_is_depth_format(baseFormat)
    749             && !_mesa_is_depthstencil_format(baseFormat)) {
    750       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
    751       return GL_TRUE;
    752    }
    753    else if (_mesa_is_ycbcr_format(format)
    754             && !_mesa_is_ycbcr_format(baseFormat)) {
    755       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
    756       return GL_TRUE;
    757    }
    758    else if (_mesa_is_depthstencil_format(format)
    759             && !_mesa_is_depthstencil_format(baseFormat)) {
    760       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
    761       return GL_TRUE;
    762    }
    763    else if (_mesa_is_dudv_format(format)
    764             && !_mesa_is_dudv_format(baseFormat)) {
    765       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
    766       return GL_TRUE;
    767    }
    768 
    769    if (!_mesa_validate_pbo_access(dimensions, &ctx->Pack, texImage->Width,
    770                                   texImage->Height, texImage->Depth,
    771                                   format, type, clientMemSize, pixels)) {
    772       if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    773          _mesa_error(ctx, GL_INVALID_OPERATION,
    774                      "glGetTexImage(out of bounds PBO access)");
    775       } else {
    776          _mesa_error(ctx, GL_INVALID_OPERATION,
    777                      "glGetnTexImageARB(out of bounds access:"
    778                      " bufSize (%d) is too small)", clientMemSize);
    779       }
    780       return GL_TRUE;
    781    }
    782 
    783    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    784       /* PBO should not be mapped */
    785       if (_mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
    786          _mesa_error(ctx, GL_INVALID_OPERATION,
    787                      "glGetTexImage(PBO is mapped)");
    788          return GL_TRUE;
    789       }
    790    }
    791 
    792    return GL_FALSE;
    793 }
    794 
    795 
    796 
    797 /**
    798  * Get texture image.  Called by glGetTexImage.
    799  *
    800  * \param target texture target.
    801  * \param level image level.
    802  * \param format pixel data format for returned image.
    803  * \param type pixel data type for returned image.
    804  * \param bufSize size of the pixels data buffer.
    805  * \param pixels returned pixel data.
    806  */
    807 void GLAPIENTRY
    808 _mesa_GetnTexImageARB( GLenum target, GLint level, GLenum format,
    809                        GLenum type, GLsizei bufSize, GLvoid *pixels )
    810 {
    811    struct gl_texture_object *texObj;
    812    struct gl_texture_image *texImage;
    813    GET_CURRENT_CONTEXT(ctx);
    814    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
    815 
    816    if (getteximage_error_check(ctx, target, level, format, type,
    817                                bufSize, pixels)) {
    818       return;
    819    }
    820 
    821    if (!_mesa_is_bufferobj(ctx->Pack.BufferObj) && !pixels) {
    822       /* not an error, do nothing */
    823       return;
    824    }
    825 
    826    texObj = _mesa_get_current_tex_object(ctx, target);
    827    texImage = _mesa_select_tex_image(ctx, texObj, target, level);
    828 
    829    if (_mesa_is_zero_size_texture(texImage))
    830       return;
    831 
    832    if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) {
    833       _mesa_debug(ctx, "glGetTexImage(tex %u) format = %s, w=%d, h=%d,"
    834                   " dstFmt=0x%x, dstType=0x%x\n",
    835                   texObj->Name,
    836                   _mesa_get_format_name(texImage->TexFormat),
    837                   texImage->Width, texImage->Height,
    838                   format, type);
    839    }
    840 
    841    _mesa_lock_texture(ctx, texObj);
    842    {
    843       ctx->Driver.GetTexImage(ctx, format, type, pixels, texImage);
    844    }
    845    _mesa_unlock_texture(ctx, texObj);
    846 }
    847 
    848 
    849 void GLAPIENTRY
    850 _mesa_GetTexImage( GLenum target, GLint level, GLenum format,
    851                    GLenum type, GLvoid *pixels )
    852 {
    853    _mesa_GetnTexImageARB(target, level, format, type, INT_MAX, pixels);
    854 }
    855 
    856 
    857 /**
    858  * Do error checking for a glGetCompressedTexImage() call.
    859  * \return GL_TRUE if any error, GL_FALSE if no errors.
    860  */
    861 static GLboolean
    862 getcompressedteximage_error_check(struct gl_context *ctx, GLenum target,
    863                                   GLint level, GLsizei clientMemSize, GLvoid *img)
    864 {
    865    struct gl_texture_object *texObj;
    866    struct gl_texture_image *texImage;
    867    const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
    868    GLuint compressedSize;
    869 
    870    if (!legal_getteximage_target(ctx, target)) {
    871       _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImage(target=0x%x)",
    872                   target);
    873       return GL_TRUE;
    874    }
    875 
    876    assert(maxLevels != 0);
    877    if (level < 0 || level >= maxLevels) {
    878       _mesa_error(ctx, GL_INVALID_VALUE,
    879                   "glGetCompressedTexImageARB(bad level = %d)", level);
    880       return GL_TRUE;
    881    }
    882 
    883    texObj = _mesa_get_current_tex_object(ctx, target);
    884    if (!texObj) {
    885       _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImageARB(target)");
    886       return GL_TRUE;
    887    }
    888 
    889    texImage = _mesa_select_tex_image(ctx, texObj, target, level);
    890 
    891    if (!texImage) {
    892       /* probably invalid mipmap level */
    893       _mesa_error(ctx, GL_INVALID_VALUE,
    894                   "glGetCompressedTexImageARB(level)");
    895       return GL_TRUE;
    896    }
    897 
    898    if (!_mesa_is_format_compressed(texImage->TexFormat)) {
    899       _mesa_error(ctx, GL_INVALID_OPERATION,
    900                   "glGetCompressedTexImageARB(texture is not compressed)");
    901       return GL_TRUE;
    902    }
    903 
    904    compressedSize = _mesa_format_image_size(texImage->TexFormat,
    905                                             texImage->Width,
    906                                             texImage->Height,
    907                                             texImage->Depth);
    908 
    909    if (!_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
    910       /* do bounds checking on writing to client memory */
    911       if (clientMemSize < compressedSize) {
    912          _mesa_error(ctx, GL_INVALID_OPERATION,
    913                      "glGetnCompressedTexImageARB(out of bounds access:"
    914                      " bufSize (%d) is too small)", clientMemSize);
    915          return GL_TRUE;
    916       }
    917    } else {
    918       /* do bounds checking on PBO write */
    919       if ((const GLubyte *) img + compressedSize >
    920           (const GLubyte *) ctx->Pack.BufferObj->Size) {
    921          _mesa_error(ctx, GL_INVALID_OPERATION,
    922                      "glGetCompressedTexImage(out of bounds PBO access)");
    923          return GL_TRUE;
    924       }
    925 
    926       /* make sure PBO is not mapped */
    927       if (_mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
    928          _mesa_error(ctx, GL_INVALID_OPERATION,
    929                      "glGetCompressedTexImage(PBO is mapped)");
    930          return GL_TRUE;
    931       }
    932    }
    933 
    934    return GL_FALSE;
    935 }
    936 
    937 
    938 void GLAPIENTRY
    939 _mesa_GetnCompressedTexImageARB(GLenum target, GLint level, GLsizei bufSize,
    940                                 GLvoid *img)
    941 {
    942    struct gl_texture_object *texObj;
    943    struct gl_texture_image *texImage;
    944    GET_CURRENT_CONTEXT(ctx);
    945    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
    946 
    947    if (getcompressedteximage_error_check(ctx, target, level, bufSize, img)) {
    948       return;
    949    }
    950 
    951    if (!_mesa_is_bufferobj(ctx->Pack.BufferObj) && !img) {
    952       /* not an error, do nothing */
    953       return;
    954    }
    955 
    956    texObj = _mesa_get_current_tex_object(ctx, target);
    957    texImage = _mesa_select_tex_image(ctx, texObj, target, level);
    958 
    959    if (_mesa_is_zero_size_texture(texImage))
    960       return;
    961 
    962    if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) {
    963       _mesa_debug(ctx,
    964                   "glGetCompressedTexImage(tex %u) format = %s, w=%d, h=%d\n",
    965                   texObj->Name,
    966                   _mesa_get_format_name(texImage->TexFormat),
    967                   texImage->Width, texImage->Height);
    968    }
    969 
    970    _mesa_lock_texture(ctx, texObj);
    971    {
    972       ctx->Driver.GetCompressedTexImage(ctx, texImage, img);
    973    }
    974    _mesa_unlock_texture(ctx, texObj);
    975 }
    976 
    977 void GLAPIENTRY
    978 _mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
    979 {
    980    _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
    981 }
    982