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
This commit is contained in:
Ryan Hunt 2017-10-12 18:37:29 -04:00
Родитель 962f98cab9
Коммит 273ecb795e
2 изменённых файлов: 154 добавлений и 123 удалений

Просмотреть файл

@ -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<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,
@ -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<DrawTarget> destDTBuffer;
RefPtr<DrawTarget> 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

Просмотреть файл

@ -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()