texgetimage.c revision d6ee86c77a8e1543557fd64c1f1c354baa0a8ad8
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.5
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 "context.h"
35#include "formats.h"
36#include "image.h"
37#include "texcompress.h"
38#include "texgetimage.h"
39#include "teximage.h"
40#include "texstate.h"
41
42
43
44#if FEATURE_EXT_texture_sRGB
45
46/**
47 * Test if given texture image is an sRGB format.
48 */
49static GLboolean
50is_srgb_teximage(const struct gl_texture_image *texImage)
51{
52   switch (texImage->TexFormat) {
53   case MESA_FORMAT_SRGB8:
54   case MESA_FORMAT_SRGBA8:
55   case MESA_FORMAT_SARGB8:
56   case MESA_FORMAT_SL8:
57   case MESA_FORMAT_SLA8:
58   case MESA_FORMAT_SRGB_DXT1:
59   case MESA_FORMAT_SRGBA_DXT1:
60   case MESA_FORMAT_SRGBA_DXT3:
61   case MESA_FORMAT_SRGBA_DXT5:
62      return GL_TRUE;
63   default:
64      return GL_FALSE;
65   }
66}
67
68
69/**
70 * Convert a float value from linear space to a
71 * non-linear sRGB value in [0, 255].
72 * Not terribly efficient.
73 */
74static INLINE GLfloat
75linear_to_nonlinear(GLfloat cl)
76{
77   /* can't have values outside [0, 1] */
78   GLfloat cs;
79   if (cl < 0.0031308f) {
80      cs = 12.92f * cl;
81   }
82   else {
83      cs = (GLfloat)(1.055 * _mesa_pow(cl, 0.41666) - 0.055);
84   }
85   return cs;
86}
87
88#endif /* FEATURE_EXT_texture_sRGB */
89
90
91/**
92 * Can the given type represent negative values?
93 */
94static INLINE GLboolean
95type_with_negative_values(GLenum type)
96{
97   switch (type) {
98   case GL_BYTE:
99   case GL_SHORT:
100   case GL_INT:
101   case GL_FLOAT:
102   case GL_HALF_FLOAT_ARB:
103      return GL_TRUE;
104   default:
105      return GL_FALSE;
106   }
107}
108
109
110/**
111 * This is the software fallback for Driver.GetTexImage().
112 * All error checking will have been done before this routine is called.
113 */
114void
115_mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
116                   GLenum format, GLenum type, GLvoid *pixels,
117                   struct gl_texture_object *texObj,
118                   struct gl_texture_image *texImage)
119{
120   const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
121
122   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
123      /* Packing texture image into a PBO.
124       * Map the (potentially) VRAM-based buffer into our process space so
125       * we can write into it with the code below.
126       * A hardware driver might use a sophisticated blit to move the
127       * texture data to the PBO if the PBO is in VRAM along with the texture.
128       */
129      GLubyte *buf = (GLubyte *)
130         ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
131                               GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
132      if (!buf) {
133         /* buffer is already mapped - that's an error */
134         _mesa_error(ctx, GL_INVALID_OPERATION,"glGetTexImage(PBO is mapped)");
135         return;
136      }
137      /* <pixels> was an offset into the PBO.
138       * Now make it a real, client-side pointer inside the mapped region.
139       */
140      pixels = ADD_POINTERS(buf, pixels);
141   }
142   else if (!pixels) {
143      /* not an error */
144      return;
145   }
146
147   {
148      const GLint width = texImage->Width;
149      const GLint height = texImage->Height;
150      const GLint depth = texImage->Depth;
151      GLint img, row;
152      for (img = 0; img < depth; img++) {
153         for (row = 0; row < height; row++) {
154            /* compute destination address in client memory */
155            GLvoid *dest = _mesa_image_address( dimensions, &ctx->Pack, pixels,
156                                                width, height, format, type,
157                                                img, row, 0);
158            assert(dest);
159
160            if (format == GL_COLOR_INDEX) {
161               GLuint indexRow[MAX_WIDTH];
162               GLint col;
163               GLuint indexBits = _mesa_get_format_bits(texImage->TexFormat, GL_TEXTURE_INDEX_SIZE_EXT);
164               /* Can't use FetchTexel here because that returns RGBA */
165               if (indexBits == 8) {
166                  const GLubyte *src = (const GLubyte *) texImage->Data;
167                  src += width * (img * texImage->Height + row);
168                  for (col = 0; col < width; col++) {
169                     indexRow[col] = src[col];
170                  }
171               }
172               else if (indexBits == 16) {
173                  const GLushort *src = (const GLushort *) texImage->Data;
174                  src += width * (img * texImage->Height + row);
175                  for (col = 0; col < width; col++) {
176                     indexRow[col] = src[col];
177                  }
178               }
179               else {
180                  _mesa_problem(ctx,
181                                "Color index problem in _mesa_GetTexImage");
182               }
183               _mesa_pack_index_span(ctx, width, type, dest,
184                                     indexRow, &ctx->Pack,
185                                     0 /* no image transfer */);
186            }
187            else if (format == GL_DEPTH_COMPONENT) {
188               GLfloat depthRow[MAX_WIDTH];
189               GLint col;
190               for (col = 0; col < width; col++) {
191                  (*texImage->FetchTexelf)(texImage, col, row, img,
192                                           depthRow + col);
193               }
194               _mesa_pack_depth_span(ctx, width, dest, type,
195                                     depthRow, &ctx->Pack);
196            }
197            else if (format == GL_DEPTH_STENCIL_EXT) {
198               /* XXX Note: we're bypassing texImage->FetchTexel()! */
199               const GLuint *src = (const GLuint *) texImage->Data;
200               src += width * row + width * height * img;
201               _mesa_memcpy(dest, src, width * sizeof(GLuint));
202               if (ctx->Pack.SwapBytes) {
203                  _mesa_swap4((GLuint *) dest, width);
204               }
205            }
206            else if (format == GL_YCBCR_MESA) {
207               /* No pixel transfer */
208               const GLint rowstride = texImage->RowStride;
209               MEMCPY(dest,
210                      (const GLushort *) texImage->Data + row * rowstride,
211                      width * sizeof(GLushort));
212               /* check for byte swapping */
213               if ((texImage->TexFormat == MESA_FORMAT_YCBCR
214                    && type == GL_UNSIGNED_SHORT_8_8_REV_MESA) ||
215                   (texImage->TexFormat == MESA_FORMAT_YCBCR_REV
216                    && type == GL_UNSIGNED_SHORT_8_8_MESA)) {
217                  if (!ctx->Pack.SwapBytes)
218                     _mesa_swap2((GLushort *) dest, width);
219               }
220               else if (ctx->Pack.SwapBytes) {
221                  _mesa_swap2((GLushort *) dest, width);
222               }
223            }
224#if FEATURE_EXT_texture_sRGB
225            else if (is_srgb_teximage(texImage)) {
226               /* special case this since need to backconvert values */
227               /* convert row to RGBA format */
228               GLfloat rgba[MAX_WIDTH][4];
229               GLint col;
230               GLbitfield transferOps = 0x0;
231
232               for (col = 0; col < width; col++) {
233                  (*texImage->FetchTexelf)(texImage, col, row, img, rgba[col]);
234                  if (texImage->_BaseFormat == GL_LUMINANCE) {
235                     rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
236                     rgba[col][GCOMP] = 0.0;
237                     rgba[col][BCOMP] = 0.0;
238                  }
239                  else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
240                     rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
241                     rgba[col][GCOMP] = 0.0;
242                     rgba[col][BCOMP] = 0.0;
243                  }
244                  else if (texImage->_BaseFormat == GL_RGB ||
245                     texImage->_BaseFormat == GL_RGBA) {
246                     rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
247                     rgba[col][GCOMP] = linear_to_nonlinear(rgba[col][GCOMP]);
248                     rgba[col][BCOMP] = linear_to_nonlinear(rgba[col][BCOMP]);
249                  }
250               }
251               _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
252                                          format, type, dest,
253                                          &ctx->Pack, transferOps);
254            }
255#endif /* FEATURE_EXT_texture_sRGB */
256            else {
257               /* general case:  convert row to RGBA format */
258               GLfloat rgba[MAX_WIDTH][4];
259               GLint col;
260               GLbitfield transferOps = 0x0;
261               GLenum dataType =
262                  _mesa_get_format_datatype(texImage->TexFormat);
263
264               /* clamp does not apply to GetTexImage (final conversion)?
265                * Looks like we need clamp though when going from format
266                * containing negative values to unsigned format.
267                */
268               if (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA)
269                  transferOps |= IMAGE_CLAMP_BIT;
270               else if (!type_with_negative_values(type) &&
271                        (dataType == GL_FLOAT ||
272                         dataType == GL_SIGNED_NORMALIZED))
273                  transferOps |= IMAGE_CLAMP_BIT;
274
275               for (col = 0; col < width; col++) {
276                  (*texImage->FetchTexelf)(texImage, col, row, img, rgba[col]);
277                  if (texImage->_BaseFormat == GL_ALPHA) {
278                     rgba[col][RCOMP] = 0.0;
279                     rgba[col][GCOMP] = 0.0;
280                     rgba[col][BCOMP] = 0.0;
281                  }
282                  else if (texImage->_BaseFormat == GL_LUMINANCE) {
283                     rgba[col][GCOMP] = 0.0;
284                     rgba[col][BCOMP] = 0.0;
285                     rgba[col][ACOMP] = 1.0;
286                  }
287                  else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
288                     rgba[col][GCOMP] = 0.0;
289                     rgba[col][BCOMP] = 0.0;
290                  }
291                  else if (texImage->_BaseFormat == GL_INTENSITY) {
292                     rgba[col][GCOMP] = 0.0;
293                     rgba[col][BCOMP] = 0.0;
294                     rgba[col][ACOMP] = 1.0;
295                  }
296               }
297               _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
298                                          format, type, dest,
299                                          &ctx->Pack, transferOps);
300            } /* format */
301         } /* row */
302      } /* img */
303   }
304
305   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
306      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
307                              ctx->Pack.BufferObj);
308   }
309}
310
311
312
313/**
314 * This is the software fallback for Driver.GetCompressedTexImage().
315 * All error checking will have been done before this routine is called.
316 */
317void
318_mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
319                              GLvoid *img,
320                              struct gl_texture_object *texObj,
321                              struct gl_texture_image *texImage)
322{
323   const GLuint size = _mesa_format_image_size(texImage->TexFormat,
324                                               texImage->Width,
325                                               texImage->Height,
326                                               texImage->Depth);
327
328   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
329      /* pack texture image into a PBO */
330      GLubyte *buf;
331      if ((const GLubyte *) img + size >
332          (const GLubyte *) ctx->Pack.BufferObj->Size) {
333         _mesa_error(ctx, GL_INVALID_OPERATION,
334                     "glGetCompressedTexImage(invalid PBO access)");
335         return;
336      }
337      buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
338                                              GL_WRITE_ONLY_ARB,
339                                              ctx->Pack.BufferObj);
340      if (!buf) {
341         /* buffer is already mapped - that's an error */
342         _mesa_error(ctx, GL_INVALID_OPERATION,
343                     "glGetCompressedTexImage(PBO is mapped)");
344         return;
345      }
346      img = ADD_POINTERS(buf, img);
347   }
348   else if (!img) {
349      /* not an error */
350      return;
351   }
352
353   /* just memcpy, no pixelstore or pixel transfer */
354   _mesa_memcpy(img, texImage->Data, size);
355
356   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
357      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
358                              ctx->Pack.BufferObj);
359   }
360}
361
362
363
364/**
365 * Do error checking for a glGetTexImage() call.
366 * \return GL_TRUE if any error, GL_FALSE if no errors.
367 */
368static GLboolean
369getteximage_error_check(GLcontext *ctx, GLenum target, GLint level,
370                        GLenum format, GLenum type, GLvoid *pixels )
371{
372   const struct gl_texture_unit *texUnit;
373   struct gl_texture_object *texObj;
374   struct gl_texture_image *texImage;
375   const GLuint maxLevels = _mesa_max_texture_levels(ctx, target);
376   GLenum baseFormat;
377
378   if (maxLevels == 0) {
379      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target=0x%x)", target);
380      return GL_TRUE;
381   }
382
383   if (level < 0 || level >= maxLevels) {
384      _mesa_error( ctx, GL_INVALID_VALUE, "glGetTexImage(level)" );
385      return GL_TRUE;
386   }
387
388   if (_mesa_sizeof_packed_type(type) <= 0) {
389      _mesa_error( ctx, GL_INVALID_ENUM, "glGetTexImage(type)" );
390      return GL_TRUE;
391   }
392
393   if (_mesa_components_in_format(format) <= 0 ||
394       format == GL_STENCIL_INDEX) {
395      _mesa_error( ctx, GL_INVALID_ENUM, "glGetTexImage(format)" );
396      return GL_TRUE;
397   }
398
399   if (!ctx->Extensions.EXT_paletted_texture && _mesa_is_index_format(format)) {
400      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
401      return GL_TRUE;
402   }
403
404   if (!ctx->Extensions.ARB_depth_texture && _mesa_is_depth_format(format)) {
405      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
406      return GL_TRUE;
407   }
408
409   if (!ctx->Extensions.MESA_ycbcr_texture && _mesa_is_ycbcr_format(format)) {
410      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
411      return GL_TRUE;
412   }
413
414   if (!ctx->Extensions.EXT_packed_depth_stencil
415       && _mesa_is_depthstencil_format(format)) {
416      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
417      return GL_TRUE;
418   }
419
420   if (!ctx->Extensions.ATI_envmap_bumpmap
421       && _mesa_is_dudv_format(format)) {
422      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
423      return GL_TRUE;
424   }
425
426   texUnit = _mesa_get_current_tex_unit(ctx);
427   texObj = _mesa_select_tex_object(ctx, texUnit, target);
428
429   if (!texObj || _mesa_is_proxy_texture(target)) {
430      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target)");
431      return GL_TRUE;
432   }
433
434   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
435   if (!texImage) {
436      /* out of memory */
437      return GL_TRUE;
438   }
439
440   baseFormat = _mesa_get_format_base_format(texImage->TexFormat);
441
442   /* Make sure the requested image format is compatible with the
443    * texture's format.  Note that a color index texture can be converted
444    * to RGBA so that combo is allowed.
445    */
446   if (_mesa_is_color_format(format)
447       && !_mesa_is_color_format(baseFormat)
448       && !_mesa_is_index_format(baseFormat)) {
449      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
450      return GL_TRUE;
451   }
452   else if (_mesa_is_index_format(format)
453            && !_mesa_is_index_format(baseFormat)) {
454      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
455      return GL_TRUE;
456   }
457   else if (_mesa_is_depth_format(format)
458            && !_mesa_is_depth_format(baseFormat)
459            && !_mesa_is_depthstencil_format(baseFormat)) {
460      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
461      return GL_TRUE;
462   }
463   else if (_mesa_is_ycbcr_format(format)
464            && !_mesa_is_ycbcr_format(baseFormat)) {
465      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
466      return GL_TRUE;
467   }
468   else if (_mesa_is_depthstencil_format(format)
469            && !_mesa_is_depthstencil_format(baseFormat)) {
470      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
471      return GL_TRUE;
472   }
473   else if (_mesa_is_dudv_format(format)
474            && !_mesa_is_dudv_format(baseFormat)) {
475      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
476      return GL_TRUE;
477   }
478
479   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
480      /* packing texture image into a PBO */
481      const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
482      if (!_mesa_validate_pbo_access(dimensions, &ctx->Pack, texImage->Width,
483                                     texImage->Height, texImage->Depth,
484                                     format, type, pixels)) {
485         _mesa_error(ctx, GL_INVALID_OPERATION,
486                     "glGetTexImage(invalid PBO access)");
487         return GL_TRUE;
488      }
489   }
490
491   return GL_FALSE;
492}
493
494
495
496/**
497 * Get texture image.  Called by glGetTexImage.
498 *
499 * \param target texture target.
500 * \param level image level.
501 * \param format pixel data format for returned image.
502 * \param type pixel data type for returned image.
503 * \param pixels returned pixel data.
504 */
505void GLAPIENTRY
506_mesa_GetTexImage( GLenum target, GLint level, GLenum format,
507                   GLenum type, GLvoid *pixels )
508{
509   const struct gl_texture_unit *texUnit;
510   struct gl_texture_object *texObj;
511   GET_CURRENT_CONTEXT(ctx);
512   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
513
514   if (getteximage_error_check(ctx, target, level, format, type, pixels)) {
515      return;
516   }
517
518   texUnit = _mesa_get_current_tex_unit(ctx);
519   texObj = _mesa_select_tex_object(ctx, texUnit, target);
520
521   _mesa_lock_texture(ctx, texObj);
522   {
523      struct gl_texture_image *texImage =
524         _mesa_select_tex_image(ctx, texObj, target, level);
525
526      /* typically, this will call _mesa_get_teximage() */
527      ctx->Driver.GetTexImage(ctx, target, level, format, type, pixels,
528                              texObj, texImage);
529   }
530   _mesa_unlock_texture(ctx, texObj);
531}
532
533
534void GLAPIENTRY
535_mesa_GetCompressedTexImageARB(GLenum target, GLint level, GLvoid *img)
536{
537   const struct gl_texture_unit *texUnit;
538   struct gl_texture_object *texObj;
539   struct gl_texture_image *texImage;
540   GLint maxLevels;
541   GET_CURRENT_CONTEXT(ctx);
542   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
543
544   texUnit = _mesa_get_current_tex_unit(ctx);
545   texObj = _mesa_select_tex_object(ctx, texUnit, target);
546   if (!texObj) {
547      _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImageARB");
548      return;
549   }
550
551   maxLevels = _mesa_max_texture_levels(ctx, target);
552   ASSERT(maxLevels > 0); /* 0 indicates bad target, caught above */
553
554   if (level < 0 || level >= maxLevels) {
555      _mesa_error(ctx, GL_INVALID_VALUE, "glGetCompressedTexImageARB(level)");
556      return;
557   }
558
559   if (_mesa_is_proxy_texture(target)) {
560      _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImageARB(target)");
561      return;
562   }
563
564   _mesa_lock_texture(ctx, texObj);
565   {
566      texImage = _mesa_select_tex_image(ctx, texObj, target, level);
567      if (texImage) {
568         if (_mesa_is_format_compressed(texImage->TexFormat)) {
569            /* this typically calls _mesa_get_compressed_teximage() */
570            ctx->Driver.GetCompressedTexImage(ctx, target, level, img,
571                                              texObj, texImage);
572         }
573         else {
574            _mesa_error(ctx, GL_INVALID_OPERATION,
575                        "glGetCompressedTexImageARB");
576         }
577      }
578      else {
579         /* probably invalid mipmap level */
580         _mesa_error(ctx, GL_INVALID_VALUE,
581                     "glGetCompressedTexImageARB(level)");
582      }
583   }
584   _mesa_unlock_texture(ctx, texObj);
585}
586