float GetCurrentSpan(const MultiTouchInput& aEvent)
{
    const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
    const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
    ParentLayerPoint delta = secondTouch - firstTouch;
    return delta.Length();
}
Beispiel #2
0
float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const {
  ScreenPoint velocity = MakePoint(aVelocityInchesPerMs * mAsyncPanZoomController->GetDPI());
  // Use ToScreenCoordinates() to convert a point rather than a vector by
  // treating the point as a vector, and using (0, 0) as the anchor.
  ScreenPoint panStart = mAsyncPanZoomController->ToScreenCoordinates(
      mAsyncPanZoomController->PanStart(),
      ParentLayerPoint());
  ParentLayerPoint localVelocity =
      mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
  return localVelocity.Length();
}
Beispiel #3
0
AndroidFlingAnimation::AndroidFlingAnimation(AsyncPanZoomController& aApzc,
                                             PlatformSpecificStateBase* aPlatformSpecificState,
                                             const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
                                             bool aFlingIsHandoff,
                                             const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
  : mApzc(aApzc)
  , mOverscrollHandoffChain(aOverscrollHandoffChain)
  , mScrolledApzc(aScrolledApzc)
  , mSentBounceX(false)
  , mSentBounceY(false)
{
  MOZ_ASSERT(mOverscrollHandoffChain);
  MOZ_ASSERT(aPlatformSpecificState->AsAndroidSpecificState());
  mOverScroller = aPlatformSpecificState->AsAndroidSpecificState()->mOverScroller;
  MOZ_ASSERT(mOverScroller);

  // 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();
  mPreviousVelocity = velocity;

  float scrollRangeStartX = mApzc.mX.GetPageStart().value;
  float scrollRangeEndX = mApzc.mX.GetScrollRangeEnd().value;
  float scrollRangeStartY = mApzc.mY.GetPageStart().value;
  float scrollRangeEndY = mApzc.mY.GetScrollRangeEnd().value;
  mStartOffset.x = mPreviousOffset.x = mApzc.mX.GetOrigin().value;
  mStartOffset.y = mPreviousOffset.y = mApzc.mY.GetOrigin().value;
  float length = velocity.Length();
  if (length > 0.0f) {
    mFlingDirection = velocity / length;
  }

  int32_t originX = ClampStart(mStartOffset.x, scrollRangeStartX, scrollRangeEndX);
  int32_t originY = ClampStart(mStartOffset.y, scrollRangeStartY, scrollRangeEndY);
  mOverScroller->Fling(originX, originY,
                       // Android needs the velocity in pixels per second and it is in pixels per ms.
                       (int32_t)(velocity.x * 1000.0f), (int32_t)(velocity.y * 1000.0f),
                       (int32_t)floor(scrollRangeStartX), (int32_t)ceil(scrollRangeEndX),
                       (int32_t)floor(scrollRangeStartY), (int32_t)ceil(scrollRangeEndY),
                       0, 0);
}
Maybe<LayerPoint>
HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
{
  // convert into Layer coordinate space
  gfx::Matrix4x4 localTransform = mTransform;
  if (mApzc) {
    localTransform = localTransform * mApzc->GetCurrentAsyncTransformWithOverscroll();
  }
  gfx::Point4D point = localTransform.Inverse().ProjectPoint(aPoint.ToUnknownPoint());
  return point.HasPositiveWCoord()
        ? Some(ViewAs<LayerPixel>(point.As2DPoint()))
        : Nothing();
}
void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity,
                               float aPLPPI) {
  mVelocity = aStartingVelocity.Length();
  // We should not have created a fling animation if there is no velocity.
  MOZ_ASSERT(mVelocity != 0.0f);
  const double tuningCoeff = ComputeDeceleration(aPLPPI);
  mTargetDuration = ComputeFlingDuration(mVelocity, tuningCoeff);
  MOZ_ASSERT(!mTargetDuration.IsZero());
  mDurationSoFar = TimeDuration();
  mLastPos = ParentLayerPoint();
  mCurrentPos = ParentLayerPoint();
  float coeffX = mVelocity == 0 ? 1.0f : aStartingVelocity.x / mVelocity;
  float coeffY = mVelocity == 0 ? 1.0f : aStartingVelocity.y / mVelocity;
  mTargetDistance = ComputeFlingDistance(mVelocity, tuningCoeff);
  mTargetPos =
      ParentLayerPoint(mTargetDistance * coeffX, mTargetDistance * coeffY);
  const float hyp = mTargetPos.Length();
  if (FuzzyEqualsAdditive(hyp, 0.0f)) {
    mDeltaNorm = ParentLayerPoint(1, 1);
  } else {
    mDeltaNorm = ParentLayerPoint(mTargetPos.x / hyp, mTargetPos.y / hyp);
  }
}
Beispiel #6
0
AndroidFlingAnimation::AndroidFlingAnimation(AsyncPanZoomController& aApzc,
                                             PlatformSpecificStateBase* aPlatformSpecificState,
                                             const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
                                             bool aFlingIsHandoff,
                                             const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
  : mApzc(aApzc)
  , mOverscrollHandoffChain(aOverscrollHandoffChain)
  , mScrolledApzc(aScrolledApzc)
  , mSentBounceX(false)
  , mSentBounceY(false)
  , mFlingDuration(0)
{
  MOZ_ASSERT(mOverscrollHandoffChain);
  AndroidSpecificState* state = aPlatformSpecificState->AsAndroidSpecificState();
  MOZ_ASSERT(state);
  mOverScroller = state->mOverScroller;
  MOZ_ASSERT(mOverScroller);

  // 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, ScrollDirection::HORIZONTAL)) {
    ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
    mApzc.mX.SetVelocity(0);
  }
  if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, ScrollDirection::VERTICAL)) {
    ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
    mApzc.mY.SetVelocity(0);
  }

  ParentLayerPoint velocity = mApzc.GetVelocityVector();

  float scrollRangeStartX = mApzc.mX.GetPageStart().value;
  float scrollRangeEndX = mApzc.mX.GetScrollRangeEnd().value;
  float scrollRangeStartY = mApzc.mY.GetPageStart().value;
  float scrollRangeEndY = mApzc.mY.GetScrollRangeEnd().value;
  mStartOffset.x = mPreviousOffset.x = mApzc.mX.GetOrigin().value;
  mStartOffset.y = mPreviousOffset.y = mApzc.mY.GetOrigin().value;
  float length = velocity.Length();
  if (length > 0.0f) {
    mFlingDirection = velocity / length;

    if ((sMaxFlingSpeed > 0.0f) && (length > sMaxFlingSpeed)) {
       velocity = mFlingDirection * sMaxFlingSpeed;
    }
  }

  mPreviousVelocity = velocity;

  int32_t originX = ClampStart(mStartOffset.x, scrollRangeStartX, scrollRangeEndX);
  int32_t originY = ClampStart(mStartOffset.y, scrollRangeStartY, scrollRangeEndY);
  if (!state->mLastFling.IsNull()) {
    // If it's been too long since the previous fling, or if the new fling's
    // velocity is too low, don't allow flywheel to kick in. If we do allow
    // flywheel to kick in, then we need to update the timestamp on the
    // StackScroller because otherwise it might use a stale velocity.
    TimeDuration flingDuration = TimeStamp::Now() - state->mLastFling;
    if (flingDuration.ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
        && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
      bool unused = false;
      mOverScroller->ComputeScrollOffset(flingDuration.ToMilliseconds(), &unused);
    } else {
      mOverScroller->ForceFinished(true);
    }
  }
  mOverScroller->Fling(originX, originY,
                       // Android needs the velocity in pixels per second and it is in pixels per ms.
                       (int32_t)(velocity.x * 1000.0f), (int32_t)(velocity.y * 1000.0f),
                       (int32_t)floor(scrollRangeStartX), (int32_t)ceil(scrollRangeEndX),
                       (int32_t)floor(scrollRangeStartY), (int32_t)ceil(scrollRangeEndY),
                       0, 0, 0);
  state->mLastFling = TimeStamp::Now();
}
Beispiel #7
0
/**
 * Advances a fling by an interpolated amount based on the Android OverScroller.
 * 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.
 */
bool
AndroidFlingAnimation::DoSample(FrameMetrics& aFrameMetrics,
                                const TimeDuration& aDelta)
{
  bool shouldContinueFling = true;

  mFlingDuration += aDelta.ToMilliseconds();
  mOverScroller->ComputeScrollOffset(mFlingDuration, &shouldContinueFling);

  int32_t currentX = 0;
  int32_t currentY = 0;
  mOverScroller->GetCurrX(&currentX);
  mOverScroller->GetCurrY(&currentY);
  ParentLayerPoint offset((float)currentX, (float)currentY);
  ParentLayerPoint preCheckedOffset(offset);

  bool hitBoundX = CheckBounds(mApzc.mX, offset.x, mFlingDirection.x, &(offset.x));
  bool hitBoundY = CheckBounds(mApzc.mY, offset.y, mFlingDirection.y, &(offset.y));

  ParentLayerPoint velocity = mPreviousVelocity;

  // Sometimes the OverScroller fails to update the offset for a frame.
  // If the frame can still scroll we just use the velocity from the previous
  // frame. However, if the frame can no longer scroll in the direction
  // of the fling, then end the animation.
  if (offset != mPreviousOffset) {
    if (aDelta.ToMilliseconds() > 0) {
      mOverScroller->GetCurrSpeedX(&velocity.x);
      mOverScroller->GetCurrSpeedY(&velocity.y);

      velocity.x /= 1000;
      velocity.y /= 1000;

      mPreviousVelocity = velocity;
    }
  } else if ((fabsf(offset.x - preCheckedOffset.x) > BOUNDS_EPSILON) || (fabsf(offset.y - preCheckedOffset.y) > BOUNDS_EPSILON)) {
    // The page is no longer scrolling but the fling animation is still animating beyond the page bounds. If it goes
    // beyond the BOUNDS_EPSILON then it has overflowed and will never stop. In that case, stop the fling animation.
    shouldContinueFling = false;
  } else if (hitBoundX && hitBoundY) {
    // We can't scroll any farther along either axis.
    shouldContinueFling = false;
  }

  float speed = velocity.Length();

  // gfxPrefs::APZFlingStoppedThreshold is only used in tests.
  if (!shouldContinueFling || (speed < gfxPrefs::APZFlingStoppedThreshold())) {
    if (shouldContinueFling) {
      // The OverScroller thinks it should continue but the speed is below
      // the stopping threshold so abort the animation.
      mOverScroller->AbortAnimation();
    }
    // This animation is going to end. If DeferHandleFlingOverscroll
    // has not been called and there is still some velocity left,
    // call it so that fling hand off may occur if applicable.
    if (!mSentBounceX && !mSentBounceY && (speed > 0.0f)) {
      DeferHandleFlingOverscroll(velocity);
    }
    return false;
  }

  mPreviousOffset = offset;

  mApzc.SetVelocityVector(velocity);
  aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());

  // If we hit a bounds while flinging, send the velocity so that the bounce
  // animation can play.
  if (hitBoundX || hitBoundY) {
    ParentLayerPoint bounceVelocity = velocity;

    if (!mSentBounceX && hitBoundX && fabsf(offset.x - mStartOffset.x) > BOUNDS_EPSILON) {
      mSentBounceX = true;
    } else {
      bounceVelocity.x = 0.0f;
    }

    if (!mSentBounceY && hitBoundY && fabsf(offset.y - mStartOffset.y) > BOUNDS_EPSILON) {
      mSentBounceY = true;
    } else {
      bounceVelocity.y = 0.0f;
    }
    if (!IsZero(bounceVelocity)) {
      DeferHandleFlingOverscroll(bounceVelocity);
    }
  }

  return true;
}
Beispiel #8
0
/**
 * Advances a fling by an interpolated amount based on the Android OverScroller.
 * 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.
 */
bool
AndroidFlingAnimation::DoSample(FrameMetrics& aFrameMetrics,
                                const TimeDuration& aDelta)
{
  bool shouldContinueFling = true;

  mOverScroller->ComputeScrollOffset(&shouldContinueFling);
  // OverScroller::GetCurrVelocity will sometimes return NaN. So need to externally
  // calculate current velocity and not rely on what the OverScroller calculates.
  // mOverScroller->GetCurrVelocity(&speed);
  int32_t currentX = 0;
  int32_t currentY = 0;
  mOverScroller->GetCurrX(&currentX);
  mOverScroller->GetCurrY(&currentY);
  ParentLayerPoint offset((float)currentX, (float)currentY);

  bool hitBoundX = CheckBounds(mApzc.mX, offset.x, mFlingDirection.x, &(offset.x));
  bool hitBoundY = CheckBounds(mApzc.mY, offset.y, mFlingDirection.y, &(offset.y));

  ParentLayerPoint velocity = mPreviousVelocity;

  // Sometimes the OverScroller fails to update the offset for a frame.
  // If the frame can still scroll we just use the velocity from the previous
  // frame. However, if the frame can no longer scroll in the direction
  // of the fling, then end the animation.
  if (offset != mPreviousOffset) {
    if (aDelta.ToMilliseconds() > 0) {
      velocity = (offset - mPreviousOffset) / (float)aDelta.ToMilliseconds();
      mPreviousVelocity = velocity;
    }
  } else if (hitBoundX || hitBoundY) {
    // We have reached the end of the scroll in one of the directions being scrolled and the offset has not
    // changed so end animation.
    shouldContinueFling = false;
  }

  float speed = velocity.Length();

  // gfxPrefs::APZFlingStoppedThreshold is only used in tests.
  if (!shouldContinueFling || (speed < gfxPrefs::APZFlingStoppedThreshold())) {
    if (shouldContinueFling) {
      // The OverScroller thinks it should continue but the speed is below
      // the stopping threshold so abort the animation.
      mOverScroller->AbortAnimation();
    }
    // This animation is going to end. If DeferHandleFlingOverscroll
    // has not been called and there is still some velocity left,
    // call it so that fling hand off may occur if applicable.
    if (!mSentBounceX && !mSentBounceY && (speed > 0.0f)) {
      DeferHandleFlingOverscroll(velocity);
    }
    return false;
  }

  mPreviousOffset = offset;

  mApzc.SetVelocityVector(velocity);
  aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());

  // If we hit a bounds while flinging, send the velocity so that the bounce
  // animation can play.
  if (hitBoundX || hitBoundY) {
    ParentLayerPoint bounceVelocity = velocity;

    if (!mSentBounceX && hitBoundX && fabsf(offset.x - mStartOffset.x) > BOUNDS_EPSILON) {
      mSentBounceX = true;
    } else {
      bounceVelocity.x = 0.0f;
    }

    if (!mSentBounceY && hitBoundY && fabsf(offset.y - mStartOffset.y) > BOUNDS_EPSILON) {
      mSentBounceY = true;
    } else {
      bounceVelocity.y = 0.0f;
    }
    if (!IsZero(bounceVelocity)) {
      DeferHandleFlingOverscroll(bounceVelocity);
    }
  }

  return true;
}