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);
24 renderTree(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 LOTLayerNode *AnimationImpl::renderTree(size_t frameNo, const VSize &size)
34 if (update(frameNo, size)) {
35 mCompItem->buildRenderTree();
37 return mCompItem->renderTree();
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{nullptr};
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};
109 bool success = false;
110 for (unsigned n = 0; n != _count * 32; ++n) {
111 if (_q[(i + n) % _count].try_pop(task)) {
116 if (!success && !_q[i].pop(task)) break;
118 auto result = task.playerImpl->render(task.frameNo, task.surface);
119 task.sender.set_value(result);
123 RenderTaskScheduler()
125 for (unsigned n = 0; n != _count; ++n) {
126 _threads.emplace_back([&, n] { run(n); });
131 static RenderTaskScheduler& instance()
133 static RenderTaskScheduler singleton;
137 ~RenderTaskScheduler()
139 for (auto &e : _q) e.done();
141 for (auto &e : _threads) e.join();
144 std::future<Surface> async(RenderTask &&task)
146 auto receiver = std::move(task.receiver);
149 for (unsigned n = 0; n != _count; ++n) {
150 if (_q[(i + n) % _count].try_push(std::move(task))) return receiver;
153 _q[i % _count].push(std::move(task));
158 std::future<Surface> render(AnimationImpl *impl, size_t frameNo,
162 task.playerImpl = impl;
163 task.frameNo = frameNo;
164 task.surface = std::move(surface);
165 return async(std::move(task));
170 * \breif Brief abput the Api.
171 * Description about the setFilePath Api
172 * @param path add the details
174 std::unique_ptr<Animation>
175 Animation::loadFromData(std::string jsonData, const std::string &key)
177 if (jsonData.empty()) {
178 vWarning << "jason data is empty";
183 if (loader.loadFromData(std::move(jsonData), key)) {
184 auto animation = std::unique_ptr<Animation>(new Animation);
185 animation->d->init(loader.model());
191 std::unique_ptr<Animation>
192 Animation::loadFromFile(const std::string &path)
195 vWarning << "File path is empty";
200 if (loader.load(path)) {
201 auto animation = std::unique_ptr<Animation>(new Animation);
202 animation->d->init(loader.model());
208 void Animation::size(size_t &width, size_t &height) const
210 VSize sz = d->size();
213 height = sz.height();
216 double Animation::duration() const
218 return d->duration();
221 double Animation::frameRate() const
223 return d->frameRate();
226 size_t Animation::totalFrame() const
228 return d->totalFrame();
231 size_t Animation::frameAtPos(double pos)
233 return d->frameAtPos(pos);
237 Animation::renderTree(size_t frameNo, size_t width, size_t height) const
239 return d->renderTree(frameNo, VSize(width, height));
242 std::future<Surface> Animation::render(size_t frameNo, Surface surface)
244 return RenderTaskScheduler::instance().render(d.get(), frameNo, std::move(surface));
247 void Animation::renderSync(size_t frameNo, Surface surface)
249 d->render(frameNo, surface);
252 Animation::Animation(): d(std::make_unique<AnimationImpl>()) {}
255 * this is only to supress build fail
256 * because unique_ptr expects the destructor in the same translation unit.
258 Animation::~Animation(){}
260 Surface::Surface(uint32_t *buffer,
261 size_t width, size_t height, size_t bytesPerLine)
265 mBytesPerLine(bytesPerLine) {}
270 #if defined(__ARM_NEON__)
271 set_log_level(LogLevel::OFF);
273 initialize(GuaranteedLogger(), "/tmp/", "lotti-player", 1);
274 set_log_level(LogLevel::INFO);
278 V_CONSTRUCTOR_FUNCTION(initLogging)