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