Add RotatedBuffer::AdjustTo. (bug 1409871 part 10, r=nical) draft
authorRyan Hunt <rhunt@eqrion.net>
Thu, 12 Oct 2017 18:37:29 -0400
changeset 684132 6b4e3c69d0275b13bcf305b6a8636754772be5a4
parent 684131 ed130d22917161d3fa9f98f5090bd231f65ccaed
child 684133 6e66802c290c371d3752f4a241bca4f145c9cd98
push id85567
push userbmo:rhunt@eqrion.net
push dateFri, 20 Oct 2017 22:13:22 +0000
reviewersnical
bugs1409871
milestone58.0a1
Add RotatedBuffer::AdjustTo. (bug 1409871 part 10, r=nical) This commit adds a method to rotated buffer to perform the adjusting to the new destination rect, and potential unrotating that may need to happen. The goal of this is to simplify and prepare BeginPaint to be moved to content client. MozReview-Commit-ID: B4qeZqLjORR
gfx/layers/RotatedBuffer.cpp
gfx/layers/RotatedBuffer.h
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -273,16 +273,138 @@ RotatedBuffer::UpdateDestinationFrom(con
       }
       // Flush the destination before the sources become inaccessible (Unlock).
       destDT->Flush();
       ReturnDrawTarget(destDT);
     }
   }
 }
 
+static void
+WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
+{
+  if (*aRotationPoint < 0) {
+    *aRotationPoint += aSize;
+  } else if (*aRotationPoint >= aSize) {
+    *aRotationPoint -= aSize;
+  }
+}
+
+static IntRect
+ComputeBufferRect(const IntRect& aRequestedRect)
+{
+  IntRect rect(aRequestedRect);
+  // Set a minimum width to guarantee a minimum size of buffers we
+  // allocate (and work around problems on some platforms with smaller
+  // dimensions). 64 used to be the magic number needed to work around
+  // a rendering glitch on b2g (see bug 788411). Now that we don't support
+  // this device anymore we should be fine with 8 pixels as the minimum.
+  rect.SetWidth(std::max(aRequestedRect.Width(), 8));
+  return rect;
+}
+
+bool
+RotatedBuffer::AdjustTo(const gfx::IntRect& aDestBufferRect,
+                        const gfx::IntRect& aDrawBounds,
+                        bool aCanHaveRotation,
+                        bool aCanDrawRotated)
+{
+  IntRect keepArea;
+  if (keepArea.IntersectRect(aDestBufferRect, mBufferRect)) {
+    // Set mBufferRotation so that the pixels currently in mDTBuffer
+    // will still be rendered in the right place when mBufferRect
+    // changes to aDestBufferRect.
+    IntPoint newRotation = mBufferRotation +
+      (aDestBufferRect.TopLeft() - mBufferRect.TopLeft());
+    WrapRotationAxis(&newRotation.x, mBufferRect.Width());
+    WrapRotationAxis(&newRotation.y, mBufferRect.Height());
+    NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
+                 "newRotation out of bounds");
+
+    int32_t xBoundary = aDestBufferRect.XMost() - newRotation.x;
+    int32_t yBoundary = aDestBufferRect.YMost() - newRotation.y;
+    bool drawWrapsBuffer = (aDrawBounds.x < xBoundary && xBoundary < aDrawBounds.XMost()) ||
+                           (aDrawBounds.y < yBoundary && yBoundary < aDrawBounds.YMost());
+
+    if ((drawWrapsBuffer && !aCanDrawRotated) ||
+        (newRotation != IntPoint(0,0) && !aCanHaveRotation)) {
+      // The stuff we need to redraw will wrap around an edge of the
+      // buffer (and the caller doesn't know how to support that), so
+      // move the pixels we can keep into a position that lets us
+      // redraw in just one quadrant.
+      RefPtr<gfx::DrawTarget> dtBuffer = GetDTBuffer();
+      RefPtr<gfx::DrawTarget> dtBufferOnWhite = GetDTBufferOnWhite();
+
+      if (mBufferRotation == IntPoint(0,0)) {
+        IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
+        IntPoint dest = mBufferRect.TopLeft() - aDestBufferRect.TopLeft();
+
+        MOZ_ASSERT(dtBuffer && dtBuffer->IsValid());
+        dtBuffer->CopyRect(srcRect, dest);
+        if (HaveBufferOnWhite()) {
+          MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
+          dtBufferOnWhite->CopyRect(srcRect, dest);
+        }
+
+        mDidSelfCopy = true;
+        mBufferRect = aDestBufferRect;
+      } else {
+        // With azure and a data surface perform an buffer unrotate
+        // (SelfCopy).
+        unsigned char* data;
+        IntSize size;
+        int32_t stride;
+        SurfaceFormat format;
+
+        if (dtBuffer->LockBits(&data, &size, &stride, &format)) {
+          uint8_t bytesPerPixel = BytesPerPixel(format);
+          BufferUnrotate(data,
+                         size.width * bytesPerPixel,
+                         size.height, stride,
+                         newRotation.x * bytesPerPixel, newRotation.y);
+          dtBuffer->ReleaseBits(data);
+
+          if (HaveBufferOnWhite()) {
+            MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
+            dtBufferOnWhite->LockBits(&data, &size, &stride, &format);
+            uint8_t bytesPerPixel = BytesPerPixel(format);
+            BufferUnrotate(data,
+                           size.width * bytesPerPixel,
+                           size.height, stride,
+                           newRotation.x * bytesPerPixel, newRotation.y);
+            dtBufferOnWhite->ReleaseBits(data);
+          }
+
+          // Buffer unrotate moves all the pixels
+          mDidSelfCopy = true;
+          mBufferRect = aDestBufferRect;
+          mBufferRotation = IntPoint(0, 0);
+        }
+
+        if (!mDidSelfCopy) {
+          // We couldn't unrotate the buffer, so we need to create a
+          // new one and start from scratch
+          return false;
+        }
+      }
+    } else {
+      mBufferRect = aDestBufferRect;
+      mBufferRotation = newRotation;
+    }
+  } else {
+    // No pixels are going to be kept. The whole visible region
+    // will be redrawn, so we don't need to copy anything, so we don't
+    // set destBuffer.
+    mBufferRect = aDestBufferRect;
+    mBufferRotation = IntPoint(0,0);
+  }
+
+  return true;
+}
+
 DrawTarget*
 RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
                                                  ContextSource aSource,
                                                  DrawIterator* aIter,
                                                  bool aSetTransform,
                                                  Matrix* aOutMatrix)
 {
   IntRect bounds = aBounds;
@@ -525,61 +647,32 @@ RotatedContentBuffer::HaveBuffer() const
 }
 
 bool
 RotatedContentBuffer::HaveBufferOnWhite() const
 {
   return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
 }
 
-static void
-WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
-{
-  if (*aRotationPoint < 0) {
-    *aRotationPoint += aSize;
-  } else if (*aRotationPoint >= aSize) {
-    *aRotationPoint -= aSize;
-  }
-}
-
-static IntRect
-ComputeBufferRect(const IntRect& aRequestedRect)
-{
-  IntRect rect(aRequestedRect);
-  // Set a minimum width to guarantee a minimum size of buffers we
-  // allocate (and work around problems on some platforms with smaller
-  // dimensions). 64 used to be the magic number needed to work around
-  // a rendering glitch on b2g (see bug 788411). Now that we don't support
-  // this device anymore we should be fine with 8 pixels as the minimum.
-  rect.SetWidth(std::max(aRequestedRect.Width(), 8));
-  return rect;
-}
-
 void
 RotatedContentBuffer::FlushBuffers()
 {
   if (mDTBuffer) {
     mDTBuffer->Flush();
   }
   if (mDTBufferOnWhite) {
     mDTBufferOnWhite->Flush();
   }
 }
 
 RotatedContentBuffer::PaintState
 RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
                                  uint32_t aFlags)
 {
   PaintState result;
-  // We need to disable rotation if we're going to be resampled when
-  // drawing, because we might sample across the rotation boundary.
-  // Also disable buffer rotation when using webrender.
-  bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
-                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
-                         !(aLayer->Manager()->AsWebRenderLayerManager());
 
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
   ContentType layerContentType =
     canUseOpaqueSurface ? gfxContentType::COLOR :
                           gfxContentType::COLOR_ALPHA;
 
@@ -698,124 +791,52 @@ RotatedContentBuffer::BeginPaint(Painted
       // we only need to draw the latest bits, but we need a big refactor to support
       // that ordering.
       result.mRegionToDraw = neededRegion;
       canReuseBuffer = false;
       Clear();
     }
   }
 
+  // We need to disable rotation if we're going to be resampled when
+  // drawing, because we might sample across the rotation boundary.
+  // Also disable buffer rotation when using webrender.
+  bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
+                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
+                         !(aLayer->Manager()->AsWebRenderLayerManager());
+  bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
+
   IntRect drawBounds = result.mRegionToDraw.GetBounds();
   RefPtr<DrawTarget> destDTBuffer;
   RefPtr<DrawTarget> destDTBufferOnWhite;
   uint32_t bufferFlags = 0;
   if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     bufferFlags |= BUFFER_COMPONENT_ALPHA;
   }
   if (canReuseBuffer) {
-    if (!EnsureBuffer()) {
+    if (!EnsureBuffer() ||
+        (HaveBufferOnWhite() && !EnsureBufferOnWhite())) {
       return result;
     }
-    IntRect keepArea;
-    if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
-      // Set mBufferRotation so that the pixels currently in mDTBuffer
-      // will still be rendered in the right place when mBufferRect
-      // changes to destBufferRect.
-      IntPoint newRotation = mBufferRotation +
-        (destBufferRect.TopLeft() - mBufferRect.TopLeft());
-      WrapRotationAxis(&newRotation.x, mBufferRect.Width());
-      WrapRotationAxis(&newRotation.y, mBufferRect.Height());
-      NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
-                   "newRotation out of bounds");
-      int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
-      int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
-      bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
-                             (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
-      if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
-          (newRotation != IntPoint(0,0) && !canHaveRotation)) {
-        // The stuff we need to redraw will wrap around an edge of the
-        // buffer (and the caller doesn't know how to support that), so
-        // move the pixels we can keep into a position that lets us
-        // redraw in just one quadrant.
-        if (mBufferRotation == IntPoint(0,0)) {
-          IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
-          IntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
-          MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid());
-          mDTBuffer->CopyRect(srcRect, dest);
-          if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-            if (!EnsureBufferOnWhite()) {
-              return result;
-            }
-            MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
-            mDTBufferOnWhite->CopyRect(srcRect, dest);
-          }
-          mDidSelfCopy = true;
-          // Don't set destBuffer; we special-case self-copies, and
-          // just did the necessary work above.
-          mBufferRect = destBufferRect;
-        } else {
-          // With azure and a data surface perform an buffer unrotate
-          // (SelfCopy).
-          unsigned char* data;
-          IntSize size;
-          int32_t stride;
-          SurfaceFormat format;
 
-          if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
-            uint8_t bytesPerPixel = BytesPerPixel(format);
-            BufferUnrotate(data,
-                           size.width * bytesPerPixel,
-                           size.height, stride,
-                           newRotation.x * bytesPerPixel, newRotation.y);
-            mDTBuffer->ReleaseBits(data);
-
-            if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-              if (!EnsureBufferOnWhite()) {
-                return result;
-              }
-              MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
-              mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
-              uint8_t bytesPerPixel = BytesPerPixel(format);
-              BufferUnrotate(data,
-                             size.width * bytesPerPixel,
-                             size.height, stride,
-                             newRotation.x * bytesPerPixel, newRotation.y);
-              mDTBufferOnWhite->ReleaseBits(data);
-            }
+    if (!AdjustTo(destBufferRect,
+                  drawBounds,
+                  canHaveRotation,
+                  canDrawRotated)) {
+      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+      CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
+                   &destDTBuffer, &destDTBufferOnWhite);
 
-            // Buffer unrotate moves all the pixels, note that
-            // we self copied for SyncBackToFrontBuffer
-            mDidSelfCopy = true;
-            mBufferRect = destBufferRect;
-            mBufferRotation = IntPoint(0, 0);
-          }
-
-          if (!mDidSelfCopy) {
-            destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-            CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
-                         &destDTBuffer, &destDTBufferOnWhite);
-            if (!destDTBuffer ||
-                (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-              if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.Width(), destBufferRect.Height()))) {
-                gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.Width() << ", " << destBufferRect.Height();
-              }
-              return result;
-            }
-          }
+      if (!destDTBuffer ||
+          (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
+        if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.Width(), destBufferRect.Height()))) {
+          gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.Width() << ", " << destBufferRect.Height();
         }
-      } else {
-        mBufferRect = destBufferRect;
-        mBufferRotation = newRotation;
+        return result;
       }
-    } else {
-      // No pixels are going to be kept. The whole visible region
-      // will be redrawn, so we don't need to copy anything, so we don't
-      // set destBuffer.
-      mBufferRect = destBufferRect;
-      mBufferRotation = IntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
     CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
                  &destDTBuffer, &destDTBufferOnWhite);
     if (!destDTBuffer ||
         (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
       if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.Width(), destBufferRect.Height()))) {
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -117,16 +117,26 @@ public:
               gfx::CompositionOp aOp,
               gfx::SourceSurface* aMask,
               const gfx::Matrix* aMaskTransform);
 
   void UpdateDestinationFrom(const RotatedBuffer& aSource,
                              const nsIntRegion& aUpdateRegion);
 
   /**
+   * Adjusts the buffer to be centered on the destination buffer rect,
+   * and ready to draw the specified bounds. Returns whether a new buffer
+   * needs to be created.
+   */
+  bool AdjustTo(const gfx::IntRect& aDestBufferRect,
+                const gfx::IntRect& aDrawBounds,
+                bool aCanHaveRotation,
+                bool aCanDrawRotated);
+
+  /**
    * |BufferRect()| is the rect of device pixels that this
    * RotatedBuffer covers.  That is what DrawBufferWithRotation()
    * will paint when it's called.
    */
   const gfx::IntRect& BufferRect() const { return mBufferRect; }
   const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
 
   virtual bool HaveBuffer() const = 0;