• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 Google LLC.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4#include "tools/skottie_ios_app/SkottieViewController.h"
5
6#include "include/core/SkCanvas.h"
7#include "include/core/SkPaint.h"
8#include "include/core/SkSurface.h"
9#include "include/core/SkTime.h"
10#include "modules/skottie/include/Skottie.h"
11
12#include <cmath>
13
14////////////////////////////////////////////////////////////////////////////////
15
16class SkAnimationDraw {
17public:
18    SkAnimationDraw() = default;
19    ~SkAnimationDraw() = default;
20
21    explicit operator bool() const { return fAnimation != nullptr; }
22
23    void draw(SkSize size, SkCanvas* canvas) {
24        if (size.width() != fSize.width() || size.height() != fSize.height()) {
25            // Cache the current matrix; change only if size changes.
26            if (fAnimationSize.width() > 0 && fAnimationSize.height() > 0) {
27                float scale = std::min(size.width() / fAnimationSize.width(),
28                                       size.height() / fAnimationSize.height());
29                fMatrix.setScaleTranslate(
30                        scale, scale,
31                        (size.width()  - fAnimationSize.width()  * scale) * 0.5f,
32                        (size.height() - fAnimationSize.height() * scale) * 0.5f);
33            } else {
34                fMatrix = SkMatrix();
35            }
36            fSize = size;
37        }
38        canvas->concat(fMatrix);
39        SkRect rect = {0, 0, fAnimationSize.width(), fAnimationSize.height()};
40        canvas->drawRect(rect, SkPaint(SkColors::kWhite));
41        fAnimation->render(canvas);
42    }
43
44    void load(const void* data, size_t length) {
45        skottie::Animation::Builder builder;
46        fAnimation = builder.make((const char*)data, (size_t)length);
47        fSize = {0, 0};
48        fAnimationSize = fAnimation ? fAnimation->size() : SkSize{0, 0};
49    }
50
51    void seek(double time) { if (fAnimation) { fAnimation->seekFrameTime(time, nullptr); } }
52
53    float duration() { return fAnimation ? fAnimation->duration() : 0; }
54
55    SkSize size() { return fAnimationSize; }
56
57private:
58    sk_sp<skottie::Animation> fAnimation; // owner
59    SkSize fSize;
60    SkSize fAnimationSize;
61    SkMatrix fMatrix;
62
63    SkAnimationDraw(const SkAnimationDraw&) = delete;
64    SkAnimationDraw& operator=(const SkAnimationDraw&) = delete;
65};
66
67////////////////////////////////////////////////////////////////////////////////
68
69class SkTimeKeeper {
70private:
71    double fStartTime = 0; // used when running
72    float fAnimationMoment = 0; // when paused.
73    float fDuration = 0;
74    bool fPaused = false;
75    bool fStopAtEnd = false;
76
77public:
78    void setStopAtEnd(bool s) { fStopAtEnd = s; }
79
80    float currentTime() {
81        if (0 == fDuration) {
82            return 0;
83        }
84        if (fPaused) {
85            return fAnimationMoment;
86        }
87        double time = 1e-9 * (SkTime::GetNSecs() - fStartTime);
88        if (fStopAtEnd && time >= fDuration) {
89            fPaused = true;
90            fAnimationMoment = fDuration;
91            return fAnimationMoment;
92        }
93        return std::fmod(time, fDuration);
94    }
95
96    void setDuration(float d) {
97        fDuration = d;
98        fStartTime = SkTime::GetNSecs();
99        fAnimationMoment = 0;
100    }
101
102    bool paused() const { return fPaused; }
103
104    float duration() const { return fDuration; }
105
106    void seek(float seconds) {
107        if (fPaused) {
108            fAnimationMoment = std::fmod(seconds, fDuration);
109        } else {
110            fStartTime = SkTime::GetNSecs() - 1e9 * seconds;
111        }
112    }
113
114    void togglePaused() {
115        if (fPaused) {
116            double offset = (fAnimationMoment >= fDuration) ? 0 : -1e9 * fAnimationMoment;
117            fStartTime = SkTime::GetNSecs() + offset;
118            fPaused = false;
119        } else {
120            fAnimationMoment = this->currentTime();
121            fPaused = true;
122        }
123    }
124};
125
126////////////////////////////////////////////////////////////////////////////////
127
128@implementation SkottieViewController {
129    SkAnimationDraw fDraw;
130    SkTimeKeeper fClock;
131}
132
133- (bool)loadAnimation:(NSData*) data {
134    fDraw.load((const void*)[data bytes], (size_t)[data length]);
135    fClock.setDuration(fDraw.duration());
136    return (bool)fDraw;
137}
138
139- (void)setStopAtEnd:(bool)stop { fClock.setStopAtEnd(stop); }
140
141- (float)animationDurationSeconds { return fClock.duration(); }
142
143- (float)currentTime { return fDraw ? fClock.currentTime() : 0; }
144
145- (void)seek:(float)seconds {
146    if (fDraw) {
147        fClock.seek(seconds);
148    }
149}
150
151- (CGSize)size { return {(CGFloat)fDraw.size().width(), (CGFloat)fDraw.size().height()}; }
152
153- (bool)togglePaused {
154    fClock.togglePaused();
155    return fClock.paused();
156}
157
158- (bool)isPaused { return fClock.paused(); }
159
160- (void)draw:(CGRect)rect toCanvas:(SkCanvas*)canvas atSize:(CGSize)size {
161    // TODO(halcanary): Use the rect and the InvalidationController to speed up rendering.
162    if (rect.size.width > 0 && rect.size.height > 0 && fDraw && canvas) {
163        if (!fClock.paused()) {
164            fDraw.seek(fClock.currentTime());
165        }
166        fDraw.draw(SkSize{(float)size.width, (float)size.height}, canvas);
167    }
168}
169
170@end
171