| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SkScalar.h" | |
| #include "SkTime.h" | |
| #ifndef SkAnimTimer_DEFINED | |
| #define SkAnimTimer_DEFINED | |
| /** | |
| * Class to track a "timer". It supports 3 states: stopped, paused, running. | |
| * | |
| * The caller must call updateTime() to resync with the clock (typically just before | |
| * using the timer). Forcing the caller to do this ensures that the timer's return values | |
| * are consistent if called repeatedly, as they only reflect the time since the last | |
| * calle to updateTimer(). | |
| */ | |
| class SkAnimTimer { | |
| public: | |
| enum State { | |
| kStopped_State, | |
| kPaused_State, | |
| kRunning_State | |
| }; | |
| /** | |
| * Class begins in the "stopped" state. | |
| */ | |
| SkAnimTimer() : fBaseTimeNanos(0), fCurrTimeNanos(0), fState(kStopped_State) {} | |
| SkAnimTimer(double base, double curr, State state) | |
| : fBaseTimeNanos(base) | |
| , fCurrTimeNanos(curr) | |
| , fState(state) {} | |
| bool isStopped() const { return kStopped_State == fState; } | |
| bool isRunning() const { return kRunning_State == fState; } | |
| bool isPaused() const { return kPaused_State == fState; } | |
| /** | |
| * Stops the timer, and resets it, such that the next call to run or togglePauseResume | |
| * will begin at time 0. | |
| */ | |
| void stop() { | |
| this->setState(kStopped_State); | |
| } | |
| /** | |
| * If the timer is paused or stopped, it will resume (or start if it was stopped). | |
| */ | |
| void run() { | |
| this->setState(kRunning_State); | |
| } | |
| /** | |
| * If the timer is stopped, this has no effect, else it toggles between paused and running. | |
| */ | |
| void togglePauseResume() { | |
| if (kRunning_State == fState) { | |
| this->setState(kPaused_State); | |
| } else { | |
| this->setState(kRunning_State); | |
| } | |
| } | |
| /** | |
| * Call this each time you want to sample the clock for the timer. This is NOT done | |
| * automatically, so that repeated calls to msec() or secs() will always return the | |
| * same value. | |
| * | |
| * This may safely be called with the timer in any state. | |
| */ | |
| void updateTime() { | |
| if (kRunning_State == fState) { | |
| fCurrTimeNanos = SkTime::GetNSecs(); | |
| } | |
| } | |
| /** | |
| * Return the time in milliseconds the timer has been in the running state. | |
| * Returns 0 if the timer is stopped. Behavior is undefined if the timer | |
| * has been running longer than SK_MSecMax. | |
| */ | |
| SkMSec msec() const { | |
| const double msec = (fCurrTimeNanos - fBaseTimeNanos) * 1e-6; | |
| SkASSERT(SK_MSecMax >= msec); | |
| return static_cast<SkMSec>(msec); | |
| } | |
| /** | |
| * Return the time in seconds the timer has been in the running state. | |
| * Returns 0 if the timer is stopped. | |
| */ | |
| double secs() const { return (fCurrTimeNanos - fBaseTimeNanos) * 1e-9; } | |
| /** | |
| * Return the time in seconds the timer has been in the running state, | |
| * scaled by "speed" and (if not zero) mod by period. | |
| * Returns 0 if the timer is stopped. | |
| */ | |
| SkScalar scaled(SkScalar speed, SkScalar period = 0) const { | |
| double value = this->secs() * speed; | |
| if (period) { | |
| value = ::fmod(value, SkScalarToDouble(period)); | |
| } | |
| return SkDoubleToScalar(value); | |
| } | |
| /** | |
| * Transitions from ends->mid->ends linearly over period seconds. The phase specifies a phase | |
| * shift in seconds. | |
| */ | |
| SkScalar pingPong(SkScalar period, SkScalar phase, SkScalar ends, SkScalar mid) const { | |
| return PingPong(this->secs(), period, phase, ends, mid); | |
| } | |
| /** Helper for computing a ping-pong value without a SkAnimTimer object. */ | |
| static SkScalar PingPong(double t, SkScalar period, SkScalar phase, SkScalar ends, | |
| SkScalar mid) { | |
| double value = ::fmod(t + phase, period); | |
| double half = period / 2.0; | |
| double diff = ::fabs(value - half); | |
| return SkDoubleToScalar(ends + (1.0 - diff / half) * (mid - ends)); | |
| } | |
| private: | |
| double fBaseTimeNanos; | |
| double fCurrTimeNanos; | |
| State fState; | |
| void setState(State newState) { | |
| switch (newState) { | |
| case kStopped_State: | |
| fBaseTimeNanos = fCurrTimeNanos = 0; | |
| fState = kStopped_State; | |
| break; | |
| case kPaused_State: | |
| if (kRunning_State == fState) { | |
| fState = kPaused_State; | |
| } // else stay stopped or paused | |
| break; | |
| case kRunning_State: | |
| switch (fState) { | |
| case kStopped_State: | |
| fBaseTimeNanos = fCurrTimeNanos = SkTime::GetNSecs(); | |
| break; | |
| case kPaused_State: {// they want "resume" | |
| double now = SkTime::GetNSecs(); | |
| fBaseTimeNanos += now - fCurrTimeNanos; | |
| fCurrTimeNanos = now; | |
| } break; | |
| case kRunning_State: | |
| break; | |
| } | |
| fState = kRunning_State; | |
| break; | |
| } | |
| } | |
| }; | |
| #endif |