• 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, 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