зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
70c0ee1ad2
Коммит
dcc0b2a16d
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче