Sync with trunk r63647.
[reactos.git] / dll / opengl / mesa / main / texgetimage.c
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 * Code for glGetTexImage() and glGetCompressedTexImage().
28 */
29
30 #include <precomp.h>
31
32 /**
33 * Can the given type represent negative values?
34 */
35 static inline GLboolean
36 type_needs_clamping(GLenum type)
37 {
38 switch (type) {
39 case GL_BYTE:
40 case GL_SHORT:
41 case GL_INT:
42 case GL_FLOAT:
43 case GL_UNSIGNED_INT_10F_11F_11F_REV:
44 case GL_UNSIGNED_INT_5_9_9_9_REV:
45 return GL_FALSE;
46 default:
47 return GL_TRUE;
48 }
49 }
50
51
52 /**
53 * glGetTexImage for depth/Z pixels.
54 */
55 static void
56 get_tex_depth(struct gl_context *ctx, GLuint dimensions,
57 GLenum format, GLenum type, GLvoid *pixels,
58 struct gl_texture_image *texImage)
59 {
60 const GLint width = texImage->Width;
61 const GLint height = texImage->Height;
62 const GLint depth = texImage->Depth;
63 GLint img, row;
64 GLfloat *depthRow = (GLfloat *) malloc(width * sizeof(GLfloat));
65
66 if (!depthRow) {
67 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
68 return;
69 }
70
71 for (img = 0; img < depth; img++) {
72 GLubyte *srcMap;
73 GLint srcRowStride;
74
75 /* map src texture buffer */
76 ctx->Driver.MapTextureImage(ctx, texImage, img,
77 0, 0, width, height, GL_MAP_READ_BIT,
78 &srcMap, &srcRowStride);
79
80 if (srcMap) {
81 for (row = 0; row < height; row++) {
82 void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
83 width, height, format, type,
84 img, row, 0);
85 const GLubyte *src = srcMap + row * srcRowStride;
86 _mesa_unpack_float_z_row(texImage->TexFormat, width, src, depthRow);
87 _mesa_pack_depth_span(ctx, width, dest, type, depthRow, &ctx->Pack);
88 }
89
90 ctx->Driver.UnmapTextureImage(ctx, texImage, img);
91 }
92 else {
93 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
94 break;
95 }
96 }
97
98 free(depthRow);
99 }
100
101 /**
102 * glGetTexImage for YCbCr pixels.
103 */
104 static void
105 get_tex_ycbcr(struct gl_context *ctx, GLuint dimensions,
106 GLenum format, GLenum type, GLvoid *pixels,
107 struct gl_texture_image *texImage)
108 {
109 const GLint width = texImage->Width;
110 const GLint height = texImage->Height;
111 const GLint depth = texImage->Depth;
112 GLint img, row;
113
114 for (img = 0; img < depth; img++) {
115 GLubyte *srcMap;
116 GLint rowstride;
117
118 /* map src texture buffer */
119 ctx->Driver.MapTextureImage(ctx, texImage, img,
120 0, 0, width, height, GL_MAP_READ_BIT,
121 &srcMap, &rowstride);
122
123 if (srcMap) {
124 for (row = 0; row < height; row++) {
125 const GLubyte *src = srcMap + row * rowstride;
126 void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
127 width, height, format, type,
128 img, row, 0);
129 memcpy(dest, src, width * sizeof(GLushort));
130
131 /* check for byte swapping */
132 if ((texImage->TexFormat == MESA_FORMAT_YCBCR
133 && type == GL_UNSIGNED_SHORT_8_8_REV_MESA) ||
134 (texImage->TexFormat == MESA_FORMAT_YCBCR_REV
135 && type == GL_UNSIGNED_SHORT_8_8_MESA)) {
136 if (!ctx->Pack.SwapBytes)
137 _mesa_swap2((GLushort *) dest, width);
138 }
139 else if (ctx->Pack.SwapBytes) {
140 _mesa_swap2((GLushort *) dest, width);
141 }
142 }
143
144 ctx->Driver.UnmapTextureImage(ctx, texImage, img);
145 }
146 else {
147 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
148 break;
149 }
150 }
151 }
152
153 /**
154 * Get an uncompressed color texture image.
155 */
156 static void
157 get_tex_rgba_uncompressed(struct gl_context *ctx, GLuint dimensions,
158 GLenum format, GLenum type, GLvoid *pixels,
159 struct gl_texture_image *texImage,
160 GLbitfield transferOps)
161 {
162 const gl_format texFormat = texImage->TexFormat;
163 const GLuint width = texImage->Width;
164 const GLenum destBaseFormat = _mesa_base_tex_format(ctx, format);
165 GLenum rebaseFormat = GL_NONE;
166 GLuint height = texImage->Height;
167 GLuint depth = texImage->Depth;
168 GLuint img, row;
169 GLfloat (*rgba)[4];
170 GLuint (*rgba_uint)[4];
171 GLboolean is_integer = _mesa_is_format_integer_color(texImage->TexFormat);
172
173 /* Allocate buffer for one row of texels */
174 rgba = (GLfloat (*)[4]) malloc(4 * width * sizeof(GLfloat));
175 rgba_uint = (GLuint (*)[4]) rgba;
176 if (!rgba) {
177 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()");
178 return;
179 }
180
181 if (texImage->_BaseFormat == GL_LUMINANCE ||
182 texImage->_BaseFormat == GL_INTENSITY ||
183 texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
184 /* If a luminance (or intensity) texture is read back as RGB(A), the
185 * returned value should be (L,0,0,1), not (L,L,L,1). Set rebaseFormat
186 * here to get G=B=0.
187 */
188 rebaseFormat = texImage->_BaseFormat;
189 }
190 else if ((texImage->_BaseFormat == GL_RGBA ||
191 texImage->_BaseFormat == GL_RGB) &&
192 (destBaseFormat == GL_LUMINANCE ||
193 destBaseFormat == GL_LUMINANCE_ALPHA ||
194 destBaseFormat == GL_LUMINANCE_INTEGER_EXT ||
195 destBaseFormat == GL_LUMINANCE_ALPHA_INTEGER_EXT)) {
196 /* If we're reading back an RGB(A) texture as luminance then we need
197 * to return L=tex(R). Note, that's different from glReadPixels which
198 * returns L=R+G+B.
199 */
200 rebaseFormat = GL_LUMINANCE_ALPHA; /* this covers GL_LUMINANCE too */
201 }
202
203 for (img = 0; img < depth; img++) {
204 GLubyte *srcMap;
205 GLint rowstride;
206
207 /* map src texture buffer */
208 ctx->Driver.MapTextureImage(ctx, texImage, img,
209 0, 0, width, height, GL_MAP_READ_BIT,
210 &srcMap, &rowstride);
211 if (srcMap) {
212 for (row = 0; row < height; row++) {
213 const GLubyte *src = srcMap + row * rowstride;
214 void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels,
215 width, height, format, type,
216 img, row, 0);
217
218 if (is_integer) {
219 _mesa_unpack_uint_rgba_row(texFormat, width, src, rgba_uint);
220 if (rebaseFormat)
221 _mesa_rebase_rgba_uint(width, rgba_uint, rebaseFormat);
222 _mesa_pack_rgba_span_int(ctx, width, rgba_uint,
223 format, type, dest);
224 } else {
225 _mesa_unpack_rgba_row(texFormat, width, src, rgba);
226 if (rebaseFormat)
227 _mesa_rebase_rgba_float(width, rgba, rebaseFormat);
228 _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
229 format, type, dest,
230 &ctx->Pack, transferOps);
231 }
232 }
233
234 /* Unmap the src texture buffer */
235 ctx->Driver.UnmapTextureImage(ctx, texImage, img);
236 }
237 else {
238 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
239 break;
240 }
241 }
242
243 free(rgba);
244 }
245
246
247 /**
248 * glGetTexImage for color formats (RGBA, RGB, alpha, LA, etc).
249 * Compressed textures are handled here as well.
250 */
251 static void
252 get_tex_rgba(struct gl_context *ctx, GLuint dimensions,
253 GLenum format, GLenum type, GLvoid *pixels,
254 struct gl_texture_image *texImage)
255 {
256 const GLenum dataType = _mesa_get_format_datatype(texImage->TexFormat);
257 GLbitfield transferOps = 0x0;
258
259 /* In general, clamping does not apply to glGetTexImage, except when
260 * the returned type of the image can't hold negative values.
261 */
262 if (type_needs_clamping(type)) {
263 /* the returned image type can't have negative values */
264 if (dataType == GL_FLOAT ||
265 dataType == GL_SIGNED_NORMALIZED ||
266 format == GL_LUMINANCE ||
267 format == GL_LUMINANCE_ALPHA) {
268 transferOps |= IMAGE_CLAMP_BIT;
269 }
270 }
271 /* This applies to RGB, RGBA textures. if the format is either LUMINANCE
272 * or LUMINANCE ALPHA, luminance (L) is computed as L=R+G+B .we need to
273 * clamp the sum to [0,1].
274 */
275 else if ((format == GL_LUMINANCE ||
276 format == GL_LUMINANCE_ALPHA) &&
277 dataType == GL_UNSIGNED_NORMALIZED) {
278 transferOps |= IMAGE_CLAMP_BIT;
279 }
280 get_tex_rgba_uncompressed(ctx, dimensions, format, type,
281 pixels, texImage, transferOps);
282 }
283
284
285 /**
286 * Try to do glGetTexImage() with simple memcpy().
287 * \return GL_TRUE if done, GL_FALSE otherwise
288 */
289 static GLboolean
290 get_tex_memcpy(struct gl_context *ctx, GLenum format, GLenum type,
291 GLvoid *pixels,
292 struct gl_texture_image *texImage)
293 {
294 const GLenum target = texImage->TexObject->Target;
295 GLboolean memCopy = GL_FALSE;
296
297 /*
298 * Check if the src/dst formats are compatible.
299 * Also note that GL's pixel transfer ops don't apply to glGetTexImage()
300 * so we don't have to worry about those.
301 * XXX more format combinations could be supported here.
302 */
303 if (target == GL_TEXTURE_1D ||
304 target == GL_TEXTURE_2D ||
305 _mesa_is_cube_face(target)) {
306 if ((texImage->TexFormat == MESA_FORMAT_ARGB8888) &&
307 format == GL_BGRA &&
308 (type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) &&
309 !ctx->Pack.SwapBytes &&
310 _mesa_little_endian()) {
311 memCopy = GL_TRUE;
312 }
313 else if ((texImage->TexFormat == MESA_FORMAT_AL88) &&
314 format == GL_LUMINANCE_ALPHA &&
315 type == GL_UNSIGNED_BYTE &&
316 !ctx->Pack.SwapBytes &&
317 _mesa_little_endian()) {
318 memCopy = GL_TRUE;
319 }
320 else if ((texImage->TexFormat == MESA_FORMAT_L8) &&
321 format == GL_LUMINANCE &&
322 type == GL_UNSIGNED_BYTE) {
323 memCopy = GL_TRUE;
324 }
325 else if (texImage->TexFormat == MESA_FORMAT_L16 &&
326 format == GL_LUMINANCE &&
327 type == GL_UNSIGNED_SHORT) {
328 memCopy = GL_TRUE;
329 }
330 else if (texImage->TexFormat == MESA_FORMAT_A8 &&
331 format == GL_ALPHA &&
332 type == GL_UNSIGNED_BYTE) {
333 memCopy = GL_TRUE;
334 }
335 else if (texImage->TexFormat == MESA_FORMAT_A16 &&
336 format == GL_ALPHA &&
337 type == GL_UNSIGNED_SHORT) {
338 memCopy = GL_TRUE;
339 }
340 }
341
342 if (memCopy) {
343 const GLuint bpp = _mesa_get_format_bytes(texImage->TexFormat);
344 const GLuint bytesPerRow = texImage->Width * bpp;
345 GLubyte *dst =
346 _mesa_image_address2d(&ctx->Pack, pixels, texImage->Width,
347 texImage->Height, format, type, 0, 0);
348 const GLint dstRowStride =
349 _mesa_image_row_stride(&ctx->Pack, texImage->Width, format, type);
350 GLubyte *src;
351 GLint srcRowStride;
352
353 /* map src texture buffer */
354 ctx->Driver.MapTextureImage(ctx, texImage, 0,
355 0, 0, texImage->Width, texImage->Height,
356 GL_MAP_READ_BIT, &src, &srcRowStride);
357
358 if (src) {
359 if (bytesPerRow == dstRowStride && bytesPerRow == srcRowStride) {
360 memcpy(dst, src, bytesPerRow * texImage->Height);
361 }
362 else {
363 GLuint row;
364 for (row = 0; row < texImage->Height; row++) {
365 memcpy(dst, src, bytesPerRow);
366 dst += dstRowStride;
367 src += srcRowStride;
368 }
369 }
370
371 /* unmap src texture buffer */
372 ctx->Driver.UnmapTextureImage(ctx, texImage, 0);
373 }
374 else {
375 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage");
376 }
377 }
378
379 return memCopy;
380 }
381
382
383 /**
384 * This is the software fallback for Driver.GetTexImage().
385 * All error checking will have been done before this routine is called.
386 * We'll call ctx->Driver.MapTextureImage() to access the data, then
387 * unmap with ctx->Driver.UnmapTextureImage().
388 */
389 void
390 _mesa_get_teximage(struct gl_context *ctx,
391 GLenum format, GLenum type, GLvoid *pixels,
392 struct gl_texture_image *texImage)
393 {
394 GLuint dimensions;
395
396 switch (texImage->TexObject->Target) {
397 case GL_TEXTURE_1D:
398 dimensions = 1;
399 break;
400 default:
401 dimensions = 2;
402 }
403
404 if (get_tex_memcpy(ctx, format, type, pixels, texImage)) {
405 /* all done */
406 }
407 else if (format == GL_DEPTH_COMPONENT) {
408 get_tex_depth(ctx, dimensions, format, type, pixels, texImage);
409 }
410 else if (format == GL_YCBCR_MESA) {
411 get_tex_ycbcr(ctx, dimensions, format, type, pixels, texImage);
412 }
413 else {
414 get_tex_rgba(ctx, dimensions, format, type, pixels, texImage);
415 }
416 }
417
418 /**
419 * Do error checking for a glGetTexImage() call.
420 * \return GL_TRUE if any error, GL_FALSE if no errors.
421 */
422 static GLboolean
423 getteximage_error_check(struct gl_context *ctx, GLenum target, GLint level,
424 GLenum format, GLenum type, GLvoid *pixels )
425 {
426 struct gl_texture_object *texObj;
427 struct gl_texture_image *texImage;
428 const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
429 GLenum baseFormat, err;
430
431 if (maxLevels == 0) {
432 _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target=0x%x)", target);
433 return GL_TRUE;
434 }
435
436 if (level < 0 || level >= maxLevels) {
437 _mesa_error( ctx, GL_INVALID_VALUE, "glGetTexImage(level)" );
438 return GL_TRUE;
439 }
440
441 if (_mesa_sizeof_packed_type(type) <= 0) {
442 _mesa_error( ctx, GL_INVALID_ENUM, "glGetTexImage(type)" );
443 return GL_TRUE;
444 }
445
446 if (_mesa_components_in_format(format) <= 0 ||
447 format == GL_STENCIL_INDEX ||
448 format == GL_COLOR_INDEX) {
449 _mesa_error( ctx, GL_INVALID_ENUM, "glGetTexImage(format)" );
450 return GL_TRUE;
451 }
452
453 if (_mesa_is_depth_format(format)) {
454 _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
455 return GL_TRUE;
456 }
457
458 if (!ctx->Extensions.MESA_ycbcr_texture && _mesa_is_ycbcr_format(format)) {
459 _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(format)");
460 return GL_TRUE;
461 }
462
463 err = _mesa_error_check_format_and_type(ctx, format, type);
464 if (err != GL_NO_ERROR) {
465 _mesa_error(ctx, err, "glGetTexImage(format/type)");
466 return GL_TRUE;
467 }
468
469 texObj = _mesa_select_tex_object(ctx, target);
470
471 if (!texObj || _mesa_is_proxy_texture(target)) {
472 _mesa_error(ctx, GL_INVALID_ENUM, "glGetTexImage(target)");
473 return GL_TRUE;
474 }
475
476 texImage = _mesa_select_tex_image(ctx, texObj, target, level);
477 if (!texImage) {
478 /* non-existant texture image */
479 return GL_TRUE;
480 }
481
482 baseFormat = _mesa_get_format_base_format(texImage->TexFormat);
483
484 /* Make sure the requested image format is compatible with the
485 * texture's format.
486 */
487 if (_mesa_is_color_format(format)
488 && !_mesa_is_color_format(baseFormat)) {
489 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
490 return GL_TRUE;
491 }
492 else if (_mesa_is_depth_format(format)
493 && !_mesa_is_depth_format(baseFormat)) {
494 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
495 return GL_TRUE;
496 }
497 else if (_mesa_is_ycbcr_format(format)
498 && !_mesa_is_ycbcr_format(baseFormat)) {
499 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetTexImage(format mismatch)");
500 return GL_TRUE;
501 }
502
503 return GL_FALSE;
504 }
505
506
507
508 /**
509 * Get texture image. Called by glGetTexImage.
510 *
511 * \param target texture target.
512 * \param level image level.
513 * \param format pixel data format for returned image.
514 * \param type pixel data type for returned image.
515 * \param bufSize size of the pixels data buffer.
516 * \param pixels returned pixel data.
517 */
518 void GLAPIENTRY
519 _mesa_GetTexImage( GLenum target, GLint level, GLenum format,
520 GLenum type, GLvoid *pixels )
521 {
522 struct gl_texture_object *texObj;
523 struct gl_texture_image *texImage;
524 GET_CURRENT_CONTEXT(ctx);
525 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
526
527 if (getteximage_error_check(ctx, target, level, format, type, pixels)) {
528 return;
529 }
530
531 if (!pixels) {
532 /* not an error, do nothing */
533 return;
534 }
535
536 texObj = _mesa_select_tex_object(ctx, target);
537 texImage = _mesa_select_tex_image(ctx, texObj, target, level);
538
539 if (_mesa_is_zero_size_texture(texImage))
540 return;
541
542 if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) {
543 _mesa_debug(ctx, "glGetTexImage(tex %u) format = %s, w=%d, h=%d,"
544 " dstFmt=0x%x, dstType=0x%x\n",
545 texObj->Name,
546 _mesa_get_format_name(texImage->TexFormat),
547 texImage->Width, texImage->Height,
548 format, type);
549 }
550
551 _mesa_lock_texture(ctx, texObj);
552 {
553 ctx->Driver.GetTexImage(ctx, format, type, pixels, texImage);
554 }
555 _mesa_unlock_texture(ctx, texObj);
556 }
557