author | Augustin Trancart <augustin.trancart@gmail.com> |
Wed, 04 Sep 2013 14:07:11 -0400 | |
changeset 145498 | f746195f55957d694a7aa45c5560cf0641acf843 |
parent 145497 | 05c2d7b68a229d4221c25e1052689e6473ecd51f |
child 145499 | 0fb65049cb956db14f3bf604b4c3b78010ea6b46 |
push id | 25214 |
push user | kwierso@gmail.com |
push date | Thu, 05 Sep 2013 00:02:20 +0000 |
treeherder | mozilla-central@99bd249e5a20 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | Cwiiis, kats |
bugs | 711959 |
milestone | 26.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -916,16 +916,29 @@ public class GeckoLayerClient implements /** Implementation of PanZoomTarget */ @Override public boolean post(Runnable action) { return mView.post(action); } /** Implementation of PanZoomTarget */ @Override + public void postRenderTask(RenderTask task) { + mView.postRenderTask(task); + } + + /** Implementation of PanZoomTarget */ + @Override + public void removeRenderTask(RenderTask task) { + mView.removeRenderTask(task); + } + + + /** Implementation of PanZoomTarget */ + @Override public boolean postDelayed(Runnable action, long delayMillis) { return mView.postDelayed(action, delayMillis); } /** Implementation of PanZoomTarget */ @Override public Object getLock() { return this;
--- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -25,19 +25,16 @@ import android.os.Build; import android.util.FloatMath; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import java.util.Timer; -import java.util.TimerTask; - /* * Handles the kinetic scrolling and zooming physics for a layer controller. * * Many ideas are from Joe Hewitt's Scrollability: * https://github.com/joehewitt/scrollability/ */ class JavaPanZoomController extends GestureDetector.SimpleOnGestureListener @@ -72,18 +69,18 @@ class JavaPanZoomController private static final float MAX_ZOOM = 4.0f; // The maximum amount we would like to scroll with the mouse private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi(); // The maximum zoom factor adjustment per frame of the AUTONAV animation private static final float MAX_ZOOM_DELTA = 0.125f; - // Length of the bounce animation in ms - private static final int BOUNCE_ANIMATION_DURATION = 250; + // The number of time a bounce animation run at most. + private static final int BOUNCE_FRAME_NUMBER = 15; private enum PanZoomState { NOTHING, /* no touch-start events received */ FLING, /* all touches removed, but we're still scrolling page */ TOUCHING, /* one touch-start event received */ PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */ PANNING_LOCKED_Y, /* as above for Y axis */ PANNING, /* panning without axis lock */ @@ -110,20 +107,18 @@ class JavaPanZoomController private final PanZoomTarget mTarget; private final SubdocumentScrollHelper mSubscroller; private final Axis mX; private final Axis mY; private final TouchEventHandler mTouchEventHandler; private final EventDispatcher mEventDispatcher; - /* The timer that handles flings or bounces. */ - private Timer mAnimationTimer; - /* The runnable being scheduled by the animation timer. */ - private AnimationRunnable mAnimationRunnable; + /* The task that handles flings, autonav or bounces. */ + private PanZoomRenderTask mAnimationRenderTask; /* The zoom focus at the first zoom event (in page coordinates). */ private PointF mLastZoomFocus; /* The time the last motion event took place. */ private long mLastEventTime; /* Current state the pan/zoom UI is in. */ private PanZoomState mState; /* The per-frame zoom delta for the currently-running AUTONAV animation. */ private float mAutonavZoomDelta; @@ -414,17 +409,17 @@ class JavaPanZoomController /* * Panning/scrolling */ private boolean handleTouchStart(MotionEvent event) { // user is taking control of movement, so stop // any auto-movement we have going - stopAnimationTimer(); + stopAnimationTask(); switch (mState) { case ANIMATED_ZOOM: // We just interrupted a double-tap animation, so force a redraw in // case this touchstart is just a tap that doesn't end up triggering // a redraw mTarget.forceRedraw(null); // fall through @@ -601,17 +596,17 @@ class JavaPanZoomController bounce(); // if not needed, this will automatically go to state NOTHING return true; } return false; } if (mState == PanZoomState.NOTHING) { setState(PanZoomState.AUTONAV); - startAnimationTimer(new AutonavRunnable()); + startAnimationRenderTask(new AutonavRenderTask()); } if (mState == PanZoomState.AUTONAV) { mX.setAutoscrollVelocity(velocityX); mY.setAutoscrollVelocity(velocityY); mAutonavZoomDelta = zoomDelta; return true; } return false; @@ -727,74 +722,67 @@ class JavaPanZoomController private void scrollBy(float dx, float dy) { mTarget.scrollBy(dx, dy); } private void fling() { updatePosition(); - stopAnimationTimer(); + stopAnimationTask(); boolean stopped = stopped(); mX.startFling(stopped); mY.startFling(stopped); - startAnimationTimer(new FlingRunnable()); + startAnimationRenderTask(new FlingRenderTask()); } /* Performs a bounce-back animation to the given viewport metrics. */ private void bounce(ImmutableViewportMetrics metrics, PanZoomState state) { - stopAnimationTimer(); + stopAnimationTask(); ImmutableViewportMetrics bounceStartMetrics = getMetrics(); if (bounceStartMetrics.fuzzyEquals(metrics)) { setState(PanZoomState.NOTHING); return; } setState(state); // At this point we have already set mState to BOUNCE or ANIMATED_ZOOM, so // getRedrawHint() is returning false. This means we can safely call // setAnimationTarget to set the new final display port and not have it get // clobbered by display ports from intermediate animation frames. mTarget.setAnimationTarget(metrics); - startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics)); + startAnimationRenderTask(new BounceRenderTask(bounceStartMetrics, metrics)); } /* Performs a bounce-back animation to the nearest valid viewport metrics. */ private void bounce() { bounce(getValidViewportMetrics(), PanZoomState.BOUNCE); } /* Starts the fling or bounce animation. */ - private void startAnimationTimer(final AnimationRunnable runnable) { - if (mAnimationTimer != null) { - Log.e(LOGTAG, "Attempted to start a new timer without canceling the old one!"); - stopAnimationTimer(); + private void startAnimationRenderTask(final PanZoomRenderTask task) { + if (mAnimationRenderTask != null) { + Log.e(LOGTAG, "Attempted to start a new task without canceling the old one!"); + stopAnimationTask(); } - mAnimationTimer = new Timer("Animation Timer"); - mAnimationRunnable = runnable; - mAnimationTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { mTarget.post(runnable); } - }, 0, (int)Axis.MS_PER_FRAME); + mAnimationRenderTask = task; + mTarget.postRenderTask(mAnimationRenderTask); } /* Stops the fling or bounce animation. */ - private void stopAnimationTimer() { - if (mAnimationTimer != null) { - mAnimationTimer.cancel(); - mAnimationTimer = null; - } - if (mAnimationRunnable != null) { - mAnimationRunnable.terminate(); - mAnimationRunnable = null; + private void stopAnimationTask() { + if (mAnimationRenderTask != null) { + mAnimationRenderTask.terminate(); + mTarget.removeRenderTask(mAnimationRenderTask); + mAnimationRenderTask = null; } } private float getVelocity() { float xvel = mX.getRealVelocity(); float yvel = mY.getRealVelocity(); return FloatMath.sqrt(xvel * xvel + yvel * yvel); } @@ -825,69 +813,89 @@ class JavaPanZoomController } } else { synchronized (mTarget.getLock()) { scrollBy(displacement.x, displacement.y); } } } - private abstract class AnimationRunnable implements Runnable { - private boolean mAnimationTerminated; + /** + * This class is an implementation of RenderTask which enforces its implementor to run in the UI thread. + * + */ + private abstract class PanZoomRenderTask extends RenderTask { - /* This should always run on the UI thread */ - @Override - public final void run() { - /* - * Since the animation timer queues this runnable on the UI thread, it - * is possible that even when the animation timer is cancelled, there - * are multiple instances of this queued, so we need to have another - * mechanism to abort. This is done by using the mAnimationTerminated flag. - */ - if (mAnimationTerminated) { - return; + private final Runnable mRunnable = new Runnable() { + @Override + public final void run() { + if (mContinueAnimation) { + animateFrame(); + } } - animateFrame(); + }; + + private boolean mContinueAnimation = true; + + public PanZoomRenderTask() { + super(false); } + @Override + protected final boolean internalRun(long timeDelta, long currentFrameStartTime) { + + mTarget.post(mRunnable); + return mContinueAnimation; + } + + /** + * The method subclasses must override. This method is run on the UI thread thanks to internalRun + */ protected abstract void animateFrame(); - /* This should always run on the UI thread */ - protected final void terminate() { - mAnimationTerminated = true; + /** + * Terminate the animation. + */ + public void terminate() { + mContinueAnimation = false; } } - private class AutonavRunnable extends AnimationRunnable { + private class AutonavRenderTask extends PanZoomRenderTask { + public AutonavRenderTask() { + super(); + } + @Override protected void animateFrame() { if (mState != PanZoomState.AUTONAV) { finishAnimation(); return; } updatePosition(); synchronized (mTarget.getLock()) { mTarget.setViewportMetrics(applyZoomDelta(getMetrics(), mAutonavZoomDelta)); } } } - /* The callback that performs the bounce animation. */ - private class BounceRunnable extends AnimationRunnable { - /* The current frame of the bounce-back animation */ - private int mBounceFrame; + /* The task that performs the bounce animation. */ + private class BounceRenderTask extends PanZoomRenderTask { + /* * The viewport metrics that represent the start and end of the bounce-back animation, * respectively. */ private ImmutableViewportMetrics mBounceStartMetrics; private ImmutableViewportMetrics mBounceEndMetrics; + private int mBounceFrame; - BounceRunnable(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) { + BounceRenderTask(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) { + super(); mBounceStartMetrics = startMetrics; mBounceEndMetrics = endMetrics; } @Override protected void animateFrame() { /* * The pan/zoom controller might have signaled to us that it wants to abort the @@ -895,48 +903,53 @@ class JavaPanZoomController * out. */ if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) { finishAnimation(); return; } /* Perform the next frame of the bounce-back animation. */ - if (mBounceFrame < (int)(BOUNCE_ANIMATION_DURATION / Axis.MS_PER_FRAME)) { + if (mBounceFrame < BOUNCE_FRAME_NUMBER) { advanceBounce(); return; } /* Finally, if there's nothing else to do, complete the animation and go to sleep. */ finishBounce(); finishAnimation(); setState(PanZoomState.NOTHING); } /* Performs one frame of a bounce animation. */ private void advanceBounce() { synchronized (mTarget.getLock()) { - float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / BOUNCE_ANIMATION_DURATION); + float t = easeOut(((float)mBounceFrame) / BOUNCE_FRAME_NUMBER); ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); mTarget.setViewportMetrics(newMetrics); mBounceFrame++; } } /* Concludes a bounce animation and snaps the viewport into place. */ private void finishBounce() { synchronized (mTarget.getLock()) { mTarget.setViewportMetrics(mBounceEndMetrics); mBounceFrame = -1; } } } // The callback that performs the fling animation. - private class FlingRunnable extends AnimationRunnable { + private class FlingRenderTask extends PanZoomRenderTask { + + public FlingRenderTask() { + super(); + } + @Override protected void animateFrame() { /* * The pan/zoom controller might have signaled to us that it wants to abort the * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail * out. */ if (mState != PanZoomState.FLING) { @@ -978,17 +991,17 @@ class JavaPanZoomController setState(PanZoomState.NOTHING); } } } private void finishAnimation() { checkMainThread(); - stopAnimationTimer(); + stopAnimationTask(); // Force a viewport synchronisation mTarget.forceRedraw(null); } /* Returns the nearest viewport metrics with no overscroll visible. */ private ImmutableViewportMetrics getValidViewportMetrics() { return getValidViewportMetrics(getMetrics());
--- a/mobile/android/base/gfx/PanZoomTarget.java +++ b/mobile/android/base/gfx/PanZoomTarget.java @@ -21,11 +21,13 @@ public interface PanZoomTarget { public void scrollBy(float dx, float dy); public void onSubdocumentScrollBy(float dx, float dy); public void panZoomStopped(); /** This triggers an (asynchronous) viewport update/redraw. */ public void forceRedraw(DisplayPortMetrics displayPort); public boolean post(Runnable action); public boolean postDelayed(Runnable action, long delayMillis); + public void postRenderTask(RenderTask task); + public void removeRenderTask(RenderTask task); public Object getLock(); public PointF convertViewPointToLayerPoint(PointF viewPoint); }