mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 01:35:35 +00:00
Bug 681192. Part 10: Control rounding direction of scaledOffset to try to make the residual be close to mActiveScrolledRootPosition. r=tnikkel
--HG-- extra : rebase_source : f27fb737f8956ed117df0be3a71da847cc2048c0
This commit is contained in:
parent
bbb8c90aac
commit
ecec98a34e
@ -934,8 +934,39 @@ GetTranslationForThebesLayer(ThebesLayer* aLayer)
|
||||
return nsIntPoint(PRInt32(transform.x0), PRInt32(transform.y0));
|
||||
}
|
||||
|
||||
static PRBool FuzzyEqual(gfxPoint aV1, gfxPoint aV2) {
|
||||
return fabs(aV2.x - aV1.x) < 0.02 && fabs(aV2.y - aV1.y) < 0.02;
|
||||
static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
|
||||
|
||||
static PRBool
|
||||
SubpixelOffsetFuzzyEqual(gfxPoint aV1, gfxPoint aV2)
|
||||
{
|
||||
return fabs(aV2.x - aV1.x) < SUBPIXEL_OFFSET_EPSILON &&
|
||||
fabs(aV2.y - aV1.y) < SUBPIXEL_OFFSET_EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
* This normally computes NSToIntRoundUp(aValue). However, if that would
|
||||
* give a residual near 0.5 while aOldResidual is near -0.5, or
|
||||
* it would give a residual near -0.5 while aOldResidual is near 0.5, then
|
||||
* instead we return the integer in the other direction so that the residual
|
||||
* is close to aOldResidual.
|
||||
*/
|
||||
static PRInt32
|
||||
RoundToMatchResidual(double aValue, double aOldResidual)
|
||||
{
|
||||
PRInt32 v = NSToIntRoundUp(aValue);
|
||||
double residual = aValue - v;
|
||||
if (aOldResidual < 0) {
|
||||
if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
|
||||
// Round up instead
|
||||
return PRInt32(ceil(aValue));
|
||||
}
|
||||
} else if (aOldResidual > 0) {
|
||||
if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
|
||||
// Round down instead
|
||||
return PRInt32(floor(aValue));
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
already_AddRefed<ThebesLayer>
|
||||
@ -999,7 +1030,10 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||
gfxPoint scaledOffset(
|
||||
NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
|
||||
NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
|
||||
nsIntPoint pixOffset(NSToIntRoundUp(scaledOffset.x), NSToIntRoundUp(scaledOffset.y));
|
||||
// We call RoundToMatchResidual here so that the residual after rounding
|
||||
// is close to data->mActiveScrolledRootPosition if possible.
|
||||
nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, data->mActiveScrolledRootPosition.x),
|
||||
RoundToMatchResidual(scaledOffset.y, data->mActiveScrolledRootPosition.y));
|
||||
gfxMatrix matrix;
|
||||
matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
|
||||
layer->SetTransform(gfx3DMatrix::From2D(matrix));
|
||||
@ -1012,7 +1046,7 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
||||
// 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 (!FuzzyEqual(activeScrolledRootTopLeft, data->mActiveScrolledRootPosition)) {
|
||||
if (!SubpixelOffsetFuzzyEqual(activeScrolledRootTopLeft, data->mActiveScrolledRootPosition)) {
|
||||
data->mActiveScrolledRootPosition = activeScrolledRootTopLeft;
|
||||
nsIntRect invalidate = layer->GetValidRegion().GetBounds();
|
||||
layer->InvalidateRegion(invalidate);
|
||||
|
@ -75,6 +75,7 @@ _CHROME_FILES = \
|
||||
test_printpreview_bug482976.xul \
|
||||
printpreview_bug482976_helper.xul \
|
||||
test_transformed_scrolling_repaints.html \
|
||||
test_transformed_scrolling_repaints_2.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_CHROME_FILES)
|
||||
|
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them (1.1 scale)</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="paint_listener.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<!-- Need a timeout here to allow paint unsuppression before we start the test -->
|
||||
<body onload="setTimeout(startTest,0)">
|
||||
<div id="t" style="-moz-transform: scale(1.1, 1.1); -moz-transform-origin:top left; width:200px; height:100px; background:yellow; overflow:hidden">
|
||||
<div style="height:40px;">Hello</div>
|
||||
<div id="e" style="height:30px; background:lime">Kitty</div>
|
||||
<div style="height:300px; background:yellow">Kitty</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var t = document.getElementById("t");
|
||||
var e = document.getElementById("e");
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
function startTest() {
|
||||
// Do a couple of scrolls to ensure we've triggered activity heuristics
|
||||
waitForAllPaintsFlushed(function () {
|
||||
t.scrollTop = 5;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
t.scrollTop = 10;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
// Clear paint state now and scroll again.
|
||||
utils.checkAndClearPaintedState(e);
|
||||
t.scrollTop = 33;
|
||||
waitForAllPaintsFlushed(function () {
|
||||
var painted = utils.checkAndClearPaintedState(e);
|
||||
is(painted, false, "Fully-visible scrolled element should not have been painted");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user