171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| // Copyright 2019 Google LLC.
 | |
| // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
 | |
| 
 | |
| #include "tools/skottie_ios_app/SkottieViewController.h"
 | |
| 
 | |
| #include "include/core/SkCanvas.h"
 | |
| #include "include/core/SkPaint.h"
 | |
| #include "include/core/SkSurface.h"
 | |
| #include "include/core/SkTime.h"
 | |
| #include "modules/skottie/include/Skottie.h"
 | |
| 
 | |
| #include <cmath>
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| class SkAnimationDraw {
 | |
| public:
 | |
|     SkAnimationDraw() = default;
 | |
|     ~SkAnimationDraw() = default;
 | |
| 
 | |
|     explicit operator bool() const { return fAnimation != nullptr; }
 | |
| 
 | |
|     void draw(SkSize size, SkCanvas* canvas) {
 | |
|         if (size.width() != fSize.width() || size.height() != fSize.height()) {
 | |
|             // Cache the current matrix; change only if size changes.
 | |
|             if (fAnimationSize.width() > 0 && fAnimationSize.height() > 0) {
 | |
|                 float scale = std::min(size.width() / fAnimationSize.width(),
 | |
|                                        size.height() / fAnimationSize.height());
 | |
|                 fMatrix.setScaleTranslate(
 | |
|                         scale, scale,
 | |
|                         (size.width()  - fAnimationSize.width()  * scale) * 0.5f,
 | |
|                         (size.height() - fAnimationSize.height() * scale) * 0.5f);
 | |
|             } else {
 | |
|                 fMatrix = SkMatrix();
 | |
|             }
 | |
|             fSize = size;
 | |
|         }
 | |
|         canvas->concat(fMatrix);
 | |
|         SkRect rect = {0, 0, fAnimationSize.width(), fAnimationSize.height()};
 | |
|         canvas->drawRect(rect, SkPaint(SkColors::kWhite));
 | |
|         fAnimation->render(canvas);
 | |
|     }
 | |
| 
 | |
|     void load(const void* data, size_t length) {
 | |
|         skottie::Animation::Builder builder;
 | |
|         fAnimation = builder.make((const char*)data, (size_t)length);
 | |
|         fSize = {0, 0};
 | |
|         fAnimationSize = fAnimation ? fAnimation->size() : SkSize{0, 0};
 | |
|     }
 | |
| 
 | |
|     void seek(double time) { if (fAnimation) { fAnimation->seekFrameTime(time, nullptr); } }
 | |
| 
 | |
|     float duration() { return fAnimation ? fAnimation->duration() : 0; }
 | |
| 
 | |
|     SkSize size() { return fAnimationSize; }
 | |
| 
 | |
| private:
 | |
|     sk_sp<skottie::Animation> fAnimation; // owner
 | |
|     SkSize fSize;
 | |
|     SkSize fAnimationSize;
 | |
|     SkMatrix fMatrix;
 | |
| 
 | |
|     SkAnimationDraw(const SkAnimationDraw&) = delete;
 | |
|     SkAnimationDraw& operator=(const SkAnimationDraw&) = delete;
 | |
| };
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| class SkTimeKeeper {
 | |
| private:
 | |
|     double fStartTime = 0; // used when running
 | |
|     float fAnimationMoment = 0; // when paused.
 | |
|     float fDuration = 0;
 | |
|     bool fPaused = false;
 | |
|     bool fStopAtEnd = false;
 | |
| 
 | |
| public:
 | |
|     void setStopAtEnd(bool s) { fStopAtEnd = s; }
 | |
| 
 | |
|     float currentTime() {
 | |
|         if (0 == fDuration) {
 | |
|             return 0;
 | |
|         }
 | |
|         if (fPaused) {
 | |
|             return fAnimationMoment;
 | |
|         }
 | |
|         double time = 1e-9 * (SkTime::GetNSecs() - fStartTime);
 | |
|         if (fStopAtEnd && time >= fDuration) {
 | |
|             fPaused = true;
 | |
|             fAnimationMoment = fDuration;
 | |
|             return fAnimationMoment;
 | |
|         }
 | |
|         return std::fmod(time, fDuration);
 | |
|     }
 | |
| 
 | |
|     void setDuration(float d) {
 | |
|         fDuration = d;
 | |
|         fStartTime = SkTime::GetNSecs();
 | |
|         fAnimationMoment = 0;
 | |
|     }
 | |
| 
 | |
|     bool paused() const { return fPaused; }
 | |
| 
 | |
|     float duration() const { return fDuration; }
 | |
| 
 | |
|     void seek(float seconds) {
 | |
|         if (fPaused) {
 | |
|             fAnimationMoment = std::fmod(seconds, fDuration);
 | |
|         } else {
 | |
|             fStartTime = SkTime::GetNSecs() - 1e9 * seconds;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void togglePaused() {
 | |
|         if (fPaused) {
 | |
|             double offset = (fAnimationMoment >= fDuration) ? 0 : -1e9 * fAnimationMoment;
 | |
|             fStartTime = SkTime::GetNSecs() + offset;
 | |
|             fPaused = false;
 | |
|         } else {
 | |
|             fAnimationMoment = this->currentTime();
 | |
|             fPaused = true;
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| @implementation SkottieViewController {
 | |
|     SkAnimationDraw fDraw;
 | |
|     SkTimeKeeper fClock;
 | |
| }
 | |
| 
 | |
| - (bool)loadAnimation:(NSData*) data {
 | |
|     fDraw.load((const void*)[data bytes], (size_t)[data length]);
 | |
|     fClock.setDuration(fDraw.duration());
 | |
|     return (bool)fDraw;
 | |
| }
 | |
| 
 | |
| - (void)setStopAtEnd:(bool)stop { fClock.setStopAtEnd(stop); }
 | |
| 
 | |
| - (float)animationDurationSeconds { return fClock.duration(); }
 | |
| 
 | |
| - (float)currentTime { return fDraw ? fClock.currentTime() : 0; }
 | |
| 
 | |
| - (void)seek:(float)seconds {
 | |
|     if (fDraw) {
 | |
|         fClock.seek(seconds);
 | |
|     }
 | |
| }
 | |
| 
 | |
| - (CGSize)size { return {(CGFloat)fDraw.size().width(), (CGFloat)fDraw.size().height()}; }
 | |
| 
 | |
| - (bool)togglePaused {
 | |
|     fClock.togglePaused();
 | |
|     return fClock.paused();
 | |
| }
 | |
| 
 | |
| - (bool)isPaused { return fClock.paused(); }
 | |
| 
 | |
| - (void)draw:(CGRect)rect toCanvas:(SkCanvas*)canvas atSize:(CGSize)size {
 | |
|     // TODO(halcanary): Use the rect and the InvalidationController to speed up rendering.
 | |
|     if (rect.size.width > 0 && rect.size.height > 0 && fDraw && canvas) {
 | |
|         if (!fClock.paused()) {
 | |
|             fDraw.seek(fClock.currentTime());
 | |
|         }
 | |
|         fDraw.draw(SkSize{(float)size.width, (float)size.height}, canvas);
 | |
|     }
 | |
| }
 | |
| 
 | |
| @end
 |