Merge branch 'ext-provoking-vertex'
[mesa.git] / src / mesa / main / texgetimage.c
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 "context.h"
34 #include "image.h"
35 #include "texcompress.h"
36 #include "texformat.h"
37 #include "texgetimage.h"
38
39
40
41 #if FEATURE_EXT_texture_sRGB
42
43 /**
44 * Test if given texture image is an sRGB format.
45 */
46 static GLboolean
47 is_srgb_teximage(const struct gl_texture_image *texImage)
48 {
49 switch (texImage->TexFormat->MesaFormat) {
50 case MESA_FORMAT_SRGB8:
51 case MESA_FORMAT_SRGBA8:
52 case MESA_FORMAT_SARGB8:
53 case MESA_FORMAT_SL8:
54 case MESA_FORMAT_SLA8:
55 case MESA_FORMAT_SRGB_DXT1:
56 case MESA_FORMAT_SRGBA_DXT1:
57 case MESA_FORMAT_SRGBA_DXT3:
58 case MESA_FORMAT_SRGBA_DXT5:
59 return GL_TRUE;
60 default:
61 return GL_FALSE;
62 }
63 }
64
65
66 /**
67 * Convert a float value from linear space to a
68 * non-linear sRGB value in [0, 255].
69 * Not terribly efficient.
70 */
71 static INLINE GLfloat
72 linear_to_nonlinear(GLfloat cl)
73 {
74 /* can't have values outside [0, 1] */
75 GLfloat cs;
76 if (cl < 0.0031308f) {
77 cs = 12.92f * cl;
78 }
79 else {
80 cs = (GLfloat)(1.055 * _mesa_pow(cl, 0.41666) - 0.055);
81 }
82 return cs;
83 }
84
85 #endif /* FEATURE_EXT_texture_sRGB */
86
87
88 /**
89 * Can the given type represent negative values?
90 */
91 static INLINE GLboolean
92 type_with_negative_values(GLenum type)
93 {
94 switch (type) {
95 case GL_BYTE:
96 case GL_SHORT:
97 case GL_INT:
98 case GL_FLOAT:
99 case GL_HALF_FLOAT_ARB:
100 return GL_TRUE;
101 default:
102 return GL_FALSE;
103 }
104 }
105
106
107 /**
108 * This is the software fallback for Driver.GetTexImage().
109 * All error checking will have been done before this routine is called.
110 */
111 void
112 _mesa_get_teximage(GLcontext *ctx, GLenum target, GLint level,
113 GLenum format, GLenum type, GLvoid *pixels,
114 struct gl_texture_object *texObj,
115 struct gl_texture_image *texImage)
116 {
117 const GLuint dimensions = (target == GL_TEXTURE_3D) ? 3 : 2;
118
119 if (ctx->Pack.BufferObj->Name) {
120 /* Packing texture image into a PBO.
121 * Map the (potentially) VRAM-based buffer into our process space so
122 * we can write into it with the code below.
123 * A hardware driver might use a sophisticated blit to move the
124 * texture data to the PBO if the PBO is in VRAM along with the texture.
125 */
126 GLubyte *buf = (GLubyte *)
127 ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
128 GL_WRITE_ONLY_ARB, ctx->Pack.BufferObj);
129 if (!buf) {
130 /* buffer is already mapped - that's an error */
131 _mesa_error(ctx, GL_INVALID_OPERATION,"glGetTexImage(PBO is mapped)");
132 return;
133 }
134 /* <pixels> was an offset into the PBO.
135 * Now make it a real, client-side pointer inside the mapped region.
136 */
137 pixels = ADD_POINTERS(buf, pixels);
138 }
139 else if (!pixels) {
140 /* not an error */
141 return;
142 }
143
144 {
145 const GLint width = texImage->Width;
146 const GLint height = texImage->Height;
147 const GLint depth = texImage->Depth;
148 GLint img, row;
149 for (img = 0; img < depth; img++) {
150 for (row = 0; row < height; row++) {
151 /* compute destination address in client memory */
152 GLvoid *dest = _mesa_image_address( dimensions, &ctx->Pack, pixels,
153 width, height, format, type,
154 img, row, 0);
155 assert(dest);
156
157 if (format == GL_COLOR_INDEX) {
158 GLuint indexRow[MAX_WIDTH];
159 GLint col;
160 /* Can't use FetchTexel here because that returns RGBA */
161 if (texImage->TexFormat->IndexBits == 8) {
162 const GLubyte *src = (const GLubyte *) texImage->Data;
163 src += width * (img * texImage->Height + row);
164 for (col = 0; col < width; col++) {
165 indexRow[col] = src[col];
166 }
167 }
168 else if (texImage->TexFormat->IndexBits == 16) {
169 const GLushort *src = (const GLushort *) texImage->Data;
170 src += width * (img * texImage->Height + row);
171 for (col = 0; col < width; col++) {
172 indexRow[col] = src[col];
173 }
174 }
175 else {
176 _mesa_problem(ctx,
177 "Color index problem in _mesa_GetTexImage");
178 }
179 _mesa_pack_index_span(ctx, width, type, dest,
180 indexRow, &ctx->Pack,
181 0 /* no image transfer */);
182 }
183 else if (format == GL_DEPTH_COMPONENT) {
184 GLfloat depthRow[MAX_WIDTH];
185 GLint col;
186 for (col = 0; col < width; col++) {
187 (*texImage->FetchTexelf)(texImage, col, row, img,
188 depthRow + col);
189 }
190 _mesa_pack_depth_span(ctx, width, dest, type,
191 depthRow, &ctx->Pack);
192 }
193 else if (format == GL_DEPTH_STENCIL_EXT) {
194 /* XXX Note: we're bypassing texImage->FetchTexel()! */
195 const GLuint *src = (const GLuint *) texImage->Data;
196 src += width * row + width * height * img;
197 _mesa_memcpy(dest, src, width * sizeof(GLuint));
198 if (ctx->Pack.SwapBytes) {
199 _mesa_swap4((GLuint *) dest, width);
200 }
201 }
202 else if (format == GL_YCBCR_MESA) {
203 /* No pixel transfer */
204 const GLint rowstride = texImage->RowStride;
205 MEMCPY(dest,
206 (const GLushort *) texImage->Data + row * rowstride,
207 width * sizeof(GLushort));
208 /* check for byte swapping */
209 if ((texImage->TexFormat->MesaFormat == MESA_FORMAT_YCBCR
210 && type == GL_UNSIGNED_SHORT_8_8_REV_MESA) ||
211 (texImage->TexFormat->MesaFormat == MESA_FORMAT_YCBCR_REV
212 && type == GL_UNSIGNED_SHORT_8_8_MESA)) {
213 if (!ctx->Pack.SwapBytes)
214 _mesa_swap2((GLushort *) dest, width);
215 }
216 else if (ctx->Pack.SwapBytes) {
217 _mesa_swap2((GLushort *) dest, width);
218 }
219 }
220 #if FEATURE_EXT_texture_sRGB
221 else if (is_srgb_teximage(texImage)) {
222 /* special case this since need to backconvert values */
223 /* convert row to RGBA format */
224 GLfloat rgba[MAX_WIDTH][4];
225 GLint col;
226 GLbitfield transferOps = 0x0;
227
228 for (col = 0; col < width; col++) {
229 (*texImage->FetchTexelf)(texImage, col, row, img, rgba[col]);
230 if (texImage->_BaseFormat == GL_LUMINANCE) {
231 rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
232 rgba[col][GCOMP] = 0.0;
233 rgba[col][BCOMP] = 0.0;
234 }
235 else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
236 rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
237 rgba[col][GCOMP] = 0.0;
238 rgba[col][BCOMP] = 0.0;
239 }
240 else if (texImage->_BaseFormat == GL_RGB ||
241 texImage->_BaseFormat == GL_RGBA) {
242 rgba[col][RCOMP] = linear_to_nonlinear(rgba[col][RCOMP]);
243 rgba[col][GCOMP] = linear_to_nonlinear(rgba[col][GCOMP]);
244 rgba[col][BCOMP] = linear_to_nonlinear(rgba[col][BCOMP]);
245 }
246 }
247 _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
248 format, type, dest,
249 &ctx->Pack, transferOps);
250 }
251 #endif /* FEATURE_EXT_texture_sRGB */
252 else {
253 /* general case: convert row to RGBA format */
254 GLfloat rgba[MAX_WIDTH][4];
255 GLint col;
256 GLbitfield transferOps = 0x0;
257
258 /* clamp does not apply to GetTexImage (final conversion)?
259 * Looks like we need clamp though when going from format
260 * containing negative values to unsigned format.
261 */
262 if (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA)
263 transferOps |= IMAGE_CLAMP_BIT;
264 else if (!type_with_negative_values(type) &&
265 (texImage->TexFormat->DataType == GL_FLOAT ||
266 texImage->TexFormat->DataType == GL_SIGNED_NORMALIZED))
267 transferOps |= IMAGE_CLAMP_BIT;
268
269 for (col = 0; col < width; col++) {
270 (*texImage->FetchTexelf)(texImage, col, row, img, rgba[col]);
271 if (texImage->_BaseFormat == GL_ALPHA) {
272 rgba[col][RCOMP] = 0.0;
273 rgba[col][GCOMP] = 0.0;
274 rgba[col][BCOMP] = 0.0;
275 }
276 else if (texImage->_BaseFormat == GL_LUMINANCE) {
277 rgba[col][GCOMP] = 0.0;
278 rgba[col][BCOMP] = 0.0;
279 rgba[col][ACOMP] = 1.0;
280 }
281 else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) {
282 rgba[col][GCOMP] = 0.0;
283 rgba[col][BCOMP] = 0.0;
284 }
285 else if (texImage->_BaseFormat == GL_INTENSITY) {
286 rgba[col][GCOMP] = 0.0;
287 rgba[col][BCOMP] = 0.0;
288 rgba[col][ACOMP] = 1.0;
289 }
290 }
291 _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba,
292 format, type, dest,
293 &ctx->Pack, transferOps);
294 } /* format */
295 } /* row */
296 } /* img */
297 }
298
299 if (ctx->Pack.BufferObj->Name) {
300 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
301 ctx->Pack.BufferObj);
302 }
303 }
304
305
306
307 /**
308 * This is the software fallback for Driver.GetCompressedTexImage().
309 * All error checking will have been done before this routine is called.
310 */
311 void
312 _mesa_get_compressed_teximage(GLcontext *ctx, GLenum target, GLint level,
313 GLvoid *img,
314 struct gl_texture_object *texObj,
315 struct gl_texture_image *texImage)
316 {
317 GLuint size;
318
319 if (ctx->Pack.BufferObj->Name) {
320 /* pack texture image into a PBO */
321 GLubyte *buf;
322 if ((const GLubyte *) img + texImage->CompressedSize >
323 (const GLubyte *) ctx->Pack.BufferObj->Size) {
324 _mesa_error(ctx, GL_INVALID_OPERATION,
325 "glGetCompressedTexImage(invalid PBO access)");
326 return;
327 }
328 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
329 GL_WRITE_ONLY_ARB,
330 ctx->Pack.BufferObj);
331 if (!buf) {
332 /* buffer is already mapped - that's an error */
333 _mesa_error(ctx, GL_INVALID_OPERATION,
334 "glGetCompressedTexImage(PBO is mapped)");
335 return;
336 }
337 img = ADD_POINTERS(buf, img);
338 }
339 else if (!img) {
340 /* not an error */
341 return;
342 }
343
344 /* don't use texImage->CompressedSize since that may be padded out */
345 size = _mesa_compressed_texture_size(ctx, texImage->Width, texImage->Height,
346 texImage->Depth,
347 texImage->TexFormat->MesaFormat);
348
349 /* just memcpy, no pixelstore or pixel transfer */
350 _mesa_memcpy(img, texImage->Data, size);
351
352 if (ctx->Pack.BufferObj->Name) {
353 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
354 ctx->Pack.BufferObj);
355 }
356 }