blob: b7b9bbf2276e96578e501bdbb76f7d0662cf5f58 [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
35 bool isStopped() const { return kStopped_State == fState; }
36 bool isRunning() const { return kRunning_State == fState; }
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 */
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 */
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 */
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 */
72 void updateTime() {
73 if (kRunning_State == fState) {
benjaminwagnerec4d4d72016-03-25 12:59:53 -070074 fCurrTimeNanos = SkTime::GetNSecs();
reed76113a92015-02-02 12:55:02 -080075 }
76 }
77
78 /**
79 * Return the time in milliseconds the timer has been in the running state.
benjaminwagnerec4d4d72016-03-25 12:59:53 -070080 * Returns 0 if the timer is stopped. Behavior is undefined if the timer
81 * has been running longer than SK_MSecMax.
reed76113a92015-02-02 12:55:02 -080082 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070083 SkMSec msec() const {
84 const double msec = (fCurrTimeNanos - fBaseTimeNanos) * 1e-6;
85 SkASSERT(SK_MSecMax >= msec);
86 return static_cast<SkMSec>(msec);
87 }
reed76113a92015-02-02 12:55:02 -080088
89 /**
90 * Return the time in seconds the timer has been in the running state.
91 * Returns 0 if the timer is stopped.
92 */
benjaminwagnerec4d4d72016-03-25 12:59:53 -070093 double secs() const { return (fCurrTimeNanos - fBaseTimeNanos) * 1e-9; }
reed76113a92015-02-02 12:55:02 -080094
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
bsalomonc67bb572016-05-13 13:48:48 -0700108 /**
109 * Transitions from ends->mid->ends linearly over period seconds. The phase specifies a phase
110 * shift in seconds.
111 */
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. */
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
reed76113a92015-02-02 12:55:02 -0800125private:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700126 double fBaseTimeNanos;
127 double fCurrTimeNanos;
reed76113a92015-02-02 12:55:02 -0800128 State fState;
129
130 void setState(State newState) {
131 switch (newState) {
132 case kStopped_State:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700133 fBaseTimeNanos = fCurrTimeNanos = 0;
reed76113a92015-02-02 12:55:02 -0800134 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:
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700144 fBaseTimeNanos = fCurrTimeNanos = SkTime::GetNSecs();
reed76113a92015-02-02 12:55:02 -0800145 break;
146 case kPaused_State: {// they want "resume"
benjaminwagnerec4d4d72016-03-25 12:59:53 -0700147 double now = SkTime::GetNSecs();
148 fBaseTimeNanos += now - fCurrTimeNanos;
149 fCurrTimeNanos = now;
reed76113a92015-02-02 12:55:02 -0800150 } break;
151 case kRunning_State:
152 break;
153 }
154 fState = kRunning_State;
155 break;
156 }
157 }
158};
159
160#endif