mesa: Add support to allow blitting to multiple color draw buffers
[mesa.git] / src / mesa / swrast / s_blit.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5
4 *
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 #include "main/glheader.h"
27 #include "main/condrender.h"
28 #include "main/image.h"
29 #include "main/macros.h"
30 #include "main/format_unpack.h"
31 #include "main/format_pack.h"
32 #include "s_context.h"
33
34
35 #define ABS(X) ((X) < 0 ? -(X) : (X))
36
37
38 /**
39 * Generate a row resampler function for GL_NEAREST mode.
40 */
41 #define RESAMPLE(NAME, PIXELTYPE, SIZE) \
42 static void \
43 NAME(GLint srcWidth, GLint dstWidth, \
44 const GLvoid *srcBuffer, GLvoid *dstBuffer, \
45 GLboolean flip) \
46 { \
47 const PIXELTYPE *src = (const PIXELTYPE *) srcBuffer;\
48 PIXELTYPE *dst = (PIXELTYPE *) dstBuffer; \
49 GLint dstCol; \
50 \
51 if (flip) { \
52 for (dstCol = 0; dstCol < dstWidth; dstCol++) { \
53 GLint srcCol = (dstCol * srcWidth) / dstWidth; \
54 ASSERT(srcCol >= 0); \
55 ASSERT(srcCol < srcWidth); \
56 srcCol = srcWidth - 1 - srcCol; /* flip */ \
57 if (SIZE == 1) { \
58 dst[dstCol] = src[srcCol]; \
59 } \
60 else if (SIZE == 2) { \
61 dst[dstCol*2+0] = src[srcCol*2+0]; \
62 dst[dstCol*2+1] = src[srcCol*2+1]; \
63 } \
64 else if (SIZE == 4) { \
65 dst[dstCol*4+0] = src[srcCol*4+0]; \
66 dst[dstCol*4+1] = src[srcCol*4+1]; \
67 dst[dstCol*4+2] = src[srcCol*4+2]; \
68 dst[dstCol*4+3] = src[srcCol*4+3]; \
69 } \
70 } \
71 } \
72 else { \
73 for (dstCol = 0; dstCol < dstWidth; dstCol++) { \
74 GLint srcCol = (dstCol * srcWidth) / dstWidth; \
75 ASSERT(srcCol >= 0); \
76 ASSERT(srcCol < srcWidth); \
77 if (SIZE == 1) { \
78 dst[dstCol] = src[srcCol]; \
79 } \
80 else if (SIZE == 2) { \
81 dst[dstCol*2+0] = src[srcCol*2+0]; \
82 dst[dstCol*2+1] = src[srcCol*2+1]; \
83 } \
84 else if (SIZE == 4) { \
85 dst[dstCol*4+0] = src[srcCol*4+0]; \
86 dst[dstCol*4+1] = src[srcCol*4+1]; \
87 dst[dstCol*4+2] = src[srcCol*4+2]; \
88 dst[dstCol*4+3] = src[srcCol*4+3]; \
89 } \
90 } \
91 } \
92 }
93
94 /**
95 * Resamplers for 1, 2, 4, 8 and 16-byte pixels.
96 */
97 RESAMPLE(resample_row_1, GLubyte, 1)
98 RESAMPLE(resample_row_2, GLushort, 1)
99 RESAMPLE(resample_row_4, GLuint, 1)
100 RESAMPLE(resample_row_8, GLuint, 2)
101 RESAMPLE(resample_row_16, GLuint, 4)
102
103
104 /**
105 * Blit color, depth or stencil with GL_NEAREST filtering.
106 */
107 static void
108 blit_nearest(struct gl_context *ctx,
109 GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
110 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
111 GLbitfield buffer)
112 {
113 struct gl_renderbuffer *readRb, *drawRb;
114 struct gl_renderbuffer_attachment *readAtt, *drawAtt;
115 struct gl_framebuffer *readFb = ctx->ReadBuffer;
116 struct gl_framebuffer *drawFb = ctx->DrawBuffer;
117 GLint NumDrawBuffers = 0;
118 GLuint i;
119
120 const GLint srcWidth = ABS(srcX1 - srcX0);
121 const GLint dstWidth = ABS(dstX1 - dstX0);
122 const GLint srcHeight = ABS(srcY1 - srcY0);
123 const GLint dstHeight = ABS(dstY1 - dstY0);
124
125 const GLint srcXpos = MIN2(srcX0, srcX1);
126 const GLint srcYpos = MIN2(srcY0, srcY1);
127 const GLint dstXpos = MIN2(dstX0, dstX1);
128 const GLint dstYpos = MIN2(dstY0, dstY1);
129
130 const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
131 const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
132 enum mode {
133 DIRECT,
134 UNPACK_RGBA_FLOAT,
135 UNPACK_Z_FLOAT,
136 UNPACK_Z_INT,
137 UNPACK_S,
138 } mode;
139 GLubyte *srcMap, *dstMap;
140 GLint srcRowStride, dstRowStride;
141 GLint dstRow;
142
143 GLint pixelSize;
144 GLvoid *srcBuffer, *dstBuffer;
145 GLint prevY = -1;
146
147 typedef void (*resample_func)(GLint srcWidth, GLint dstWidth,
148 const GLvoid *srcBuffer, GLvoid *dstBuffer,
149 GLboolean flip);
150 resample_func resampleRow;
151
152 switch (buffer) {
153 case GL_COLOR_BUFFER_BIT:
154 readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex];
155 readRb = readFb->_ColorReadBuffer;
156 NumDrawBuffers = drawFb->_NumColorDrawBuffers;
157 break;
158 case GL_DEPTH_BUFFER_BIT:
159 readAtt = &readFb->Attachment[BUFFER_DEPTH];
160 drawAtt = &drawFb->Attachment[BUFFER_DEPTH];
161 readRb = readAtt->Renderbuffer;
162 drawRb = drawAtt->Renderbuffer;
163 NumDrawBuffers = 1;
164
165 /* Note that for depth/stencil, the formats of src/dst must match. By
166 * using the core helpers for pack/unpack, we avoid needing to handle
167 * masking for things like DEPTH copies of Z24S8.
168 */
169 if (readRb->Format == MESA_FORMAT_Z32_FLOAT ||
170 readRb->Format == MESA_FORMAT_Z32_FLOAT_X24S8) {
171 mode = UNPACK_Z_FLOAT;
172 } else {
173 mode = UNPACK_Z_INT;
174 }
175 pixelSize = 4;
176 break;
177 case GL_STENCIL_BUFFER_BIT:
178 readAtt = &readFb->Attachment[BUFFER_STENCIL];
179 drawAtt = &drawFb->Attachment[BUFFER_STENCIL];
180 readRb = readAtt->Renderbuffer;
181 drawRb = drawAtt->Renderbuffer;
182 NumDrawBuffers = 1;
183 mode = UNPACK_S;
184 pixelSize = 1;
185 break;
186 default:
187 _mesa_problem(ctx, "unexpected buffer in blit_nearest()");
188 return;
189 }
190
191 /* choose row resampler */
192 switch (pixelSize) {
193 case 1:
194 resampleRow = resample_row_1;
195 break;
196 case 2:
197 resampleRow = resample_row_2;
198 break;
199 case 4:
200 resampleRow = resample_row_4;
201 break;
202 case 8:
203 resampleRow = resample_row_8;
204 break;
205 case 16:
206 resampleRow = resample_row_16;
207 break;
208 default:
209 _mesa_problem(ctx, "unexpected pixel size (%d) in blit_nearest",
210 pixelSize);
211 return;
212 }
213
214 /* Blit to all the draw buffers */
215 for (i = 0; i < NumDrawBuffers; i++) {
216 if (buffer == GL_COLOR_BUFFER_BIT) {
217 int idx = drawFb->_ColorDrawBufferIndexes[i];
218 if (idx == -1)
219 continue;
220 drawAtt = &drawFb->Attachment[idx];
221 drawRb = drawAtt->Renderbuffer;
222
223 if (readRb->Format == drawRb->Format) {
224 mode = DIRECT;
225 pixelSize = _mesa_get_format_bytes(readRb->Format);
226 } else {
227 mode = UNPACK_RGBA_FLOAT;
228 pixelSize = 16;
229 }
230 }
231
232 if ((readRb == drawRb) ||
233 (readAtt->Texture && drawAtt->Texture &&
234 (readAtt->Texture == drawAtt->Texture))) {
235 /* map whole buffer for read/write */
236 /* XXX we could be clever and just map the union region of the
237 * source and dest rects.
238 */
239 GLubyte *map;
240 GLint rowStride;
241 GLint formatSize = _mesa_get_format_bytes(readRb->Format);
242
243 ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0,
244 readRb->Width, readRb->Height,
245 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
246 &map, &rowStride);
247 if (!map) {
248 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
249 return;
250 }
251
252 srcMap = map + srcYpos * rowStride + srcXpos * formatSize;
253 dstMap = map + dstYpos * rowStride + dstXpos * formatSize;
254
255 /* this handles overlapping copies */
256 if (srcY0 < dstY0) {
257 /* copy in reverse (top->down) order */
258 srcMap += rowStride * (readRb->Height - 1);
259 dstMap += rowStride * (readRb->Height - 1);
260 srcRowStride = -rowStride;
261 dstRowStride = -rowStride;
262 }
263 else {
264 /* copy in normal (bottom->up) order */
265 srcRowStride = rowStride;
266 dstRowStride = rowStride;
267 }
268 }
269 else {
270 /* different src/dst buffers */
271 ctx->Driver.MapRenderbuffer(ctx, readRb,
272 srcXpos, srcYpos,
273 srcWidth, srcHeight,
274 GL_MAP_READ_BIT, &srcMap, &srcRowStride);
275 if (!srcMap) {
276 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
277 return;
278 }
279 ctx->Driver.MapRenderbuffer(ctx, drawRb,
280 dstXpos, dstYpos,
281 dstWidth, dstHeight,
282 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
283 if (!dstMap) {
284 ctx->Driver.UnmapRenderbuffer(ctx, readRb);
285 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
286 return;
287 }
288 }
289
290 /* allocate the src/dst row buffers */
291 srcBuffer = malloc(pixelSize * srcWidth);
292 if (!srcBuffer) {
293 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
294 return;
295 }
296 dstBuffer = malloc(pixelSize * dstWidth);
297 if (!dstBuffer) {
298 free(srcBuffer);
299 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
300 return;
301 }
302
303 for (dstRow = 0; dstRow < dstHeight; dstRow++) {
304 GLint srcRow = (dstRow * srcHeight) / dstHeight;
305 GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
306
307 ASSERT(srcRow >= 0);
308 ASSERT(srcRow < srcHeight);
309
310 if (invertY) {
311 srcRow = srcHeight - 1 - srcRow;
312 }
313
314 /* get pixel row from source and resample to match dest width */
315 if (prevY != srcRow) {
316 GLubyte *srcRowStart = srcMap + srcRowStride * srcRow;
317
318 switch (mode) {
319 case DIRECT:
320 memcpy(srcBuffer, srcRowStart, pixelSize * srcWidth);
321 break;
322 case UNPACK_RGBA_FLOAT:
323 _mesa_unpack_rgba_row(readRb->Format, srcWidth, srcRowStart,
324 srcBuffer);
325 break;
326 case UNPACK_Z_FLOAT:
327 _mesa_unpack_float_z_row(readRb->Format, srcWidth, srcRowStart,
328 srcBuffer);
329 break;
330 case UNPACK_Z_INT:
331 _mesa_unpack_uint_z_row(readRb->Format, srcWidth, srcRowStart,
332 srcBuffer);
333 break;
334 case UNPACK_S:
335 _mesa_unpack_ubyte_stencil_row(readRb->Format, srcWidth,
336 srcRowStart, srcBuffer);
337 break;
338 }
339
340 (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
341 prevY = srcRow;
342 }
343
344 /* store pixel row in destination */
345 switch (mode) {
346 case DIRECT:
347 memcpy(dstRowStart, dstBuffer, pixelSize * srcWidth);
348 break;
349 case UNPACK_RGBA_FLOAT:
350 _mesa_pack_float_rgba_row(drawRb->Format, dstWidth, dstBuffer,
351 dstRowStart);
352 break;
353 case UNPACK_Z_FLOAT:
354 _mesa_pack_float_z_row(drawRb->Format, dstWidth, dstBuffer,
355 dstRowStart);
356 break;
357 case UNPACK_Z_INT:
358 _mesa_pack_uint_z_row(drawRb->Format, dstWidth, dstBuffer,
359 dstRowStart);
360 break;
361 case UNPACK_S:
362 _mesa_pack_ubyte_stencil_row(drawRb->Format, dstWidth, dstBuffer,
363 dstRowStart);
364 break;
365 }
366 }
367
368 free(srcBuffer);
369 free(dstBuffer);
370
371 ctx->Driver.UnmapRenderbuffer(ctx, readRb);
372 if (drawRb != readRb) {
373 ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
374 }
375 }
376 }
377
378
379
380 #define LERP(T, A, B) ( (A) + (T) * ((B) - (A)) )
381
382 static inline GLfloat
383 lerp_2d(GLfloat a, GLfloat b,
384 GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
385 {
386 const GLfloat temp0 = LERP(a, v00, v10);
387 const GLfloat temp1 = LERP(a, v01, v11);
388 return LERP(b, temp0, temp1);
389 }
390
391
392 /**
393 * Bilinear interpolation of two source rows.
394 * GLubyte pixels.
395 */
396 static void
397 resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
398 const GLvoid *srcBuffer0, const GLvoid *srcBuffer1,
399 GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight)
400 {
401 const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
402 const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
403 GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
404 const GLfloat dstWidthF = (GLfloat) dstWidth;
405 GLint dstCol;
406
407 for (dstCol = 0; dstCol < dstWidth; dstCol++) {
408 const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
409 GLint srcCol0 = IFLOOR(srcCol);
410 GLint srcCol1 = srcCol0 + 1;
411 GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
412 GLfloat red, green, blue, alpha;
413
414 ASSERT(srcCol0 >= 0);
415 ASSERT(srcCol0 < srcWidth);
416 ASSERT(srcCol1 <= srcWidth);
417
418 if (srcCol1 == srcWidth) {
419 /* last column fudge */
420 srcCol1--;
421 colWeight = 0.0;
422 }
423
424 if (flip) {
425 srcCol0 = srcWidth - 1 - srcCol0;
426 srcCol1 = srcWidth - 1 - srcCol1;
427 }
428
429 red = lerp_2d(colWeight, rowWeight,
430 srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP],
431 srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]);
432 green = lerp_2d(colWeight, rowWeight,
433 srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP],
434 srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]);
435 blue = lerp_2d(colWeight, rowWeight,
436 srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP],
437 srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]);
438 alpha = lerp_2d(colWeight, rowWeight,
439 srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
440 srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
441
442 dstColor[dstCol][RCOMP] = IFLOOR(red);
443 dstColor[dstCol][GCOMP] = IFLOOR(green);
444 dstColor[dstCol][BCOMP] = IFLOOR(blue);
445 dstColor[dstCol][ACOMP] = IFLOOR(alpha);
446 }
447 }
448
449
450 /**
451 * Bilinear interpolation of two source rows. floating point pixels.
452 */
453 static void
454 resample_linear_row_float(GLint srcWidth, GLint dstWidth,
455 const GLvoid *srcBuffer0, const GLvoid *srcBuffer1,
456 GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight)
457 {
458 const GLfloat (*srcColor0)[4] = (const GLfloat (*)[4]) srcBuffer0;
459 const GLfloat (*srcColor1)[4] = (const GLfloat (*)[4]) srcBuffer1;
460 GLfloat (*dstColor)[4] = (GLfloat (*)[4]) dstBuffer;
461 const GLfloat dstWidthF = (GLfloat) dstWidth;
462 GLint dstCol;
463
464 for (dstCol = 0; dstCol < dstWidth; dstCol++) {
465 const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
466 GLint srcCol0 = IFLOOR(srcCol);
467 GLint srcCol1 = srcCol0 + 1;
468 GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
469 GLfloat red, green, blue, alpha;
470
471 ASSERT(srcCol0 >= 0);
472 ASSERT(srcCol0 < srcWidth);
473 ASSERT(srcCol1 <= srcWidth);
474
475 if (srcCol1 == srcWidth) {
476 /* last column fudge */
477 srcCol1--;
478 colWeight = 0.0;
479 }
480
481 if (flip) {
482 srcCol0 = srcWidth - 1 - srcCol0;
483 srcCol1 = srcWidth - 1 - srcCol1;
484 }
485
486 red = lerp_2d(colWeight, rowWeight,
487 srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP],
488 srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]);
489 green = lerp_2d(colWeight, rowWeight,
490 srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP],
491 srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]);
492 blue = lerp_2d(colWeight, rowWeight,
493 srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP],
494 srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]);
495 alpha = lerp_2d(colWeight, rowWeight,
496 srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
497 srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
498
499 dstColor[dstCol][RCOMP] = red;
500 dstColor[dstCol][GCOMP] = green;
501 dstColor[dstCol][BCOMP] = blue;
502 dstColor[dstCol][ACOMP] = alpha;
503 }
504 }
505
506
507
508 /**
509 * Bilinear filtered blit (color only, non-integer values).
510 */
511 static void
512 blit_linear(struct gl_context *ctx,
513 GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
514 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
515 {
516 struct gl_framebuffer *drawFb = ctx->DrawBuffer;
517 struct gl_renderbuffer *drawRb = NULL;
518 struct gl_renderbuffer_attachment *drawAtt = NULL;
519 struct gl_framebuffer *readFb = ctx->ReadBuffer;
520 struct gl_renderbuffer *readRb = readFb->_ColorReadBuffer;
521 struct gl_renderbuffer_attachment *readAtt =
522 &readFb->Attachment[readFb->_ColorReadBufferIndex];
523
524 const GLint srcWidth = ABS(srcX1 - srcX0);
525 const GLint dstWidth = ABS(dstX1 - dstX0);
526 const GLint srcHeight = ABS(srcY1 - srcY0);
527 const GLint dstHeight = ABS(dstY1 - dstY0);
528 const GLfloat dstHeightF = (GLfloat) dstHeight;
529
530 const GLint srcXpos = MIN2(srcX0, srcX1);
531 const GLint srcYpos = MIN2(srcY0, srcY1);
532 const GLint dstXpos = MIN2(dstX0, dstX1);
533 const GLint dstYpos = MIN2(dstY0, dstY1);
534
535 const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
536 const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
537
538 GLint dstRow;
539
540 GLint pixelSize;
541 GLvoid *srcBuffer0, *srcBuffer1;
542 GLint srcBufferY0 = -1, srcBufferY1 = -1;
543 GLvoid *dstBuffer;
544
545 gl_format readFormat = _mesa_get_srgb_format_linear(readRb->Format);
546 gl_format drawFormat = _mesa_get_srgb_format_linear(drawRb->Format);
547 GLuint bpp = _mesa_get_format_bytes(readFormat);
548
549 GLenum pixelType;
550
551 GLubyte *srcMap, *dstMap;
552 GLint srcRowStride, dstRowStride;
553 GLuint i;
554
555
556 /* Determine datatype for resampling */
557 if (_mesa_get_format_max_bits(readFormat) == 8 &&
558 _mesa_get_format_datatype(readFormat) == GL_UNSIGNED_NORMALIZED) {
559 pixelType = GL_UNSIGNED_BYTE;
560 pixelSize = 4 * sizeof(GLubyte);
561 }
562 else {
563 pixelType = GL_FLOAT;
564 pixelSize = 4 * sizeof(GLfloat);
565 }
566
567 /* Allocate the src/dst row buffers.
568 * Keep two adjacent src rows around for bilinear sampling.
569 */
570 srcBuffer0 = malloc(pixelSize * srcWidth);
571 if (!srcBuffer0) {
572 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
573 return;
574 }
575 srcBuffer1 = malloc(pixelSize * srcWidth);
576 if (!srcBuffer1) {
577 free(srcBuffer0);
578 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
579 return;
580 }
581 dstBuffer = malloc(pixelSize * dstWidth);
582 if (!dstBuffer) {
583 free(srcBuffer0);
584 free(srcBuffer1);
585 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
586 return;
587 }
588
589 for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) {
590 int idx = drawFb->_ColorDrawBufferIndexes[i];
591 if (idx == -1)
592 continue;
593 drawAtt = &drawFb->Attachment[idx];
594 drawRb = drawAtt->Renderbuffer;
595 /*
596 * Map src / dst renderbuffers
597 */
598 if ((readRb == drawRb) ||
599 (readAtt->Texture && drawAtt->Texture &&
600 (readAtt->Texture = drawAtt->Texture))) {
601 /* map whole buffer for read/write */
602 ctx->Driver.MapRenderbuffer(ctx, readRb,
603 0, 0, readRb->Width, readRb->Height,
604 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
605 &srcMap, &srcRowStride);
606 if (!srcMap) {
607 free(srcBuffer0);
608 free(srcBuffer1);
609 free(dstBuffer);
610 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
611 return;
612 }
613
614 dstMap = srcMap;
615 dstRowStride = srcRowStride;
616 }
617 else {
618 /* different src/dst buffers */
619 /* XXX with a bit of work we could just map the regions to be
620 * read/written instead of the whole buffers.
621 */
622 ctx->Driver.MapRenderbuffer(ctx, readRb,
623 0, 0, readRb->Width, readRb->Height,
624 GL_MAP_READ_BIT, &srcMap, &srcRowStride);
625 if (!srcMap) {
626 free(srcBuffer0);
627 free(srcBuffer1);
628 free(dstBuffer);
629 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
630 return;
631 }
632 ctx->Driver.MapRenderbuffer(ctx, drawRb,
633 0, 0, drawRb->Width, drawRb->Height,
634 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
635 if (!dstMap) {
636 ctx->Driver.UnmapRenderbuffer(ctx, readRb);
637 free(srcBuffer0);
638 free(srcBuffer1);
639 free(dstBuffer);
640 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
641 return;
642 }
643 }
644
645 for (dstRow = 0; dstRow < dstHeight; dstRow++) {
646 const GLint dstY = dstYpos + dstRow;
647 const GLfloat srcRow = (dstRow * srcHeight) / dstHeightF;
648 GLint srcRow0 = IFLOOR(srcRow);
649 GLint srcRow1 = srcRow0 + 1;
650 GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */
651
652 ASSERT(srcRow >= 0);
653 ASSERT(srcRow < srcHeight);
654
655 if (srcRow1 == srcHeight) {
656 /* last row fudge */
657 srcRow1 = srcRow0;
658 rowWeight = 0.0;
659 }
660
661 if (invertY) {
662 srcRow0 = srcHeight - 1 - srcRow0;
663 srcRow1 = srcHeight - 1 - srcRow1;
664 }
665
666 srcY0 = srcYpos + srcRow0;
667 srcY1 = srcYpos + srcRow1;
668
669 /* get the two source rows */
670 if (srcY0 == srcBufferY0 && srcY1 == srcBufferY1) {
671 /* use same source row buffers again */
672 }
673 else if (srcY0 == srcBufferY1) {
674 /* move buffer1 into buffer0 by swapping pointers */
675 GLvoid *tmp = srcBuffer0;
676 srcBuffer0 = srcBuffer1;
677 srcBuffer1 = tmp;
678 /* get y1 row */
679 {
680 GLubyte *src = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
681 if (pixelType == GL_UNSIGNED_BYTE) {
682 _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
683 src, srcBuffer1);
684 }
685 else {
686 _mesa_unpack_rgba_row(readFormat, srcWidth,
687 src, srcBuffer1);
688 }
689 }
690 srcBufferY0 = srcY0;
691 srcBufferY1 = srcY1;
692 }
693 else {
694 /* get both new rows */
695 {
696 GLubyte *src0 = srcMap + srcY0 * srcRowStride + srcXpos * bpp;
697 GLubyte *src1 = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
698 if (pixelType == GL_UNSIGNED_BYTE) {
699 _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
700 src0, srcBuffer0);
701 _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
702 src1, srcBuffer1);
703 }
704 else {
705 _mesa_unpack_rgba_row(readFormat, srcWidth, src0, srcBuffer0);
706 _mesa_unpack_rgba_row(readFormat, srcWidth, src1, srcBuffer1);
707 }
708 }
709 srcBufferY0 = srcY0;
710 srcBufferY1 = srcY1;
711 }
712
713 if (pixelType == GL_UNSIGNED_BYTE) {
714 resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
715 dstBuffer, invertX, rowWeight);
716 }
717 else {
718 resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
719 dstBuffer, invertX, rowWeight);
720 }
721
722 /* store pixel row in destination */
723 {
724 GLubyte *dst = dstMap + dstY * dstRowStride + dstXpos * bpp;
725 if (pixelType == GL_UNSIGNED_BYTE) {
726 _mesa_pack_ubyte_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
727 }
728 else {
729 _mesa_pack_float_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
730 }
731 }
732 }
733
734 free(srcBuffer0);
735 free(srcBuffer1);
736 free(dstBuffer);
737
738 ctx->Driver.UnmapRenderbuffer(ctx, readRb);
739 if (drawRb != readRb) {
740 ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
741 }
742 }
743 }
744
745
746
747 /**
748 * Software fallback for glBlitFramebufferEXT().
749 */
750 void
751 _swrast_BlitFramebuffer(struct gl_context *ctx,
752 GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
753 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
754 GLbitfield mask, GLenum filter)
755 {
756 static const GLbitfield buffers[3] = {
757 GL_COLOR_BUFFER_BIT,
758 GL_DEPTH_BUFFER_BIT,
759 GL_STENCIL_BUFFER_BIT
760 };
761 static const GLenum buffer_enums[3] = {
762 GL_COLOR,
763 GL_DEPTH,
764 GL_STENCIL,
765 };
766 GLint i;
767
768 if (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
769 &dstX0, &dstY0, &dstX1, &dstY1)) {
770 return;
771 }
772
773 if (SWRAST_CONTEXT(ctx)->NewState)
774 _swrast_validate_derived(ctx);
775
776 /* First, try covering whatever buffers possible using the fast 1:1 copy
777 * path.
778 */
779 if (srcX1 - srcX0 == dstX1 - dstX0 &&
780 srcY1 - srcY0 == dstY1 - dstY0 &&
781 srcX0 < srcX1 &&
782 srcY0 < srcY1 &&
783 dstX0 < dstX1 &&
784 dstY0 < dstY1) {
785 for (i = 0; i < 3; i++) {
786 if (mask & buffers[i]) {
787 if (swrast_fast_copy_pixels(ctx,
788 srcX0, srcY0,
789 srcX1 - srcX0, srcY1 - srcY0,
790 dstX0, dstY0,
791 buffer_enums[i])) {
792 mask &= ~buffers[i];
793 }
794 }
795 }
796
797 if (!mask)
798 return;
799 }
800
801 if (filter == GL_NEAREST) {
802 for (i = 0; i < 3; i++) {
803 if (mask & buffers[i]) {
804 blit_nearest(ctx, srcX0, srcY0, srcX1, srcY1,
805 dstX0, dstY0, dstX1, dstY1, buffers[i]);
806 }
807 }
808 }
809 else {
810 ASSERT(filter == GL_LINEAR);
811 if (mask & GL_COLOR_BUFFER_BIT) { /* depth/stencil not allowed */
812 blit_linear(ctx, srcX0, srcY0, srcX1, srcY1,
813 dstX0, dstY0, dstX1, dstY1);
814 }
815 }
816
817 }