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