mesa: treat Color._AdvancedBlendMode as enum
[mesa.git] / src / mesa / swrast / s_blit.c
index 8303e4debc75dcb5b15c18543f40232c4e2e04c3..6b7e5e4533381a077be10248ee7917716460a77e 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
 /*
  * Mesa 3-D graphics library
- * Version:  6.5
  *
  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
  *
  *
  * 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
  * 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/glheader.h"
+#include "main/condrender.h"
 #include "main/image.h"
 #include "main/macros.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"
 
 
 #include "s_context.h"
 
 
@@ -48,8 +52,8 @@ NAME(GLint srcWidth, GLint dstWidth,                  \
    if (flip) {                                         \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
    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];                 \
          srcCol = srcWidth - 1 - srcCol; /* flip */    \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
@@ -69,8 +73,8 @@ NAME(GLint srcWidth, GLint dstWidth,                  \
    else {                                              \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
    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];                 \
          }                                             \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
          }                                             \
@@ -102,12 +106,17 @@ RESAMPLE(resample_row_16, GLuint, 4)
  * Blit color, depth or stencil with GL_NEAREST filtering.
  */
 static void
  * 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,
              GLbitfield buffer)
 {
              GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
              GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
              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 GLint srcWidth = ABS(srcX1 - srcX0);
    const GLint dstWidth = ABS(dstX1 - dstX0);
@@ -121,10 +130,18 @@ blit_nearest(GLcontext *ctx,
 
    const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
    const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
 
    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 dstRow;
 
-   GLint comps, pixelSize;
+   GLint pixelSize = 0;
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
@@ -135,114 +152,238 @@ blit_nearest(GLcontext *ctx,
 
    switch (buffer) {
    case GL_COLOR_BUFFER_BIT:
 
    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:
       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:
       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;
    }
 
       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 */
    /* 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;
+         }
+      }
+
+      /* 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;
+      }
 
 
-   for (dstRow = 0; dstRow < dstHeight; dstRow++) {
-      const GLint dstY = dstYpos + dstRow;
-      GLint srcRow = (dstRow * srcHeight) / dstHeight;
-      GLint srcY;
+      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;
+         }
 
 
-      ASSERT(srcRow >= 0);
-      ASSERT(srcRow < srcHeight);
+         srcMap = map + srcYpos * rowStride + srcXpos * formatSize;
+         dstMap = map + dstYpos * rowStride + dstXpos * formatSize;
 
 
-      if (invertY) {
-         srcRow = srcHeight - 1 - srcRow;
+         /* 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 = lroundf(srcRowF);
+         GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
+
+         assert(srcRow >= 0);
+         assert(srcRow < srcHeight);
 
 
-      /* 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;
+         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;
+         }
       }
 
       }
 
-      /* 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)) )
 
 }
 
 
 
 #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)
 {
 lerp_2d(GLfloat a, GLfloat b,
         GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
 {
@@ -264,19 +405,17 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
    const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
    const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
    GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
    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++) {
    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, util_ifloor(srcCol));
       GLint srcCol1 = srcCol0 + 1;
       GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
       GLfloat red, green, blue, alpha;
 
       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 */
 
       if (srcCol1 == srcWidth) {
          /* last column fudge */
@@ -301,32 +440,89 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
       alpha = lerp_2d(colWeight, rowWeight,
                     srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
                     srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
       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);
+
+      dstColor[dstCol][RCOMP] = util_ifloor(red);
+      dstColor[dstCol][GCOMP] = util_ifloor(green);
+      dstColor[dstCol][BCOMP] = util_ifloor(blue);
+      dstColor[dstCol][ACOMP] = util_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, util_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
  */
 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)
 {
             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 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 srcXpos = MIN2(srcX0, srcX1);
    const GLint srcYpos = MIN2(srcY0, srcY1);
@@ -343,219 +539,207 @@ blit_linear(GLcontext *ctx,
    GLint srcBufferY0 = -1, srcBufferY1 = -1;
    GLvoid *dstBuffer;
 
    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);
       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);
       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.
     */
    }
 
    /* 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;
-      }
+      drawAtt = &drawFb->Attachment[idx];
+      drawRb = drawAtt->Renderbuffer;
+      if (!drawRb)
+         continue;
 
 
-      if (invertY) {
-         srcRow0 = srcHeight - 1 - srcRow0;
-         srcRow1 = srcHeight - 1 - srcRow1;
-      }
-
-      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 {
       }
       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);
-}
+      for (dstRow = 0; dstRow < dstHeight; dstRow++) {
+         const GLint dstY = dstYpos + dstRow;
+         GLfloat srcRow = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F;
+         GLint srcRow0 = MAX2(0, util_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;
+         }
 
 
-/**
- * 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,
-            GLbitfield 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;
-   }
+         srcY0 = srcYpos + srcRow0;
+         srcY1 = srcYpos + srcRow1;
 
 
-   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;
-   }
+         /* 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;
+         }
 
 
-   ASSERT(readRb->DataType == drawRb->DataType);
+         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);
+         }
 
 
-   /* 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;
-   }
+         /* 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);
+            }
+         }
+      }
 
 
-   /* allocate the row buffer */
-   rowBuffer = _mesa_malloc(bytesPerRow);
-   if (!rowBuffer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
+      ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+      if (drawRb != readRb) {
+         ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+      }
    }
 
    }
 
-   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;
-   }
+   free(srcBuffer0);
+   free(srcBuffer1);
+   free(dstBuffer);
+   return;
 
 
-   _mesa_free(rowBuffer);
+fail_no_memory:
+   free(srcBuffer0);
+   free(srcBuffer1);
+   free(dstBuffer);
+   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
 }
 
 
 }
 
 
+
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
 void
 /**
  * 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)
                         GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                         GLbitfield mask, GLenum filter)
@@ -565,51 +749,68 @@ _swrast_BlitFramebuffer(GLcontext *ctx,
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
+   static const GLenum buffer_enums[3] = {
+      GL_COLOR,
+      GL_DEPTH,
+      GL_STENCIL,
+   };
    GLint i;
 
    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 (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
+   if (!_mesa_clip_blit(ctx, readFb, drawFb, &srcX0, &srcY0, &srcX1, &srcY1,
                         &dstX0, &dstY0, &dstX1, &dstY1)) {
       return;
    }
 
                         &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) {
    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]) {
       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);
 }
 }