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 "SkTime.h"
9
10#ifndef SkAnimTimer_DEFINED
11#define SkAnimTimer_DEFINED
12
13/**
14 *  Class to track a "timer". It supports 3 states: stopped, paused, running.
15 *
16 *  The caller must call updateTime() to resync with the clock (typically just before
17 *  using the timer). Forcing the caller to do this ensures that the timer's return values
18 *  are consistent if called repeatedly, as they only reflect the time since the last
19 *  calle to updateTimer().
20 */
21class SkAnimTimer {
22public:
23    enum State {
24        kStopped_State,
25        kPaused_State,
26        kRunning_State
27    };
28
29    /**
30     *  Class begins in the "stopped" state.
31     */
32    SkAnimTimer() : fBaseTime(0), fCurrTime(0), fState(kStopped_State) {}
33
34    bool isStopped() const { return kStopped_State == fState; }
35    bool isRunning() const { return kRunning_State == fState; }
36    bool isPaused() const { return kPaused_State == fState; }
37
38    /**
39     *  Stops the timer, and resets it, such that the next call to run or togglePauseResume
40     *  will begin at time 0.
41     */
42    void stop() {
43        this->setState(kStopped_State);
44    }
45
46    /**
47     *  If the timer is paused or stopped, it will resume (or start if it was stopped).
48     */
49    void run() {
50        this->setState(kRunning_State);
51    }
52
53    /**
54     *  If the timer is stopped, this has no effect, else it toggles between paused and running.
55     */
56    void togglePauseResume() {
57        if (kRunning_State == fState) {
58            this->setState(kPaused_State);
59        } else {
60            this->setState(kRunning_State);
61        }
62    }
63
64    /**
65     *  Call this each time you want to sample the clock for the timer. This is NOT done
66     *  automatically, so that repeated calls to msec() or secs() will always return the
67     *  same value.
68     *
69     *  This may safely be called with the timer in any state.
70     */
71    void updateTime() {
72        if (kRunning_State == fState) {
73            fCurrTime = SkTime::GetMSecs();
74        }
75    }
76
77    /**
78     *  Return the time in milliseconds the timer has been in the running state.
79     *  Returns 0 if the timer is stopped.
80     */
81    SkMSec msec() const { return fCurrTime - fBaseTime; }
82
83    /**
84     *  Return the time in seconds the timer has been in the running state.
85     *  Returns 0 if the timer is stopped.
86     */
87    double secs() const {
88        return this->msec() * 0.001;
89    }
90
91    /**
92     *  Return the time in seconds the timer has been in the running state,
93     *  scaled by "speed" and (if not zero) mod by period.
94     *  Returns 0 if the timer is stopped.
95     */
96    SkScalar scaled(SkScalar speed, SkScalar period = 0) const {
97        double value = this->secs() * speed;
98        if (period) {
99            value = ::fmod(value, SkScalarToDouble(period));
100        }
101        return SkDoubleToScalar(value);
102    }
103
104private:
105    SkMSec  fBaseTime;
106    SkMSec  fCurrTime;
107    State   fState;
108
109    void setState(State newState) {
110        switch (newState) {
111            case kStopped_State:
112                fBaseTime = fCurrTime = 0;
113                fState = kStopped_State;
114                break;
115            case kPaused_State:
116                if (kRunning_State == fState) {
117                    fState = kPaused_State;
118                } // else stay stopped or paused
119                break;
120            case kRunning_State:
121                switch (fState) {
122                    case kStopped_State:
123                        fBaseTime = fCurrTime = SkTime::GetMSecs();
124                        break;
125                    case kPaused_State: {// they want "resume"
126                        SkMSec now = SkTime::GetMSecs();
127                        fBaseTime += now - fCurrTime;
128                        fCurrTime = now;
129                    } break;
130                    case kRunning_State:
131                        break;
132                }
133                fState = kRunning_State;
134                break;
135        }
136    }
137};
138
139#endif
140