Bug 635035, part 4: Implement MovePixels() for image surfaces. r=roc
authorChris Jones <jones.chris.g@gmail.com>
Wed, 09 Mar 2011 11:27:37 -0600
changeset 63369 d4a93181df15b4a678c0eeb6999e3f71c8fa5af7
parent 63368 173971a4af7efeaeead7a8b38a1c3abb9e879eee
child 63370 4ab41ca945e0f4e03be72cd6da4b3b3b03f5fe56
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs635035
milestone2.0b13pre
Bug 635035, part 4: Implement MovePixels() for image surfaces. r=roc
gfx/thebes/gfxImageSurface.cpp
gfx/thebes/gfxImageSurface.h
--- a/gfx/thebes/gfxImageSurface.cpp
+++ b/gfx/thebes/gfxImageSurface.cpp
@@ -249,8 +249,75 @@ gfxSubimageSurface::gfxSubimageSurface(g
 }
 
 already_AddRefed<gfxImageSurface>
 gfxImageSurface::GetAsImageSurface()
 {
   nsRefPtr<gfxImageSurface> surface = this;
   return surface.forget();
 }
+
+void
+gfxImageSurface::MovePixels(const nsIntRect& aSourceRect,
+                            const nsIntPoint& aDestTopLeft)
+{
+    const nsIntRect bounds(0, 0, mSize.width, mSize.height);
+    nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); 
+    nsIntRect clippedSource = aSourceRect;
+    clippedSource.IntersectRect(clippedSource, bounds);
+    nsIntRect clippedDest = clippedSource + offset;
+    clippedDest.IntersectRect(clippedDest, bounds);
+    const nsIntRect dest = clippedDest;
+    const nsIntRect source = dest - offset;
+    // NB: this relies on IntersectRect() and operator+/- preserving
+    // x/y for empty rectangles
+    NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) &&
+                      aSourceRect.Contains(source) &&
+                      nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) &&
+                      source.Size() == dest.Size() &&
+                      offset == (dest.TopLeft() - source.TopLeft()),
+                      "Messed up clipping, crash or corruption will follow");
+    if (source.IsEmpty() || source == dest) {
+        return;
+    }
+
+    long naturalStride = ComputeStride(mSize, mFormat);
+    if (mStride == naturalStride && dest.width == bounds.width) {
+        // Fast path: this is a vertical shift of some rows in a
+        // "normal" image surface.  We can directly memmove and
+        // hopefully stay in SIMD land.
+        unsigned char* dst = mData + dest.y * mStride;
+        const unsigned char* src = mData + source.y * mStride;
+        size_t nBytes = dest.height * mStride;
+        memmove(dst, src, nBytes);
+        return;
+    }
+
+    // Slow(er) path: have to move row-by-row.
+    const PRInt32 bpp = BytePerPixelFromFormat(mFormat);
+    const size_t nRowBytes = dest.width * bpp;
+    // dstRow points at the first pixel within the current destination
+    // row, and similarly for srcRow.  endSrcRow is one row beyond the
+    // last row we need to copy.  stride is either +mStride or
+    // -mStride, depending on which direction we're copying.
+    unsigned char* dstRow;
+    unsigned char* srcRow;
+    unsigned char* endSrcRow;   // NB: this may point outside the image
+    long stride;
+    if (dest.y > source.y) {
+        // We're copying down from source to dest, so walk backwards
+        // starting from the last rows to avoid stomping pixels we
+        // need.
+        stride = -mStride;
+        dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride;
+        srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride;
+        endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride;
+    } else {
+        stride = mStride;
+        dstRow = mData + dest.x * bpp + dest.y * mStride;
+        srcRow = mData + source.x * bpp + source.y * mStride;
+        endSrcRow = mData + source.x * bpp + source.YMost() * mStride;
+    }
+
+    for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) {
+        memmove(dstRow, srcRow, nRowBytes);
+    }
+}
--- a/gfx/thebes/gfxImageSurface.h
+++ b/gfx/thebes/gfxImageSurface.h
@@ -105,16 +105,21 @@ public:
      * and size of aRect.size. New subimage keeping current image reference
      */
     already_AddRefed<gfxSubimageSurface> GetSubimage(const gfxRect& aRect);
 
     virtual already_AddRefed<gfxImageSurface> GetAsImageSurface();
 
     virtual PRBool SupportsSelfCopy() { return PR_FALSE; }
 
+    /** See gfxASurface.h. */
+    NS_OVERRIDE
+    virtual void MovePixels(const nsIntRect& aSourceRect,
+                            const nsIntPoint& aDestTopLeft);
+
 protected:
     gfxImageSurface();
     void InitWithData(unsigned char *aData, const gfxIntSize& aSize,
                       long aStride, gfxImageFormat aFormat);
     void InitFromSurface(cairo_surface_t *csurf);
     long ComputeStride() const { return ComputeStride(mSize, mFormat); }
 
     static long ComputeStride(const gfxIntSize&, gfxImageFormat);