/*
* Mesa 3-D graphics library
- * Version: 6.5
*
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
*
* 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
- * BRIAN PAUL 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.
+ * 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"
if (flip) { \
for (dstCol = 0; dstCol < dstWidth; dstCol++) { \
GLint srcCol = (dstCol * srcWidth) / dstWidth; \
- ASSERT(srcCol >= 0); \
- ASSERT(srcCol < srcWidth); \
+ assert(srcCol >= 0); \
+ assert(srcCol < srcWidth); \
srcCol = srcWidth - 1 - srcCol; /* flip */ \
if (SIZE == 1) { \
dst[dstCol] = src[srcCol]; \
else { \
for (dstCol = 0; dstCol < dstWidth; dstCol++) { \
GLint srcCol = (dstCol * srcWidth) / dstWidth; \
- ASSERT(srcCol >= 0); \
- ASSERT(srcCol < srcWidth); \
+ assert(srcCol >= 0); \
+ assert(srcCol < srcWidth); \
if (SIZE == 1) { \
dst[dstCol] = src[srcCol]; \
} \
* Blit color, depth or stencil with GL_NEAREST filtering.
*/
static void
-blit_nearest(GLcontext *ctx,
+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,
- GLenum buffer)
+ GLbitfield buffer)
{
- struct gl_renderbuffer *readRb, *drawRb;
+ 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 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 comps, pixelSize;
+ GLint pixelSize = 0;
GLvoid *srcBuffer, *dstBuffer;
GLint prevY = -1;
switch (buffer) {
case GL_COLOR_BUFFER_BIT:
- readRb = ctx->ReadBuffer->_ColorReadBuffer;
- drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
- comps = 4;
+ readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex];
+ readRb = readFb->_ColorReadBuffer;
+ numDrawBuffers = drawFb->_NumColorDrawBuffers;
break;
case GL_DEPTH_BUFFER_BIT:
- readRb = ctx->ReadBuffer->_DepthBuffer;
- drawRb = ctx->DrawBuffer->_DepthBuffer;
- comps = 1;
+ 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:
- readRb = ctx->ReadBuffer->_StencilBuffer;
- drawRb = ctx->DrawBuffer->_StencilBuffer;
- comps = 1;
+ 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;
}
- switch (readRb->DataType) {
- case GL_UNSIGNED_BYTE:
- pixelSize = comps * sizeof(GLubyte);
- break;
- case GL_UNSIGNED_SHORT:
- pixelSize = comps * sizeof(GLushort);
- break;
- case GL_UNSIGNED_INT:
- pixelSize = comps * sizeof(GLuint);
- break;
- case GL_FLOAT:
- pixelSize = comps * sizeof(GLfloat);
- break;
- default:
- _mesa_problem(ctx, "unexpected buffer type (0x%x) in blit_nearest",
- readRb->DataType);
- 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;
- }
-
/* allocate the src/dst row buffers */
- srcBuffer = _mesa_malloc(pixelSize * srcWidth);
- if (!srcBuffer) {
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
- }
- dstBuffer = _mesa_malloc(pixelSize * dstWidth);
- if (!dstBuffer) {
- _mesa_free(srcBuffer);
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
- }
+ 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) {
+ gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
+ if (idx == BUFFER_NONE)
+ 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;
+ }
+ }
- for (dstRow = 0; dstRow < dstHeight; dstRow++) {
- const GLint dstY = dstYpos + dstRow;
- GLint srcRow = (dstRow * srcHeight) / dstHeight;
- GLint srcY;
+ /* 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;
+ }
- ASSERT(srcRow >= 0);
- ASSERT(srcRow < srcHeight);
+ 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, readFb->FlipY);
+ if (!map) {
+ goto fail_no_memory;
+ }
- if (invertY) {
- srcRow = srcHeight - 1 - srcRow;
+ 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,
+ readFb->FlipY);
+ if (!srcMap) {
+ goto fail_no_memory;
+ }
+ ctx->Driver.MapRenderbuffer(ctx, drawRb,
+ dstXpos, dstYpos,
+ dstWidth, dstHeight,
+ GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+ drawFb->FlipY);
+ if (!dstMap) {
+ ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+ goto fail_no_memory;
+ }
}
- srcY = srcYpos + srcRow;
+ 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 != srcY) {
- readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY, srcBuffer);
- (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
- prevY = srcY;
+ /* 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;
+ }
}
- /* store pixel row in destination */
- drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
+ ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+ if (drawRb != readRb) {
+ ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+ }
}
- _mesa_free(srcBuffer);
- _mesa_free(dstBuffer);
+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
+static inline GLfloat
lerp_2d(GLfloat a, GLfloat b,
GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
{
const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
- const GLfloat dstWidthF = (GLfloat) dstWidth;
GLint dstCol;
for (dstCol = 0; dstCol < dstWidth; dstCol++) {
- const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
- GLint srcCol0 = IFLOOR(srcCol);
+ 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 >= 0);
- ASSERT(srcCol0 < srcWidth);
- ASSERT(srcCol1 <= srcWidth);
+ assert(srcCol0 < srcWidth);
+ assert(srcCol1 <= srcWidth);
if (srcCol1 == srcWidth) {
/* last column fudge */
}
+/**
+ * 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).
+ * Bilinear filtered blit (color only, non-integer values).
*/
static void
-blit_linear(GLcontext *ctx,
+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 = ctx->ReadBuffer->_ColorReadBuffer;
- struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
+ 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 GLfloat dstHeightF = (GLfloat) dstHeight;
const GLint srcXpos = MIN2(srcX0, srcX1);
const GLint srcYpos = MIN2(srcY0, srcY1);
GLint srcBufferY0 = -1, srcBufferY1 = -1;
GLvoid *dstBuffer;
- switch (readRb->DataType) {
- case GL_UNSIGNED_BYTE:
+ 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);
- break;
- case GL_UNSIGNED_SHORT:
- pixelSize = 4 * sizeof(GLushort);
- break;
- case GL_UNSIGNED_INT:
- pixelSize = 4 * sizeof(GLuint);
- break;
- case GL_FLOAT:
+ }
+ else {
+ pixelType = GL_FLOAT;
pixelSize = 4 * sizeof(GLfloat);
- break;
- default:
- _mesa_problem(ctx, "unexpected buffer type (0x%x) in blit_nearest",
- readRb->DataType);
- return;
}
/* Allocate the src/dst row buffers.
* Keep two adjacent src rows around for bilinear sampling.
*/
- srcBuffer0 = _mesa_malloc(pixelSize * srcWidth);
- if (!srcBuffer0) {
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
- }
- srcBuffer1 = _mesa_malloc(pixelSize * srcWidth);
- if (!srcBuffer1) {
- _mesa_free(srcBuffer0);
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
- }
- dstBuffer = _mesa_malloc(pixelSize * dstWidth);
- if (!dstBuffer) {
- _mesa_free(srcBuffer0);
- _mesa_free(srcBuffer1);
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
+ srcBuffer0 = malloc(pixelSize * srcWidth);
+ srcBuffer1 = malloc(pixelSize * srcWidth);
+ dstBuffer = malloc(pixelSize * dstWidth);
+ if (!srcBuffer0 || !srcBuffer1 || !dstBuffer) {
+ goto fail_no_memory;
}
- 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 */
+ for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) {
+ gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
+ struct gl_renderbuffer_attachment *drawAtt;
+ struct gl_renderbuffer *drawRb;
+ mesa_format drawFormat;
- ASSERT(srcRow >= 0);
- ASSERT(srcRow < srcHeight);
+ if (idx == BUFFER_NONE)
+ continue;
- if (srcRow1 == srcHeight) {
- /* last row fudge */
- srcRow1 = srcRow0;
- rowWeight = 0.0;
- }
-
- if (invertY) {
- srcRow0 = srcHeight - 1 - srcRow0;
- srcRow1 = srcHeight - 1 - srcRow1;
- }
+ drawAtt = &drawFb->Attachment[idx];
+ drawRb = drawAtt->Renderbuffer;
+ if (!drawRb)
+ continue;
- srcY0 = srcYpos + srcRow0;
- srcY1 = srcYpos + srcRow1;
+ drawFormat = _mesa_get_srgb_format_linear(drawRb->Format);
- /* 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 */
- readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
- srcBufferY0 = srcY0;
- srcBufferY1 = srcY1;
- }
- else {
- /* get both new rows */
- readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY0, srcBuffer0);
- readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
- srcBufferY0 = srcY0;
- srcBufferY1 = srcY1;
- }
+ /*
+ * 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,
+ readFb->FlipY);
+ if (!srcMap) {
+ goto fail_no_memory;
+ }
- if (readRb->DataType == GL_UNSIGNED_BYTE) {
- resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
- dstBuffer, invertX, rowWeight);
+ dstMap = srcMap;
+ dstRowStride = srcRowStride;
}
else {
- _mesa_problem(ctx, "Unsupported color channel type in sw blit");
- break;
+ /* 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,
+ readFb->FlipY);
+ if (!srcMap) {
+ goto fail_no_memory;
+ }
+ ctx->Driver.MapRenderbuffer(ctx, drawRb,
+ 0, 0, drawRb->Width, drawRb->Height,
+ GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+ drawFb->FlipY);
+ if (!dstMap) {
+ ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+ goto fail_no_memory;
+ }
}
- /* store pixel row in destination */
- drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
- }
-
- _mesa_free(srcBuffer0);
- _mesa_free(srcBuffer1);
- _mesa_free(dstBuffer);
-}
-
-
-/**
- * Simple case: Blit color, depth or stencil with no scaling or flipping.
- * XXX we could easily support vertical flipping here.
- */
-static void
-simple_blit(GLcontext *ctx,
- GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
- GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
- GLenum buffer)
-{
- struct gl_renderbuffer *readRb, *drawRb;
- const GLint width = srcX1 - srcX0;
- const GLint height = srcY1 - srcY0;
- GLint row, srcY, dstY, yStep;
- GLint comps, bytesPerRow;
- void *rowBuffer;
-
- /* only one buffer */
- ASSERT(_mesa_bitcount(buffer) == 1);
- /* no flipping checks */
- ASSERT(srcX0 < srcX1);
- ASSERT(srcY0 < srcY1);
- ASSERT(dstX0 < dstX1);
- ASSERT(dstY0 < dstY1);
- /* size checks */
- ASSERT(srcX1 - srcX0 == dstX1 - dstX0);
- ASSERT(srcY1 - srcY0 == dstY1 - dstY0);
-
- /* determine if copy should be bottom-to-top or top-to-bottom */
- if (srcY0 > dstY0) {
- /* src above dst: copy bottom-to-top */
- yStep = 1;
- srcY = srcY0;
- dstY = dstY0;
- }
- else {
- /* src below dst: copy top-to-bottom */
- yStep = -1;
- srcY = srcY1 - 1;
- dstY = dstY1 - 1;
- }
-
- switch (buffer) {
- case GL_COLOR_BUFFER_BIT:
- readRb = ctx->ReadBuffer->_ColorReadBuffer;
- drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
- comps = 4;
- break;
- case GL_DEPTH_BUFFER_BIT:
- readRb = ctx->ReadBuffer->_DepthBuffer;
- drawRb = ctx->DrawBuffer->_DepthBuffer;
- comps = 1;
- break;
- case GL_STENCIL_BUFFER_BIT:
- readRb = ctx->ReadBuffer->_StencilBuffer;
- drawRb = ctx->DrawBuffer->_StencilBuffer;
- comps = 1;
- break;
- default:
- _mesa_problem(ctx, "unexpected buffer in simple_blit()");
- return;
- }
-
- ASSERT(readRb->DataType == drawRb->DataType);
+ 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;
+ }
- /* compute bytes per row */
- switch (readRb->DataType) {
- case GL_UNSIGNED_BYTE:
- bytesPerRow = comps * width * sizeof(GLubyte);
- break;
- case GL_UNSIGNED_SHORT:
- bytesPerRow = comps * width * sizeof(GLushort);
- break;
- case GL_UNSIGNED_INT:
- bytesPerRow = comps * width * sizeof(GLuint);
- break;
- case GL_FLOAT:
- bytesPerRow = comps * width * sizeof(GLfloat);
- break;
- default:
- _mesa_problem(ctx, "unexpected buffer type in simple_blit");
- return;
- }
+ if (invertY) {
+ srcRow0 = srcHeight - 1 - srcRow0;
+ srcRow1 = srcHeight - 1 - srcRow1;
+ }
- /* allocate the row buffer */
- rowBuffer = _mesa_malloc(bytesPerRow);
- if (!rowBuffer) {
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
- return;
- }
+ srcY0 = srcYpos + srcRow0;
+ srcY1 = srcYpos + srcRow1;
- for (row = 0; row < height; row++) {
- readRb->GetRow(ctx, readRb, width, srcX0, srcY, rowBuffer);
- drawRb->PutRow(ctx, drawRb, width, dstX0, dstY, rowBuffer, NULL);
- srcY += yStep;
- dstY += yStep;
- }
+ /* 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;
+ }
- _mesa_free(rowBuffer);
-}
+ 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);
+ }
+ }
+ }
-/**
- * Clip dst coords against Xmax (or Ymax).
- */
-static INLINE void
-clip_right_or_top(GLint *srcX0, GLint *srcX1,
- GLint *dstX0, GLint *dstX1,
- GLint maxValue)
-{
- GLfloat t, bias;
-
- if (*dstX1 > maxValue) {
- /* X1 outside right edge */
- ASSERT(*dstX0 < maxValue); /* X0 should be inside right edge */
- t = (GLfloat) (maxValue - *dstX0) / (GLfloat) (*dstX1 - *dstX0);
- /* chop off [t, 1] part */
- ASSERT(t >= 0.0 && t <= 1.0);
- *dstX1 = maxValue;
- bias = (*srcX0 < *srcX1) ? 0.5 : -0.5;
- *srcX1 = *srcX0 + (GLint) (t * (*srcX1 - *srcX0) + bias);
- }
- else if (*dstX0 > maxValue) {
- /* X0 outside right edge */
- ASSERT(*dstX1 < maxValue); /* X1 should be inside right edge */
- t = (GLfloat) (maxValue - *dstX1) / (GLfloat) (*dstX0 - *dstX1);
- /* chop off [t, 1] part */
- ASSERT(t >= 0.0 && t <= 1.0);
- *dstX0 = maxValue;
- bias = (*srcX0 < *srcX1) ? -0.5 : 0.5;
- *srcX0 = *srcX1 + (GLint) (t * (*srcX0 - *srcX1) + bias);
+ ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+ if (drawRb != readRb) {
+ ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+ }
}
-}
+ free(srcBuffer0);
+ free(srcBuffer1);
+ free(dstBuffer);
+ return;
-/**
- * Clip dst coords against Xmin (or Ymin).
- */
-static INLINE void
-clip_left_or_bottom(GLint *srcX0, GLint *srcX1,
- GLint *dstX0, GLint *dstX1,
- GLint minValue)
-{
- GLfloat t, bias;
-
- if (*dstX0 < minValue) {
- /* X0 outside left edge */
- ASSERT(*dstX1 > minValue); /* X1 should be inside left edge */
- t = (GLfloat) (minValue - *dstX0) / (GLfloat) (*dstX1 - *dstX0);
- /* chop off [0, t] part */
- ASSERT(t >= 0.0 && t <= 1.0);
- *dstX0 = minValue;
- bias = (*srcX0 < *srcX1) ? 0.5 : -0.5; /* flipped??? */
- *srcX0 = *srcX0 + (GLint) (t * (*srcX1 - *srcX0) + bias);
- }
- else if (*dstX1 < minValue) {
- /* X1 outside left edge */
- ASSERT(*dstX0 > minValue); /* X0 should be inside left edge */
- t = (GLfloat) (minValue - *dstX1) / (GLfloat) (*dstX0 - *dstX1);
- /* chop off [0, t] part */
- ASSERT(t >= 0.0 && t <= 1.0);
- *dstX1 = minValue;
- bias = (*srcX0 < *srcX1) ? 0.5 : -0.5;
- *srcX1 = *srcX1 + (GLint) (t * (*srcX0 - *srcX1) + bias);
- }
+fail_no_memory:
+ free(srcBuffer0);
+ free(srcBuffer1);
+ free(dstBuffer);
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
}
-/**
- * Do clipping of blit src/dest rectangles.
- * The dest rect is clipped against both the buffer bounds and scissor bounds.
- * The src rect is just clipped against the buffer bounds.
- *
- * When either the src or dest rect is clipped, the other is also clipped
- * proportionately!
- *
- * Note that X0 need not be less than X1 (same for Y) for either the source
- * and dest rects. That makes the clipping a little trickier.
- *
- * \return GL_TRUE if anything is left to draw, GL_FALSE if totally clipped
- */
-static GLboolean
-clip_blit(GLcontext *ctx,
- GLint *srcX0, GLint *srcY0, GLint *srcX1, GLint *srcY1,
- GLint *dstX0, GLint *dstY0, GLint *dstX1, GLint *dstY1)
-{
- const GLint srcXmin = 0;
- const GLint srcXmax = ctx->ReadBuffer->Width;
- const GLint srcYmin = 0;
- const GLint srcYmax = ctx->ReadBuffer->Height;
-
- /* these include scissor bounds */
- const GLint dstXmin = ctx->DrawBuffer->_Xmin;
- const GLint dstXmax = ctx->DrawBuffer->_Xmax;
- const GLint dstYmin = ctx->DrawBuffer->_Ymin;
- const GLint dstYmax = ctx->DrawBuffer->_Ymax;
-
- /*
- printf("PreClipX: src: %d .. %d dst: %d .. %d\n",
- *srcX0, *srcX1, *dstX0, *dstX1);
- printf("PreClipY: src: %d .. %d dst: %d .. %d\n",
- *srcY0, *srcY1, *dstY0, *dstY1);
- */
-
- /* trivial rejection tests */
- if (*dstX0 == *dstX1)
- return GL_FALSE; /* no width */
- if (*dstX0 <= dstXmin && *dstX1 <= dstXmin)
- return GL_FALSE; /* totally out (left) of bounds */
- if (*dstX0 >= dstXmax && *dstX1 >= dstXmax)
- return GL_FALSE; /* totally out (right) of bounds */
-
- if (*dstY0 == *dstY1)
- return GL_FALSE;
- if (*dstY0 <= dstYmin && *dstY1 <= dstYmin)
- return GL_FALSE;
- if (*dstY0 >= dstYmax && *dstY1 >= dstYmax)
- return GL_FALSE;
-
- if (*srcX0 == *srcX1)
- return GL_FALSE;
- if (*srcX0 <= srcXmin && *srcX1 <= srcXmin)
- return GL_FALSE;
- if (*srcX0 >= srcXmax && *srcX1 >= srcXmax)
- return GL_FALSE;
-
- if (*srcY0 == *srcY1)
- return GL_FALSE;
- if (*srcY0 <= srcYmin && *srcY1 <= srcYmin)
- return GL_FALSE;
- if (*srcY0 >= srcYmax && *srcY1 >= srcYmax)
- return GL_FALSE;
-
- /*
- * dest clip
- */
- clip_right_or_top(srcX0, srcX1, dstX0, dstX1, dstXmax);
- clip_right_or_top(srcY0, srcY1, dstY0, dstY1, dstYmax);
- clip_left_or_bottom(srcX0, srcX1, dstX0, dstX1, dstXmin);
- clip_left_or_bottom(srcY0, srcY1, dstY0, dstY1, dstYmin);
-
- /*
- * src clip (just swap src/dst values from above)
- */
- clip_right_or_top(dstX0, dstX1, srcX0, srcX1, srcXmax);
- clip_right_or_top(dstY0, dstY1, srcY0, srcY1, srcYmax);
- clip_left_or_bottom(dstX0, dstX1, srcX0, srcX1, srcXmin);
- clip_left_or_bottom(dstY0, dstY1, srcY0, srcY1, srcYmin);
-
- /*
- printf("PostClipX: src: %d .. %d dst: %d .. %d\n",
- *srcX0, *srcX1, *dstX0, *dstX1);
- printf("PostClipY: src: %d .. %d dst: %d .. %d\n",
- *srcY0, *srcY1, *dstY0, *dstY1);
- */
-
- ASSERT(*dstX0 >= dstXmin);
- ASSERT(*dstX0 <= dstXmax);
- ASSERT(*dstX1 >= dstXmin);
- ASSERT(*dstX1 <= dstXmax);
-
- ASSERT(*dstY0 >= dstYmin);
- ASSERT(*dstY0 <= dstYmax);
- ASSERT(*dstY1 >= dstYmin);
- ASSERT(*dstY1 <= dstYmax);
-
- ASSERT(*srcX0 >= srcXmin);
- ASSERT(*srcX0 <= srcXmax);
- ASSERT(*srcX1 >= srcXmin);
- ASSERT(*srcX1 <= srcXmax);
-
- ASSERT(*srcY0 >= srcYmin);
- ASSERT(*srcY0 <= srcYmax);
- ASSERT(*srcY1 >= srcYmin);
- ASSERT(*srcY1 <= srcYmax);
-
- return GL_TRUE;
-}
-
/**
* Software fallback for glBlitFramebufferEXT().
*/
void
-_swrast_BlitFramebuffer(GLcontext *ctx,
+_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 GLint buffers[3] = {
+ 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 (!ctx->DrawBuffer->_NumColorDrawBuffers)
- return;
+ /* 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 (!clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
- &dstX0, &dstY0, &dstX1, &dstY1)) {
+ if (!_mesa_clip_blit(ctx, readFb, drawFb, &srcX0, &srcY0, &srcX1, &srcY1,
+ &dstX0, &dstY0, &dstX1, &dstY1)) {
return;
}
- swrast_render_start(ctx);
+ 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) {
- /* no stretching or flipping.
- * filter doesn't matter.
- */
for (i = 0; i < 3; i++) {
if (mask & buffers[i]) {
- simple_blit(ctx, srcX0, srcY0, srcX1, srcY1,
- dstX0, dstY0, dstX1, dstY1, 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;
}
- else {
- 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]);
- }
- }
+
+ 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, srcX0, srcY0, srcX1, srcY1,
- dstX0, dstY0, dstX1, dstY1);
- }
+ }
+ 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);
}
}
- swrast_render_finish(ctx);
}