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