208 lines
9.6 KiB
C++
208 lines
9.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_layers_GenericFlingAnimation_h_
|
|
#define mozilla_layers_GenericFlingAnimation_h_
|
|
|
|
#include "APZUtils.h"
|
|
#include "AsyncPanZoomAnimation.h"
|
|
#include "AsyncPanZoomController.h"
|
|
#include "FrameMetrics.h"
|
|
#include "Layers.h"
|
|
#include "Units.h"
|
|
#include "OverscrollHandoffState.h"
|
|
#include "gfxPrefs.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Monitor.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#define FLING_LOG(...)
|
|
// #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
class GenericFlingAnimation: public AsyncPanZoomAnimation {
|
|
public:
|
|
GenericFlingAnimation(AsyncPanZoomController& aApzc,
|
|
PlatformSpecificStateBase* aPlatformSpecificState,
|
|
const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
|
|
bool aFlingIsHandedOff,
|
|
const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
|
|
: mApzc(aApzc)
|
|
, mOverscrollHandoffChain(aOverscrollHandoffChain)
|
|
, mScrolledApzc(aScrolledApzc)
|
|
{
|
|
MOZ_ASSERT(mOverscrollHandoffChain);
|
|
TimeStamp now = aApzc.GetFrameTime();
|
|
|
|
// Drop any velocity on axes where we don't have room to scroll anyways
|
|
// (in this APZC, or an APZC further in the handoff chain).
|
|
// This ensures that we don't take the 'overscroll' path in Sample()
|
|
// on account of one axis which can't scroll having a velocity.
|
|
if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
|
|
ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
|
|
mApzc.mX.SetVelocity(0);
|
|
}
|
|
if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
|
|
ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
|
|
mApzc.mY.SetVelocity(0);
|
|
}
|
|
|
|
ParentLayerPoint velocity = mApzc.GetVelocityVector();
|
|
|
|
// If the last fling was very recent and in the same direction as this one,
|
|
// boost the velocity to be the sum of the two. Check separate axes separately
|
|
// because we could have two vertical flings with small horizontal components
|
|
// on the opposite side of zero, and we still want the y-fling to get accelerated.
|
|
// Note that the acceleration code is only applied on the APZC that initiates
|
|
// the fling; the accelerated velocities are then handed off using the
|
|
// normal DispatchFling codepath.
|
|
// Acceleration is only applied in the APZC that originated the fling,
|
|
// not in APZCs further down the handoff chain during handoff.
|
|
bool applyAcceleration = !aFlingIsHandedOff;
|
|
if (applyAcceleration && !mApzc.mLastFlingTime.IsNull()
|
|
&& (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
|
|
&& velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
|
|
if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
|
|
velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
|
|
FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
|
|
&mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
|
|
mApzc.mX.SetVelocity(velocity.x);
|
|
}
|
|
if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
|
|
velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
|
|
FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
|
|
&mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
|
|
mApzc.mY.SetVelocity(velocity.y);
|
|
}
|
|
}
|
|
|
|
mApzc.mLastFlingTime = now;
|
|
mApzc.mLastFlingVelocity = velocity;
|
|
}
|
|
|
|
/**
|
|
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
|
* This should be called whenever sampling the content transform for this
|
|
* frame. Returns true if the fling animation should be advanced by one frame,
|
|
* or false if there is no fling or the fling has ended.
|
|
*/
|
|
virtual bool DoSample(FrameMetrics& aFrameMetrics,
|
|
const TimeDuration& aDelta) override
|
|
{
|
|
float friction = gfxPrefs::APZFlingFriction();
|
|
float threshold = gfxPrefs::APZFlingStoppedThreshold();
|
|
|
|
bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
|
|
shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
|
|
// If we shouldn't continue the fling, let's just stop and repaint.
|
|
if (!shouldContinueFlingX && !shouldContinueFlingY) {
|
|
FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
|
|
// This APZC or an APZC further down the handoff chain may be be overscrolled.
|
|
// Start a snap-back animation on the overscrolled APZC.
|
|
// Note:
|
|
// This needs to be a deferred task even though it can safely run
|
|
// while holding mMonitor, because otherwise, if the overscrolled APZC
|
|
// is this one, then the SetState(NOTHING) in UpdateAnimation will
|
|
// stomp on the SetState(SNAP_BACK) it does.
|
|
mDeferredTasks.AppendElement(
|
|
NewRunnableMethod<AsyncPanZoomController*>(mOverscrollHandoffChain.get(),
|
|
&OverscrollHandoffChain::SnapBackOverscrolledApzc,
|
|
&mApzc));
|
|
return false;
|
|
}
|
|
|
|
// AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
|
|
// Since we need to hand off the velocity to the tree manager in such a case,
|
|
// we save it here. Would be ParentLayerVector instead of ParentLayerPoint
|
|
// if we had vector classes.
|
|
ParentLayerPoint velocity = mApzc.GetVelocityVector();
|
|
|
|
ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
|
|
|
|
// Ordinarily we might need to do a ScheduleComposite if either of
|
|
// the following AdjustDisplacement calls returns true, but this
|
|
// is already running as part of a FlingAnimation, so we'll be compositing
|
|
// per frame of animation anyway.
|
|
ParentLayerPoint overscroll;
|
|
ParentLayerPoint adjustedOffset;
|
|
mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
|
|
mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
|
|
|
|
aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
|
|
|
|
// The fling may have caused us to reach the end of our scroll range.
|
|
if (!IsZero(overscroll)) {
|
|
// Hand off the fling to the next APZC in the overscroll handoff chain.
|
|
|
|
// We may have reached the end of the scroll range along one axis but
|
|
// not the other. In such a case we only want to hand off the relevant
|
|
// component of the fling.
|
|
if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
|
|
velocity.x = 0;
|
|
} else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
|
|
velocity.y = 0;
|
|
}
|
|
|
|
// To hand off the fling, we attempt to find a target APZC and start a new
|
|
// fling with the same velocity on that APZC. For simplicity, the actual
|
|
// overscroll of the current sample is discarded rather than being handed
|
|
// off. The compositor should sample animations sufficiently frequently
|
|
// that this is not noticeable. The target APZC is chosen by seeing if
|
|
// there is an APZC further in the handoff chain which is pannable; if
|
|
// there isn't, we take the new fling ourselves, entering an overscrolled
|
|
// state.
|
|
// Note: APZC is holding mMonitor, so directly calling
|
|
// HandleFlingOverscroll() (which acquires the tree lock) would violate
|
|
// the lock ordering. Instead we schedule HandleFlingOverscroll() to be
|
|
// called after mMonitor is released.
|
|
FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
|
|
mDeferredTasks.AppendElement(
|
|
NewRunnableMethod<ParentLayerPoint,
|
|
RefPtr<const OverscrollHandoffChain>,
|
|
RefPtr<const AsyncPanZoomController>>(&mApzc,
|
|
&AsyncPanZoomController::HandleFlingOverscroll,
|
|
velocity,
|
|
mOverscrollHandoffChain,
|
|
mScrolledApzc));
|
|
|
|
// If there is a remaining velocity on this APZC, continue this fling
|
|
// as well. (This fling and the handed-off fling will run concurrently.)
|
|
// Note that AdjustDisplacement() will have zeroed out the velocity
|
|
// along the axes where we're overscrolled.
|
|
return !IsZero(mApzc.GetVelocityVector());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
static bool SameDirection(float aVelocity1, float aVelocity2)
|
|
{
|
|
return (aVelocity1 == 0.0f)
|
|
|| (aVelocity2 == 0.0f)
|
|
|| (IsNegative(aVelocity1) == IsNegative(aVelocity2));
|
|
}
|
|
|
|
static float Accelerate(float aBase, float aSupplemental)
|
|
{
|
|
return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
|
|
+ (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
|
|
}
|
|
|
|
AsyncPanZoomController& mApzc;
|
|
RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
|
|
RefPtr<const AsyncPanZoomController> mScrolledApzc;
|
|
};
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_layers_GenericFlingAnimation_h_
|