diff -r 000000000000 -r 6474c204b198 gfx/layers/apz/src/Axis.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/apz/src/Axis.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,296 @@ +/* -*- 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/. */ + +#include "Axis.h" +#include // for fabsf, pow, powf +#include // for max +#include "AsyncPanZoomController.h" // for AsyncPanZoomController +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "FrameMetrics.h" // for FrameMetrics +#include "mozilla/Attributes.h" // for MOZ_FINAL +#include "mozilla/Preferences.h" // for Preferences +#include "mozilla/gfx/Rect.h" // for RoundedIn +#include "mozilla/mozalloc.h" // for operator new +#include "nsMathUtils.h" // for NS_lround +#include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc +#include "nscore.h" // for NS_IMETHOD +#include "gfxPrefs.h" // for the preferences + +namespace mozilla { +namespace layers { + +Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController) + : mPos(0), + mVelocity(0.0f), + mAxisLocked(false), + mAsyncPanZoomController(aAsyncPanZoomController) +{ +} + +void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) { + float newVelocity = mAxisLocked ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds(); + if (gfxPrefs::APZMaxVelocity() > 0.0f) { + newVelocity = std::min(newVelocity, gfxPrefs::APZMaxVelocity() * APZCTreeManager::GetDPI()); + } + + mVelocity = newVelocity; + mPos = aPos; + + // Limit queue size pased on pref + mVelocityQueue.AppendElement(mVelocity); + if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) { + mVelocityQueue.RemoveElementAt(0); + } +} + +void Axis::StartTouch(int32_t aPos) { + mStartPos = aPos; + mPos = aPos; + mAxisLocked = false; +} + +float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut) { + if (mAxisLocked) { + aOverscrollAmountOut = 0; + return 0; + } + + float displacement = aDisplacement; + + // If this displacement will cause an overscroll, throttle it. Can potentially + // bring it to 0 even if the velocity is high. + if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) { + // No need to have a velocity along this axis anymore; it won't take us + // anywhere, so we're just spinning needlessly. + mVelocity = 0.0f; + aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement); + displacement -= aOverscrollAmountOut; + } + return displacement; +} + +float Axis::PanDistance() { + return fabsf(mPos - mStartPos); +} + +float Axis::PanDistance(float aPos) { + return fabsf(aPos - mStartPos); +} + +void Axis::EndTouch() { + // Calculate the mean velocity and empty the queue. + int count = mVelocityQueue.Length(); + if (count) { + mVelocity = 0; + while (!mVelocityQueue.IsEmpty()) { + mVelocity += mVelocityQueue[0]; + mVelocityQueue.RemoveElementAt(0); + } + mVelocity /= count; + } +} + +void Axis::CancelTouch() { + mVelocity = 0.0f; + while (!mVelocityQueue.IsEmpty()) { + mVelocityQueue.RemoveElementAt(0); + } +} + +bool Axis::Scrollable() { + if (mAxisLocked) { + return false; + } + return GetCompositionLength() < GetPageLength(); +} + +bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) { + if (fabsf(mVelocity) <= gfxPrefs::APZFlingStoppedThreshold()) { + // If the velocity is very low, just set it to 0 and stop the fling, + // otherwise we'll just asymptotically approach 0 and the user won't + // actually see any changes. + mVelocity = 0.0f; + return false; + } else { + mVelocity *= pow(1.0f - gfxPrefs::APZFlingFriction(), float(aDelta.ToMilliseconds())); + } + return true; +} + +Axis::Overscroll Axis::GetOverscroll() { + // If the current pan takes the window to the left of or above the current + // page rect. + bool minus = GetOrigin() < GetPageStart(); + // If the current pan takes the window to the right of or below the current + // page rect. + bool plus = GetCompositionEnd() > GetPageEnd(); + if (minus && plus) { + return OVERSCROLL_BOTH; + } + if (minus) { + return OVERSCROLL_MINUS; + } + if (plus) { + return OVERSCROLL_PLUS; + } + return OVERSCROLL_NONE; +} + +float Axis::GetExcess() { + switch (GetOverscroll()) { + case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart(); + case OVERSCROLL_PLUS: return GetCompositionEnd() - GetPageEnd(); + case OVERSCROLL_BOTH: return (GetCompositionEnd() - GetPageEnd()) + + (GetPageStart() - GetOrigin()); + default: return 0; + } +} + +Axis::Overscroll Axis::DisplacementWillOverscroll(float aDisplacement) { + // If the current pan plus a displacement takes the window to the left of or + // above the current page rect. + bool minus = GetOrigin() + aDisplacement < GetPageStart(); + // If the current pan plus a displacement takes the window to the right of or + // below the current page rect. + bool plus = GetCompositionEnd() + aDisplacement > GetPageEnd(); + if (minus && plus) { + return OVERSCROLL_BOTH; + } + if (minus) { + return OVERSCROLL_MINUS; + } + if (plus) { + return OVERSCROLL_PLUS; + } + return OVERSCROLL_NONE; +} + +float Axis::DisplacementWillOverscrollAmount(float aDisplacement) { + switch (DisplacementWillOverscroll(aDisplacement)) { + case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart(); + case OVERSCROLL_PLUS: return (GetCompositionEnd() + aDisplacement) - GetPageEnd(); + // Don't handle overscrolled in both directions; a displacement can't cause + // this, it must have already been zoomed out too far. + default: return 0; + } +} + +float Axis::ScaleWillOverscrollAmount(float aScale, float aFocus) { + float originAfterScale = (GetOrigin() + aFocus) - (aFocus / aScale); + + bool both = ScaleWillOverscrollBothSides(aScale); + bool minus = originAfterScale < GetPageStart(); + bool plus = (originAfterScale + (GetCompositionLength() / aScale)) > GetPageEnd(); + + if ((minus && plus) || both) { + // If we ever reach here it's a bug in the client code. + MOZ_ASSERT(false, "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount"); + return 0; + } + if (minus) { + return originAfterScale - GetPageStart(); + } + if (plus) { + return originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd(); + } + return 0; +} + +float Axis::GetVelocity() { + return mAxisLocked ? 0 : mVelocity; +} + +void Axis::SetVelocity(float aVelocity) { + mVelocity = aVelocity; +} + +float Axis::GetCompositionEnd() const { + return GetOrigin() + GetCompositionLength(); +} + +float Axis::GetPageEnd() const { + return GetPageStart() + GetPageLength(); +} + +float Axis::GetOrigin() const { + CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().GetScrollOffset(); + return GetPointOffset(origin); +} + +float Axis::GetCompositionLength() const { + const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics(); + return GetRectLength(metrics.CalculateCompositedRectInCssPixels()); +} + +float Axis::GetPageStart() const { + CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect; + return GetRectOffset(pageRect); +} + +float Axis::GetPageLength() const { + CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect; + return GetRectLength(pageRect); +} + +bool Axis::ScaleWillOverscrollBothSides(float aScale) { + const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics(); + + CSSToParentLayerScale scale(metrics.GetZoomToParent().scale * aScale); + CSSRect cssCompositionBounds = metrics.mCompositionBounds / scale; + + return GetRectLength(metrics.mScrollableRect) < GetRectLength(cssCompositionBounds); +} + +bool Axis::HasRoomToPan() const { + return GetOrigin() > GetPageStart() + || GetCompositionEnd() < GetPageEnd(); +} + + +AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController) + : Axis(aAsyncPanZoomController) +{ + +} + +float AxisX::GetPointOffset(const CSSPoint& aPoint) const +{ + return aPoint.x; +} + +float AxisX::GetRectLength(const CSSRect& aRect) const +{ + return aRect.width; +} + +float AxisX::GetRectOffset(const CSSRect& aRect) const +{ + return aRect.x; +} + +AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController) + : Axis(aAsyncPanZoomController) +{ + +} + +float AxisY::GetPointOffset(const CSSPoint& aPoint) const +{ + return aPoint.y; +} + +float AxisY::GetRectLength(const CSSRect& aRect) const +{ + return aRect.height; +} + +float AxisY::GetRectOffset(const CSSRect& aRect) const +{ + return aRect.y; +} + +} +}