mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 772679. RestrictToLayerPixels needs to accurately convert between appunits scroll offsets and ThebesLayer pixel coordinates. r=tnikkel
Change GetThebesLayerResolutionForFrame to GetThebesLayerScaleForFrame, which just returns a scale. Ensure that the scale is as accurate as possible even if dedicated layers for scrolled content (or any layers at all) have not been created yet, by taking into account transforms that have not yet generated layers. This makes the decisions made by nsGfxScrollFrameInner::ScrollToImpl independent of whether there is currently an active layer for the scrolled content (or much more nearly so). In nsGfxScrollFrameInner::ScrollToImpl, do not use the current internal fractional offset of the ThebesLayer, which is in a mostly unrelated coordinate space to our scroll positions. Instead, just try to make sure that the previous and next scroll position differ by a whole number of layer pixels.
This commit is contained in:
parent
f21609d807
commit
8e16c0a415
@ -2505,44 +2505,50 @@ FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
FrameLayerBuilder::GetThebesLayerResolutionForFrame(nsIFrame* aFrame,
|
||||
double* aXres, double* aYres,
|
||||
gfxPoint* aPoint)
|
||||
static gfxSize
|
||||
PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale,
|
||||
const gfxSize& aScale)
|
||||
{
|
||||
nsTArray<DisplayItemData> *array = GetDisplayItemDataArrayForFrame(aFrame);
|
||||
if (array) {
|
||||
for (PRUint32 i = 0; i < array->Length(); ++i) {
|
||||
Layer* layer = array->ElementAt(i).mLayer;
|
||||
if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
|
||||
ThebesDisplayItemLayerUserData* data =
|
||||
static_cast<ThebesDisplayItemLayerUserData*>
|
||||
(layer->GetUserData(&gThebesDisplayItemLayerUserData));
|
||||
*aXres = data->mXScale;
|
||||
*aYres = data->mYScale;
|
||||
*aPoint = data->mActiveScrolledRootPosition;
|
||||
return true;
|
||||
gfx3DMatrix transform =
|
||||
gfx3DMatrix::ScalingMatrix(aScale.width, aScale.height, 1.0);
|
||||
// aTransform is applied first, then the scale is applied to the result
|
||||
transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform;
|
||||
gfxMatrix transform2d;
|
||||
if (transform.CanDraw2D(&transform2d)) {
|
||||
return transform2d.ScaleFactors(true);
|
||||
}
|
||||
return gfxSize(1.0, 1.0);
|
||||
}
|
||||
|
||||
gfxSize
|
||||
FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame* aFrame)
|
||||
{
|
||||
nsIFrame* last;
|
||||
for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
|
||||
last = f;
|
||||
if (f->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) {
|
||||
nsTArray<DisplayItemData>* array = GetDisplayItemDataArrayForFrame(f);
|
||||
NS_ASSERTION(array, "Must have display item data for container");
|
||||
for (PRUint32 i = 0; i < array->Length(); ++i) {
|
||||
Layer* layer = array->ElementAt(i).mLayer;
|
||||
ContainerLayer* container = layer->AsContainerLayer();
|
||||
if (!container) {
|
||||
continue;
|
||||
}
|
||||
for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
ThebesDisplayItemLayerUserData* data =
|
||||
static_cast<ThebesDisplayItemLayerUserData*>
|
||||
(l->GetUserData(&gThebesDisplayItemLayerUserData));
|
||||
if (data) {
|
||||
return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIFrame::ChildListIterator lists(aFrame);
|
||||
for (; !lists.IsDone(); lists.Next()) {
|
||||
if (lists.CurrentID() == nsIFrame::kPopupList ||
|
||||
lists.CurrentID() == nsIFrame::kSelectPopupList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsFrameList::Enumerator childFrames(lists.CurrentList());
|
||||
for (; !childFrames.AtEnd(); childFrames.Next()) {
|
||||
if (GetThebesLayerResolutionForFrame(childFrames.get(),
|
||||
aXres, aYres, aPoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return PredictScaleForContent(aFrame, last,
|
||||
last->PresContext()->PresShell()->GetResolution());
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
|
@ -369,15 +369,13 @@ public:
|
||||
nsIntPoint GetLastPaintOffset(ThebesLayer* aLayer);
|
||||
|
||||
/**
|
||||
* Return resolution and scroll offset of ThebesLayer content associated
|
||||
* with aFrame's subtree.
|
||||
* Returns true if some ThebesLayer was found.
|
||||
* This just looks for the first ThebesLayer and returns its data. There
|
||||
* could be other ThebesLayers with different resolution and offsets.
|
||||
* Return the resolution at which we expect to render aFrame's contents,
|
||||
* assuming they are being painted to retained layers. This takes into account
|
||||
* the resolution the contents of the ContainerLayer containing aFrame are
|
||||
* being rendered at, as well as any currently-inactive transforms between
|
||||
* aFrame and that container layer.
|
||||
*/
|
||||
static bool GetThebesLayerResolutionForFrame(nsIFrame* aFrame,
|
||||
double* aXRes, double* aYRes,
|
||||
gfxPoint* aPoint);
|
||||
static gfxSize GetThebesLayerScaleForFrame(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Clip represents the intersection of an optional rectangle with a
|
||||
|
@ -1196,6 +1196,7 @@ public:
|
||||
* The resolution defaults to 1.0.
|
||||
*/
|
||||
virtual nsresult SetResolution(float aXResolution, float aYResolution) = 0;
|
||||
gfxSize GetResolution() { return gfxSize(mXResolution, mYResolution); }
|
||||
float GetXResolution() { return mXResolution; }
|
||||
float GetYResolution() { return mYResolution; }
|
||||
|
||||
|
@ -1967,38 +1967,34 @@ void nsGfxScrollFrameInner::ScrollVisual(nsPoint aOldScrolledFramePos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the desired scroll value in given range
|
||||
* in order to get resulting scroll by whole amount of layer pixels.
|
||||
* Current implementation is not checking that result value is the best.
|
||||
* Ideally it's would be possible to find best value by implementing
|
||||
* test function which is repeating last part of CreateOrRecycleThebesLayer,
|
||||
* and checking other points in allowed range, but that may cause another perf hit.
|
||||
* Let's keep it as TODO.
|
||||
* Return an appunit value close to aDesired and between aLower and aUpper
|
||||
* such that (aDesired - aCurrent)*aRes/aAppUnitsPerPixel is an integer (or
|
||||
* as close as we can get modulo rounding to appunits). If that
|
||||
* can't be done, just returns aDesired.
|
||||
*/
|
||||
static nscoord
|
||||
RestrictToLayerPixels(nscoord aDesired, nscoord aLower,
|
||||
nscoord aUpper, nscoord aAppUnitsPerPixel,
|
||||
double aRes, double aCurrentLayerOffset)
|
||||
AlignWithLayerPixels(nscoord aDesired, nscoord aLower,
|
||||
nscoord aUpper, nscoord aAppUnitsPerPixel,
|
||||
double aRes, nscoord aCurrent)
|
||||
{
|
||||
// convert the result to layer pixels
|
||||
double layerVal = aRes * double(aDesired) / aAppUnitsPerPixel;
|
||||
double currentLayerVal = (aRes*aCurrent)/aAppUnitsPerPixel;
|
||||
double desiredLayerVal = (aRes*aDesired)/aAppUnitsPerPixel;
|
||||
double delta = desiredLayerVal - currentLayerVal;
|
||||
double nearestVal = NS_round(delta) + currentLayerVal;
|
||||
|
||||
// Correct value using current layer offset
|
||||
layerVal -= aCurrentLayerOffset;
|
||||
|
||||
// Try nearest pixel bound first
|
||||
double nearestVal = NS_round(layerVal);
|
||||
// Convert back from ThebesLayer space to appunits relative to the top-left
|
||||
// of the scrolled frame.
|
||||
nscoord nearestAppUnitVal =
|
||||
NSToCoordRoundWithClamp(nearestVal * aAppUnitsPerPixel / aRes);
|
||||
NSToCoordRoundWithClamp(nearestVal*aAppUnitsPerPixel/aRes);
|
||||
|
||||
// Check if nearest layer pixel result fit into allowed and scroll range
|
||||
if (nearestAppUnitVal >= aLower && nearestAppUnitVal <= aUpper) {
|
||||
return nearestAppUnitVal;
|
||||
} else if (nearestVal != layerVal) {
|
||||
} else if (nearestVal != desiredLayerVal) {
|
||||
// Check if opposite pixel boundary fit into scroll range
|
||||
double oppositeVal = nearestVal + ((nearestVal < layerVal) ? 1 : -1);
|
||||
double oppositeVal = nearestVal + ((nearestVal < desiredLayerVal) ? 1 : -1);
|
||||
nscoord oppositeAppUnitVal =
|
||||
NSToCoordRoundWithClamp(oppositeVal * aAppUnitsPerPixel / aRes);
|
||||
NSToCoordRoundWithClamp(oppositeVal*aAppUnitsPerPixel/aRes);
|
||||
if (oppositeAppUnitVal >= aLower && oppositeAppUnitVal <= aUpper) {
|
||||
return oppositeAppUnitVal;
|
||||
}
|
||||
@ -2007,17 +2003,17 @@ RestrictToLayerPixels(nscoord aDesired, nscoord aLower,
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp desired scroll position aPt to aBounds (if aBounds is non-null) and then snap
|
||||
* it to the nearest layer pixel edges, keeping it within aRange during snapping
|
||||
* (if aRange is non-null). aCurrScroll is the current scroll position.
|
||||
* Clamp desired scroll position aPt to aBounds and then snap
|
||||
* it to the same layer pixel edges as aCurrent, keeping it within aRange
|
||||
* during snapping. aCurrent is the current scroll position.
|
||||
*/
|
||||
static nsPoint
|
||||
ClampAndRestrictToLayerPixels(const nsPoint& aPt,
|
||||
const nsRect& aBounds,
|
||||
nscoord aAppUnitsPerPixel,
|
||||
const nsRect& aRange,
|
||||
double aXRes, double aYRes,
|
||||
const gfxPoint& aCurrScroll)
|
||||
ClampAndAlignWithLayerPixels(const nsPoint& aPt,
|
||||
const nsRect& aBounds,
|
||||
const nsRect& aRange,
|
||||
const nsPoint& aCurrent,
|
||||
nscoord aAppUnitsPerPixel,
|
||||
const gfxSize& aScale)
|
||||
{
|
||||
nsPoint pt = aBounds.ClampPoint(aPt);
|
||||
// Intersect scroll range with allowed range, by clamping the corners
|
||||
@ -2025,10 +2021,10 @@ ClampAndRestrictToLayerPixels(const nsPoint& aPt,
|
||||
nsPoint rangeTopLeft = aBounds.ClampPoint(aRange.TopLeft());
|
||||
nsPoint rangeBottomRight = aBounds.ClampPoint(aRange.BottomRight());
|
||||
|
||||
return nsPoint(RestrictToLayerPixels(pt.x, rangeTopLeft.x, rangeBottomRight.x,
|
||||
aAppUnitsPerPixel, aXRes, aCurrScroll.x),
|
||||
RestrictToLayerPixels(pt.y, rangeTopLeft.y, rangeBottomRight.y,
|
||||
aAppUnitsPerPixel, aYRes, aCurrScroll.y));
|
||||
return nsPoint(AlignWithLayerPixels(pt.x, rangeTopLeft.x, rangeBottomRight.x,
|
||||
aAppUnitsPerPixel, aScale.width, aCurrent.x),
|
||||
AlignWithLayerPixels(pt.y, rangeTopLeft.y, rangeBottomRight.y,
|
||||
aAppUnitsPerPixel, aScale.height, aCurrent.y));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
@ -2061,19 +2057,28 @@ nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt, const nsRect& aRange)
|
||||
{
|
||||
nsPresContext* presContext = mOuter->PresContext();
|
||||
nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
|
||||
|
||||
double xres = 1.0, yres = 1.0;
|
||||
gfxPoint activeScrolledRootPosition;
|
||||
FrameLayerBuilder::GetThebesLayerResolutionForFrame(mScrolledFrame, &xres, &yres,
|
||||
&activeScrolledRootPosition);
|
||||
nsPoint pt =
|
||||
ClampAndRestrictToLayerPixels(aPt,
|
||||
GetScrollRangeForClamping(),
|
||||
appUnitsPerDevPixel,
|
||||
aRange, xres, yres,
|
||||
activeScrolledRootPosition);
|
||||
|
||||
// 'scale' is our estimate of the scale factor that will be applied
|
||||
// when rendering the scrolled content to its own ThebesLayer.
|
||||
gfxSize scale = FrameLayerBuilder::GetThebesLayerScaleForFrame(mScrolledFrame);
|
||||
nsPoint curPos = GetScrollPosition();
|
||||
// Try to align aPt with curPos so they have an integer number of layer
|
||||
// pixels between them. This gives us the best chance of scrolling without
|
||||
// having to invalidate due to changes in subpixel rendering.
|
||||
// Note that when we actually draw into a ThebesLayer, the coordinates
|
||||
// that get mapped onto the layer buffer pixels are from the display list,
|
||||
// which are relative to the display root frame's top-left increasing down,
|
||||
// whereas here our coordinates are scroll positions which increase upward
|
||||
// and are relative to the scrollport top-left. This difference doesn't actually
|
||||
// matter since all we are about is that there be an integer number of
|
||||
// layer pixels between pt and curPos.
|
||||
nsPoint pt =
|
||||
ClampAndAlignWithLayerPixels(aPt,
|
||||
GetScrollRangeForClamping(),
|
||||
aRange,
|
||||
curPos,
|
||||
appUnitsPerDevPixel,
|
||||
scale);
|
||||
|
||||
if (pt == curPos) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user