Home | History | Annotate | Download | only in gm
      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      */
     33     SkAnimTimer() : fBaseTimeNanos(0), fCurrTimeNanos(0), fState(kStopped_State) {}
     34 
     35     SkAnimTimer(double base, double curr, State state)
     36         : fBaseTimeNanos(base)
     37         , fCurrTimeNanos(curr)
     38         , fState(state) {}
     39 
     40     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) {
     79             fCurrTimeNanos = SkTime::GetNSecs();
     80         }
     81     }
     82 
     83     /**
     84      *  Return the time in milliseconds the timer has been in the running state.
     85      *  Returns 0 if the timer is stopped. Behavior is undefined if the timer
     86      *  has been running longer than SK_MSecMax.
     87      */
     88     SkMSec msec() const {
     89         const double msec = (fCurrTimeNanos - fBaseTimeNanos) * 1e-6;
     90         SkASSERT(SK_MSecMax >= msec);
     91         return static_cast<SkMSec>(msec);
     92     }
     93 
     94     /**
     95      *  Return the time in seconds the timer has been in the running state.
     96      *  Returns 0 if the timer is stopped.
     97      */
     98     double secs() const { return (fCurrTimeNanos - fBaseTimeNanos) * 1e-9; }
     99 
    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 
    113     /**
    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 
    130 private:
    131     double  fBaseTimeNanos;
    132     double  fCurrTimeNanos;
    133     State   fState;
    134 
    135     void setState(State newState) {
    136         switch (newState) {
    137             case kStopped_State:
    138                 fBaseTimeNanos = fCurrTimeNanos = 0;
    139                 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:
    149                         fBaseTimeNanos = fCurrTimeNanos = SkTime::GetNSecs();
    150                         break;
    151                     case kPaused_State: {// they want "resume"
    152                         double now = SkTime::GetNSecs();
    153                         fBaseTimeNanos += now - fCurrTimeNanos;
    154                         fCurrTimeNanos = now;
    155                     } break;
    156                     case kRunning_State:
    157                         break;
    158                 }
    159                 fState = kRunning_State;
    160                 break;
    161         }
    162     }
    163 };
    164 
    165 #endif
    166