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 */
53static inline GLboolean
54type_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 */
74static void
75get_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 */
124static void
125get_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 */
169static void
170get_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 */
222static void
223get_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 */
316static void
317get_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 */
427static void
428get_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 */
463static GLboolean
464get_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 */
532void
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 */
599void
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 */
665static GLboolean
666legal_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 */
696static GLboolean
697getteximage_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 */
807void 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
849void 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 */
861static GLboolean
862getcompressedteximage_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
938void 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
977void GLAPIENTRY
978_mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
979{
980   _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
981}
982