diff options
Diffstat (limited to 'gfx/layers/apz/src/GenericFlingAnimation.h')
-rw-r--r-- | gfx/layers/apz/src/GenericFlingAnimation.h | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/GenericFlingAnimation.h b/gfx/layers/apz/src/GenericFlingAnimation.h new file mode 100644 index 000000000..deec37b47 --- /dev/null +++ b/gfx/layers/apz/src/GenericFlingAnimation.h @@ -0,0 +1,207 @@ +/* -*- 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_ |