Bug 637852. Part 22: Detect when the contents of a ThebesLayer have shifted by a subpixel amount and repaint the entire layer when that happens. r=tnikkel

This fixes artifacts when we're scrolling inside scaled content, and includes a test for that (which fails without
this code change).
This commit is contained in:
Robert O'Callahan 2011-06-23 00:11:28 +12:00
parent 612c9f5ad3
commit 6965db4c13
4 changed files with 65 additions and 7 deletions

View File

@ -393,7 +393,9 @@ class ThebesDisplayItemLayerUserData : public LayerUserData
{
public:
ThebesDisplayItemLayerUserData() :
mForcedBackgroundColor(NS_RGBA(0,0,0,0)) {}
mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
mXScale(1.f), mYScale(1.f),
mActiveScrolledRootPosition(0, 0) {}
/**
* A color that should be painted over the bounds of the layer's visible
@ -404,6 +406,17 @@ public:
* The resolution scale used.
*/
float mXScale, mYScale;
/**
* We try to make 0,0 of the ThebesLayer be the top-left of the
* border-box of the "active scrolled root" frame (i.e. the nearest ancestor
* frame for the display items that is being actively scrolled). But
* we force the ThebesLayer transform to be an integer translation, and we may
* have a resolution scale, so we have to snap the ThebesLayer transform, so
* 0,0 may not be exactly the top-left of the active scrolled root. Here we
* store the coordinates in ThebesLayer space of the top-left of the
* active scrolled root.
*/
gfxPoint mActiveScrolledRootPosition;
};
/**
@ -854,17 +867,28 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
// Set up transform so that 0,0 in the Thebes layer corresponds to the
// (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);
nsIntPoint pixOffset = offset.ScaleToNearestPixels(
mParameters.mXScale, mParameters.mYScale,
aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel());
nscoord appUnitsPerDevPixel = aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel();
gfxPoint scaledOffset(
NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
nsIntPoint pixOffset(NSToIntRoundUp(scaledOffset.x), NSToIntRoundUp(scaledOffset.y));
gfxMatrix matrix;
matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
layer->SetTransform(gfx3DMatrix::From2D(matrix));
// Calculate exact position of the top-left of the active scrolled root.
// This might not be 0,0 due to the snapping in ScaleToNearestPixels.
gfxPoint activeScrolledRootTopLeft = scaledOffset - matrix.GetTranslation();
// If it has changed, then we need to invalidate the entire layer since the
// pixels in the layer buffer have the content at a (subpixel) offset
// from what we need.
if (activeScrolledRootTopLeft != data->mActiveScrolledRootPosition) {
data->mActiveScrolledRootPosition = activeScrolledRootTopLeft;
nsIntRect invalidate = layer->GetValidRegion().GetBounds();
layer->InvalidateRegion(invalidate);
}
return layer.forget();
}

View File

@ -7,5 +7,7 @@ HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref
random-if(cocoaWidget) HTTP == opacity-mixed-scrolling-2.html opacity-mixed-scrolling-2.html?ref # see bug 625357
HTTP == simple-1.html simple-1.html?ref
HTTP == text-1.html text-1.html?ref
HTTP == transformed-1.html transformed-1.html?ref
HTTP == transformed-1.html?up transformed-1.html?ref
== uncovering-1.html uncovering-1-ref.html
== uncovering-2.html uncovering-2-ref.html

View File

@ -24,6 +24,13 @@ function doScroll(d)
if (document.location.search == '?ref') {
doScroll(20);
} else if (document.location.search == '?up') {
doScroll(40);
document.documentElement.setAttribute("class", "reftest-wait");
window.addEventListener("MozReftestInvalidate", function() {
document.documentElement.removeAttribute("class");
doScroll(20);
}, false);
} else {
doScroll(1);
document.documentElement.setAttribute("class", "reftest-wait");

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html>
<body>
<div class="scrollTop" style="height:100px; width:100px; overflow:hidden;
-moz-transform:scale(2.7); transform:scale(2.7); -moz-transform-origin:top left; transform-origin:top left;">
<div style="background:yellow;">
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
<div>Hello Kitty</div>
</div>
</div>
<script>document.body.getBoundingClientRect();</script>
<script src="scrolling.js"></script>
</body>
</html>