blob: 86ce7acd4bb43fe099cc6fa91ea32ddafb39677e [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/**
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 */
22class SkAnimTimer {
23public:
24 enum State {
25 kStopped_State,
26 kPaused_State,
27 kRunning_State
28 };
29
30 /**
31 * Class begins in the "stopped" state.
32 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070033 SkAnimTimer() : fBaseTimeNanos(0), fCurrTimeNanos(0), fState(kStopped_State) {}
reed76113a92015-02-02 12:55:02 -080034
Florin Malita76a076b2018-02-15 18:40:48 -050035 SkAnimTimer(double base, double curr, State state)
36 : fBaseTimeNanos(base)
37 , fCurrTimeNanos(curr)
38 , fState(state) {}
39
reed76113a92015-02-02 12:55:02 -080040 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) {
benjaminwagnerec4d4d72016-03-25 12:59:53 -070079 fCurrTimeNanos = SkTime::GetNSecs();
reed76113a92015-02-02 12:55:02 -080080 }
81 }
82
83 /**
84 * Return the time in milliseconds the timer has been in the running state.
benjaminwagnerec4d4d72016-03-25 12:59:53 -070085 * Returns 0 if the timer is stopped. Behavior is undefined if the timer
86 * has been running longer than SK_MSecMax.
reed76113a92015-02-02 12:55:02 -080087 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070088 SkMSec msec() const {
89 const double msec = (fCurrTimeNanos - fBaseTimeNanos) * 1e-6;
90 SkASSERT(SK_MSecMax >= msec);
91 return static_cast<SkMSec>(msec);
92 }
reed76113a92015-02-02 12:55:02 -080093
94 /**
95 * Return the time in seconds the timer has been in the running state.
96 * Returns 0 if the timer is stopped.
97 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070098 double secs() const { return (fCurrTimeNanos - fBaseTimeNanos) * 1e-9; }
reed76113a92015-02-02 12:55:02 -080099
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
bsalomonc67bb572016-05-13 13:48:48 -0700113 /**
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
reed76113a92015-02-02 12:55:02 -0800130private:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700131 double fBaseTimeNanos;
132 double fCurrTimeNanos;
reed76113a92015-02-02 12:55:02 -0800133 State fState;
134
135 void setState(State newState) {
136 switch (newState) {
137 case kStopped_State:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700138 fBaseTimeNanos = fCurrTimeNanos = 0;
reed76113a92015-02-02 12:55:02 -0800139 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:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700149 fBaseTimeNanos = fCurrTimeNanos = SkTime::GetNSecs();
reed76113a92015-02-02 12:55:02 -0800150 break;
151 case kPaused_State: {// they want "resume"
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700152 double now = SkTime::GetNSecs();
153 fBaseTimeNanos += now - fCurrTimeNanos;
154 fCurrTimeNanos = now;
reed76113a92015-02-02 12:55:02 -0800155 } break;
156 case kRunning_State:
157 break;
158 }
159 fState = kRunning_State;
160 break;
161 }
162 }
163};
164
165#endif