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