• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
type_needs_clamping(GLenum type)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
get_tex_depth(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
get_tex_depth_stencil(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
get_tex_ycbcr(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
get_tex_rgba_compressed(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage,GLbitfield transferOps)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
get_tex_rgba_uncompressed(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage,GLbitfield transferOps)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
get_tex_rgba(struct gl_context * ctx,GLuint dimensions,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
get_tex_memcpy(struct gl_context * ctx,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
_mesa_get_teximage(struct gl_context * ctx,GLenum format,GLenum type,GLvoid * pixels,struct gl_texture_image * texImage)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
_mesa_get_compressed_teximage(struct gl_context * ctx,struct gl_texture_image * texImage,GLvoid * img)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
legal_getteximage_target(struct gl_context * ctx,GLenum target)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
getteximage_error_check(struct gl_context * ctx,GLenum target,GLint level,GLenum format,GLenum type,GLsizei clientMemSize,GLvoid * pixels)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
_mesa_GetnTexImageARB(GLenum target,GLint level,GLenum format,GLenum type,GLsizei bufSize,GLvoid * pixels)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
_mesa_GetTexImage(GLenum target,GLint level,GLenum format,GLenum type,GLvoid * pixels)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
getcompressedteximage_error_check(struct gl_context * ctx,GLenum target,GLint level,GLsizei clientMemSize,GLvoid * img)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
_mesa_GetnCompressedTexImageARB(GLenum target,GLint level,GLsizei bufSize,GLvoid * img)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
_mesa_GetCompressedTexImageARB(GLenum target,GLint level,GLvoid * img)978 _mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
979 {
980    _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
981 }
982