author | Chris Jones <jones.chris.g@gmail.com> |
Fri, 03 Sep 2010 15:10:46 -0500 | |
changeset 54083 | 8321155e8dad2fb39353be91023c23f85600f215 |
parent 54082 | 3467965f4d9f4e10d7c49a596e7715f5d5ff5bdb |
child 54084 | a1526ab475c9a7f5c5e484e622bbbd2abfe43828 |
push id | 15768 |
push user | dougt@mozilla.com |
push date | Thu, 16 Sep 2010 01:40:23 +0000 |
treeherder | mozilla-central@cdb90b48f19f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc |
bugs | 590294 |
milestone | 2.0b6pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/layers/ThebesLayerBuffer.cpp +++ b/gfx/layers/ThebesLayerBuffer.cpp @@ -39,16 +39,29 @@ #include "Layers.h" #include "gfxContext.h" #include "gfxPlatform.h" #include "gfxUtils.h" 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) { // quadrantTranslation is the amount we translate the top-left // of the quadrant by to get coordinates relative to the layer nsIntPoint quadrantTranslation = -mBufferRotation; quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0; quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0; @@ -62,86 +75,118 @@ ThebesLayerBuffer::GetQuadrantRectangle( * mBufferRect). * @param aYSide TOP means we draw from the top side of the buffer (which * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from * the bottom side of the buffer (which is drawn on the top side of * mBufferRect). */ 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 // pixels in the right place, but we're only going to paint within // mBufferRect nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide); nsIntRect fillRect; if (!fillRect.IntersectRect(mBufferRect, quadrantRect)) 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(); aTarget->Paint(aOpacity); aTarget->Restore(); } else { aTarget->Fill(); } } 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 WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize) { if (*aRotationPoint < 0) { *aRotationPoint += aSize; } else if (*aRotationPoint >= aSize) { *aRotationPoint -= 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(); } if (result.mRegionToDraw.IsEmpty()) return result; 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. 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()); @@ -164,17 +209,19 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye // The stuff we need to redraw will wrap around an edge of the // buffer, so we will need to do a self-copy if (mBufferRotation == nsIntPoint(0,0)) { destBuffer = mBuffer; } else { // 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; } } else { mBufferRect = destBufferRect; mBufferRotation = newRotation; } } else { @@ -182,33 +229,38 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye // will be redrawn, so we don't need to copy anything, so we don't // set destBuffer. mBufferRect = destBufferRect; mBufferRotation = nsIntPoint(0,0); } } 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; } // If we have no buffered data already, then destBuffer will be a fresh buffer // and we do not need to clear it below. PRBool isClear = mBuffer == nsnull; if (destBuffer) { if (mBuffer) { // Copy the bits 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(); mBufferRect = destBufferRect; mBufferRotation = nsIntPoint(0,0); } nsIntRegion invalidate; @@ -219,16 +271,17 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye // Figure out which quadrant to draw in PRInt32 xBoundary = mBufferRect.XMost() - mBufferRotation.x; PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y; XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT; 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); if (aContentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) { result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR); result.mContext->Paint(); result.mContext->SetOperator(gfxContext::OPERATOR_OVER); }
--- a/gfx/layers/ThebesLayerBuffer.h +++ b/gfx/layers/ThebesLayerBuffer.h @@ -119,17 +119,18 @@ public: /** * 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. * This queries aLayer to get the currently valid and visible regions. * The returned mContext may be null if mRegionToDraw is empty. * 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|. */ virtual already_AddRefed<gfxASurface> CreateBuffer(ContentType aType, const nsIntSize& aSize) = 0; /** @@ -142,18 +143,20 @@ public: protected: enum XSide { LEFT, RIGHT }; enum YSide { 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; } already_AddRefed<gfxASurface> SetBuffer(gfxASurface* aBuffer, const nsIntRect& aBufferRect, const nsIntPoint& aBufferRotation) {
--- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -419,29 +419,36 @@ BasicThebesLayer::Paint(gfxContext* aCon target->PopGroupToSource(); target->Paint(aOpacity); } target->Restore(); return; } { - 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) { // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion); InheritContextFlags(target, state.mContext); 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. NS_ASSERTION(state.mRegionToDraw.IsEmpty(), "If we need to draw, we should have a context"); } } @@ -454,17 +461,18 @@ BasicThebesLayerBuffer::DrawTo(ThebesLay gfxContext* aTarget, float aOpacity) { aTarget->Save(); gfxUtils::ClipToRegion(aTarget, aLayer->GetVisibleRegion()); if (aIsOpaqueContent) { aTarget->SetOperator(gfxContext::OPERATOR_SOURCE); } - DrawBufferWithRotation(aTarget, aOpacity); + DrawBufferWithRotation(aTarget, aOpacity, + aLayer->GetXResolution(), aLayer->GetYResolution()); aTarget->Restore(); } already_AddRefed<gfxASurface> BasicThebesLayerBuffer::CreateBuffer(ContentType aType, const nsIntSize& aSize) { return mLayer->CreateBuffer(aType, aSize);
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp +++ b/gfx/layers/opengl/ThebesLayerOGL.cpp @@ -183,17 +183,17 @@ public: NS_ABORT_IF_FALSE(mTmpSurface, "SurfaceBuffer without backing surface??"); } virtual ~SurfaceBufferOGL() {} // ThebesLayerBufferOGL interface 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 virtual already_AddRefed<gfxASurface> CreateBuffer(ContentType aType, const nsIntSize& aSize) { NS_ASSERTION(gfxASurface::CONTENT_ALPHA != aType,"ThebesBuffer has color");
--- a/gfx/src/nsRect.cpp +++ b/gfx/src/nsRect.cpp @@ -184,22 +184,22 @@ nsMargin nsRect::operator-(const nsRect& margin.left = aRect.x - x; margin.right = XMost() - aRect.XMost(); margin.top = aRect.y - y; margin.bottom = YMost() - aRect.YMost(); return margin; } // 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; } #ifdef DEBUG // Diagnostics
--- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -175,17 +175,18 @@ struct NS_GFX nsRect { nsMargin operator-(const nsRect& aRect) const; // Find difference as nsMargin nsRect& operator+=(const nsMargin& aMargin) { Inflate(aMargin); return *this; } nsRect& operator-=(const nsMargin& aMargin) { Deflate(aMargin); return *this; } nsRect operator+(const nsMargin& aMargin) const { return nsRect(*this) += aMargin; } nsRect operator-(const nsMargin& aMargin) const { return nsRect(*this) -= aMargin; } // 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 // unrounded result. In the RoundIn version we make the rect the largest rect // contained in the unrounded result. inline nsRect ConvertAppUnitsRoundOut(PRInt32 aFromAPP, PRInt32 aToAPP) const; inline nsRect ConvertAppUnitsRoundIn(PRInt32 aFromAPP, PRInt32 aToAPP) const;
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -429,16 +429,26 @@ void nsDisplayList::PaintForFrame(nsDisp presShell->GetRootScrollFrameAsScrollable(); if (rootScrollableFrame) { metrics.mViewportScrollOffset = rootScrollableFrame->GetScrollPosition().ToNearestPixels(auPerCSSPixel); } 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, aBuilder); aBuilder->LayerBuilder()->DidEndTransaction(layerManager); if (aFlags & PAINT_FLUSH_LAYERS) { FrameLayerBuilder::InvalidateAllLayers(layerManager);