Bug 590294, part 8: Implement resolution-scaled drawing for basic layers. r=roc

This commit is contained in:
Chris Jones 2010-09-03 15:10:46 -05:00
Родитель f502883ffc
Коммит a2c93af9b1
7 изменённых файлов: 101 добавлений и 26 удалений

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

@ -44,6 +44,19 @@
namespace mozilla {
namespace layers {
static nsIntSize
ScaledSize(const nsIntSize& aSize, float aXScale, float aYScale)
{
if (aXScale == 1.0 && aYScale == 1.0) {
return aSize;
}
gfxRect rect(0, 0, aSize.width, aSize.height);
rect.Scale(aXScale, aYScale);
rect.RoundOut();
return nsIntSize(rect.size.width, rect.size.height);
}
nsIntRect
ThebesLayerBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide)
{
@ -67,7 +80,9 @@ ThebesLayerBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide)
*/
void
ThebesLayerBuffer::DrawBufferQuadrant(gfxContext* aTarget,
XSide aXSide, YSide aYSide, float aOpacity)
XSide aXSide, YSide aYSide,
float aOpacity,
float aXRes, float aYRes)
{
// The rectangle that we're going to fill. Basically we're going to
// render the buffer at mBufferRect + quadrantTranslation to get the
@ -79,9 +94,21 @@ ThebesLayerBuffer::DrawBufferQuadrant(gfxContext* aTarget,
return;
aTarget->NewPath();
aTarget->Rectangle(gfxRect(fillRect.x, fillRect.y, fillRect.width, fillRect.height),
aTarget->Rectangle(gfxRect(fillRect.x, fillRect.y,
fillRect.width, fillRect.height),
PR_TRUE);
aTarget->SetSource(mBuffer, gfxPoint(quadrantRect.x, quadrantRect.y));
gfxPoint quadrantTranslation(quadrantRect.x, quadrantRect.y);
nsRefPtr<gfxPattern> pattern = new gfxPattern(mBuffer);
// Transform from user -> buffer space.
gfxMatrix transform;
transform.Scale(aXRes, aYRes);
transform.Translate(-quadrantTranslation);
pattern->SetMatrix(transform);
aTarget->SetPattern(pattern);
if (aOpacity != 1.0) {
aTarget->Save();
aTarget->Clip();
@ -93,14 +120,15 @@ ThebesLayerBuffer::DrawBufferQuadrant(gfxContext* aTarget,
}
void
ThebesLayerBuffer::DrawBufferWithRotation(gfxContext* aTarget, float aOpacity)
ThebesLayerBuffer::DrawBufferWithRotation(gfxContext* aTarget, float aOpacity,
float aXRes, float aYRes)
{
// Draw four quadrants. We could use REPEAT_, but it's probably better
// not to, to be performance-safe.
DrawBufferQuadrant(aTarget, LEFT, TOP, aOpacity);
DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity);
DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity);
DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity);
DrawBufferQuadrant(aTarget, LEFT, TOP, aOpacity, aXRes, aYRes);
DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity, aXRes, aYRes);
DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity, aXRes, aYRes);
DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity, aXRes, aYRes);
}
static void
@ -114,15 +142,27 @@ WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
}
ThebesLayerBuffer::PaintState
ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
float aXResolution, float aYResolution)
{
PaintState result;
result.mRegionToDraw.Sub(aLayer->GetVisibleRegion(), aLayer->GetValidRegion());
if (mBuffer && aContentType != mBuffer->GetContentType()) {
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();
@ -133,10 +173,15 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
nsIntRect visibleBounds = aLayer->GetVisibleRegion().GetBounds();
nsIntSize scaledBufferSize = ScaledSize(visibleBounds.Size(),
aXResolution, aYResolution);
nsRefPtr<gfxASurface> destBuffer;
nsIntRect destBufferRect;
if (BufferSizeOkFor(visibleBounds.Size())) {
if (BufferSizeOkFor(scaledBufferSize)) {
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.
@ -169,7 +214,9 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
// We can't do a real self-copy because the buffer is rotated.
// So allocate a new buffer for the destination.
destBufferRect = visibleBounds;
destBuffer = CreateBuffer(aContentType, destBufferRect.Size());
destBuffer = CreateBuffer(aContentType,
ScaledSize(destBufferRect.Size(),
aXResolution, aYResolution));
if (!destBuffer)
return result;
}
@ -187,7 +234,9 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
} else {
// The buffer's not big enough, so allocate a new one
destBufferRect = visibleBounds;
destBuffer = CreateBuffer(aContentType, destBufferRect.Size());
destBuffer = CreateBuffer(aContentType,
ScaledSize(destBufferRect.Size(),
aXResolution, aYResolution));
if (!destBuffer)
return result;
}
@ -202,8 +251,11 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
nsRefPtr<gfxContext> tmpCtx = new gfxContext(destBuffer);
nsIntPoint offset = -destBufferRect.TopLeft();
tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpCtx->Scale(aXResolution, aYResolution);
tmpCtx->Translate(gfxPoint(offset.x, offset.y));
DrawBufferWithRotation(tmpCtx, 1.0);
NS_ASSERTION(curXRes == aXResolution && curYRes == aYResolution,
"resolution changes must Clear()!");
DrawBufferWithRotation(tmpCtx, 1.0, aXResolution, aYResolution);
}
mBuffer = destBuffer.forget();
@ -224,6 +276,7 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
result.mContext->Scale(aXResolution, aYResolution);
result.mContext->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
gfxUtils::ClipToRegion(result.mContext, result.mRegionToDraw);

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

@ -124,7 +124,8 @@ public:
* Otherwise it must not be null.
* mRegionToInvalidate will contain mRegionToDraw.
*/
PaintState BeginPaint(ThebesLayer* aLayer, ContentType aContentType);
PaintState BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
float aXResolution, float aYResolution);
/**
* Return a new surface of |aSize| and |aType|.
@ -147,8 +148,10 @@ protected:
TOP, BOTTOM
};
nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide);
void DrawBufferQuadrant(gfxContext* aTarget, XSide aXSide, YSide aYSide, float aOpacity);
void DrawBufferWithRotation(gfxContext* aTarget, float aOpacity);
void DrawBufferQuadrant(gfxContext* aTarget, XSide aXSide, YSide aYSide,
float aOpacity, float aXRes, float aYRes);
void DrawBufferWithRotation(gfxContext* aTarget, float aOpacity,
float aXRes, float aYRes);
const nsIntRect& BufferRect() const { return mBufferRect; }
const nsIntPoint& BufferRotation() const { return mBufferRotation; }

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

@ -424,7 +424,10 @@ BasicThebesLayer::Paint(gfxContext* aContext,
}
{
Buffer::PaintState state = mBuffer.BeginPaint(this, contentType);
float paintXRes = BasicManager()->XResolution();
float paintYRes = BasicManager()->YResolution();
Buffer::PaintState state =
mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
if (state.mContext) {
@ -437,6 +440,10 @@ BasicThebesLayer::Paint(gfxContext* aContext,
PaintBuffer(state.mContext,
state.mRegionToDraw, state.mRegionToInvalidate,
aCallback, aCallbackData);
mXResolution = paintXRes;
mYResolution = paintYRes;
Mutated();
} else {
// It's possible that state.mRegionToInvalidate is nonempty here,
// if we are shrinking the valid region to nothing.
@ -459,7 +466,8 @@ BasicThebesLayerBuffer::DrawTo(ThebesLayer* aLayer,
if (aIsOpaqueContent) {
aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
}
DrawBufferWithRotation(aTarget, aOpacity);
DrawBufferWithRotation(aTarget, aOpacity,
aLayer->GetXResolution(), aLayer->GetYResolution());
aTarget->Restore();
}

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

@ -188,7 +188,7 @@ public:
virtual PaintState BeginPaint(ContentType aContentType)
{
// Let ThebesLayerBuffer do all the hard work for us! :D
return ThebesLayerBuffer::BeginPaint(mLayer, aContentType);
return ThebesLayerBuffer::BeginPaint(mLayer, aContentType, 1.0, 1.0);
}
// ThebesLayerBuffer interface

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

@ -189,12 +189,12 @@ nsMargin nsRect::operator-(const nsRect& aRect) const
}
// scale the rect but round to smallest containing rect
nsRect& nsRect::ScaleRoundOut(float aScale)
nsRect& nsRect::ScaleRoundOut(float aXScale, float aYScale)
{
nscoord right = NSToCoordCeil(float(XMost()) * aScale);
nscoord bottom = NSToCoordCeil(float(YMost()) * aScale);
x = NSToCoordFloor(float(x) * aScale);
y = NSToCoordFloor(float(y) * aScale);
nscoord right = NSToCoordCeil(float(XMost()) * aXScale);
nscoord bottom = NSToCoordCeil(float(YMost()) * aYScale);
x = NSToCoordFloor(float(x) * aXScale);
y = NSToCoordFloor(float(y) * aYScale);
width = (right - x);
height = (bottom - y);
return *this;

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

@ -180,7 +180,8 @@ struct NS_GFX nsRect {
// Scale by aScale, converting coordinates to integers so that the result is
// the smallest integer-coordinate rectangle containing the unrounded result.
nsRect& ScaleRoundOut(float aScale);
nsRect& ScaleRoundOut(float aScale) { return ScaleRoundOut(aScale, aScale); }
nsRect& ScaleRoundOut(float aXScale, float aYScale);
// Converts this rect from aFromAPP, an appunits per pixel ratio, to aToAPP.
// In the RoundOut version we make the rect the smallest rect containing the

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

@ -434,6 +434,16 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
root->SetFrameMetrics(metrics);
// If the layer manager supports resolution scaling, set that up
if (LayerManager::LAYERS_BASIC == layerManager->GetBackendType()) {
BasicLayerManager* basicManager =
static_cast<BasicLayerManager*>(layerManager.get());
// This is free if both resolutions are 1.0, or neither resolution
// has changed since the last transaction
basicManager->SetResolution(presShell->GetXResolution(),
presShell->GetYResolution());
}
layerManager->SetRoot(root);
aBuilder->LayerBuilder()->WillEndTransaction(layerManager);
layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,