Bug 635035, part 6: Use gfxASurface::MovePixels() for self-copies of ThebesLayerBuffers. r=roc
authorChris Jones <jones.chris.g@gmail.com>
Wed, 09 Mar 2011 11:27:37 -0600
changeset 63371 edf7507120f7f33ba37c21c3bd9e4763ea914228
parent 63370 4ab41ca945e0f4e03be72cd6da4b3b3b03f5fe56
child 63372 54f451b29081be265e0b4fa1c794c0b8c4acc8ac
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersroc
bugs635035
milestone2.0b13pre
Bug 635035, part 6: Use gfxASurface::MovePixels() for self-copies of ThebesLayerBuffers. r=roc
gfx/layers/ThebesLayerBuffer.cpp
--- a/gfx/layers/ThebesLayerBuffer.cpp
+++ b/gfx/layers/ThebesLayerBuffer.cpp
@@ -35,16 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ThebesLayerBuffer.h"
 #include "Layers.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
+#include "nsIDeviceContext.h"
 
 namespace mozilla {
 namespace layers {
 
 static nsIntSize
 ScaledSize(const nsIntSize& aSize, float aXScale, float aYScale)
 {
   if (aXScale == 1.0 && aYScale == 1.0) {
@@ -163,16 +164,53 @@ ThebesLayerBuffer::GetContextForQuadrant
   nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants");
   ctx->Scale(aXResolution, aYResolution);
   ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
 
   return ctx.forget();
 }
 
+// Move the pixels in aBuffer specified by |aSourceRect| to |aDest|.
+// |aSourceRect| and |aDest| are in the space of |aBuffer|, but
+// unscaled by the resolution.  This helper does the scaling.
+static void
+MovePixels(gfxASurface* aBuffer,
+           const nsIntRect& aSourceRect, const nsIntPoint& aDest,
+           float aXResolution, float aYResolution)
+{
+  gfxRect src(aSourceRect.x, aSourceRect.y, aSourceRect.width, aSourceRect.height);
+  gfxRect dest(aDest.x, aDest.y,  aSourceRect.width, aSourceRect.height);
+  src.Scale(aXResolution, aYResolution);
+  dest.Scale(aXResolution, aYResolution);
+
+#ifdef DEBUG
+  // If we're doing a self-copy, enforce that the rects we're copying
+  // were computed in order to round to device pixels.  If the rects
+  // we're moving *weren't* computed to round, then glitches like
+  // seaming are likely.  Assume that the precision of these
+  // computations is 1 app unit, and toss in a fudge factor of 2.0.
+  static const gfxFloat kPrecision =
+    1.0 / gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel());
+  // FIXME/bug 637852: we've decided to live with transient glitches
+  // during fast-panning for the time being.
+  NS_WARN_IF_FALSE(
+    src.WithinEpsilonOfIntegerPixels(2.0 * kPrecision * aXResolution) &&
+    dest.WithinEpsilonOfIntegerPixels(2.0 * kPrecision * aXResolution),
+    "Rects don't round to device pixels within precision; glitches likely to follow");
+#endif
+
+  src.Round();
+  dest.Round();
+
+  aBuffer->MovePixels(nsIntRect(src.pos.x, src.pos.y,
+                                src.size.width, src.size.height),
+                      nsIntPoint(dest.pos.x, dest.pos.y));
+}
+
 static void
 WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
 {
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
     *aRotationPoint -= aSize;
   }
@@ -287,19 +325,25 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
       NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                    "newRotation out of bounds");
       PRInt32 xBoundary = destBufferRect.XMost() - newRotation.x;
       PRInt32 yBoundary = destBufferRect.YMost() - newRotation.y;
       if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
           (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
           (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
         // The stuff we need to redraw will wrap around an edge of the
-        // buffer, so we will need to do a self-copy
-        if (mBuffer->SupportsSelfCopy() && mBufferRotation == nsIntPoint(0,0)) {
-          destBuffer = mBuffer;
+        // buffer, so move the pixels we can keep into a position that
+        // lets us redraw in just one quadrant.
+        if (mBufferRotation == nsIntPoint(0,0)) {
+          nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
+          nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
+          MovePixels(mBuffer, srcRect, dest, curXRes, curYRes);
+          // Don't set destBuffer; we special-case self-copies, and
+          // just did the necessary work above.
+          mBufferRect = destBufferRect;
         } else {
           // We can't do a real self-copy because the buffer is rotated.
           // So allocate a new buffer for the destination.
           destBufferRect = neededRegion.GetBounds();
           bufferDimsChanged = PR_TRUE;
           destBuffer = CreateBuffer(contentType, destBufferDims);
           if (!destBuffer)
             return result;