From 273ecb795e0fdf96904073737ab4ebf2b017c524 Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Thu, 12 Oct 2017 18:37:29 -0400 Subject: [PATCH] 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 --HG-- extra : rebase_source : 8ee7fb96ce8f1207b73027bb1d171dabd5b35788 --- gfx/layers/RotatedBuffer.cpp | 267 +++++++++++++++++++---------------- gfx/layers/RotatedBuffer.h | 10 ++ 2 files changed, 154 insertions(+), 123 deletions(-) diff --git a/gfx/layers/RotatedBuffer.cpp b/gfx/layers/RotatedBuffer.cpp index 2896b877b965..3fd99b509d88 100644 --- a/gfx/layers/RotatedBuffer.cpp +++ b/gfx/layers/RotatedBuffer.cpp @@ -278,6 +278,128 @@ RotatedBuffer::UpdateDestinationFrom(const RotatedBuffer& aSource, } } +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 dtBuffer = GetDTBuffer(); + RefPtr 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, @@ -530,29 +652,6 @@ 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() { @@ -569,12 +668,6 @@ 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(); @@ -703,6 +796,14 @@ RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer, } } + // 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 destDTBuffer; RefPtr destDTBufferOnWhite; @@ -711,106 +812,26 @@ RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer, 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 (!AdjustTo(destBufferRect, + drawBounds, + canHaveRotation, + canDrawRotated)) { + destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); + CreateBuffer(result.mContentType, destBufferRect, bufferFlags, + &destDTBuffer, &destDTBufferOnWhite); - 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); - } - - // 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 diff --git a/gfx/layers/RotatedBuffer.h b/gfx/layers/RotatedBuffer.h index 897c0d8eb38a..950913c8b2e2 100644 --- a/gfx/layers/RotatedBuffer.h +++ b/gfx/layers/RotatedBuffer.h @@ -121,6 +121,16 @@ public: 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()