C++程序  |  161行  |  5 KB

/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkScalar.h"
#include "SkTime.h"

#ifndef SkAnimTimer_DEFINED
#define SkAnimTimer_DEFINED

/**
 *  Class to track a "timer". It supports 3 states: stopped, paused, running.
 *
 *  The caller must call updateTime() to resync with the clock (typically just before
 *  using the timer). Forcing the caller to do this ensures that the timer's return values
 *  are consistent if called repeatedly, as they only reflect the time since the last
 *  calle to updateTimer().
 */
class SkAnimTimer {
public:
    enum State {
        kStopped_State,
        kPaused_State,
        kRunning_State
    };

    /**
     *  Class begins in the "stopped" state.
     */
    SkAnimTimer() : fBaseTimeNanos(0), fCurrTimeNanos(0), fState(kStopped_State) {}

    bool isStopped() const { return kStopped_State == fState; }
    bool isRunning() const { return kRunning_State == fState; }
    bool isPaused() const { return kPaused_State == fState; }

    /**
     *  Stops the timer, and resets it, such that the next call to run or togglePauseResume
     *  will begin at time 0.
     */
    void stop() {
        this->setState(kStopped_State);
    }

    /**
     *  If the timer is paused or stopped, it will resume (or start if it was stopped).
     */
    void run() {
        this->setState(kRunning_State);
    }

    /**
     *  If the timer is stopped, this has no effect, else it toggles between paused and running.
     */
    void togglePauseResume() {
        if (kRunning_State == fState) {
            this->setState(kPaused_State);
        } else {
            this->setState(kRunning_State);
        }
    }

    /**
     *  Call this each time you want to sample the clock for the timer. This is NOT done
     *  automatically, so that repeated calls to msec() or secs() will always return the
     *  same value.
     *
     *  This may safely be called with the timer in any state.
     */
    void updateTime() {
        if (kRunning_State == fState) {
            fCurrTimeNanos = SkTime::GetNSecs();
        }
    }

    /**
     *  Return the time in milliseconds the timer has been in the running state.
     *  Returns 0 if the timer is stopped. Behavior is undefined if the timer
     *  has been running longer than SK_MSecMax.
     */
    SkMSec msec() const {
        const double msec = (fCurrTimeNanos - fBaseTimeNanos) * 1e-6;
        SkASSERT(SK_MSecMax >= msec);
        return static_cast<SkMSec>(msec);
    }

    /**
     *  Return the time in seconds the timer has been in the running state.
     *  Returns 0 if the timer is stopped.
     */
    double secs() const { return (fCurrTimeNanos - fBaseTimeNanos) * 1e-9; }

    /**
     *  Return the time in seconds the timer has been in the running state,
     *  scaled by "speed" and (if not zero) mod by period.
     *  Returns 0 if the timer is stopped.
     */
    SkScalar scaled(SkScalar speed, SkScalar period = 0) const {
        double value = this->secs() * speed;
        if (period) {
            value = ::fmod(value, SkScalarToDouble(period));
        }
        return SkDoubleToScalar(value);
    }

    /**
     * Transitions from ends->mid->ends linearly over period seconds. The phase specifies a phase
     * shift in seconds.
     */
    SkScalar pingPong(SkScalar period, SkScalar phase, SkScalar ends, SkScalar mid) const {
        return PingPong(this->secs(), period, phase, ends, mid);
    }

    /** Helper for computing a ping-pong value without a SkAnimTimer object. */
    static SkScalar PingPong(double t, SkScalar period, SkScalar phase, SkScalar ends,
                             SkScalar mid) {
        double value = ::fmod(t + phase, period);
        double half = period / 2.0;
        double diff = ::fabs(value - half);
        return SkDoubleToScalar(ends + (1.0 - diff / half) * (mid - ends));
    }

private:
    double  fBaseTimeNanos;
    double  fCurrTimeNanos;
    State   fState;

    void setState(State newState) {
        switch (newState) {
            case kStopped_State:
                fBaseTimeNanos = fCurrTimeNanos = 0;
                fState = kStopped_State;
                break;
            case kPaused_State:
                if (kRunning_State == fState) {
                    fState = kPaused_State;
                } // else stay stopped or paused
                break;
            case kRunning_State:
                switch (fState) {
                    case kStopped_State:
                        fBaseTimeNanos = fCurrTimeNanos = SkTime::GetNSecs();
                        break;
                    case kPaused_State: {// they want "resume"
                        double now = SkTime::GetNSecs();
                        fBaseTimeNanos += now - fCurrTimeNanos;
                        fCurrTimeNanos = now;
                    } break;
                    case kRunning_State:
                        break;
                }
                fState = kRunning_State;
                break;
        }
    }
};

#endif