swrast: remove dstType param from _swrast_read_rgba_span()
[mesa.git] / src / mesa / swrast / s_blit.c
index 8303e4debc75dcb5b15c18543f40232c4e2e04c3..6d0b889c1950b44560e900f0f6109d8a9992585d 100644 (file)
 
 
 #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 "s_context.h"
 
 
 #include "s_context.h"
 
 
@@ -102,7 +105,7 @@ 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,
              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)
@@ -121,10 +124,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;
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
    GLint dstRow;
 
    GLint dstRow;
 
-   GLint comps, pixelSize;
+   GLint pixelSize;
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
@@ -137,42 +148,43 @@ blit_nearest(GLcontext *ctx,
    case GL_COLOR_BUFFER_BIT:
       readRb = ctx->ReadBuffer->_ColorReadBuffer;
       drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
    case GL_COLOR_BUFFER_BIT:
       readRb = ctx->ReadBuffer->_ColorReadBuffer;
       drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
-      comps = 4;
+
+      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:
       break;
    case GL_DEPTH_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_DepthBuffer;
-      drawRb = ctx->DrawBuffer->_DepthBuffer;
-      comps = 1;
+      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:
       break;
    case GL_STENCIL_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_StencilBuffer;
-      drawRb = ctx->DrawBuffer->_StencilBuffer;
-      comps = 1;
+      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;
    }
 
       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:
    /* choose row resampler */
    switch (pixelSize) {
    case 1:
@@ -196,23 +208,78 @@ blit_nearest(GLcontext *ctx,
       return;
    }
 
       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 */
    /* allocate the src/dst row buffers */
-   srcBuffer = _mesa_malloc(pixelSize * srcWidth);
+   srcBuffer = malloc(pixelSize * srcWidth);
    if (!srcBuffer) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
    if (!srcBuffer) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
-   dstBuffer = _mesa_malloc(pixelSize * dstWidth);
+   dstBuffer = malloc(pixelSize * dstWidth);
    if (!dstBuffer) {
    if (!dstBuffer) {
-      _mesa_free(srcBuffer);
+      free(srcBuffer);
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
 
    for (dstRow = 0; dstRow < dstHeight; dstRow++) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
 
    for (dstRow = 0; dstRow < dstHeight; dstRow++) {
-      const GLint dstY = dstYpos + dstRow;
       GLint srcRow = (dstRow * srcHeight) / dstHeight;
       GLint srcRow = (dstRow * srcHeight) / dstHeight;
-      GLint srcY;
+      GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
 
       ASSERT(srcRow >= 0);
       ASSERT(srcRow < srcHeight);
 
       ASSERT(srcRow >= 0);
       ASSERT(srcRow < srcHeight);
@@ -221,28 +288,74 @@ blit_nearest(GLcontext *ctx,
          srcRow = srcHeight - 1 - srcRow;
       }
 
          srcRow = srcHeight - 1 - srcRow;
       }
 
-      srcY = srcYpos + srcRow;
-
       /* get pixel row from source and resample to match dest width */
       /* get pixel row from source and resample to match dest width */
-      if (prevY != srcY) {
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY, srcBuffer);
+      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);
          (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
-         prevY = srcY;
+         prevY = srcRow;
       }
 
       /* store pixel row in destination */
       }
 
       /* store pixel row in destination */
-      drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
+      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;
+      }
    }
 
    }
 
-   _mesa_free(srcBuffer);
-   _mesa_free(dstBuffer);
+   free(srcBuffer);
+   free(dstBuffer);
+
+   ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+   if (drawRb != readRb) {
+      ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+   }
 }
 
 
 
 #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)
 {
@@ -310,12 +423,69 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
 }
 
 
 }
 
 
+/**
+ * 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;
+   const GLfloat dstWidthF = (GLfloat) dstWidth;
+   GLint dstCol;
+
+   for (dstCol = 0; dstCol < dstWidth; dstCol++) {
+      const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
+      GLint srcCol0 = 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);
+
+      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,
             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)
 {
@@ -343,47 +513,88 @@ 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:
+   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);
       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);
+   srcBuffer0 = malloc(pixelSize * srcWidth);
    if (!srcBuffer0) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
    if (!srcBuffer0) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
-   srcBuffer1 = _mesa_malloc(pixelSize * srcWidth);
+   srcBuffer1 = malloc(pixelSize * srcWidth);
    if (!srcBuffer1) {
    if (!srcBuffer1) {
-      _mesa_free(srcBuffer0);
+      free(srcBuffer0);
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
-   dstBuffer = _mesa_malloc(pixelSize * dstWidth);
+   dstBuffer = malloc(pixelSize * dstWidth);
    if (!dstBuffer) {
    if (!dstBuffer) {
-      _mesa_free(srcBuffer0);
-      _mesa_free(srcBuffer1);
+      free(srcBuffer0);
+      free(srcBuffer1);
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
       return;
    }
 
       _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) {
+         _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) {
+         _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);
+         _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;
    for (dstRow = 0; dstRow < dstHeight; dstRow++) {
       const GLint dstY = dstYpos + dstRow;
       const GLfloat srcRow = (dstRow * srcHeight) / dstHeightF;
@@ -418,144 +629,78 @@ blit_linear(GLcontext *ctx,
          srcBuffer0 = srcBuffer1;
          srcBuffer1 = tmp;
          /* get y1 row */
          srcBuffer0 = srcBuffer1;
          srcBuffer1 = tmp;
          /* get y1 row */
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
+         {
+            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 */
          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);
+         {
+            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;
       }
 
          srcBufferY0 = srcY0;
          srcBufferY1 = srcY1;
       }
 
-      if (readRb->DataType == GL_UNSIGNED_BYTE) {
+      if (pixelType == GL_UNSIGNED_BYTE) {
          resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
                                 dstBuffer, invertX, rowWeight);
       }
       else {
          resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
                                 dstBuffer, invertX, rowWeight);
       }
       else {
-         _mesa_problem(ctx, "Unsupported color channel type in sw blit");
-         break;
+         resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
+                                   dstBuffer, invertX, rowWeight);
       }
 
       /* store pixel row in destination */
       }
 
       /* 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,
-            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;
-   }
-
-   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;
+      {
+         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);
+         }
+      }
    }
 
    }
 
-   ASSERT(readRb->DataType == drawRb->DataType);
+   free(srcBuffer0);
+   free(srcBuffer1);
+   free(dstBuffer);
 
 
-   /* 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;
-   }
-
-   /* 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;
-   }
-
-   _mesa_free(rowBuffer);
 }
 
 
 }
 
 
+
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
 void
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
 void
-_swrast_BlitFramebuffer(GLcontext *ctx,
+_swrast_BlitFramebuffer(struct gl_context *ctx,
                         GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                         GLbitfield mask, GLenum filter)
                         GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                         GLbitfield mask, GLenum filter)
@@ -565,51 +710,60 @@ _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;
-
    if (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
                         &dstX0, &dstY0, &dstX1, &dstY1)) {
       return;
    }
 
    if (!_mesa_clip_blit(ctx, &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) {
    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,
+                                       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,  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,  srcX0, srcY0, srcX1, srcY1,
+                    dstX0, dstY0, dstX1, dstY1);
       }
    }
 
       }
    }
 
-   swrast_render_finish(ctx);
 }
 }