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.
4 #include "tools/skottie_ios_app/SkottieViewController.h"
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"
14 ////////////////////////////////////////////////////////////////////////////////
16 class SkAnimationDraw {
18 SkAnimationDraw() = default;
19 ~SkAnimationDraw() = default;
21 explicit operator bool() const { return fAnimation != nullptr; }
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(
31 (size.width() - fAnimationSize.width() * scale) * 0.5f,
32 (size.height() - fAnimationSize.height() * scale) * 0.5f);
38 canvas->concat(fMatrix);
39 SkRect rect = {0, 0, fAnimationSize.width(), fAnimationSize.height()};
40 canvas->drawRect(rect, SkPaint(SkColors::kWhite));
41 fAnimation->render(canvas);
44 void load(const void* data, size_t length) {
45 skottie::Animation::Builder builder;
46 fAnimation = builder.make((const char*)data, (size_t)length);
48 fAnimationSize = fAnimation ? fAnimation->size() : SkSize{0, 0};
51 void seek(double time) { if (fAnimation) { fAnimation->seekFrameTime(time, nullptr); } }
53 float duration() { return fAnimation ? fAnimation->duration() : 0; }
55 SkSize size() { return fAnimationSize; }
58 sk_sp<skottie::Animation> fAnimation; // owner
60 SkSize fAnimationSize;
63 SkAnimationDraw(const SkAnimationDraw&) = delete;
64 SkAnimationDraw& operator=(const SkAnimationDraw&) = delete;
67 ////////////////////////////////////////////////////////////////////////////////
71 double fStartTime = 0; // used when running
72 float fAnimationMoment = 0; // when paused.
75 bool fStopAtEnd = false;
78 void setStopAtEnd(bool s) { fStopAtEnd = s; }
85 return fAnimationMoment;
87 double time = 1e-9 * (SkTime::GetNSecs() - fStartTime);
88 if (fStopAtEnd && time >= fDuration) {
90 fAnimationMoment = fDuration;
91 return fAnimationMoment;
93 return std::fmod(time, fDuration);
96 void setDuration(float d) {
98 fStartTime = SkTime::GetNSecs();
102 bool paused() const { return fPaused; }
104 float duration() const { return fDuration; }
106 void seek(float seconds) {
108 fAnimationMoment = std::fmod(seconds, fDuration);
110 fStartTime = SkTime::GetNSecs() - 1e9 * seconds;
114 void togglePaused() {
116 double offset = (fAnimationMoment >= fDuration) ? 0 : -1e9 * fAnimationMoment;
117 fStartTime = SkTime::GetNSecs() + offset;
120 fAnimationMoment = this->currentTime();
126 ////////////////////////////////////////////////////////////////////////////////
128 @implementation SkottieViewController {
129 SkAnimationDraw fDraw;
133 - (bool)loadAnimation:(NSData*) data {
134 fDraw.load((const void*)[data bytes], (size_t)[data length]);
135 fClock.setDuration(fDraw.duration());
139 - (void)setStopAtEnd:(bool)stop { fClock.setStopAtEnd(stop); }
141 - (float)animationDurationSeconds { return fClock.duration(); }
143 - (float)currentTime { return fDraw ? fClock.currentTime() : 0; }
145 - (void)seek:(float)seconds {
147 fClock.seek(seconds);
151 - (CGSize)size { return {(CGFloat)fDraw.size().width(), (CGFloat)fDraw.size().height()}; }
153 - (bool)togglePaused {
154 fClock.togglePaused();
155 return fClock.paused();
158 - (bool)isPaused { return fClock.paused(); }
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());
166 fDraw.draw(SkSize{(float)size.width, (float)size.height}, canvas);