Bug 1019996 - Apply overscroll effect to fixed and sticky layers. r=kats, r=Cwiiis

This commit is contained in:
Botond Ballo 2014-06-12 20:27:32 -04:00
parent 3f3c080e26
commit 6d1e6271e0
4 changed files with 83 additions and 38 deletions

View File

@ -1974,8 +1974,8 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
return false;
}
void AsyncPanZoomController::ApplyOverscrollEffect(ViewTransform* aTransform) const {
// The overscroll effect applied here is a combination of a translation in
void AsyncPanZoomController::GetOverscrollTransform(ViewTransform* aTransform) const {
// The overscroll effect is a combination of a translation in
// the direction of overscroll, and shrinking in both directions.
// With the effect applied, we can think of the composited region as being
// made up of the following subregions.
@ -2066,8 +2066,9 @@ void AsyncPanZoomController::ApplyOverscrollEffect(ViewTransform* aTransform) co
}
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
ViewTransform* aNewTransform,
ScreenPoint& aScrollOffset) {
ViewTransform* aOutTransform,
ScreenPoint& aScrollOffset,
ViewTransform* aOutOverscrollTransform) {
// The eventual return value of this function. The compositor needs to know
// whether or not to advance by a frame as soon as it can. For example, if a
// fling is happening, it has to keep compositing so that the animation is
@ -2082,12 +2083,22 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
requestAnimationFrame = UpdateAnimation(aSampleTime, &deferredTasks);
aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
*aNewTransform = GetCurrentAsyncTransform();
*aOutTransform = GetCurrentAsyncTransform();
if (IsOverscrolled()) {
// GetCurrentAsyncTransform() does not consider any overscroll we may have.
// Adjust the transform to account for that.
ApplyOverscrollEffect(aNewTransform);
// If we are overscrolled, we would like the compositor to apply an
// additional transform that produces an overscroll effect.
if (aOutOverscrollTransform && IsOverscrolled()) {
GetOverscrollTransform(aOutOverscrollTransform);
// Since the caller will apply aOverscrollTransform after aNewTransform,
// aOverscrollTransform's translation will be not be scaled by
// aNewTransform's scale. Since the resulting transform is then
// multiplied by the CSS transform, which cancels out the non-transient
// part of aNewTransform->mScale, this results in an overscroll
// translation whose magnitude varies with the zoom. To avoid this,
// we adjust for that here.
aOutOverscrollTransform->mTranslation.x *= aOutTransform->mScale.scale;
aOutOverscrollTransform->mTranslation.y *= aOutTransform->mScale.scale;
}
LogRendertraceRect(GetGuid(), "viewport", "red",

View File

@ -154,21 +154,26 @@ public:
Vector<Task*>* aOutDeferredTasks);
/**
* The compositor calls this when it's about to draw pannable/zoomable content
* and is setting up transforms for compositing the layer tree. This is not
* idempotent. For example, a fling transform can be applied each time this is
* called (though not necessarily). |aSampleTime| is the time that this is
* sampled at; this is used for interpolating animations. Calling this sets a
* new transform in |aNewTransform| which should be multiplied to the transform
* in the shadow layer corresponding to this APZC.
* RQuery the transforms that should be applied to the layer corresponding
* to this APZC due to asynchronous panning and zooming.
* |aSampleTime| is the time that this is sampled at; this is used for
* interpolating animations.
* This function returns two transforms via out parameters:
* |aOutTransform| is the transform due to regular panning and zooming
* |aOverscrollTransform| is the transform due to overscrolling
* The two are separated because some clients want to ignore the overscroll
* transform for some purposes (and for convenience to these clients, the
* overscroll transform parameter may be nullptr). Clients who do not want
* to ignore the overscroll transform should multiply the two transforms
* together.
*
* Return value indicates whether or not any currently running animation
* should continue. That is, if true, the compositor should schedule another
* composite.
* The return value indicates whether or not any currently running animation
* should continue. If true, the compositor should schedule another composite.
*/
bool SampleContentTransformForFrame(const TimeStamp& aSampleTime,
ViewTransform* aNewTransform,
ScreenPoint& aScrollOffset);
ViewTransform* aOutTransform,
ScreenPoint& aScrollOffset,
ViewTransform* aOutOverscrollTransform = nullptr);
/**
* A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics
@ -643,10 +648,10 @@ private:
bool IsPanningState(PanZoomState mState);
/**
* Apply to |aTransform| a visual effect that reflects this apzc's
* Return in |aTransform| a visual effect that reflects this apzc's
* overscrolled state, if any.
*/
void ApplyOverscrollEffect(ViewTransform* aTransform) const;
void GetOverscrollTransform(ViewTransform* aTransform) const;
enum AxisLockMode {
FREE, /* No locking at all */

View File

@ -239,6 +239,7 @@ void
AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
Layer* aTransformedSubtreeRoot,
const Matrix4x4& aPreviousTransformForRoot,
const Matrix4x4& aCurrentTransformForRoot,
const LayerMargin& aFixedLayerMargins)
{
bool isRootFixed = aLayer->GetIsFixedPosition() &&
@ -262,7 +263,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
Matrix oldRootTransform;
Matrix newRootTransform;
if (!aPreviousTransformForRoot.Is2D(&oldRootTransform) ||
!aTransformedSubtreeRoot->GetLocalTransform().Is2D(&newRootTransform)) {
!aCurrentTransformForRoot.Is2D(&newRootTransform)) {
return;
}
@ -347,7 +348,8 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
AlignFixedAndStickyLayers(child, aTransformedSubtreeRoot,
aPreviousTransformForRoot, aFixedLayerMargins);
aPreviousTransformForRoot,
aCurrentTransformForRoot, aFixedLayerMargins);
}
}
@ -498,6 +500,15 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
return activeAnimations;
}
Matrix4x4
CombineWithCSSTransform(const gfx3DMatrix& treeTransform, Layer* aLayer)
{
Matrix4x4 result;
ToMatrix4x4(treeTransform, result);
result = result * aLayer->GetTransform();
return result;
}
bool
AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame,
Layer *aLayer,
@ -519,12 +530,13 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
LayerComposite* layerComposite = aLayer->AsLayerComposite();
Matrix4x4 oldTransform = aLayer->GetTransform();
ViewTransform treeTransform;
ViewTransform treeTransformWithoutOverscroll, overscrollTransform;
ScreenPoint scrollOffset;
*aWantNextFrame |=
controller->SampleContentTransformForFrame(aCurrentFrame,
&treeTransform,
scrollOffset);
&treeTransformWithoutOverscroll,
scrollOffset,
&overscrollTransform);
const FrameMetrics& metrics = container->GetFrameMetrics();
CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
@ -532,9 +544,9 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
metrics.mDisplayPort : metrics.mCriticalDisplayPort);
LayerMargin fixedLayerMargins(0, 0, 0, 0);
ScreenPoint offset(0, 0);
SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect,
mLayersUpdated, displayPort, paintScale,
mIsFirstPaint, fixedLayerMargins, offset);
SyncFrameMetrics(scrollOffset, treeTransformWithoutOverscroll.mScale.scale,
metrics.mScrollableRect, mLayersUpdated, displayPort,
paintScale, mIsFirstPaint, fixedLayerMargins, offset);
mIsFirstPaint = false;
mLayersUpdated = false;
@ -542,9 +554,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
// Apply the render offset
mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
Matrix4x4 transform;
ToMatrix4x4(gfx3DMatrix(treeTransform), transform);
transform = transform * aLayer->GetTransform();
Matrix4x4 transform = CombineWithCSSTransform(
treeTransformWithoutOverscroll * overscrollTransform, aLayer);
// GetTransform already takes the pre- and post-scale into account. Since we
// will apply the pre- and post-scale again when computing the effective
@ -564,7 +575,14 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
LayoutDeviceToLayerScale resolution = metrics.mCumulativeResolution;
oldTransform.Scale(resolution.scale, resolution.scale, 1);
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
// For the purpose of aligning fixed and sticky layers, we disregard
// the overscroll transform when computing the 'aCurrentTransformForRoot'
// parameter. This ensures that the overscroll transform is not unapplied,
// and therefore that the visual effect applies to fixed and sticky layers.
Matrix4x4 transformWithoutOverscroll = CombineWithCSSTransform(
treeTransformWithoutOverscroll, aLayer);
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform,
transformWithoutOverscroll, fixedLayerMargins);
appliedTransform = true;
}
@ -867,7 +885,8 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
// Make sure fixed position layers don't move away from their anchor points
// when we're asynchronously panning or zooming
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform,
aLayer->GetLocalTransform(), fixedLayerMargins);
}
bool

View File

@ -41,6 +41,12 @@ struct ViewTransform {
gfx3DMatrix::ScalingMatrix(mScale.scale, mScale.scale, 1);
}
// For convenience, to avoid writing the cumbersome
// "gfx3dMatrix(a) * gfx3DMatrix(b)".
friend gfx3DMatrix operator*(const ViewTransform& a, const ViewTransform& b) {
return gfx3DMatrix(a) * gfx3DMatrix(b);
}
bool operator==(const ViewTransform& rhs) const {
return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
}
@ -155,14 +161,18 @@ private:
* aTransformedSubtreeRoot. The translation is chosen so that the layer's
* anchor point relative to aTransformedSubtreeRoot's parent layer is the same
* as it was when aTransformedSubtreeRoot's GetLocalTransform() was
* aPreviousTransformForRoot. For sticky position layers, the translation is
* further intersected with the layer's sticky scroll ranges.
* aPreviousTransformForRoot. aCurrentTransformForRoot is
* aTransformedSubtreeRoot's current GetLocalTransform() modulo any
* overscroll-related transform, which we don't want to adjust for.
* For sticky position layers, the translation is further intersected with
* the layer's sticky scroll ranges.
* This function will also adjust layers so that the given content document
* fixed position margins will be respected during asynchronous panning and
* zooming.
*/
void AlignFixedAndStickyLayers(Layer* aLayer, Layer* aTransformedSubtreeRoot,
const gfx::Matrix4x4& aPreviousTransformForRoot,
const gfx::Matrix4x4& aCurrentTransformForRoot,
const LayerMargin& aFixedLayerMargins);
/**