зеркало из https://github.com/mozilla/gecko-dev.git
Bug 637852. Part 6: Implement resolution scaling in FrameLayerBuilder. r=tnikkel
FrameLayerBuilder::BuildContainerLayerFor takes responsibility for resolution scaling. The ContainerParameters passed in are added to any transform requested. Then we extract the scale part of the transform, round the scale up to the nearest power of two if the transform may be actively animated (so we don't have to redraw layer contents constantly), pass that scale down to be applied by each child and set the residual transform on the ContainerLayer. For child layers built via BuildLayer, we just pass the requested scale factor in via the ContainerParameters. If the returned layer is a ContainerLayer then BuildLayer is guaranteed to have already done necessary scaling. If the returned layer is not a ContainerLayer then we apply the scale ourselves by adding the scale to the child layer's transform. For child ThebesLayers containing non-layer display items, we scale the drawing of those display items so that the child ThebesLayers are simply larger or smaller (larger or smaller visible regions). We have to scale all visible rects, clip rects etc that are in the coordinates of ThebesLayers or the parent ContainerLayer. To keep things simple we do this whenever we convert from appunits to integer layer coordinates. When a ThebesLayer's resolution changes we need to rerender the whole thing. nsDisplayList::PaintForFrame needs to respect the presshell's resolution setting. We do that by building a layer tree with a ContainerParameters requesting a scale up by the presshell resolution; once that layer tree is built, we adjust the root layer transform to scale back down by the resolution.
This commit is contained in:
Родитель
723e5c86ee
Коммит
9ddbfbb285
|
@ -305,6 +305,16 @@ struct BaseRect {
|
||||||
width = right - x;
|
width = right - x;
|
||||||
height = bottom - y;
|
height = bottom - y;
|
||||||
}
|
}
|
||||||
|
void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
|
||||||
|
void ScaleRoundIn(double aXScale, double aYScale)
|
||||||
|
{
|
||||||
|
T right = static_cast<T>(NS_floor(double(XMost()) * aXScale));
|
||||||
|
T bottom = static_cast<T>(NS_floor(double(YMost()) * aYScale));
|
||||||
|
x = static_cast<T>(NS_ceil(double(x) * aXScale));
|
||||||
|
y = static_cast<T>(NS_ceil(double(y) * aYScale));
|
||||||
|
width = NS_MAX<T>(0, right - x);
|
||||||
|
height = NS_MAX<T>(0, bottom - y);
|
||||||
|
}
|
||||||
void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
|
void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
|
||||||
void ScaleInverseRoundOut(double aXScale, double aYScale)
|
void ScaleInverseRoundOut(double aXScale, double aYScale)
|
||||||
{
|
{
|
||||||
|
|
|
@ -119,7 +119,8 @@ public:
|
||||||
static PRBool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
|
static PRBool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamp aVal to a power of kScaleResolution.
|
* Return the smallest power of kScaleResolution (2) greater than or equal to
|
||||||
|
* aVal.
|
||||||
*/
|
*/
|
||||||
static gfxFloat ClampToScaleFactor(gfxFloat aVal);
|
static gfxFloat ClampToScaleFactor(gfxFloat aVal);
|
||||||
};
|
};
|
||||||
|
|
|
@ -134,9 +134,11 @@ public:
|
||||||
ContainerState(nsDisplayListBuilder* aBuilder,
|
ContainerState(nsDisplayListBuilder* aBuilder,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
nsIFrame* aContainerFrame,
|
nsIFrame* aContainerFrame,
|
||||||
ContainerLayer* aContainerLayer) :
|
ContainerLayer* aContainerLayer,
|
||||||
|
const FrameLayerBuilder::ContainerParameters& aParameters) :
|
||||||
mBuilder(aBuilder), mManager(aManager),
|
mBuilder(aBuilder), mManager(aManager),
|
||||||
mContainerFrame(aContainerFrame), mContainerLayer(aContainerLayer),
|
mContainerFrame(aContainerFrame), mContainerLayer(aContainerLayer),
|
||||||
|
mParameters(aParameters),
|
||||||
mNextFreeRecycledThebesLayer(0), mNextFreeRecycledColorLayer(0),
|
mNextFreeRecycledThebesLayer(0), mNextFreeRecycledColorLayer(0),
|
||||||
mNextFreeRecycledImageLayer(0), mInvalidateAllThebesContent(PR_FALSE)
|
mNextFreeRecycledImageLayer(0), mInvalidateAllThebesContent(PR_FALSE)
|
||||||
{
|
{
|
||||||
|
@ -202,7 +204,7 @@ protected:
|
||||||
* @param aSolidColor if non-null, the visible area of the item is
|
* @param aSolidColor if non-null, the visible area of the item is
|
||||||
* a constant color given by *aSolidColor
|
* a constant color given by *aSolidColor
|
||||||
*/
|
*/
|
||||||
void Accumulate(nsDisplayListBuilder* aBuilder,
|
void Accumulate(ContainerState* aState,
|
||||||
nsDisplayItem* aItem,
|
nsDisplayItem* aItem,
|
||||||
const nsIntRect& aVisibleRect,
|
const nsIntRect& aVisibleRect,
|
||||||
const nsIntRect& aDrawRect,
|
const nsIntRect& aDrawRect,
|
||||||
|
@ -288,6 +290,7 @@ protected:
|
||||||
*/
|
*/
|
||||||
FrameLayerBuilder::Clip mImageClip;
|
FrameLayerBuilder::Clip mImageClip;
|
||||||
};
|
};
|
||||||
|
friend class ThebesLayerData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grab the next recyclable ThebesLayer, or create one if there are no
|
* Grab the next recyclable ThebesLayer, or create one if there are no
|
||||||
|
@ -362,6 +365,7 @@ protected:
|
||||||
LayerManager* mManager;
|
LayerManager* mManager;
|
||||||
nsIFrame* mContainerFrame;
|
nsIFrame* mContainerFrame;
|
||||||
ContainerLayer* mContainerLayer;
|
ContainerLayer* mContainerLayer;
|
||||||
|
FrameLayerBuilder::ContainerParameters mParameters;
|
||||||
/**
|
/**
|
||||||
* The region of ThebesLayers that should be invalidated every time
|
* The region of ThebesLayers that should be invalidated every time
|
||||||
* we recycle one.
|
* we recycle one.
|
||||||
|
@ -396,6 +400,10 @@ public:
|
||||||
* region before any other content is painted.
|
* region before any other content is painted.
|
||||||
*/
|
*/
|
||||||
nscolor mForcedBackgroundColor;
|
nscolor mForcedBackgroundColor;
|
||||||
|
/**
|
||||||
|
* The resolution scale used.
|
||||||
|
*/
|
||||||
|
float mXScale, mYScale;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -792,6 +800,7 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||||
{
|
{
|
||||||
// We need a new thebes layer
|
// We need a new thebes layer
|
||||||
nsRefPtr<ThebesLayer> layer;
|
nsRefPtr<ThebesLayer> layer;
|
||||||
|
ThebesDisplayItemLayerUserData* data;
|
||||||
if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
|
if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
|
||||||
// Recycle a layer
|
// Recycle a layer
|
||||||
layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
|
layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
|
||||||
|
@ -800,6 +809,10 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||||
// reapply any necessary clipping.
|
// reapply any necessary clipping.
|
||||||
layer->SetClipRect(nsnull);
|
layer->SetClipRect(nsnull);
|
||||||
|
|
||||||
|
data = static_cast<ThebesDisplayItemLayerUserData*>
|
||||||
|
(layer->GetUserData(&gThebesDisplayItemLayerUserData));
|
||||||
|
NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
|
||||||
|
|
||||||
// This gets called on recycled ThebesLayers that are going to be in the
|
// This gets called on recycled ThebesLayers that are going to be in the
|
||||||
// final layer tree, so it's a convenient time to invalidate the
|
// final layer tree, so it's a convenient time to invalidate the
|
||||||
// content that changed where we don't know what ThebesLayer it belonged
|
// content that changed where we don't know what ThebesLayer it belonged
|
||||||
|
@ -808,7 +821,9 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||||
// transform. See nsGfxScrollFrame::InvalidateInternal, where
|
// transform. See nsGfxScrollFrame::InvalidateInternal, where
|
||||||
// we ensure that mInvalidThebesContent is updated according to the
|
// we ensure that mInvalidThebesContent is updated according to the
|
||||||
// scroll position as of the most recent paint.
|
// scroll position as of the most recent paint.
|
||||||
if (mInvalidateAllThebesContent) {
|
if (mInvalidateAllThebesContent ||
|
||||||
|
data->mXScale != mParameters.mXScale ||
|
||||||
|
data->mYScale != mParameters.mYScale) {
|
||||||
nsIntRect invalidate = layer->GetValidRegion().GetBounds();
|
nsIntRect invalidate = layer->GetValidRegion().GetBounds();
|
||||||
layer->InvalidateRegion(invalidate);
|
layer->InvalidateRegion(invalidate);
|
||||||
} else {
|
} else {
|
||||||
|
@ -824,16 +839,22 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||||
if (!layer)
|
if (!layer)
|
||||||
return nsnull;
|
return nsnull;
|
||||||
// Mark this layer as being used for Thebes-painting display items
|
// Mark this layer as being used for Thebes-painting display items
|
||||||
layer->SetUserData(&gThebesDisplayItemLayerUserData,
|
data = new ThebesDisplayItemLayerUserData();
|
||||||
new ThebesDisplayItemLayerUserData());
|
layer->SetUserData(&gThebesDisplayItemLayerUserData, data);
|
||||||
}
|
}
|
||||||
|
data->mXScale = mParameters.mXScale;
|
||||||
|
data->mYScale = mParameters.mYScale;
|
||||||
|
|
||||||
mBuilder->LayerBuilder()->SaveLastPaintOffset(layer);
|
mBuilder->LayerBuilder()->SaveLastPaintOffset(layer);
|
||||||
|
|
||||||
// Set up transform so that 0,0 in the Thebes layer corresponds to the
|
// Set up transform so that 0,0 in the Thebes layer corresponds to the
|
||||||
// (pixel-snapped) top-left of the aActiveScrolledRoot.
|
// (pixel-snapped) top-left of the aActiveScrolledRoot.
|
||||||
|
// XXX if the transform has changed, and the difference between the old and
|
||||||
|
// new offsets (not transforms!) is not an integer number of pixels after
|
||||||
|
// scaling, we need to invalidate the entire layer.
|
||||||
nsPoint offset = mBuilder->ToReferenceFrame(aActiveScrolledRoot);
|
nsPoint offset = mBuilder->ToReferenceFrame(aActiveScrolledRoot);
|
||||||
nsIntPoint pixOffset = offset.ToNearestPixels(
|
nsIntPoint pixOffset = offset.ScaleToNearestPixels(
|
||||||
|
mParameters.mXScale, mParameters.mYScale,
|
||||||
aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel());
|
aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel());
|
||||||
gfxMatrix matrix;
|
gfxMatrix matrix;
|
||||||
matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
|
matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
|
||||||
|
@ -921,6 +942,7 @@ ContainerState::FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex)
|
||||||
nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
||||||
nsRect rect =
|
nsRect rect =
|
||||||
target->mVisibleRegion.GetBounds().ToAppUnits(appUnitsPerDevPixel);
|
target->mVisibleRegion.GetBounds().ToAppUnits(appUnitsPerDevPixel);
|
||||||
|
rect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
|
||||||
return mBuilder->LayerBuilder()->
|
return mBuilder->LayerBuilder()->
|
||||||
FindOpaqueColorCovering(mBuilder, candidate->mLayer, rect);
|
FindOpaqueColorCovering(mBuilder, candidate->mLayer, rect);
|
||||||
}
|
}
|
||||||
|
@ -960,9 +982,9 @@ ContainerState::PopThebesLayerData()
|
||||||
if (data->mImageClip.mHaveClipRect) {
|
if (data->mImageClip.mHaveClipRect) {
|
||||||
nsPresContext* presContext = mContainerFrame->PresContext();
|
nsPresContext* presContext = mContainerFrame->PresContext();
|
||||||
nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
||||||
nsIntRect clip = data->mImageClip.mClipRect.ToNearestPixels(appUnitsPerDevPixel);
|
nsIntRect clip = data->mImageClip.mClipRect.ScaleToNearestPixels(
|
||||||
imageLayer->IntersectClipRect(
|
mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
|
||||||
data->mImageClip.mClipRect.ToNearestPixels(appUnitsPerDevPixel));
|
imageLayer->IntersectClipRect(clip);
|
||||||
}
|
}
|
||||||
layer = imageLayer;
|
layer = imageLayer;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1101,20 +1123,22 @@ WindowHasTransparency(nsDisplayListBuilder* aBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder,
|
ContainerState::ThebesLayerData::Accumulate(ContainerState* aState,
|
||||||
nsDisplayItem* aItem,
|
nsDisplayItem* aItem,
|
||||||
const nsIntRect& aVisibleRect,
|
const nsIntRect& aVisibleRect,
|
||||||
const nsIntRect& aDrawRect,
|
const nsIntRect& aDrawRect,
|
||||||
const FrameLayerBuilder::Clip& aClip)
|
const FrameLayerBuilder::Clip& aClip)
|
||||||
{
|
{
|
||||||
nscolor uniformColor;
|
nscolor uniformColor;
|
||||||
PRBool isUniform = aItem->IsUniform(aBuilder, &uniformColor);
|
PRBool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor);
|
||||||
// Some display items have to exist (so they can set forceTransparentSurface
|
// Some display items have to exist (so they can set forceTransparentSurface
|
||||||
// below) but don't draw anything. They'll return true for isUniform but
|
// below) but don't draw anything. They'll return true for isUniform but
|
||||||
// a color with opacity 0.
|
// a color with opacity 0.
|
||||||
if (!isUniform || NS_GET_A(uniformColor) > 0) {
|
if (!isUniform || NS_GET_A(uniformColor) > 0) {
|
||||||
if (isUniform &&
|
if (isUniform &&
|
||||||
aItem->GetBounds(aBuilder).ToInsidePixels(AppUnitsPerDevPixel(aItem)).Contains(aVisibleRect)) {
|
aItem->GetBounds(aState->mBuilder).ScaleToInsidePixels(
|
||||||
|
aState->mParameters.mXScale, aState->mParameters.mYScale,
|
||||||
|
AppUnitsPerDevPixel(aItem)).Contains(aVisibleRect)) {
|
||||||
if (mVisibleRegion.IsEmpty()) {
|
if (mVisibleRegion.IsEmpty()) {
|
||||||
// This color is all we have
|
// This color is all we have
|
||||||
mSolidColor = uniformColor;
|
mSolidColor = uniformColor;
|
||||||
|
@ -1147,7 +1171,7 @@ ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
PRBool forceTransparentSurface = PR_FALSE;
|
PRBool forceTransparentSurface = PR_FALSE;
|
||||||
nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &forceTransparentSurface);
|
nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &forceTransparentSurface);
|
||||||
if (!opaque.IsEmpty()) {
|
if (!opaque.IsEmpty()) {
|
||||||
nsRegionRectIterator iter(opaque);
|
nsRegionRectIterator iter(opaque);
|
||||||
nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
||||||
|
@ -1157,25 +1181,28 @@ ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder,
|
||||||
// is a large opaque background at the bottom of z-order (e.g.,
|
// is a large opaque background at the bottom of z-order (e.g.,
|
||||||
// a canvas background), so we need to make sure that the first rect
|
// a canvas background), so we need to make sure that the first rect
|
||||||
// we see doesn't get discarded.
|
// we see doesn't get discarded.
|
||||||
nsIntRect rect = aClip.ApproximateIntersect(*r).ToInsidePixels(appUnitsPerDevPixel);
|
nsIntRect rect = aClip.ApproximateIntersect(*r).ScaleToInsidePixels(
|
||||||
|
aState->mParameters.mXScale, aState->mParameters.mYScale,
|
||||||
|
appUnitsPerDevPixel);
|
||||||
nsIntRegion tmp;
|
nsIntRegion tmp;
|
||||||
tmp.Or(mOpaqueRegion, rect);
|
tmp.Or(mOpaqueRegion, rect);
|
||||||
// Opaque display items in chrome documents whose window is partially
|
// Opaque display items in chrome documents whose window is partially
|
||||||
// transparent are always added to the opaque region. This helps ensure
|
// transparent are always added to the opaque region. This helps ensure
|
||||||
// that we get as much subpixel-AA as possible in the chrome.
|
// that we get as much subpixel-AA as possible in the chrome.
|
||||||
if (tmp.GetNumRects() <= 4 ||
|
if (tmp.GetNumRects() <= 4 ||
|
||||||
(WindowHasTransparency(aBuilder) &&
|
(WindowHasTransparency(aState->mBuilder) &&
|
||||||
aItem->GetUnderlyingFrame()->PresContext()->IsChrome())) {
|
aItem->GetUnderlyingFrame()->PresContext()->IsChrome())) {
|
||||||
mOpaqueRegion = tmp;
|
mOpaqueRegion = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nsRect componentAlpha = aItem->GetComponentAlphaBounds(aBuilder);
|
nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder);
|
||||||
componentAlpha.IntersectRect(componentAlpha, aItem->GetVisibleRect());
|
componentAlpha.IntersectRect(componentAlpha, aItem->GetVisibleRect());
|
||||||
if (!componentAlpha.IsEmpty()) {
|
if (!componentAlpha.IsEmpty()) {
|
||||||
nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
||||||
if (!mOpaqueRegion.Contains(componentAlpha.ToOutsidePixels(appUnitsPerDevPixel))) {
|
if (!mOpaqueRegion.Contains(componentAlpha.ScaleToOutsidePixels(
|
||||||
if (SuppressComponentAlpha(aBuilder, aItem, componentAlpha)) {
|
aState->mParameters.mXScale, aState->mParameters.mYScale, appUnitsPerDevPixel))) {
|
||||||
|
if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) {
|
||||||
aItem->DisableComponentAlpha();
|
aItem->DisableComponentAlpha();
|
||||||
} else {
|
} else {
|
||||||
mNeedComponentAlpha = PR_TRUE;
|
mNeedComponentAlpha = PR_TRUE;
|
||||||
|
@ -1244,7 +1271,7 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
|
||||||
layer = thebesLayerData->mLayer;
|
layer = thebesLayerData->mLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
thebesLayerData->Accumulate(mBuilder, aItem, aVisibleRect, aDrawRect, aClip);
|
thebesLayerData->Accumulate(this, aItem, aVisibleRect, aDrawRect, aClip);
|
||||||
return layer.forget();
|
return layer.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1309,13 +1336,15 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||||
"items in a container layer should all have the same app units per dev pixel");
|
"items in a container layer should all have the same app units per dev pixel");
|
||||||
|
|
||||||
nsIntRect itemVisibleRect =
|
nsIntRect itemVisibleRect =
|
||||||
item->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
|
item->GetVisibleRect().ScaleToOutsidePixels(
|
||||||
|
mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
|
||||||
nsRect itemContent = item->GetBounds(mBuilder);
|
nsRect itemContent = item->GetBounds(mBuilder);
|
||||||
if (aClip.mHaveClipRect) {
|
if (aClip.mHaveClipRect) {
|
||||||
itemContent.IntersectRect(aClip.mClipRect, itemContent);
|
itemContent.IntersectRect(aClip.mClipRect, itemContent);
|
||||||
}
|
}
|
||||||
mBounds.UnionRect(mBounds, itemContent);
|
mBounds.UnionRect(mBounds, itemContent);
|
||||||
nsIntRect itemDrawRect = itemContent.ToOutsidePixels(appUnitsPerDevPixel);
|
nsIntRect itemDrawRect = itemContent.ScaleToOutsidePixels(
|
||||||
|
mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
|
||||||
LayerState layerState = item->GetLayerState(mBuilder, mManager);
|
LayerState layerState = item->GetLayerState(mBuilder, mManager);
|
||||||
|
|
||||||
nsIFrame* activeScrolledRoot =
|
nsIFrame* activeScrolledRoot =
|
||||||
|
@ -1347,13 +1376,21 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||||
aClip.RemoveRoundedCorners();
|
aClip.RemoveRoundedCorners();
|
||||||
|
|
||||||
// Just use its layer.
|
// Just use its layer.
|
||||||
nsRefPtr<Layer> ownLayer =
|
nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
|
||||||
item->BuildLayer(mBuilder, mManager, FrameLayerBuilder::ContainerParameters());
|
|
||||||
if (!ownLayer) {
|
if (!ownLayer) {
|
||||||
InvalidateForLayerChange(item, ownLayer);
|
InvalidateForLayerChange(item, ownLayer);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's not a ContainerLayer, we need to apply the scale transform
|
||||||
|
// ourselves.
|
||||||
|
if (!ownLayer->AsContainerLayer()) {
|
||||||
|
// The layer's current transform is applied first, then the result is scaled.
|
||||||
|
gfx3DMatrix transform = ownLayer->GetTransform()*
|
||||||
|
gfx3DMatrix::Scale(mParameters.mXScale, mParameters.mYScale, 1.0f);
|
||||||
|
ownLayer->SetTransform(transform);
|
||||||
|
}
|
||||||
|
|
||||||
ownLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling(
|
ownLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling(
|
||||||
activeScrolledRoot, mBuilder));
|
activeScrolledRoot, mBuilder));
|
||||||
|
|
||||||
|
@ -1364,7 +1401,8 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||||
// It has its own layer. Update that layer's clip and visible rects.
|
// It has its own layer. Update that layer's clip and visible rects.
|
||||||
if (aClip.mHaveClipRect) {
|
if (aClip.mHaveClipRect) {
|
||||||
ownLayer->IntersectClipRect(
|
ownLayer->IntersectClipRect(
|
||||||
aClip.mClipRect.ToNearestPixels(appUnitsPerDevPixel));
|
aClip.mClipRect.ScaleToNearestPixels(
|
||||||
|
mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel));
|
||||||
}
|
}
|
||||||
ThebesLayerData* data = GetTopThebesLayerData();
|
ThebesLayerData* data = GetTopThebesLayerData();
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -1424,17 +1462,25 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer)
|
||||||
// in the container via regular frame invalidation.
|
// in the container via regular frame invalidation.
|
||||||
nsRect bounds = aItem->GetBounds(mBuilder);
|
nsRect bounds = aItem->GetBounds(mBuilder);
|
||||||
PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
|
||||||
nsIntRect r = bounds.ToOutsidePixels(appUnitsPerDevPixel);
|
|
||||||
|
|
||||||
ThebesLayer* t = oldLayer->AsThebesLayer();
|
ThebesLayer* t = oldLayer->AsThebesLayer();
|
||||||
if (t) {
|
if (t) {
|
||||||
InvalidatePostTransformRegion(t, r,
|
ThebesDisplayItemLayerUserData* data =
|
||||||
|
static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
|
||||||
|
// Note that whenever the layer's scale changes, we invalidate the whole thing,
|
||||||
|
// so it doesn't matter whether we are using the old scale at last paint
|
||||||
|
// or a new scale here
|
||||||
|
InvalidatePostTransformRegion(t,
|
||||||
|
bounds.ScaleToOutsidePixels(data->mXScale, data->mYScale, appUnitsPerDevPixel),
|
||||||
mBuilder->LayerBuilder()->GetLastPaintOffset(t));
|
mBuilder->LayerBuilder()->GetLastPaintOffset(t));
|
||||||
}
|
}
|
||||||
if (aNewLayer) {
|
if (aNewLayer) {
|
||||||
ThebesLayer* newLayer = aNewLayer->AsThebesLayer();
|
ThebesLayer* newLayer = aNewLayer->AsThebesLayer();
|
||||||
if (newLayer) {
|
if (newLayer) {
|
||||||
InvalidatePostTransformRegion(newLayer, r,
|
ThebesDisplayItemLayerUserData* data =
|
||||||
|
static_cast<ThebesDisplayItemLayerUserData*>(newLayer->GetUserData(&gThebesDisplayItemLayerUserData));
|
||||||
|
InvalidatePostTransformRegion(newLayer,
|
||||||
|
bounds.ScaleToOutsidePixels(data->mXScale, data->mYScale, appUnitsPerDevPixel),
|
||||||
GetTranslationForThebesLayer(newLayer));
|
GetTranslationForThebesLayer(newLayer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1598,6 +1644,56 @@ ContainerState::Finish(PRUint32* aTextContentFlags)
|
||||||
*aTextContentFlags = textContentFlags;
|
*aTextContentFlags = textContentFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FrameLayerBuilder::ContainerParameters
|
||||||
|
ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
|
||||||
|
nsIFrame* aContainerFrame,
|
||||||
|
const gfx3DMatrix* aTransform,
|
||||||
|
const FrameLayerBuilder::ContainerParameters& aIncomingScale,
|
||||||
|
ContainerLayer* aLayer)
|
||||||
|
{
|
||||||
|
gfx3DMatrix transform =
|
||||||
|
gfx3DMatrix::Scale(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
|
||||||
|
if (aTransform) {
|
||||||
|
// aTransform is applied first, then the scale is applied to the result
|
||||||
|
transform = (*aTransform)*transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfxMatrix transform2d;
|
||||||
|
gfxSize scale;
|
||||||
|
// Only fiddle with scale factors for the retaining layer manager, since
|
||||||
|
// it only matters for retained layers
|
||||||
|
if (aLayerBuilder->GetRetainingLayerManager() == aLayer->Manager() &&
|
||||||
|
transform.Is2D(&transform2d)) {
|
||||||
|
//Scale factors are normalized to a power of 2 to reduce the number of resolution changes
|
||||||
|
scale = transform2d.ScaleFactors(PR_TRUE);
|
||||||
|
// For frames that aren't marked active (i.e. opacity/transform not
|
||||||
|
// changed recently), let them take their exact resolution. Otherwise
|
||||||
|
// round up to nearest power-of-2 boundary so that we don't keep having to
|
||||||
|
// redraw the content as it scales up and down. Rounding up to nearest
|
||||||
|
// power-of-2 boundary ensures we never scale up, only down --- avoiding
|
||||||
|
// jaggies. It also ensures we never scale down by more than a factor of 2,
|
||||||
|
// avoiding bad downscaling quality.
|
||||||
|
if (aContainerFrame->AreLayersMarkedActive()) {
|
||||||
|
scale.width = gfxUtils::ClampToScaleFactor(scale.width);
|
||||||
|
scale.height = gfxUtils::ClampToScaleFactor(scale.height);
|
||||||
|
} else {
|
||||||
|
// XXX Do we need to move nearly-integer values to integers here?
|
||||||
|
}
|
||||||
|
// If the scale factors are too small, just use 1.0. The content is being
|
||||||
|
// scaled out of sight anyway.
|
||||||
|
if (abs(scale.width) < 1e-8 || abs(scale.height) < 1e-8) {
|
||||||
|
scale.width = scale.height = 1.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scale = gfxSize(1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the inverse of our resolution-scale before the rest of our transform
|
||||||
|
transform = gfx3DMatrix::Scale(1.0/scale.width, 1.0/scale.height, 1.0)*transform;
|
||||||
|
aLayer->SetTransform(transform);
|
||||||
|
return FrameLayerBuilder::ContainerParameters(scale.width, scale.height);
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<ContainerLayer>
|
already_AddRefed<ContainerLayer>
|
||||||
FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
|
FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
|
@ -1650,7 +1746,11 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
|
||||||
return containerLayer.forget();
|
return containerLayer.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerState state(aBuilder, aManager, aContainerFrame, containerLayer);
|
ContainerParameters scaleParameters =
|
||||||
|
ChooseScaleAndSetTransform(this, aContainerFrame, aTransform, aParameters,
|
||||||
|
containerLayer);
|
||||||
|
ContainerState state(aBuilder, aManager, aContainerFrame, containerLayer,
|
||||||
|
scaleParameters);
|
||||||
nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
|
nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
|
||||||
|
|
||||||
if (aManager == mRetainingManager) {
|
if (aManager == mRetainingManager) {
|
||||||
|
@ -1670,7 +1770,8 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
|
||||||
nsPoint offset = offsetAtLastPaint ? *offsetAtLastPaint : currentOffset;
|
nsPoint offset = offsetAtLastPaint ? *offsetAtLastPaint : currentOffset;
|
||||||
invalidThebesContent->MoveBy(offset);
|
invalidThebesContent->MoveBy(offset);
|
||||||
state.SetInvalidThebesContent(invalidThebesContent->
|
state.SetInvalidThebesContent(invalidThebesContent->
|
||||||
ToOutsidePixels(appUnitsPerDevPixel));
|
ScaleToOutsidePixels(scaleParameters.mXScale, scaleParameters.mYScale,
|
||||||
|
appUnitsPerDevPixel));
|
||||||
// We have to preserve the current contents of invalidThebesContent
|
// We have to preserve the current contents of invalidThebesContent
|
||||||
// because there might be multiple container layers for the same
|
// because there might be multiple container layers for the same
|
||||||
// frame and we need to invalidate the ThebesLayer children of all
|
// frame and we need to invalidate the ThebesLayer children of all
|
||||||
|
@ -1695,20 +1796,21 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
|
||||||
|
|
||||||
nsRect bounds = state.GetChildrenBounds();
|
nsRect bounds = state.GetChildrenBounds();
|
||||||
NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
|
NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
|
||||||
nsIntRect pixBounds = bounds.ToOutsidePixels(appUnitsPerDevPixel);
|
nsIntRect pixBounds =
|
||||||
|
bounds.ScaleToOutsidePixels(scaleParameters.mXScale, scaleParameters.mYScale,
|
||||||
|
appUnitsPerDevPixel);
|
||||||
containerLayer->SetVisibleRegion(pixBounds);
|
containerLayer->SetVisibleRegion(pixBounds);
|
||||||
// Make sure that rounding the visible region out didn't add any area
|
// Make sure that rounding the visible region out didn't add any area
|
||||||
// we won't paint
|
// we won't paint
|
||||||
if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface() &&
|
if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
|
||||||
bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
|
bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
|
||||||
|
if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
|
||||||
// Clear CONTENT_COMPONENT_ALPHA
|
// Clear CONTENT_COMPONENT_ALPHA
|
||||||
flags = Layer::CONTENT_OPAQUE;
|
flags = Layer::CONTENT_OPAQUE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
containerLayer->SetContentFlags(flags);
|
containerLayer->SetContentFlags(flags);
|
||||||
|
|
||||||
if (aTransform) {
|
|
||||||
containerLayer->SetTransform(*aTransform);
|
|
||||||
}
|
|
||||||
return containerLayer.forget();
|
return containerLayer.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1871,12 +1973,14 @@ FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
|
||||||
// ThebesLayer
|
// ThebesLayer
|
||||||
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
|
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
|
||||||
nsIntPoint offset = GetTranslationForThebesLayer(aLayer);
|
nsIntPoint offset = GetTranslationForThebesLayer(aLayer);
|
||||||
|
aContext->Scale(userData->mXScale, userData->mYScale);
|
||||||
aContext->Translate(-gfxPoint(offset.x, offset.y));
|
aContext->Translate(-gfxPoint(offset.x, offset.y));
|
||||||
|
|
||||||
nsPresContext* presContext = containerLayerFrame->PresContext();
|
nsPresContext* presContext = containerLayerFrame->PresContext();
|
||||||
PRInt32 appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
PRInt32 appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
||||||
nsRect r = (aRegionToInvalidate.GetBounds() + offset).
|
nsRect r = (aRegionToInvalidate.GetBounds() + offset).
|
||||||
ToAppUnits(appUnitsPerDevPixel);
|
ToAppUnits(appUnitsPerDevPixel);
|
||||||
|
r.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
|
||||||
containerLayerFrame->InvalidateWithFlags(r,
|
containerLayerFrame->InvalidateWithFlags(r,
|
||||||
nsIFrame::INVALIDATE_NO_THEBES_LAYERS |
|
nsIFrame::INVALIDATE_NO_THEBES_LAYERS |
|
||||||
nsIFrame::INVALIDATE_EXCLUDE_CURRENT_PAINT);
|
nsIFrame::INVALIDATE_EXCLUDE_CURRENT_PAINT);
|
||||||
|
@ -1890,6 +1994,7 @@ FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
|
||||||
nsRegion visible = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
|
nsRegion visible = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
|
||||||
visible.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
|
visible.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
|
||||||
NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
|
NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
|
||||||
|
visible.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
|
||||||
|
|
||||||
for (i = items.Length(); i > 0; --i) {
|
for (i = items.Length(); i > 0; --i) {
|
||||||
ClippedDisplayItem* cdi = &items[i - 1];
|
ClippedDisplayItem* cdi = &items[i - 1];
|
||||||
|
|
|
@ -93,6 +93,15 @@ enum LayerState {
|
||||||
* corresponds to the (pixel-snapped) top-left of the aActiveScrolledRoot.
|
* corresponds to the (pixel-snapped) top-left of the aActiveScrolledRoot.
|
||||||
* It sets up ContainerLayers so that 0,0 in the container layer
|
* It sets up ContainerLayers so that 0,0 in the container layer
|
||||||
* corresponds to the snapped top-left of the display list reference frame.
|
* corresponds to the snapped top-left of the display list reference frame.
|
||||||
|
*
|
||||||
|
* When we construct a container layer, we know the transform that will be
|
||||||
|
* applied to the layer. If the transform scales the content, we can get
|
||||||
|
* better results when intermediate buffers are used by pushing some scale
|
||||||
|
* from the container's transform down to the children. For ThebesLayer
|
||||||
|
* children, the scaling can be achieved by changing the size of the layer
|
||||||
|
* and drawing into it with increased or decreased resolution. By convention,
|
||||||
|
* integer types (nsIntPoint/nsIntSize/nsIntRect/nsIntRegion) are all in layer
|
||||||
|
* coordinates, post-scaling, whereas appunit types are all pre-scaling.
|
||||||
*/
|
*/
|
||||||
class FrameLayerBuilder {
|
class FrameLayerBuilder {
|
||||||
public:
|
public:
|
||||||
|
@ -134,7 +143,9 @@ public:
|
||||||
|
|
||||||
struct ContainerParameters {
|
struct ContainerParameters {
|
||||||
ContainerParameters() : mXScale(1), mYScale(1) {}
|
ContainerParameters() : mXScale(1), mYScale(1) {}
|
||||||
double mXScale, mYScale;
|
ContainerParameters(float aXScale, float aYScale) :
|
||||||
|
mXScale(aXScale), mYScale(aYScale) {}
|
||||||
|
float mXScale, mYScale;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Build a container layer for a display item that contains a child
|
* Build a container layer for a display item that contains a child
|
||||||
|
|
|
@ -589,15 +589,22 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
|
||||||
aBuilder->LayerBuilder()->DidBeginRetainedLayerTransaction(layerManager);
|
aBuilder->LayerBuilder()->DidBeginRetainedLayerTransaction(layerManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRefPtr<ContainerLayer> root = aBuilder->LayerBuilder()->
|
|
||||||
BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nsnull, *this,
|
|
||||||
nsDisplayItem::ContainerParameters(), nsnull);
|
|
||||||
if (!root)
|
|
||||||
return;
|
|
||||||
|
|
||||||
nsPresContext* presContext = aForFrame->PresContext();
|
nsPresContext* presContext = aForFrame->PresContext();
|
||||||
nsIPresShell* presShell = presContext->GetPresShell();
|
nsIPresShell* presShell = presContext->GetPresShell();
|
||||||
|
|
||||||
|
nsDisplayItem::ContainerParameters containerParameters
|
||||||
|
(presShell->GetXResolution(), presShell->GetYResolution());
|
||||||
|
nsRefPtr<ContainerLayer> root = aBuilder->LayerBuilder()->
|
||||||
|
BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nsnull, *this,
|
||||||
|
containerParameters, nsnull);
|
||||||
|
if (!root)
|
||||||
|
return;
|
||||||
|
// Root is being scaled up by the X/Y resolution. Scale it back down.
|
||||||
|
gfx3DMatrix rootTransform = root->GetTransform()*
|
||||||
|
gfx3DMatrix::Scale(1.0f/containerParameters.mXScale,
|
||||||
|
1.0f/containerParameters.mYScale, 1.0f);
|
||||||
|
root->SetTransform(rootTransform);
|
||||||
|
|
||||||
ViewID id = presContext->IsRootContentDocument() ? FrameMetrics::ROOT_SCROLL_ID
|
ViewID id = presContext->IsRootContentDocument() ? FrameMetrics::ROOT_SCROLL_ID
|
||||||
: FrameMetrics::NULL_SCROLL_ID;
|
: FrameMetrics::NULL_SCROLL_ID;
|
||||||
|
|
||||||
|
@ -614,16 +621,6 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
|
||||||
root, mVisibleRect, mVisibleRect,
|
root, mVisibleRect, mVisibleRect,
|
||||||
(usingDisplayport ? &displayport : nsnull), id);
|
(usingDisplayport ? &displayport : nsnull), id);
|
||||||
|
|
||||||
// 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);
|
layerManager->SetRoot(root);
|
||||||
aBuilder->LayerBuilder()->WillEndTransaction(layerManager);
|
aBuilder->LayerBuilder()->WillEndTransaction(layerManager);
|
||||||
layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
|
layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче