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