/* * Mesa 3-D graphics library * * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "main/glheader.h" #include "main/condrender.h" #include "main/image.h" #include "main/macros.h" #include "main/format_unpack.h" #include "main/format_pack.h" #include "s_context.h" #define ABS(X) ((X) < 0 ? -(X) : (X)) /** * Generate a row resampler function for GL_NEAREST mode. */ #define RESAMPLE(NAME, PIXELTYPE, SIZE) \ static void \ NAME(GLint srcWidth, GLint dstWidth, \ const GLvoid *srcBuffer, GLvoid *dstBuffer, \ GLboolean flip) \ { \ const PIXELTYPE *src = (const PIXELTYPE *) srcBuffer;\ PIXELTYPE *dst = (PIXELTYPE *) dstBuffer; \ GLint dstCol; \ \ if (flip) { \ for (dstCol = 0; dstCol < dstWidth; dstCol++) { \ GLint srcCol = (dstCol * srcWidth) / dstWidth; \ ASSERT(srcCol >= 0); \ ASSERT(srcCol < srcWidth); \ srcCol = srcWidth - 1 - srcCol; /* flip */ \ if (SIZE == 1) { \ dst[dstCol] = src[srcCol]; \ } \ else if (SIZE == 2) { \ dst[dstCol*2+0] = src[srcCol*2+0]; \ dst[dstCol*2+1] = src[srcCol*2+1]; \ } \ else if (SIZE == 4) { \ dst[dstCol*4+0] = src[srcCol*4+0]; \ dst[dstCol*4+1] = src[srcCol*4+1]; \ dst[dstCol*4+2] = src[srcCol*4+2]; \ dst[dstCol*4+3] = src[srcCol*4+3]; \ } \ } \ } \ else { \ for (dstCol = 0; dstCol < dstWidth; dstCol++) { \ GLint srcCol = (dstCol * srcWidth) / dstWidth; \ ASSERT(srcCol >= 0); \ ASSERT(srcCol < srcWidth); \ if (SIZE == 1) { \ dst[dstCol] = src[srcCol]; \ } \ else if (SIZE == 2) { \ dst[dstCol*2+0] = src[srcCol*2+0]; \ dst[dstCol*2+1] = src[srcCol*2+1]; \ } \ else if (SIZE == 4) { \ dst[dstCol*4+0] = src[srcCol*4+0]; \ dst[dstCol*4+1] = src[srcCol*4+1]; \ dst[dstCol*4+2] = src[srcCol*4+2]; \ dst[dstCol*4+3] = src[srcCol*4+3]; \ } \ } \ } \ } /** * Resamplers for 1, 2, 4, 8 and 16-byte pixels. */ RESAMPLE(resample_row_1, GLubyte, 1) RESAMPLE(resample_row_2, GLushort, 1) RESAMPLE(resample_row_4, GLuint, 1) RESAMPLE(resample_row_8, GLuint, 2) RESAMPLE(resample_row_16, GLuint, 4) /** * Blit color, depth or stencil with GL_NEAREST filtering. */ static void blit_nearest(struct gl_context *ctx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield buffer) { struct gl_renderbuffer *readRb, *drawRb = NULL; struct gl_renderbuffer_attachment *readAtt = NULL, *drawAtt = NULL; struct gl_framebuffer *readFb = ctx->ReadBuffer; struct gl_framebuffer *drawFb = ctx->DrawBuffer; GLuint numDrawBuffers = 0; GLuint i; const GLint srcWidth = ABS(srcX1 - srcX0); const GLint dstWidth = ABS(dstX1 - dstX0); const GLint srcHeight = ABS(srcY1 - srcY0); const GLint dstHeight = ABS(dstY1 - dstY0); const GLint srcXpos = MIN2(srcX0, srcX1); const GLint srcYpos = MIN2(srcY0, srcY1); const GLint dstXpos = MIN2(dstX0, dstX1); const GLint dstYpos = MIN2(dstY0, dstY1); const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0); const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0); enum mode { DIRECT, UNPACK_RGBA_FLOAT, UNPACK_Z_FLOAT, UNPACK_Z_INT, UNPACK_S, } mode = DIRECT; GLubyte *srcMap, *dstMap; GLint srcRowStride, dstRowStride; GLint dstRow; GLint pixelSize = 0; GLvoid *srcBuffer, *dstBuffer; GLint prevY = -1; typedef void (*resample_func)(GLint srcWidth, GLint dstWidth, const GLvoid *srcBuffer, GLvoid *dstBuffer, GLboolean flip); resample_func resampleRow; switch (buffer) { case GL_COLOR_BUFFER_BIT: readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex]; readRb = readFb->_ColorReadBuffer; numDrawBuffers = drawFb->_NumColorDrawBuffers; break; case GL_DEPTH_BUFFER_BIT: readAtt = &readFb->Attachment[BUFFER_DEPTH]; drawAtt = &drawFb->Attachment[BUFFER_DEPTH]; readRb = readAtt->Renderbuffer; drawRb = drawAtt->Renderbuffer; numDrawBuffers = 1; /* Note that for depth/stencil, the formats of src/dst must match. By * using the core helpers for pack/unpack, we avoid needing to handle * masking for things like DEPTH copies of Z24S8. */ if (readRb->Format == MESA_FORMAT_Z32_FLOAT || readRb->Format == MESA_FORMAT_Z32_FLOAT_X24S8) { mode = UNPACK_Z_FLOAT; } else { mode = UNPACK_Z_INT; } pixelSize = 4; break; case GL_STENCIL_BUFFER_BIT: readAtt = &readFb->Attachment[BUFFER_STENCIL]; drawAtt = &drawFb->Attachment[BUFFER_STENCIL]; readRb = readAtt->Renderbuffer; drawRb = drawAtt->Renderbuffer; numDrawBuffers = 1; mode = UNPACK_S; pixelSize = 1; break; default: _mesa_problem(ctx, "unexpected buffer in blit_nearest()"); return; } /* allocate the src/dst row buffers */ srcBuffer = malloc(MAX_PIXEL_BYTES * srcWidth); dstBuffer = malloc(MAX_PIXEL_BYTES * dstWidth); if (!srcBuffer || !dstBuffer) goto fail_no_memory; /* Blit to all the draw buffers */ for (i = 0; i < numDrawBuffers; i++) { if (buffer == GL_COLOR_BUFFER_BIT) { int idx = drawFb->_ColorDrawBufferIndexes[i]; if (idx == -1) continue; drawAtt = &drawFb->Attachment[idx]; drawRb = drawAtt->Renderbuffer; if (!drawRb) continue; if (readRb->Format == drawRb->Format) { mode = DIRECT; pixelSize = _mesa_get_format_bytes(readRb->Format); } else { mode = UNPACK_RGBA_FLOAT; pixelSize = 16; } } /* choose row resampler */ switch (pixelSize) { case 1: resampleRow = resample_row_1; break; case 2: resampleRow = resample_row_2; break; case 4: resampleRow = resample_row_4; break; case 8: resampleRow = resample_row_8; break; case 16: resampleRow = resample_row_16; break; default: _mesa_problem(ctx, "unexpected pixel size (%d) in blit_nearest", pixelSize); goto fail; } if ((readRb == drawRb) || (readAtt->Texture && drawAtt->Texture && (readAtt->Texture == drawAtt->Texture))) { /* map whole buffer for read/write */ /* XXX we could be clever and just map the union region of the * source and dest rects. */ GLubyte *map; GLint rowStride; GLint formatSize = _mesa_get_format_bytes(readRb->Format); ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0, readRb->Width, readRb->Height, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, &map, &rowStride); if (!map) { goto fail_no_memory; } srcMap = map + srcYpos * rowStride + srcXpos * formatSize; dstMap = map + dstYpos * rowStride + dstXpos * formatSize; /* this handles overlapping copies */ if (srcY0 < dstY0) { /* copy in reverse (top->down) order */ srcMap += rowStride * (readRb->Height - 1); dstMap += rowStride * (readRb->Height - 1); srcRowStride = -rowStride; dstRowStride = -rowStride; } else { /* copy in normal (bottom->up) order */ srcRowStride = rowStride; dstRowStride = rowStride; } } else { /* different src/dst buffers */ ctx->Driver.MapRenderbuffer(ctx, readRb, srcXpos, srcYpos, srcWidth, srcHeight, GL_MAP_READ_BIT, &srcMap, &srcRowStride); if (!srcMap) { goto fail_no_memory; } ctx->Driver.MapRenderbuffer(ctx, drawRb, dstXpos, dstYpos, dstWidth, dstHeight, GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); if (!dstMap) { ctx->Driver.UnmapRenderbuffer(ctx, readRb); goto fail_no_memory; } } for (dstRow = 0; dstRow < dstHeight; dstRow++) { GLfloat srcRowF = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F; GLint srcRow = IROUND(srcRowF); GLubyte *dstRowStart = dstMap + dstRowStride * dstRow; ASSERT(srcRow >= 0); ASSERT(srcRow < srcHeight); if (invertY) { srcRow = srcHeight - 1 - srcRow; } /* get pixel row from source and resample to match dest width */ if (prevY != srcRow) { GLubyte *srcRowStart = srcMap + srcRowStride * srcRow; switch (mode) { case DIRECT: memcpy(srcBuffer, srcRowStart, pixelSize * srcWidth); break; case UNPACK_RGBA_FLOAT: _mesa_unpack_rgba_row(readRb->Format, srcWidth, srcRowStart, srcBuffer); break; case UNPACK_Z_FLOAT: _mesa_unpack_float_z_row(readRb->Format, srcWidth, srcRowStart, srcBuffer); break; case UNPACK_Z_INT: _mesa_unpack_uint_z_row(readRb->Format, srcWidth, srcRowStart, srcBuffer); break; case UNPACK_S: _mesa_unpack_ubyte_stencil_row(readRb->Format, srcWidth, srcRowStart, srcBuffer); break; } (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX); prevY = srcRow; } /* store pixel row in destination */ switch (mode) { case DIRECT: memcpy(dstRowStart, dstBuffer, pixelSize * dstWidth); break; case UNPACK_RGBA_FLOAT: _mesa_pack_float_rgba_row(drawRb->Format, dstWidth, dstBuffer, dstRowStart); break; case UNPACK_Z_FLOAT: _mesa_pack_float_z_row(drawRb->Format, dstWidth, dstBuffer, dstRowStart); break; case UNPACK_Z_INT: _mesa_pack_uint_z_row(drawRb->Format, dstWidth, dstBuffer, dstRowStart); break; case UNPACK_S: _mesa_pack_ubyte_stencil_row(drawRb->Format, dstWidth, dstBuffer, dstRowStart); break; } } ctx->Driver.UnmapRenderbuffer(ctx, readRb); if (drawRb != readRb) { ctx->Driver.UnmapRenderbuffer(ctx, drawRb); } } fail: free(srcBuffer); free(dstBuffer); return; fail_no_memory: free(srcBuffer); free(dstBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBuffer"); } #define LERP(T, A, B) ( (A) + (T) * ((B) - (A)) ) static inline GLfloat lerp_2d(GLfloat a, GLfloat b, GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11) { const GLfloat temp0 = LERP(a, v00, v10); const GLfloat temp1 = LERP(a, v01, v11); return LERP(b, temp0, temp1); } /** * Bilinear interpolation of two source rows. * GLubyte pixels. */ static void resample_linear_row_ub(GLint srcWidth, GLint dstWidth, const GLvoid *srcBuffer0, const GLvoid *srcBuffer1, GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight) { const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0; const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1; GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer; GLint dstCol; for (dstCol = 0; dstCol < dstWidth; dstCol++) { const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F; GLint srcCol0 = MAX2(0, IFLOOR(srcCol)); GLint srcCol1 = srcCol0 + 1; GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */ GLfloat red, green, blue, alpha; ASSERT(srcCol0 < srcWidth); ASSERT(srcCol1 <= srcWidth); if (srcCol1 == srcWidth) { /* last column fudge */ srcCol1--; colWeight = 0.0; } if (flip) { srcCol0 = srcWidth - 1 - srcCol0; srcCol1 = srcWidth - 1 - srcCol1; } red = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP], srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]); green = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP], srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]); blue = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP], srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]); alpha = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP], srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]); dstColor[dstCol][RCOMP] = IFLOOR(red); dstColor[dstCol][GCOMP] = IFLOOR(green); dstColor[dstCol][BCOMP] = IFLOOR(blue); dstColor[dstCol][ACOMP] = IFLOOR(alpha); } } /** * Bilinear interpolation of two source rows. floating point pixels. */ static void resample_linear_row_float(GLint srcWidth, GLint dstWidth, const GLvoid *srcBuffer0, const GLvoid *srcBuffer1, GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight) { const GLfloat (*srcColor0)[4] = (const GLfloat (*)[4]) srcBuffer0; const GLfloat (*srcColor1)[4] = (const GLfloat (*)[4]) srcBuffer1; GLfloat (*dstColor)[4] = (GLfloat (*)[4]) dstBuffer; GLint dstCol; for (dstCol = 0; dstCol < dstWidth; dstCol++) { const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F; GLint srcCol0 = MAX2(0, IFLOOR(srcCol)); GLint srcCol1 = srcCol0 + 1; GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */ GLfloat red, green, blue, alpha; ASSERT(srcCol0 < srcWidth); ASSERT(srcCol1 <= srcWidth); if (srcCol1 == srcWidth) { /* last column fudge */ srcCol1--; colWeight = 0.0; } if (flip) { srcCol0 = srcWidth - 1 - srcCol0; srcCol1 = srcWidth - 1 - srcCol1; } red = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP], srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]); green = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP], srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]); blue = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP], srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]); alpha = lerp_2d(colWeight, rowWeight, srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP], srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]); dstColor[dstCol][RCOMP] = red; dstColor[dstCol][GCOMP] = green; dstColor[dstCol][BCOMP] = blue; dstColor[dstCol][ACOMP] = alpha; } } /** * Bilinear filtered blit (color only, non-integer values). */ static void blit_linear(struct gl_context *ctx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1) { struct gl_framebuffer *drawFb = ctx->DrawBuffer; struct gl_framebuffer *readFb = ctx->ReadBuffer; struct gl_renderbuffer *readRb = readFb->_ColorReadBuffer; struct gl_renderbuffer_attachment *readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex]; const GLint srcWidth = ABS(srcX1 - srcX0); const GLint dstWidth = ABS(dstX1 - dstX0); const GLint srcHeight = ABS(srcY1 - srcY0); const GLint dstHeight = ABS(dstY1 - dstY0); const GLint srcXpos = MIN2(srcX0, srcX1); const GLint srcYpos = MIN2(srcY0, srcY1); const GLint dstXpos = MIN2(dstX0, dstX1); const GLint dstYpos = MIN2(dstY0, dstY1); const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0); const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0); GLint dstRow; GLint pixelSize; GLvoid *srcBuffer0, *srcBuffer1; GLint srcBufferY0 = -1, srcBufferY1 = -1; GLvoid *dstBuffer; gl_format readFormat = _mesa_get_srgb_format_linear(readRb->Format); GLuint bpp = _mesa_get_format_bytes(readFormat); GLenum pixelType; GLubyte *srcMap, *dstMap; GLint srcRowStride, dstRowStride; GLuint i; /* Determine datatype for resampling */ if (_mesa_get_format_max_bits(readFormat) == 8 && _mesa_get_format_datatype(readFormat) == GL_UNSIGNED_NORMALIZED) { pixelType = GL_UNSIGNED_BYTE; pixelSize = 4 * sizeof(GLubyte); } else { pixelType = GL_FLOAT; pixelSize = 4 * sizeof(GLfloat); } /* Allocate the src/dst row buffers. * Keep two adjacent src rows around for bilinear sampling. */ srcBuffer0 = malloc(pixelSize * srcWidth); srcBuffer1 = malloc(pixelSize * srcWidth); dstBuffer = malloc(pixelSize * dstWidth); if (!srcBuffer0 || !srcBuffer1 || !dstBuffer) { goto fail_no_memory; } for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) { GLint idx = drawFb->_ColorDrawBufferIndexes[i]; struct gl_renderbuffer_attachment *drawAtt; struct gl_renderbuffer *drawRb; gl_format drawFormat; if (idx == -1) continue; drawAtt = &drawFb->Attachment[idx]; drawRb = drawAtt->Renderbuffer; if (!drawRb) continue; drawFormat = _mesa_get_srgb_format_linear(drawRb->Format); /* * Map src / dst renderbuffers */ if ((readRb == drawRb) || (readAtt->Texture && drawAtt->Texture && (readAtt->Texture == drawAtt->Texture))) { /* map whole buffer for read/write */ ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0, readRb->Width, readRb->Height, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, &srcMap, &srcRowStride); if (!srcMap) { goto fail_no_memory; } dstMap = srcMap; dstRowStride = srcRowStride; } else { /* different src/dst buffers */ /* XXX with a bit of work we could just map the regions to be * read/written instead of the whole buffers. */ ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0, readRb->Width, readRb->Height, GL_MAP_READ_BIT, &srcMap, &srcRowStride); if (!srcMap) { goto fail_no_memory; } ctx->Driver.MapRenderbuffer(ctx, drawRb, 0, 0, drawRb->Width, drawRb->Height, GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); if (!dstMap) { ctx->Driver.UnmapRenderbuffer(ctx, readRb); goto fail_no_memory; } } for (dstRow = 0; dstRow < dstHeight; dstRow++) { const GLint dstY = dstYpos + dstRow; GLfloat srcRow = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F; GLint srcRow0 = MAX2(0, IFLOOR(srcRow)); GLint srcRow1 = srcRow0 + 1; GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */ if (srcRow1 == srcHeight) { /* last row fudge */ srcRow1 = srcRow0; rowWeight = 0.0; } if (invertY) { srcRow0 = srcHeight - 1 - srcRow0; srcRow1 = srcHeight - 1 - srcRow1; } srcY0 = srcYpos + srcRow0; srcY1 = srcYpos + srcRow1; /* get the two source rows */ if (srcY0 == srcBufferY0 && srcY1 == srcBufferY1) { /* use same source row buffers again */ } else if (srcY0 == srcBufferY1) { /* move buffer1 into buffer0 by swapping pointers */ GLvoid *tmp = srcBuffer0; srcBuffer0 = srcBuffer1; srcBuffer1 = tmp; /* get y1 row */ { GLubyte *src = srcMap + srcY1 * srcRowStride + srcXpos * bpp; if (pixelType == GL_UNSIGNED_BYTE) { _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth, src, srcBuffer1); } else { _mesa_unpack_rgba_row(readFormat, srcWidth, src, srcBuffer1); } } srcBufferY0 = srcY0; srcBufferY1 = srcY1; } else { /* get both new rows */ { GLubyte *src0 = srcMap + srcY0 * srcRowStride + srcXpos * bpp; GLubyte *src1 = srcMap + srcY1 * srcRowStride + srcXpos * bpp; if (pixelType == GL_UNSIGNED_BYTE) { _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth, src0, srcBuffer0); _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth, src1, srcBuffer1); } else { _mesa_unpack_rgba_row(readFormat, srcWidth, src0, srcBuffer0); _mesa_unpack_rgba_row(readFormat, srcWidth, src1, srcBuffer1); } } srcBufferY0 = srcY0; srcBufferY1 = srcY1; } if (pixelType == GL_UNSIGNED_BYTE) { resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1, dstBuffer, invertX, rowWeight); } else { resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1, dstBuffer, invertX, rowWeight); } /* store pixel row in destination */ { GLubyte *dst = dstMap + dstY * dstRowStride + dstXpos * bpp; if (pixelType == GL_UNSIGNED_BYTE) { _mesa_pack_ubyte_rgba_row(drawFormat, dstWidth, dstBuffer, dst); } else { _mesa_pack_float_rgba_row(drawFormat, dstWidth, dstBuffer, dst); } } } ctx->Driver.UnmapRenderbuffer(ctx, readRb); if (drawRb != readRb) { ctx->Driver.UnmapRenderbuffer(ctx, drawRb); } } free(srcBuffer0); free(srcBuffer1); free(dstBuffer); return; fail_no_memory: free(srcBuffer0); free(srcBuffer1); free(dstBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); } /** * Software fallback for glBlitFramebufferEXT(). */ void _swrast_BlitFramebuffer(struct gl_context *ctx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { static const GLbitfield buffers[3] = { GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT }; static const GLenum buffer_enums[3] = { GL_COLOR, GL_DEPTH, GL_STENCIL, }; GLint i; if (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1, &dstX0, &dstY0, &dstX1, &dstY1)) { return; } if (SWRAST_CONTEXT(ctx)->NewState) _swrast_validate_derived(ctx); /* First, try covering whatever buffers possible using the fast 1:1 copy * path. */ if (srcX1 - srcX0 == dstX1 - dstX0 && srcY1 - srcY0 == dstY1 - dstY0 && srcX0 < srcX1 && srcY0 < srcY1 && dstX0 < dstX1 && dstY0 < dstY1) { for (i = 0; i < 3; i++) { if (mask & buffers[i]) { if (swrast_fast_copy_pixels(ctx, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, dstX0, dstY0, buffer_enums[i])) { mask &= ~buffers[i]; } } } if (!mask) return; } if (filter == GL_NEAREST) { for (i = 0; i < 3; i++) { if (mask & buffers[i]) { blit_nearest(ctx, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, buffers[i]); } } } else { ASSERT(filter == GL_LINEAR); if (mask & GL_COLOR_BUFFER_BIT) { /* depth/stencil not allowed */ blit_linear(ctx, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1); } } }