fb5b495c17c06c3c8a2c0ed73670aaa055be7694
[platform/core/uifw/lottie-player.git] / src / lottie / lottieanimation.cpp
1 #include "lottieanimation.h"
2
3 #include "lottieitem.h"
4 #include "lottieloader.h"
5 #include "lottiemodel.h"
6
7 #include <fstream>
8
9 using namespace lottie;
10
11 class AnimationImpl {
12
13 public:
14     void    init(const std::shared_ptr<LOTModel> &model);
15     bool    update(size_t frameNo, const VSize &size);
16     VSize   size() const {return mCompItem->size();}
17     double  duration() const {return mModel->duration();}
18     double  frameRate() const {return mModel->frameRate();}
19     size_t  totalFrame() const {return mModel->frameDuration();}
20     size_t  frameAtPos(double pos) const {return mModel->frameAtPos(pos);}
21     Surface render(size_t frameNo, const Surface &surface);
22
23     const LOTLayerNode *
24                  renderTree(size_t frameNo, const VSize &size);
25 private:
26     std::string                  mFilePath;
27     std::shared_ptr<LOTModel>    mModel;
28     std::unique_ptr<LOTCompItem> mCompItem;
29     std::atomic<bool>            mRenderInProgress;
30 };
31
32 const LOTLayerNode *AnimationImpl::renderTree(size_t frameNo, const VSize &size)
33 {
34     if (update(frameNo, size)) {
35        mCompItem->buildRenderTree();
36     }
37     return mCompItem->renderTree();
38 }
39
40 bool AnimationImpl::update(size_t frameNo, const VSize &size)
41 {
42    frameNo += mModel->startFrame();
43
44    if (frameNo > mModel->endFrame())
45        frameNo = mModel->endFrame();
46
47    if (frameNo < mModel->startFrame())
48        frameNo = mModel->startFrame();
49
50    mCompItem->resize(size);
51    return mCompItem->update(frameNo);
52 }
53
54 Surface AnimationImpl::render(size_t frameNo, const Surface &surface)
55 {
56     bool renderInProgress = mRenderInProgress.load();
57     if (renderInProgress)
58       {
59         vCritical << "Already Rendering Scheduled for this Animation";
60         return surface;
61       }
62
63     mRenderInProgress.store(true);
64     update(frameNo, VSize(surface.width(), surface.height()));
65     mCompItem->render(surface);
66     mRenderInProgress.store(false);
67
68     return surface;
69 }
70
71 void AnimationImpl::init(const std::shared_ptr<LOTModel> &model)
72 {
73     mModel = model;
74     mCompItem = std::make_unique<LOTCompItem>(mModel.get());
75     mRenderInProgress = false;
76 }
77
78 /*
79  * Implement a task stealing schduler to perform render task
80  * As each player draws into its own buffer we can delegate this
81  * task to a slave thread. The scheduler creates a threadpool depending
82  * on the number of cores available in the system and does a simple fair
83  * scheduling by assigning the task in a round-robin fashion. Each thread
84  * in the threadpool has its own queue. once it finishes all the task on its
85  * own queue it goes through rest of the queue and looks for task if it founds
86  * one it steals the task from it and executes. if it couldn't find one then it
87  * just waits for new task on its own queue.
88  */
89 struct RenderTask {
90     RenderTask() { receiver = sender.get_future(); }
91     std::promise<Surface> sender;
92     std::future<Surface>  receiver;
93     AnimationImpl     *playerImpl{nullptr};
94     size_t              frameNo{0};
95     Surface            surface;
96 };
97
98 #include <vtaskqueue.h>
99 class RenderTaskScheduler {
100     const unsigned           _count{std::thread::hardware_concurrency()};
101     std::vector<std::thread> _threads;
102     std::vector<TaskQueue<RenderTask>> _q{_count};
103     std::atomic<unsigned>              _index{0};
104
105     void run(unsigned i)
106     {
107         RenderTask task;
108         while (true) {
109             bool success = false;
110             for (unsigned n = 0; n != _count * 32; ++n) {
111                 if (_q[(i + n) % _count].try_pop(task)) {
112                     success = true;
113                     break;
114                 }
115             }
116             if (!success && !_q[i].pop(task)) break;
117
118             auto result = task.playerImpl->render(task.frameNo, task.surface);
119             task.sender.set_value(result);
120         }
121     }
122
123     RenderTaskScheduler()
124     {
125         for (unsigned n = 0; n != _count; ++n) {
126             _threads.emplace_back([&, n] { run(n); });
127         }
128     }
129
130 public:
131     static RenderTaskScheduler& instance()
132     {
133          static RenderTaskScheduler singleton;
134          return singleton;
135     }
136
137     ~RenderTaskScheduler()
138     {
139         for (auto &e : _q) e.done();
140
141         for (auto &e : _threads) e.join();
142     }
143
144     std::future<Surface> async(RenderTask &&task)
145     {
146         auto receiver = std::move(task.receiver);
147         auto i = _index++;
148
149         for (unsigned n = 0; n != _count; ++n) {
150             if (_q[(i + n) % _count].try_push(std::move(task))) return receiver;
151         }
152
153         _q[i % _count].push(std::move(task));
154
155         return receiver;
156     }
157
158     std::future<Surface> render(AnimationImpl *impl, size_t frameNo,
159                              Surface &&surface)
160     {
161         RenderTask task;
162         task.playerImpl = impl;
163         task.frameNo = frameNo;
164         task.surface = std::move(surface);
165         return async(std::move(task));
166     }
167 };
168
169 /**
170  * \breif Brief abput the Api.
171  * Description about the setFilePath Api
172  * @param path  add the details
173  */
174 std::unique_ptr<Animation>
175 Animation::loadFromData(std::string jsonData, const std::string &key)
176 {
177     if (jsonData.empty()) {
178         vWarning << "jason data is empty";
179         return nullptr;
180     }
181
182     LottieLoader loader;
183     if (loader.loadFromData(std::move(jsonData), key)) {
184         auto animation = std::unique_ptr<Animation>(new Animation);
185         animation->d->init(loader.model());
186         return animation;
187     }
188     return nullptr;
189 }
190
191 std::unique_ptr<Animation>
192 Animation::loadFromFile(const std::string &path)
193 {
194     if (path.empty()) {
195         vWarning << "File path is empty";
196         return nullptr;
197     }
198
199     LottieLoader loader;
200     if (loader.load(path)) {
201         auto animation = std::unique_ptr<Animation>(new Animation);
202         animation->d->init(loader.model());
203         return animation;
204     }
205     return nullptr;
206 }
207
208 void Animation::size(size_t &width, size_t &height) const
209 {
210     VSize sz = d->size();
211
212     width = sz.width();
213     height = sz.height();
214 }
215
216 double Animation::duration() const
217 {
218     return d->duration();
219 }
220
221 double Animation::frameRate() const
222 {
223     return d->frameRate();
224 }
225
226 size_t Animation::totalFrame() const
227 {
228     return d->totalFrame();
229 }
230
231 size_t Animation::frameAtPos(double pos)
232 {
233     return d->frameAtPos(pos);
234 }
235
236 const LOTLayerNode *
237 Animation::renderTree(size_t frameNo, size_t width, size_t height) const
238 {
239     return d->renderTree(frameNo, VSize(width, height));
240 }
241
242 std::future<Surface> Animation::render(size_t frameNo, Surface surface)
243 {
244     return RenderTaskScheduler::instance().render(d.get(), frameNo, std::move(surface));
245 }
246
247 void Animation::renderSync(size_t frameNo, Surface surface)
248 {
249     d->render(frameNo, surface);
250 }
251
252 Animation::Animation(): d(std::make_unique<AnimationImpl>()) {}
253
254 /*
255  * this is only to supress build fail
256  * because unique_ptr expects the destructor in the same translation unit.
257  */
258 Animation::~Animation(){}
259
260 Surface::Surface(uint32_t *buffer,
261                  size_t width, size_t height, size_t bytesPerLine)
262                 :mBuffer(buffer),
263                  mWidth(width),
264                  mHeight(height),
265                  mBytesPerLine(bytesPerLine) {}
266
267
268 void initLogging()
269 {
270 #if defined(__ARM_NEON__)
271     set_log_level(LogLevel::OFF);
272 #else
273     initialize(GuaranteedLogger(), "/tmp/", "lotti-player", 1);
274     set_log_level(LogLevel::INFO);
275 #endif
276 }
277
278 V_CONSTRUCTOR_FUNCTION(initLogging)