blob: bcec008030fcffb0dfae2ab3492a60303a255026 [file] [log] [blame]
reed76113a92015-02-02 12:55:02 -08001/*
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
Hal Canary6b20a552017-02-07 14:09:38 -05008#include "SkScalar.h"
reed76113a92015-02-02 12:55:02 -08009#include "SkTime.h"
10
11#ifndef SkAnimTimer_DEFINED
12#define SkAnimTimer_DEFINED
13
14/**
Brian Osman707d2022019-01-10 11:27:34 -050015 * Class to track a "timer". It supports 3 states: stopped, paused, and running.
16 * Playback speed is variable.
reed76113a92015-02-02 12:55:02 -080017 *
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 */
23class SkAnimTimer {
24public:
25 enum State {
26 kStopped_State,
27 kPaused_State,
28 kRunning_State
29 };
30
31 /**
32 * Class begins in the "stopped" state.
33 */
Brian Osman707d2022019-01-10 11:27:34 -050034 SkAnimTimer() : fPreviousNanos(0), fElapsedNanos(0), fSpeed(1), fState(kStopped_State) {}
reed76113a92015-02-02 12:55:02 -080035
Brian Osman707d2022019-01-10 11:27:34 -050036 SkAnimTimer(double elapsed)
37 : fPreviousNanos(0)
38 , fElapsedNanos(elapsed)
39 , fSpeed(1)
40 , fState(kRunning_State) {}
Florin Malita76a076b2018-02-15 18:40:48 -050041
reed76113a92015-02-02 12:55:02 -080042 bool isStopped() const { return kStopped_State == fState; }
43 bool isRunning() const { return kRunning_State == fState; }
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 */
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 */
57 void run() {
58 this->setState(kRunning_State);
59 }
60
61 /**
Brian Osman707d2022019-01-10 11:27:34 -050062 * Control the rate at which time advances.
63 */
64 float getSpeed() const { return fSpeed; }
65 void setSpeed(float speed) { fSpeed = speed; }
66
67 /**
68 * If the timer is stopped, start running, else it toggles between paused and running.
reed76113a92015-02-02 12:55:02 -080069 */
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 */
85 void updateTime() {
86 if (kRunning_State == fState) {
Brian Osman707d2022019-01-10 11:27:34 -050087 double now = SkTime::GetNSecs();
88 fElapsedNanos += (now - fPreviousNanos) * fSpeed;
89 fPreviousNanos = now;
reed76113a92015-02-02 12:55:02 -080090 }
91 }
92
93 /**
94 * Return the time in milliseconds the timer has been in the running state.
benjaminwagnerec4d4d72016-03-25 12:59:53 -070095 * Returns 0 if the timer is stopped. Behavior is undefined if the timer
96 * has been running longer than SK_MSecMax.
reed76113a92015-02-02 12:55:02 -080097 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070098 SkMSec msec() const {
Brian Osman707d2022019-01-10 11:27:34 -050099 const double msec = fElapsedNanos * 1e-6;
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700100 SkASSERT(SK_MSecMax >= msec);
101 return static_cast<SkMSec>(msec);
102 }
reed76113a92015-02-02 12:55:02 -0800103
104 /**
105 * Return the time in seconds the timer has been in the running state.
106 * Returns 0 if the timer is stopped.
107 */
Brian Osman707d2022019-01-10 11:27:34 -0500108 double secs() const { return fElapsedNanos * 1e-9; }
reed76113a92015-02-02 12:55:02 -0800109
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
bsalomonc67bb572016-05-13 13:48:48 -0700123 /**
124 * Transitions from ends->mid->ends linearly over period seconds. The phase specifies a phase
125 * shift in seconds.
126 */
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. */
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
reed76113a92015-02-02 12:55:02 -0800140private:
Brian Osman707d2022019-01-10 11:27:34 -0500141 double fPreviousNanos;
142 double fElapsedNanos;
143 float fSpeed;
144 State fState;
reed76113a92015-02-02 12:55:02 -0800145
146 void setState(State newState) {
147 switch (newState) {
148 case kStopped_State:
Brian Osman707d2022019-01-10 11:27:34 -0500149 fPreviousNanos = fElapsedNanos = 0;
reed76113a92015-02-02 12:55:02 -0800150 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:
Brian Osman707d2022019-01-10 11:27:34 -0500160 fPreviousNanos = SkTime::GetNSecs();
161 fElapsedNanos = 0;
reed76113a92015-02-02 12:55:02 -0800162 break;
Brian Osman707d2022019-01-10 11:27:34 -0500163 case kPaused_State: // they want "resume"
164 fPreviousNanos = SkTime::GetNSecs();
165 break;
reed76113a92015-02-02 12:55:02 -0800166 case kRunning_State:
167 break;
168 }
169 fState = kRunning_State;
170 break;
171 }
172 }
173};
174
175#endif