1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkScalar.h" 9 #include "SkTime.h" 10 11 #ifndef SkAnimTimer_DEFINED 12 #define SkAnimTimer_DEFINED 13 14 /** 15 * Class to track a "timer". It supports 3 states: stopped, paused, and running. 16 * Playback speed is variable. 17 * 18 * The caller must call updateTime() to resync with the clock (typically just before 19 * using the timer). Forcing the caller to do this ensures that the timer's return values 20 * are consistent if called repeatedly, as they only reflect the time since the last 21 * calle to updateTimer(). 22 */ 23 class SkAnimTimer { 24 public: 25 enum State { 26 kStopped_State, 27 kPaused_State, 28 kRunning_State 29 }; 30 31 /** 32 * Class begins in the "stopped" state. 33 */ SkAnimTimer()34 SkAnimTimer() : fPreviousNanos(0), fElapsedNanos(0), fSpeed(1), fState(kStopped_State) {} 35 SkAnimTimer(double elapsed)36 SkAnimTimer(double elapsed) 37 : fPreviousNanos(0) 38 , fElapsedNanos(elapsed) 39 , fSpeed(1) 40 , fState(kRunning_State) {} 41 isStopped()42 bool isStopped() const { return kStopped_State == fState; } isRunning()43 bool isRunning() const { return kRunning_State == fState; } isPaused()44 bool isPaused() const { return kPaused_State == fState; } 45 46 /** 47 * Stops the timer, and resets it, such that the next call to run or togglePauseResume 48 * will begin at time 0. 49 */ stop()50 void stop() { 51 this->setState(kStopped_State); 52 } 53 54 /** 55 * If the timer is paused or stopped, it will resume (or start if it was stopped). 56 */ run()57 void run() { 58 this->setState(kRunning_State); 59 } 60 61 /** 62 * Control the rate at which time advances. 63 */ getSpeed()64 float getSpeed() const { return fSpeed; } setSpeed(float speed)65 void setSpeed(float speed) { fSpeed = speed; } 66 67 /** 68 * If the timer is stopped, start running, else it toggles between paused and running. 69 */ togglePauseResume()70 void togglePauseResume() { 71 if (kRunning_State == fState) { 72 this->setState(kPaused_State); 73 } else { 74 this->setState(kRunning_State); 75 } 76 } 77 78 /** 79 * Call this each time you want to sample the clock for the timer. This is NOT done 80 * automatically, so that repeated calls to msec() or secs() will always return the 81 * same value. 82 * 83 * This may safely be called with the timer in any state. 84 */ updateTime()85 void updateTime() { 86 if (kRunning_State == fState) { 87 double now = SkTime::GetNSecs(); 88 fElapsedNanos += (now - fPreviousNanos) * fSpeed; 89 fPreviousNanos = now; 90 } 91 } 92 93 /** 94 * Return the time in milliseconds the timer has been in the running state. 95 * Returns 0 if the timer is stopped. Behavior is undefined if the timer 96 * has been running longer than SK_MSecMax. 97 */ msec()98 SkMSec msec() const { 99 const double msec = fElapsedNanos * 1e-6; 100 SkASSERT(SK_MSecMax >= msec); 101 return static_cast<SkMSec>(msec); 102 } 103 104 /** 105 * Return the time in seconds the timer has been in the running state. 106 * Returns 0 if the timer is stopped. 107 */ secs()108 double secs() const { return fElapsedNanos * 1e-9; } 109 110 /** 111 * Return the time in seconds the timer has been in the running state, 112 * scaled by "speed" and (if not zero) mod by period. 113 * Returns 0 if the timer is stopped. 114 */ 115 SkScalar scaled(SkScalar speed, SkScalar period = 0) const { 116 double value = this->secs() * speed; 117 if (period) { 118 value = ::fmod(value, SkScalarToDouble(period)); 119 } 120 return SkDoubleToScalar(value); 121 } 122 123 /** 124 * Transitions from ends->mid->ends linearly over period seconds. The phase specifies a phase 125 * shift in seconds. 126 */ pingPong(SkScalar period,SkScalar phase,SkScalar ends,SkScalar mid)127 SkScalar pingPong(SkScalar period, SkScalar phase, SkScalar ends, SkScalar mid) const { 128 return PingPong(this->secs(), period, phase, ends, mid); 129 } 130 131 /** Helper for computing a ping-pong value without a SkAnimTimer object. */ PingPong(double t,SkScalar period,SkScalar phase,SkScalar ends,SkScalar mid)132 static SkScalar PingPong(double t, SkScalar period, SkScalar phase, SkScalar ends, 133 SkScalar mid) { 134 double value = ::fmod(t + phase, period); 135 double half = period / 2.0; 136 double diff = ::fabs(value - half); 137 return SkDoubleToScalar(ends + (1.0 - diff / half) * (mid - ends)); 138 } 139 140 private: 141 double fPreviousNanos; 142 double fElapsedNanos; 143 float fSpeed; 144 State fState; 145 setState(State newState)146 void setState(State newState) { 147 switch (newState) { 148 case kStopped_State: 149 fPreviousNanos = fElapsedNanos = 0; 150 fState = kStopped_State; 151 break; 152 case kPaused_State: 153 if (kRunning_State == fState) { 154 fState = kPaused_State; 155 } // else stay stopped or paused 156 break; 157 case kRunning_State: 158 switch (fState) { 159 case kStopped_State: 160 fPreviousNanos = SkTime::GetNSecs(); 161 fElapsedNanos = 0; 162 break; 163 case kPaused_State: // they want "resume" 164 fPreviousNanos = SkTime::GetNSecs(); 165 break; 166 case kRunning_State: 167 break; 168 } 169 fState = kRunning_State; 170 break; 171 } 172 } 173 }; 174 175 #endif 176