| /* |
| * 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 "main/condrender.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, |
| struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, |
| 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; |
| 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_Z_FLOAT32 || |
| readRb->Format == MESA_FORMAT_Z32_FLOAT_S8X24_UINT) { |
| 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, |
| struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, |
| GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, |
| GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1) |
| { |
| 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; |
| |
| mesa_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; |
| mesa_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, |
| struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, |
| 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; |
| |
| /* Page 679 of OpenGL 4.4 spec says: |
| * "Added BlitFramebuffer to commands affected by conditional rendering in |
| * section 10.10 (Bug 9562)." |
| */ |
| if (!_mesa_check_conditional_render(ctx)) |
| return; /* Do not blit */ |
| |
| if (!_mesa_clip_blit(ctx, readFb, drawFb, &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, |
| readFb, drawFb, |
| 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, readFb, drawFb, 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, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1); |
| } |
| } |
| |
| } |