/** Put row of colors into renderbuffer */ void _swrast_put_row(struct gl_context *ctx, struct gl_renderbuffer *rb, GLenum datatype, GLuint count, GLint x, GLint y, const void *values, const GLubyte *mask) { GLubyte *dst = _swrast_pixel_address(rb, x, y); if (!mask) { if (datatype == GL_UNSIGNED_BYTE) { _mesa_pack_ubyte_rgba_row(rb->Format, count, (const GLubyte (*)[4]) values, dst); } else { assert(datatype == GL_FLOAT); _mesa_pack_float_rgba_row(rb->Format, count, (const GLfloat (*)[4]) values, dst); } } else { const GLuint bpp = _mesa_get_format_bytes(rb->Format); GLuint i, runLen, runStart; /* We can't pass a 'mask' array to the _mesa_pack_rgba_row() functions * so look for runs where mask=1... */ runLen = runStart = 0; for (i = 0; i < count; i++) { if (mask[i]) { if (runLen == 0) runStart = i; runLen++; } if (!mask[i] || i == count - 1) { /* might be the end of a run of pixels */ if (runLen > 0) { if (datatype == GL_UNSIGNED_BYTE) { _mesa_pack_ubyte_rgba_row(rb->Format, runLen, (const GLubyte (*)[4]) values + runStart, dst + runStart * bpp); } else { assert(datatype == GL_FLOAT); _mesa_pack_float_rgba_row(rb->Format, runLen, (const GLfloat (*)[4]) values + runStart, dst + runStart * bpp); } runLen = 0; } } } } }
/** * 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_renderbuffer *readRb = ctx->ReadBuffer->_ColorReadBuffer; struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0]; 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 GLfloat dstHeightF = (GLfloat) dstHeight; 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); gl_format drawFormat = _mesa_get_srgb_format_linear(drawRb->Format); GLuint bpp = _mesa_get_format_bytes(readFormat); GLenum pixelType; GLubyte *srcMap, *dstMap; GLint srcRowStride, dstRowStride; /* 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); if (!srcBuffer0) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT"); return; } srcBuffer1 = malloc(pixelSize * srcWidth); if (!srcBuffer1) { free(srcBuffer0); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT"); return; } dstBuffer = malloc(pixelSize * dstWidth); if (!dstBuffer) { free(srcBuffer0); free(srcBuffer1); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT"); return; } /* * Map src / dst renderbuffers */ if (readRb == drawRb) { /* 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) { free(srcBuffer0); free(srcBuffer1); free(dstBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } 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) { free(srcBuffer0); free(srcBuffer1); free(dstBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } ctx->Driver.MapRenderbuffer(ctx, drawRb, 0, 0, drawRb->Width, drawRb->Height, GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); if (!dstMap) { ctx->Driver.UnmapRenderbuffer(ctx, readRb); free(srcBuffer0); free(srcBuffer1); free(dstBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } } for (dstRow = 0; dstRow < dstHeight; dstRow++) { const GLint dstY = dstYpos + dstRow; const GLfloat srcRow = (dstRow * srcHeight) / dstHeightF; GLint srcRow0 = IFLOOR(srcRow); GLint srcRow1 = srcRow0 + 1; GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */ ASSERT(srcRow >= 0); ASSERT(srcRow < srcHeight); 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); } } } free(srcBuffer0); free(srcBuffer1); free(dstBuffer); ctx->Driver.UnmapRenderbuffer(ctx, readRb); if (drawRb != readRb) { ctx->Driver.UnmapRenderbuffer(ctx, drawRb); } }
/** * ColorBuffer = Accum * value */ static void accum_return(struct gl_context *ctx, GLfloat value, GLint xpos, GLint ypos, GLint width, GLint height) { struct gl_framebuffer *fb = ctx->DrawBuffer; struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer; GLubyte *accMap, *colorMap; GLint accRowStride, colorRowStride; GLuint buffer; /* Map accum buffer */ ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height, GL_MAP_READ_BIT, &accMap, &accRowStride); if (!accMap) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum"); return; } /* Loop over destination buffers */ for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) { struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer]; const GLboolean masking = (!ctx->Color.ColorMask[buffer][RCOMP] || !ctx->Color.ColorMask[buffer][GCOMP] || !ctx->Color.ColorMask[buffer][BCOMP] || !ctx->Color.ColorMask[buffer][ACOMP]); GLbitfield mappingFlags = GL_MAP_WRITE_BIT; if (masking) mappingFlags |= GL_MAP_READ_BIT; /* Map color buffer */ ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height, mappingFlags, &colorMap, &colorRowStride); if (!colorMap) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum"); continue; } if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) { const GLfloat scale = value / 32767.0f; GLint i, j; GLfloat (*rgba)[4], (*dest)[4]; rgba = malloc(width * 4 * sizeof(GLfloat)); dest = malloc(width * 4 * sizeof(GLfloat)); if (rgba && dest) { for (j = 0; j < height; j++) { GLshort *acc = (GLshort *) accMap; for (i = 0; i < width; i++) { rgba[i][0] = acc[i * 4 + 0] * scale; rgba[i][1] = acc[i * 4 + 1] * scale; rgba[i][2] = acc[i * 4 + 2] * scale; rgba[i][3] = acc[i * 4 + 3] * scale; } if (masking) { /* get existing colors from dest buffer */ _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest); /* use the dest colors where mask[channel] = 0 */ if (ctx->Color.ColorMask[buffer][RCOMP] == 0) { for (i = 0; i < width; i++) rgba[i][RCOMP] = dest[i][RCOMP]; } if (ctx->Color.ColorMask[buffer][GCOMP] == 0) { for (i = 0; i < width; i++) rgba[i][GCOMP] = dest[i][GCOMP]; } if (ctx->Color.ColorMask[buffer][BCOMP] == 0) { for (i = 0; i < width; i++) rgba[i][BCOMP] = dest[i][BCOMP]; } if (ctx->Color.ColorMask[buffer][ACOMP] == 0) { for (i = 0; i < width; i++) rgba[i][ACOMP] = dest[i][ACOMP]; } } _mesa_pack_float_rgba_row(colorRb->Format, width, (const GLfloat (*)[4]) rgba, colorMap); accMap += accRowStride; colorMap += colorRowStride; } } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum"); } free(rgba); free(dest); } else { /* other types someday? */ } ctx->Driver.UnmapRenderbuffer(ctx, colorRb); } ctx->Driver.UnmapRenderbuffer(ctx, accRb); }
/** * 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; 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; GLubyte *srcMap, *dstMap; GLint srcRowStride, dstRowStride; GLint dstRow; GLint pixelSize; 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: readRb = ctx->ReadBuffer->_ColorReadBuffer; drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0]; if (readRb->Format == drawRb->Format) { mode = DIRECT; pixelSize = _mesa_get_format_bytes(readRb->Format); } else { mode = UNPACK_RGBA_FLOAT; pixelSize = 16; } break; case GL_DEPTH_BUFFER_BIT: readRb = ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Renderbuffer; drawRb = ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer; /* 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: readRb = ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer; drawRb = ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer; mode = UNPACK_S; pixelSize = 1; break; default: _mesa_problem(ctx, "unexpected buffer in blit_nearest()"); return; } /* 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); return; } if (readRb == drawRb) { /* 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) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } 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) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } ctx->Driver.MapRenderbuffer(ctx, drawRb, dstXpos, dstYpos, dstWidth, dstHeight, GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); if (!dstMap) { ctx->Driver.UnmapRenderbuffer(ctx, readRb); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer"); return; } } /* allocate the src/dst row buffers */ srcBuffer = malloc(pixelSize * srcWidth); if (!srcBuffer) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT"); return; } dstBuffer = malloc(pixelSize * dstWidth); if (!dstBuffer) { free(srcBuffer); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT"); return; } for (dstRow = 0; dstRow < dstHeight; dstRow++) { GLint srcRow = (dstRow * srcHeight) / dstHeight; 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 * srcWidth); 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; } } free(srcBuffer); free(dstBuffer); ctx->Driver.UnmapRenderbuffer(ctx, readRb); if (drawRb != readRb) { ctx->Driver.UnmapRenderbuffer(ctx, drawRb); } }