/* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include "lottieitem.h" #include "lottiemodel.h" #include "rlottie.h" #include using namespace rlottie; using namespace rlottie::internal; RLOTTIE_API void rlottie::configureModelCacheSize(size_t cacheSize) { internal::model::configureModelCacheSize(cacheSize); } struct RenderTask { RenderTask() { receiver = sender.get_future(); } std::promise sender; std::future receiver; AnimationImpl * playerImpl{nullptr}; size_t frameNo{0}; Surface surface; bool keepAspectRatio{true}; }; using SharedRenderTask = std::shared_ptr; class AnimationImpl { public: void init(std::shared_ptr composition); bool update(size_t frameNo, const VSize &size, bool keepAspectRatio); VSize size() const { return mModel->size(); } double duration() const { return mModel->duration(); } double frameRate() const { return mModel->frameRate(); } size_t totalFrame() const { return mModel->totalFrame(); } size_t frameAtPos(double pos) const { return mModel->frameAtPos(pos); } Surface render(size_t frameNo, const Surface &surface, bool keepAspectRatio); std::future renderAsync(size_t frameNo, Surface &&surface, bool keepAspectRatio); const LOTLayerNode * renderTree(size_t frameNo, const VSize &size); const LayerInfoList &layerInfoList() const { if (mLayerList.empty()) { mLayerList = mModel->layerInfoList(); } return mLayerList; } const MarkerList &markers() const { return mModel->markers(); } void setValue(const std::string &keypath, LOTVariant &&value); void removeFilter(const std::string &keypath, Property prop); private: mutable LayerInfoList mLayerList; model::Composition * mModel; SharedRenderTask mTask; std::atomic mRenderInProgress; std::unique_ptr mRenderer{nullptr}; }; void AnimationImpl::setValue(const std::string &keypath, LOTVariant &&value) { if (keypath.empty()) return; mRenderer->setValue(keypath, value); } const LOTLayerNode *AnimationImpl::renderTree(size_t frameNo, const VSize &size) { if (update(frameNo, size, true)) { mRenderer->buildRenderTree(); } return mRenderer->renderTree(); } bool AnimationImpl::update(size_t frameNo, const VSize &size, bool keepAspectRatio) { frameNo += mModel->startFrame(); if (frameNo > mModel->endFrame()) frameNo = mModel->endFrame(); if (frameNo < mModel->startFrame()) frameNo = mModel->startFrame(); return mRenderer->update(int(frameNo), size, keepAspectRatio); } Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool keepAspectRatio) { bool renderInProgress = mRenderInProgress.load(); if (renderInProgress) { vCritical << "Already Rendering Scheduled for this Animation"; return surface; } mRenderInProgress.store(true); update( frameNo, VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())), keepAspectRatio); mRenderer->render(surface); mRenderInProgress.store(false); return surface; } void AnimationImpl::init(std::shared_ptr composition) { mModel = composition.get(); mRenderer = std::make_unique(composition); mRenderInProgress = false; } #ifdef LOTTIE_THREAD_SUPPORT #include #include "vtaskqueue.h" /* * Implement a task stealing schduler to perform render task * As each player draws into its own buffer we can delegate this * task to a slave thread. The scheduler creates a threadpool depending * on the number of cores available in the system and does a simple fair * scheduling by assigning the task in a round-robin fashion. Each thread * in the threadpool has its own queue. once it finishes all the task on its * own queue it goes through rest of the queue and looks for task if it founds * one it steals the task from it and executes. if it couldn't find one then it * just waits for new task on its own queue. */ class RenderTaskScheduler { const unsigned _count{std::thread::hardware_concurrency()}; std::vector _threads; std::vector> _q{_count}; std::atomic _index{0}; void run(unsigned i) { while (true) { bool success = false; SharedRenderTask task; for (unsigned n = 0; n != _count * 2; ++n) { if (_q[(i + n) % _count].try_pop(task)) { success = true; break; } } if (!success && !_q[i].pop(task)) break; auto result = task->playerImpl->render(task->frameNo, task->surface, task->keepAspectRatio); task->sender.set_value(result); } } RenderTaskScheduler() { for (unsigned n = 0; n != _count; ++n) { _threads.emplace_back([&, n] { run(n); }); } IsRunning = true; } public: static bool IsRunning; static RenderTaskScheduler &instance() { static RenderTaskScheduler singleton; return singleton; } ~RenderTaskScheduler() { stop(); } void stop() { if (IsRunning) { IsRunning = false; for (auto &e : _q) e.done(); for (auto &e : _threads) e.join(); } } std::future process(SharedRenderTask task) { auto receiver = std::move(task->receiver); auto i = _index++; for (unsigned n = 0; n != _count; ++n) { if (_q[(i + n) % _count].try_push(std::move(task))) return receiver; } if (_count > 0) { _q[i % _count].push(std::move(task)); } return receiver; } }; #else class RenderTaskScheduler { public: static bool IsRunning; static RenderTaskScheduler &instance() { static RenderTaskScheduler singleton; return singleton; } void stop() {} std::future process(SharedRenderTask task) { auto result = task->playerImpl->render(task->frameNo, task->surface, task->keepAspectRatio); task->sender.set_value(result); return std::move(task->receiver); } }; #endif bool RenderTaskScheduler::IsRunning{false}; std::future AnimationImpl::renderAsync(size_t frameNo, Surface &&surface, bool keepAspectRatio) { if (!mTask) { mTask = std::make_shared(); } else { mTask->sender = std::promise(); mTask->receiver = mTask->sender.get_future(); } mTask->playerImpl = this; mTask->frameNo = frameNo; mTask->surface = std::move(surface); mTask->keepAspectRatio = keepAspectRatio; return RenderTaskScheduler::instance().process(mTask); } /** * \breif Brief abput the Api. * Description about the setFilePath Api * @param path add the details */ std::unique_ptr Animation::loadFromData( std::string jsonData, const std::string &key, const std::string &resourcePath, bool cachePolicy) { if (jsonData.empty()) { vWarning << "jason data is empty"; return nullptr; } auto composition = model::loadFromData(std::move(jsonData), key, resourcePath, cachePolicy); if (composition) { auto animation = std::unique_ptr(new Animation); animation->d->init(std::move(composition)); return animation; } return nullptr; } std::unique_ptr Animation::loadFromData(std::string jsonData, std::string resourcePath, ColorFilter filter) { if (jsonData.empty()) { vWarning << "jason data is empty"; return nullptr; } auto composition = model::loadFromData( std::move(jsonData), std::move(resourcePath), std::move(filter)); if (composition) { auto animation = std::unique_ptr(new Animation); animation->d->init(std::move(composition)); return animation; } return nullptr; } std::unique_ptr Animation::loadFromFile(const std::string &path, bool cachePolicy) { if (path.empty()) { vWarning << "File path is empty"; return nullptr; } auto composition = model::loadFromFile(path, cachePolicy); if (composition) { auto animation = std::unique_ptr(new Animation); animation->d->init(std::move(composition)); return animation; } return nullptr; } void Animation::size(size_t &width, size_t &height) const { VSize sz = d->size(); width = sz.width(); height = sz.height(); } double Animation::duration() const { return d->duration(); } double Animation::frameRate() const { return d->frameRate(); } size_t Animation::totalFrame() const { return d->totalFrame(); } size_t Animation::frameAtPos(double pos) { return d->frameAtPos(pos); } const LOTLayerNode *Animation::renderTree(size_t frameNo, size_t width, size_t height) const { return d->renderTree(frameNo, VSize(int(width), int(height))); } std::future Animation::render(size_t frameNo, Surface surface, bool keepAspectRatio) { return d->renderAsync(frameNo, std::move(surface), keepAspectRatio); } void Animation::renderSync(size_t frameNo, Surface surface, bool keepAspectRatio) { d->render(frameNo, surface, keepAspectRatio); } const LayerInfoList &Animation::layers() const { return d->layerInfoList(); } const MarkerList &Animation::markers() const { return d->markers(); } void Animation::setValue(Color_Type, Property prop, const std::string &keypath, Color value) { d->setValue(keypath, LOTVariant(prop, [value](const FrameInfo &) { return value; })); } void Animation::setValue(Float_Type, Property prop, const std::string &keypath, float value) { d->setValue(keypath, LOTVariant(prop, [value](const FrameInfo &) { return value; })); } void Animation::setValue(Size_Type, Property prop, const std::string &keypath, Size value) { d->setValue(keypath, LOTVariant(prop, [value](const FrameInfo &) { return value; })); } void Animation::setValue(Point_Type, Property prop, const std::string &keypath, Point value) { d->setValue(keypath, LOTVariant(prop, [value](const FrameInfo &) { return value; })); } void Animation::setValue(Color_Type, Property prop, const std::string &keypath, std::function &&value) { d->setValue(keypath, LOTVariant(prop, value)); } void Animation::setValue(Float_Type, Property prop, const std::string &keypath, std::function &&value) { d->setValue(keypath, LOTVariant(prop, value)); } void Animation::setValue(Size_Type, Property prop, const std::string &keypath, std::function &&value) { d->setValue(keypath, LOTVariant(prop, value)); } void Animation::setValue(Point_Type, Property prop, const std::string &keypath, std::function &&value) { d->setValue(keypath, LOTVariant(prop, value)); } Animation::~Animation() = default; Animation::Animation() : d(std::make_unique()) {} Surface::Surface(uint32_t *buffer, size_t width, size_t height, size_t bytesPerLine) : mBuffer(buffer), mWidth(width), mHeight(height), mBytesPerLine(bytesPerLine) { mDrawArea.w = mWidth; mDrawArea.h = mHeight; } void Surface::setDrawRegion(size_t x, size_t y, size_t width, size_t height) { if ((x + width > mWidth) || (y + height > mHeight)) return; mDrawArea.x = x; mDrawArea.y = y; mDrawArea.w = width; mDrawArea.h = height; } namespace { void lottieShutdownRenderTaskScheduler() { if (RenderTaskScheduler::IsRunning) { RenderTaskScheduler::instance().stop(); } } } // namespace // private apis exposed to c interface void lottie_init_impl() { // do nothing for now. } extern void lottieShutdownRasterTaskScheduler(); void lottie_shutdown_impl() { lottieShutdownRenderTaskScheduler(); lottieShutdownRasterTaskScheduler(); } #ifdef LOTTIE_LOGGING_SUPPORT void initLogging() { #if defined(__ARM_NEON__) set_log_level(LogLevel::OFF); #else initialize(GuaranteedLogger(), "/tmp/", "rlottie", 1); set_log_level(LogLevel::INFO); #endif } V_CONSTRUCTOR_FUNCTION(initLogging) #endif