1 #include "lottieanimation.h"
3 #include "lottieitem.h"
4 #include "lottieloader.h"
5 #include "lottiemodel.h"
9 using namespace lottie;
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);
23 const std::vector<LOTNode *> &
24 renderList(size_t frameNo, const VSize &size);
26 std::string mFilePath;
27 std::shared_ptr<LOTModel> mModel;
28 std::unique_ptr<LOTCompItem> mCompItem;
29 std::atomic<bool> mRenderInProgress;
32 const std::vector<LOTNode *> &AnimationImpl::renderList(size_t frameNo, const VSize &size)
34 if (update(frameNo, size)) {
35 mCompItem->buildRenderList();
37 return mCompItem->renderList();
40 bool AnimationImpl::update(size_t frameNo, const VSize &size)
42 frameNo += mModel->startFrame();
44 if (frameNo > mModel->endFrame())
45 frameNo = mModel->endFrame();
47 if (frameNo < mModel->startFrame())
48 frameNo = mModel->startFrame();
50 mCompItem->resize(size);
51 return mCompItem->update(frameNo);
54 Surface AnimationImpl::render(size_t frameNo, const Surface &surface)
56 bool renderInProgress = mRenderInProgress.load();
59 vCritical << "Already Rendering Scheduled for this Animation";
63 mRenderInProgress.store(true);
64 update(frameNo, VSize(surface.width(), surface.height()));
65 mCompItem->render(surface);
66 mRenderInProgress.store(false);
71 void AnimationImpl::init(const std::shared_ptr<LOTModel> &model)
74 mCompItem = std::make_unique<LOTCompItem>(mModel.get());
75 mRenderInProgress = false;
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.
90 RenderTask() { receiver = sender.get_future(); }
91 std::promise<Surface> sender;
92 std::future<Surface> receiver;
93 AnimationImpl *playerImpl;
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};
108 RenderTask *task = nullptr;
110 for (unsigned n = 0; n != _count * 32; ++n) {
111 if (_q[(i + n) % _count].try_pop(task)) break;
113 if (!task && !_q[i].pop(task)) break;
115 auto result = task->playerImpl->render(task->frameNo, task->surface);
116 task->sender.set_value(result);
122 RenderTaskScheduler()
124 for (unsigned n = 0; n != _count; ++n) {
125 _threads.emplace_back([&, n] { run(n); });
129 ~RenderTaskScheduler()
131 for (auto &e : _q) e.done();
133 for (auto &e : _threads) e.join();
136 std::future<Surface> async(RenderTask *task)
138 auto receiver = std::move(task->receiver);
141 for (unsigned n = 0; n != _count; ++n) {
142 if (_q[(i + n) % _count].try_push(task)) return receiver;
145 _q[i % _count].push(task);
150 std::future<Surface> render(AnimationImpl *impl, size_t frameNo,
153 RenderTask *task = new RenderTask();
154 task->playerImpl = impl;
155 task->frameNo = frameNo;
156 task->surface = std::move(surface);
160 static RenderTaskScheduler render_scheduler;
163 * \breif Brief abput the Api.
164 * Description about the setFilePath Api
165 * @param path add the details
167 std::unique_ptr<Animation>
168 Animation::loadFromData(std::string jsonData, const std::string &key)
170 if (jsonData.empty()) {
171 vWarning << "jason data is empty";
176 if (loader.loadFromData(std::move(jsonData), key)) {
177 auto animation = std::unique_ptr<Animation>(new Animation);
178 animation->d->init(loader.model());
184 std::unique_ptr<Animation>
185 Animation::loadFromFile(const std::string &path)
188 vWarning << "File path is empty";
193 if (loader.load(path)) {
194 auto animation = std::unique_ptr<Animation>(new Animation);
195 animation->d->init(loader.model());
201 void Animation::size(size_t &width, size_t &height) const
203 VSize sz = d->size();
206 height = sz.height();
209 double Animation::duration() const
211 return d->duration();
214 double Animation::frameRate() const
216 return d->frameRate();
219 size_t Animation::totalFrame() const
221 return d->totalFrame();
224 size_t Animation::frameAtPos(double pos)
226 return d->frameAtPos(pos);
229 const std::vector<LOTNode *> &
230 Animation::renderList(size_t frameNo, size_t width, size_t height) const
232 return d->renderList(frameNo, VSize(width, height));
235 std::future<Surface> Animation::render(size_t frameNo, Surface surface)
237 return render_scheduler.render(d.get(), frameNo, std::move(surface));
240 void Animation::renderSync(size_t frameNo, Surface surface)
242 d->render(frameNo, surface);
245 Animation::Animation(): d(std::make_unique<AnimationImpl>()) {}
248 * this is only to supress build fail
249 * because unique_ptr expects the destructor in the same translation unit.
251 Animation::~Animation(){}
253 Surface::Surface(uint32_t *buffer,
254 size_t width, size_t height, size_t bytesPerLine)
258 mBytesPerLine(bytesPerLine) {}
263 #if defined(__ARM_NEON__)
264 set_log_level(LogLevel::OFF);
266 initialize(GuaranteedLogger(), "/tmp/", "lotti-player", 1);
267 set_log_level(LogLevel::INFO);
271 V_CONSTRUCTOR_FUNCTION(initLogging)