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