• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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