Bug 635373. Disable ThebesLayerBuffer rotation for non-identity resolutions, and add API to disable rotation explicitly. r=cjones a=beltzner

This commit is contained in:
Robert O'Callahan 2011-03-01 14:30:16 -06:00
Родитель 70c0ee1ad2
Коммит dcc0b2a16d
4 изменённых файлов: 119 добавлений и 53 удалений

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

@ -180,55 +180,96 @@ WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
ThebesLayerBuffer::PaintState
ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
float aXResolution, float aYResolution)
float aXResolution, float aYResolution,
PRUint32 aFlags)
{
PaintState result;
result.mRegionToDraw.Sub(aLayer->GetVisibleRegion(), aLayer->GetValidRegion());
float curXRes = aLayer->GetXResolution();
float curYRes = aLayer->GetYResolution();
if (mBuffer &&
(aContentType != mBuffer->GetContentType() ||
aXResolution != curXRes || aYResolution != curYRes)) {
// We're effectively clearing the valid region, so we need to draw
// the entire visible region now.
//
// XXX/cjones: a possibly worthwhile optimization to keep in mind
// is to re-use buffers when the resolution and visible region
// have changed in such a way that the buffer size stays the same.
// It might make even more sense to allocate buffers from a
// recyclable pool, so that we could keep this logic simple and
// still get back the same buffer.
result.mRegionToDraw = aLayer->GetVisibleRegion();
result.mRegionToInvalidate = aLayer->GetValidRegion();
Clear();
nsIntRegion validRegion = aLayer->GetValidRegion();
ContentType contentType;
nsIntRegion neededRegion;
nsIntSize destBufferDims;
PRBool canReuseBuffer;
nsIntRect destBufferRect;
while (PR_TRUE) {
contentType = aContentType;
neededRegion = aLayer->GetVisibleRegion();
destBufferDims = ScaledSize(neededRegion.GetBounds().Size(),
aXResolution, aYResolution);
canReuseBuffer = BufferSizeOkFor(destBufferDims);
if (canReuseBuffer) {
if (mBufferRect.Contains(neededRegion.GetBounds())) {
// We don't need to adjust mBufferRect.
destBufferRect = mBufferRect;
} else {
// The buffer's big enough but doesn't contain everything that's
// going to be visible. We'll move it.
destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
}
} else {
destBufferRect = neededRegion.GetBounds();
}
if ((aFlags & PAINT_WILL_RESAMPLE) &&
(neededRegion.GetBounds() != destBufferRect ||
neededRegion.GetNumRects() > 1)) {
// The area we add to neededRegion might not be painted opaquely
contentType = gfxASurface::CONTENT_COLOR_ALPHA;
// We need to validate the entire buffer, to make sure that only valid
// pixels are sampled
neededRegion = destBufferRect;
destBufferDims = ScaledSize(neededRegion.GetBounds().Size(),
aXResolution, aYResolution);
}
if (mBuffer &&
(contentType != mBuffer->GetContentType() ||
aXResolution != curXRes || aYResolution != curYRes)) {
// We're effectively clearing the valid region, so we need to draw
// the entire needed region now.
//
// XXX/cjones: a possibly worthwhile optimization to keep in mind
// is to re-use buffers when the resolution and visible region
// have changed in such a way that the buffer size stays the same.
// It might make even more sense to allocate buffers from a
// recyclable pool, so that we could keep this logic simple and
// still get back the same buffer.
result.mRegionToInvalidate = aLayer->GetValidRegion();
validRegion.SetEmpty();
Clear();
// Restart decision process with the cleared buffer. We can only go
// around the loop one more iteration, since mBuffer is null now.
continue;
}
break;
}
result.mRegionToDraw.Sub(neededRegion, validRegion);
if (result.mRegionToDraw.IsEmpty())
return result;
// If we have non-identity resolution then mBufferRotation might not fall
// on a buffer pixel boundary, in which case that row of pixels will contain
// a mix of two completely different rows of the layer, which would be
// a catastrophe. So disable rotation in that case.
// We also need to disable rotation if we're going to be resampled when
// drawing, because we might sample across the rotation boundary.
PRBool canHaveRotation =
!(aFlags & PAINT_WILL_RESAMPLE) && aXResolution == 1.0 && aYResolution == 1.0;
nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
nsIntRect visibleBounds = aLayer->GetVisibleRegion().GetBounds();
nsIntSize destBufferDims = ScaledSize(visibleBounds.Size(),
aXResolution, aYResolution);
nsRefPtr<gfxASurface> destBuffer;
nsIntRect destBufferRect;
PRBool bufferDimsChanged = PR_FALSE;
if (BufferSizeOkFor(destBufferDims)) {
if (canReuseBuffer) {
NS_ASSERTION(curXRes == aXResolution && curYRes == aYResolution,
"resolution changes must Clear()!");
// The current buffer is big enough to hold the visible area.
if (mBufferRect.Contains(visibleBounds)) {
// We don't need to adjust mBufferRect.
destBufferRect = mBufferRect;
} else {
// The buffer's big enough but doesn't contain everything that's
// going to be visible. We'll move it.
destBufferRect = nsIntRect(visibleBounds.TopLeft(), mBufferRect.Size());
}
nsIntRect keepArea;
if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
// Set mBufferRotation so that the pixels currently in mBuffer
@ -243,7 +284,8 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
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())) {
(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)) {
@ -251,11 +293,9 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
} else {
// We can't do a real self-copy because the buffer is rotated.
// So allocate a new buffer for the destination.
destBufferRect = visibleBounds;
destBufferDims = ScaledSize(destBufferRect.Size(),
aXResolution, aYResolution);
destBufferRect = neededRegion.GetBounds();
bufferDimsChanged = PR_TRUE;
destBuffer = CreateBuffer(aContentType, destBufferDims);
destBuffer = CreateBuffer(contentType, destBufferDims);
if (!destBuffer)
return result;
}
@ -272,14 +312,14 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
}
} else {
// The buffer's not big enough, so allocate a new one
destBufferRect = visibleBounds;
destBufferDims = ScaledSize(destBufferRect.Size(),
aXResolution, aYResolution);
destBufferRect = neededRegion.GetBounds();
bufferDimsChanged = PR_TRUE;
destBuffer = CreateBuffer(aContentType, destBufferDims);
destBuffer = CreateBuffer(contentType, destBufferDims);
if (!destBuffer)
return result;
}
NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
"If we're resampling, we need to validate the entire buffer");
// If we have no buffered data already, then destBuffer will be a fresh buffer
// and we do not need to clear it below.
@ -305,6 +345,8 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
if (bufferDimsChanged) {
mBufferDims = destBufferDims;
}
NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
"Rotation disabled, but we have nonzero rotation?");
nsIntRegion invalidate;
invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
@ -314,7 +356,7 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
aXResolution, aYResolution);
gfxUtils::ClipToRegionSnapped(result.mContext, result.mRegionToDraw);
if (aContentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) {
if (contentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) {
result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
result.mContext->Paint();
result.mContext->SetOperator(gfxContext::OPERATOR_OVER);

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

@ -118,6 +118,9 @@ public:
nsIntRegion mRegionToInvalidate;
};
enum {
PAINT_WILL_RESAMPLE = 0x01
};
/**
* Start a drawing operation. This returns a PaintState describing what
* needs to be drawn to bring the buffer up to date in the visible region.
@ -125,9 +128,18 @@ public:
* The returned mContext may be null if mRegionToDraw is empty.
* Otherwise it must not be null.
* mRegionToInvalidate will contain mRegionToDraw.
* @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
* buffer will be resampled when rendering (i.e the effective transform
* combined with the scale for the resolution is not just an integer
* translation). This will disable buffer rotation (since we don't want
* to resample across the rotation boundary) and will ensure that we
* make the entire buffer contents valid (since we don't want to sample
* invalid pixels outside the visible region, if the visible region doesn't
* fill the buffer bounds).
*/
PaintState BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
float aXResolution, float aYResolution);
float aXResolution, float aYResolution,
PRUint32 aFlags);
/**
* Return a new surface of |aSize| and |aType|.

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

@ -308,7 +308,8 @@ public:
BasicThebesLayerBuffer(BasicThebesLayer* aLayer)
: Base(ContainsVisibleBounds)
, mLayer(aLayer)
{}
{
}
virtual ~BasicThebesLayerBuffer()
{}
@ -435,11 +436,13 @@ protected:
}
aCallback(this, aContext, aRegionToDraw, aRegionToInvalidate,
aCallbackData);
// Everything that's visible has been validated. Do this instead of
// Everything that's visible has been validated. Do this instead of just
// OR-ing with aRegionToDraw, since that can lead to a very complex region
// here (OR doesn't automatically simplify to the simplest possible
// representation of a region.)
mValidRegion.Or(mValidRegion, mVisibleRegion);
nsIntRegion tmp;
tmp.Or(mVisibleRegion, aRegionToDraw);
mValidRegion.Or(mValidRegion, tmp);
}
Buffer mBuffer;
@ -577,8 +580,14 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
gfxSize scale = aContext->CurrentMatrix().ScaleFactors(PR_TRUE);
float paintXRes = BasicManager()->XResolution() * gfxUtils::ClampToScaleFactor(scale.width);
float paintYRes = BasicManager()->YResolution() * gfxUtils::ClampToScaleFactor(scale.height);
PRUint32 flags = 0;
gfxMatrix transform;
if (!GetEffectiveTransform().Is2D(&transform) ||
transform.HasNonIntegerTranslation()) {
flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
}
Buffer::PaintState state =
mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes);
mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes, flags);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
if (state.mContext) {

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

@ -304,7 +304,8 @@ public:
return ThebesLayerBuffer::BeginPaint(mLayer,
aContentType,
aXResolution,
aYResolution);
aYResolution,
0);
}
// ThebesLayerBuffer interface
@ -747,11 +748,13 @@ ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
SetAntialiasingFlags(this, state.mContext);
callback(this, state.mContext, state.mRegionToDraw,
state.mRegionToInvalidate, callbackData);
// Everything that's visible has been validated. Do this instead of
// Everything that's visible has been validated. Do this instead of just
// OR-ing with aRegionToDraw, since that can lead to a very complex region
// here (OR doesn't automatically simplify to the simplest possible
// representation of a region.)
mValidRegion.Or(mValidRegion, mVisibleRegion);
nsIntRegion tmp;
tmp.Or(mVisibleRegion, state.mRegionToDraw);
mValidRegion.Or(mValidRegion, tmp);
}
}