2022-01-06 22:15:29 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//
|
|
|
|
// EmulationStation Desktop Edition
|
|
|
|
// LottieComponent.cpp
|
|
|
|
//
|
|
|
|
// Component to play Lottie animations using the rlottie library.
|
|
|
|
//
|
|
|
|
|
2022-01-09 18:26:42 +00:00
|
|
|
#define DEBUG_ANIMATION false
|
2022-01-09 12:43:42 +00:00
|
|
|
|
2022-01-06 22:15:29 +00:00
|
|
|
#include "components/LottieComponent.h"
|
|
|
|
|
|
|
|
#include "Log.h"
|
2022-01-09 12:43:42 +00:00
|
|
|
#include "ThemeData.h"
|
2022-01-06 22:15:29 +00:00
|
|
|
#include "Window.h"
|
|
|
|
#include "resources/ResourceManager.h"
|
|
|
|
#include "utils/FileSystemUtil.h"
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
LottieComponent::LottieComponent()
|
|
|
|
: mCacheFrames {true}
|
2022-01-16 11:09:55 +00:00
|
|
|
, mMaxCacheSize {0}
|
|
|
|
, mCacheSize {0}
|
|
|
|
, mFrameSize {0}
|
|
|
|
, mAnimation {nullptr}
|
|
|
|
, mSurface {nullptr}
|
|
|
|
, mStartDirection {"normal"}
|
|
|
|
, mTotalFrames {0}
|
|
|
|
, mFrameNum {0}
|
|
|
|
, mFrameRate {0.0}
|
|
|
|
, mSpeedModifier {1.0f}
|
|
|
|
, mTargetPacing {0}
|
|
|
|
, mTimeAccumulator {0}
|
|
|
|
, mLastRenderedFrame {-1}
|
|
|
|
, mSkippedFrames {0}
|
|
|
|
, mHoldFrame {false}
|
|
|
|
, mPause {false}
|
|
|
|
, mAlternate {false}
|
|
|
|
, mKeepAspectRatio {true}
|
2022-01-06 22:15:29 +00:00
|
|
|
{
|
|
|
|
// Get an empty texture for rendering the animation.
|
|
|
|
mTexture = TextureResource::get("");
|
2022-01-13 18:39:49 +00:00
|
|
|
#if defined(USE_OPENGLES_10) || defined(USE_OPENGLES_20)
|
2022-01-07 18:09:07 +00:00
|
|
|
// This is not really supported by the OpenGL ES standard so hopefully it works
|
|
|
|
// with all drivers and on all operating systems.
|
|
|
|
mTexture->setFormat(Renderer::Texture::BGRA);
|
|
|
|
#endif
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-08 14:04:10 +00:00
|
|
|
// Keep per-file cache size within 0 to 1024 MiB.
|
|
|
|
mMaxCacheSize = static_cast<size_t>(
|
|
|
|
glm::clamp(Settings::getInstance()->getInt("LottieMaxFileCache"), 0, 1024) * 1024 * 1024);
|
|
|
|
|
|
|
|
// Keep total cache size within 0 to 4096 MiB.
|
|
|
|
int maxTotalCache =
|
|
|
|
glm::clamp(Settings::getInstance()->getInt("LottieMaxTotalCache"), 0, 4096) * 1024 * 1024;
|
|
|
|
|
|
|
|
if (mMaxTotalFrameCache != static_cast<size_t>(maxTotalCache))
|
|
|
|
mMaxTotalFrameCache = static_cast<size_t>(maxTotalCache);
|
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
// Set component defaults.
|
|
|
|
setOrigin(0.5f, 0.5f);
|
|
|
|
setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f);
|
|
|
|
setPosition(Renderer::getScreenWidth() * 0.3f, Renderer::getScreenHeight() * 0.3f);
|
2022-01-09 12:43:42 +00:00
|
|
|
setDefaultZIndex(10.0f);
|
|
|
|
setZIndex(10.0f);
|
2022-01-08 10:25:29 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 14:04:10 +00:00
|
|
|
LottieComponent::~LottieComponent()
|
|
|
|
{
|
2022-01-09 17:17:23 +00:00
|
|
|
// This is required as rlottie could otherwise crash on application shutdown.
|
2022-01-08 14:04:10 +00:00
|
|
|
if (mFuture.valid())
|
|
|
|
mFuture.get();
|
2022-01-09 17:17:23 +00:00
|
|
|
|
2022-01-08 14:04:10 +00:00
|
|
|
mTotalFrameCache -= mCacheSize;
|
|
|
|
}
|
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
void LottieComponent::setAnimation(const std::string& path)
|
|
|
|
{
|
|
|
|
if (mAnimation != nullptr) {
|
|
|
|
if (mFuture.valid())
|
|
|
|
mFuture.get();
|
|
|
|
mSurface.reset();
|
|
|
|
mAnimation.reset();
|
|
|
|
mPictureRGBA.clear();
|
2022-01-08 14:04:10 +00:00
|
|
|
mCacheSize = 0;
|
2022-01-10 16:42:01 +00:00
|
|
|
mLastRenderedFrame = -1;
|
2022-01-08 10:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mPath = path;
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
if (mPath.empty()) {
|
2022-01-06 22:15:29 +00:00
|
|
|
LOG(LogError) << "Path to Lottie animation is empty";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
if (mPath.front() == ':')
|
|
|
|
mPath = ResourceManager::getInstance().getResourcePath(mPath);
|
2022-01-06 22:15:29 +00:00
|
|
|
else
|
2022-01-08 10:25:29 +00:00
|
|
|
mPath = Utils::FileSystem::expandHomePath(mPath);
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
if (!(Utils::FileSystem::isRegularFile(mPath) || Utils::FileSystem::isSymlink(mPath))) {
|
|
|
|
LOG(LogError) << "Couldn't open Lottie animation file \"" << mPath << "\"";
|
2022-01-06 22:15:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
mAnimation = rlottie::Animation::loadFromFile(mPath);
|
2022-01-06 22:15:29 +00:00
|
|
|
|
|
|
|
if (mAnimation == nullptr) {
|
2022-01-08 10:25:29 +00:00
|
|
|
LOG(LogError) << "Couldn't parse Lottie animation file \"" << mPath << "\"";
|
2022-01-06 22:15:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
if (!mKeepAspectRatio && (mSize.x == 0.0f || mSize.y == 0.0f)) {
|
|
|
|
LOG(LogWarning) << "LottieComponent: Width or height auto sizing is incompatible with "
|
|
|
|
"disabling of <keepAspectRatio> so ignoring this setting";
|
|
|
|
}
|
|
|
|
|
2022-01-16 11:09:55 +00:00
|
|
|
size_t width {0};
|
|
|
|
size_t height {0};
|
2022-01-09 12:43:42 +00:00
|
|
|
|
|
|
|
if (mSize.x == 0.0f || mSize.y == 0.0f) {
|
2022-01-16 11:09:55 +00:00
|
|
|
size_t viewportWidth {0};
|
|
|
|
size_t viewportHeight {0};
|
2022-01-09 12:43:42 +00:00
|
|
|
|
|
|
|
mAnimation->size(viewportWidth, viewportHeight);
|
|
|
|
double sizeRatio = static_cast<double>(viewportWidth) / static_cast<double>(viewportHeight);
|
|
|
|
|
|
|
|
if (mSize.x == 0) {
|
|
|
|
width = static_cast<size_t>(static_cast<double>(mSize.y) * sizeRatio);
|
|
|
|
height = static_cast<size_t>(mSize.y);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
width = static_cast<size_t>(mSize.x);
|
|
|
|
height = static_cast<size_t>(static_cast<double>(mSize.x) / sizeRatio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
width = static_cast<size_t>(mSize.x);
|
|
|
|
height = static_cast<size_t>(mSize.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
mSize.x = static_cast<float>(width);
|
|
|
|
mSize.y = static_cast<float>(height);
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-07 18:09:07 +00:00
|
|
|
mPictureRGBA.resize(width * height * 4);
|
|
|
|
mSurface = std::make_unique<rlottie::Surface>(reinterpret_cast<uint32_t*>(&mPictureRGBA[0]),
|
|
|
|
width, height, width * sizeof(uint32_t));
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
if (mSurface == nullptr) {
|
|
|
|
LOG(LogError) << "Couldn't create Lottie surface for file \"" << mPath << "\"";
|
|
|
|
mAnimation.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some statistics for the file.
|
|
|
|
double duration = mAnimation->duration();
|
2022-01-06 22:15:29 +00:00
|
|
|
mTotalFrames = mAnimation->totalFrame();
|
|
|
|
mFrameRate = mAnimation->frameRate();
|
2022-01-08 14:04:10 +00:00
|
|
|
mFrameSize = width * height * 4;
|
2022-01-09 12:43:42 +00:00
|
|
|
mTargetPacing = static_cast<int>((1000.0 / mFrameRate) / static_cast<double>(mSpeedModifier));
|
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
mDirection = mStartDirection;
|
|
|
|
|
|
|
|
if (mDirection == "reverse")
|
|
|
|
mFrameNum = mTotalFrames - 1;
|
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
if (DEBUG_ANIMATION) {
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Rasterized width: " << mSize.x;
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Rasterized height: " << mSize.y;
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Total number of frames: "
|
|
|
|
<< mTotalFrames;
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Frame rate: " << mFrameRate;
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Speed modifier: " << mSpeedModifier;
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Target duration: "
|
|
|
|
<< duration / mSpeedModifier * 1000.0 << " ms";
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Frame size: " << mFrameSize << " bytes ("
|
|
|
|
<< std::fixed << std::setprecision(1)
|
|
|
|
<< static_cast<double>(mFrameSize) / 1024.0 / 1024.0 << " MiB)";
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Animation size: "
|
|
|
|
<< mFrameSize * mTotalFrames << " bytes (" << std::fixed
|
|
|
|
<< std::setprecision(1)
|
|
|
|
<< static_cast<double>(mFrameSize * mTotalFrames) / 1024.0 / 1024.0
|
|
|
|
<< " MiB)";
|
|
|
|
LOG(LogDebug) << "LottieComponent::setAnimation(): Per file maximum cache size: "
|
|
|
|
<< mMaxCacheSize << " bytes (" << std::fixed << std::setprecision(1)
|
|
|
|
<< static_cast<double>(mMaxCacheSize) / 1024.0 / 1024.0 << " MiB)";
|
|
|
|
}
|
2022-01-06 22:15:29 +00:00
|
|
|
}
|
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
void LottieComponent::resetFileAnimation()
|
|
|
|
{
|
|
|
|
mTimeAccumulator = 0;
|
|
|
|
mFrameNum = mStartDirection == "reverse" ? mTotalFrames - 1 : 0;
|
|
|
|
|
2022-01-10 16:42:01 +00:00
|
|
|
if (mAnimation != nullptr) {
|
|
|
|
if (mFuture.valid())
|
|
|
|
mFuture.get();
|
2022-01-09 17:17:23 +00:00
|
|
|
mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);
|
2022-01-10 18:53:23 +00:00
|
|
|
mLastRenderedFrame = static_cast<int>(mFrameNum);
|
2022-01-10 16:42:01 +00:00
|
|
|
}
|
2022-01-09 17:17:23 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
void LottieComponent::onSizeChanged()
|
|
|
|
{
|
|
|
|
// Setting the animation again will completely reinitialize it.
|
2022-01-08 14:04:10 +00:00
|
|
|
if (mPath != "")
|
|
|
|
setAnimation(mPath);
|
2022-01-08 10:25:29 +00:00
|
|
|
}
|
|
|
|
|
2022-01-06 22:15:29 +00:00
|
|
|
void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
|
|
|
const std::string& view,
|
|
|
|
const std::string& element,
|
|
|
|
unsigned int properties)
|
|
|
|
{
|
2022-01-09 12:43:42 +00:00
|
|
|
using namespace ThemeFlags;
|
2022-01-16 11:09:55 +00:00
|
|
|
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "animation")};
|
2022-01-09 12:43:42 +00:00
|
|
|
|
|
|
|
if (elem->has("size")) {
|
|
|
|
glm::vec2 size = elem->get<glm::vec2>("size");
|
|
|
|
if (size.x == 0.0f && size.y == 0.0f) {
|
|
|
|
LOG(LogWarning) << "LottieComponent: Invalid theme configuration, <size> set to \""
|
|
|
|
<< size.x << " " << size.y << "\"";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elem->has("speed")) {
|
2022-01-16 11:09:55 +00:00
|
|
|
const float speed {elem->get<float>("speed")};
|
2022-01-09 12:43:42 +00:00
|
|
|
if (speed < 0.2f || speed > 3.0f) {
|
|
|
|
LOG(LogWarning) << "LottieComponent: Invalid theme configuration, <speed> set to \""
|
|
|
|
<< std::fixed << std::setprecision(1) << speed << "\"";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mSpeedModifier = speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elem->has("keepAspectRatio")) {
|
|
|
|
mKeepAspectRatio = elem->get<bool>("keepAspectRatio");
|
|
|
|
}
|
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
if (elem->has("direction")) {
|
|
|
|
std::string direction = elem->get<std::string>("direction");
|
|
|
|
if (direction == "normal") {
|
|
|
|
mStartDirection = "normal";
|
|
|
|
mAlternate = false;
|
|
|
|
}
|
|
|
|
else if (direction == "reverse") {
|
|
|
|
mStartDirection = "reverse";
|
|
|
|
mAlternate = false;
|
|
|
|
}
|
|
|
|
else if (direction == "alternate") {
|
|
|
|
mStartDirection = "normal";
|
|
|
|
mAlternate = true;
|
|
|
|
}
|
|
|
|
else if (direction == "alternateReverse") {
|
|
|
|
mStartDirection = "reverse";
|
|
|
|
mAlternate = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG(LogWarning) << "LottieComponent: Invalid theme configuration, <direction> set to \""
|
|
|
|
<< direction << "\"";
|
|
|
|
mStartDirection = "normal";
|
|
|
|
mAlternate = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:15:29 +00:00
|
|
|
GuiComponent::applyTheme(theme, view, element, properties);
|
2022-01-09 12:43:42 +00:00
|
|
|
|
|
|
|
if (elem->has("path")) {
|
2022-01-16 11:09:55 +00:00
|
|
|
std::string path {elem->get<std::string>("path")};
|
2022-01-09 12:43:42 +00:00
|
|
|
if (path != "") {
|
|
|
|
setAnimation(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG(LogWarning) << "LottieComponent: Invalid theme configuration, <path> not set";
|
|
|
|
return;
|
|
|
|
}
|
2022-01-06 22:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LottieComponent::update(int deltaTime)
|
|
|
|
{
|
2022-01-08 10:25:29 +00:00
|
|
|
if (mAnimation == nullptr)
|
|
|
|
return;
|
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
if (mWindow->getAllowFileAnimation()) {
|
|
|
|
mPause = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mPause = true;
|
|
|
|
mTimeAccumulator = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the time accumulator value is really high something must have happened such as the
|
|
|
|
// application having been suspended. Reset it to zero in this case as it would otherwise
|
|
|
|
// never recover.
|
|
|
|
if (mTimeAccumulator > deltaTime * 200)
|
|
|
|
mTimeAccumulator = 0;
|
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
// Prevent animation from playing too quickly.
|
2022-01-09 12:43:42 +00:00
|
|
|
if (mTimeAccumulator + deltaTime < mTargetPacing) {
|
2022-01-07 18:09:07 +00:00
|
|
|
mHoldFrame = true;
|
|
|
|
mTimeAccumulator += deltaTime;
|
2022-01-06 22:15:29 +00:00
|
|
|
}
|
|
|
|
else {
|
2022-01-07 18:09:07 +00:00
|
|
|
mHoldFrame = false;
|
|
|
|
mTimeAccumulator = mTimeAccumulator - mTargetPacing + deltaTime;
|
2022-01-06 22:15:29 +00:00
|
|
|
}
|
2022-01-09 12:43:42 +00:00
|
|
|
|
|
|
|
// Rudimentary frame skipping logic, not entirely accurate but probably good enough.
|
|
|
|
while (mTimeAccumulator - deltaTime > mTargetPacing) {
|
|
|
|
if (DEBUG_ANIMATION && 0) {
|
|
|
|
LOG(LogDebug)
|
|
|
|
<< "LottieComponent::update(): Skipped frame, mTimeAccumulator / mTargetPacing: "
|
|
|
|
<< mTimeAccumulator - deltaTime << " / " << mTargetPacing;
|
|
|
|
}
|
2022-01-09 17:17:23 +00:00
|
|
|
|
|
|
|
if (mDirection == "reverse")
|
|
|
|
--mFrameNum;
|
|
|
|
else
|
|
|
|
++mFrameNum;
|
|
|
|
|
|
|
|
++mSkippedFrames;
|
2022-01-09 12:43:42 +00:00
|
|
|
mTimeAccumulator -= mTargetPacing;
|
|
|
|
}
|
2022-01-06 22:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LottieComponent::render(const glm::mat4& parentTrans)
|
|
|
|
{
|
2022-02-12 16:38:55 +00:00
|
|
|
if (!isVisible() || mThemeOpacity == 0.0f || mAnimation == nullptr)
|
2022-01-08 10:25:29 +00:00
|
|
|
return;
|
|
|
|
|
2022-01-16 11:09:55 +00:00
|
|
|
glm::mat4 trans {parentTrans * getTransform()};
|
2022-01-08 10:25:29 +00:00
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
// This is necessary as there may otherwise be no texture to render when paused.
|
|
|
|
if (mPause && mTexture->getSize().x == 0.0f) {
|
|
|
|
mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x),
|
|
|
|
static_cast<size_t>(mSize.y));
|
|
|
|
}
|
2022-01-08 14:04:10 +00:00
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
bool doRender = true;
|
|
|
|
|
|
|
|
// Don't render if a menu is open except if the cached background is getting invalidated.
|
|
|
|
if (mWindow->getGuiStackSize() > 1) {
|
|
|
|
if (mWindow->isInvalidatingCachedBackground())
|
|
|
|
doRender = true;
|
|
|
|
else
|
|
|
|
doRender = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't render any new frames if paused or if a menu is open (unless invalidating background).
|
|
|
|
if (!mPause && doRender) {
|
|
|
|
if ((mDirection == "normal" && mFrameNum >= mTotalFrames) ||
|
|
|
|
(mDirection == "reverse" && mFrameNum > mTotalFrames)) {
|
2022-01-09 12:43:42 +00:00
|
|
|
if (DEBUG_ANIMATION) {
|
2022-01-09 17:17:23 +00:00
|
|
|
LOG(LogDebug) << "LottieComponent::render(): Skipped frames: " << mSkippedFrames;
|
2022-01-09 12:43:42 +00:00
|
|
|
LOG(LogDebug) << "LottieComponent::render(): Actual duration: "
|
|
|
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::system_clock::now() - mAnimationStartTime)
|
|
|
|
.count()
|
|
|
|
<< " ms";
|
2022-01-08 14:04:10 +00:00
|
|
|
}
|
2022-01-09 17:17:23 +00:00
|
|
|
|
|
|
|
if (mAlternate) {
|
|
|
|
if (mDirection == "normal")
|
|
|
|
mDirection = "reverse";
|
|
|
|
else
|
|
|
|
mDirection = "normal";
|
|
|
|
}
|
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
mTimeAccumulator = 0;
|
2022-01-09 17:17:23 +00:00
|
|
|
mSkippedFrames = 0;
|
|
|
|
|
|
|
|
if (mDirection == "reverse")
|
|
|
|
mFrameNum = mTotalFrames - 1;
|
|
|
|
else
|
|
|
|
mFrameNum = 0;
|
2022-01-09 12:43:42 +00:00
|
|
|
}
|
2022-01-08 14:04:10 +00:00
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
if (DEBUG_ANIMATION) {
|
2022-01-09 17:17:23 +00:00
|
|
|
if ((mDirection == "normal" && mFrameNum == 0) ||
|
|
|
|
(mDirection == "reverse" && mFrameNum == mTotalFrames - 1))
|
2022-01-09 12:43:42 +00:00
|
|
|
mAnimationStartTime = std::chrono::system_clock::now();
|
|
|
|
}
|
2022-01-08 14:04:10 +00:00
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
bool renderNextFrame = false;
|
|
|
|
|
|
|
|
if (mFuture.valid()) {
|
|
|
|
if (mFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) {
|
|
|
|
mFuture.get();
|
|
|
|
// Cache frame if caching is enabled and we're not exceeding either the per-file
|
|
|
|
// max cache size or the total cache size. Note that this is completely unrelated
|
|
|
|
// to the texture caching used for images.
|
2022-01-10 16:42:01 +00:00
|
|
|
if (mCacheFrames && mLastRenderedFrame != -1 &&
|
|
|
|
mFrameCache.find(mLastRenderedFrame) == mFrameCache.end()) {
|
2022-01-09 12:43:42 +00:00
|
|
|
size_t newCacheSize = mCacheSize + mFrameSize;
|
|
|
|
if (newCacheSize < mMaxCacheSize &&
|
|
|
|
mTotalFrameCache + mFrameSize < mMaxTotalFrameCache) {
|
2022-01-10 16:42:01 +00:00
|
|
|
mFrameCache[mLastRenderedFrame] = mPictureRGBA;
|
2022-01-09 12:43:42 +00:00
|
|
|
mCacheSize += mFrameSize;
|
|
|
|
mTotalFrameCache += mFrameSize;
|
2022-01-10 16:42:01 +00:00
|
|
|
mLastRenderedFrame = -1;
|
2022-01-09 12:43:42 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-08 14:04:10 +00:00
|
|
|
|
2022-01-09 12:43:42 +00:00
|
|
|
mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x),
|
2022-01-08 14:04:10 +00:00
|
|
|
static_cast<size_t>(mSize.y));
|
2022-01-09 12:43:42 +00:00
|
|
|
|
2022-01-09 17:17:23 +00:00
|
|
|
if (mDirection == "reverse")
|
|
|
|
--mFrameNum;
|
|
|
|
else
|
|
|
|
++mFrameNum;
|
2022-01-09 12:43:42 +00:00
|
|
|
|
2022-01-09 18:26:42 +00:00
|
|
|
if (mDirection == "reverse" && mFrameNum == 0)
|
|
|
|
renderNextFrame = false;
|
|
|
|
else if (mFrameNum == mTotalFrames)
|
2022-01-09 12:43:42 +00:00
|
|
|
renderNextFrame = false;
|
|
|
|
else
|
|
|
|
renderNextFrame = true;
|
2022-01-08 14:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2022-01-09 12:43:42 +00:00
|
|
|
if (mFrameCache.find(mFrameNum) != mFrameCache.end()) {
|
|
|
|
if (!mHoldFrame) {
|
|
|
|
mTexture->initFromPixels(&mFrameCache[mFrameNum][0],
|
|
|
|
static_cast<size_t>(mSize.x),
|
|
|
|
static_cast<size_t>(mSize.y));
|
2022-01-09 17:17:23 +00:00
|
|
|
|
|
|
|
if (mDirection == "reverse")
|
|
|
|
--mFrameNum;
|
|
|
|
else
|
|
|
|
++mFrameNum;
|
2022-01-09 12:43:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
renderNextFrame = true;
|
|
|
|
}
|
2022-01-08 14:04:10 +00:00
|
|
|
}
|
|
|
|
|
2022-01-10 16:42:01 +00:00
|
|
|
if (renderNextFrame && !mHoldFrame) {
|
2022-01-09 12:43:42 +00:00
|
|
|
mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);
|
2022-01-10 18:53:23 +00:00
|
|
|
mLastRenderedFrame = static_cast<int>(mFrameNum);
|
2022-01-10 16:42:01 +00:00
|
|
|
}
|
2022-01-09 12:43:42 +00:00
|
|
|
}
|
2022-01-08 10:25:29 +00:00
|
|
|
|
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
|
|
|
if (Settings::getInstance()->getBool("DebugImage"))
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-07 18:09:07 +00:00
|
|
|
if (mTexture->getSize().x != 0.0f) {
|
|
|
|
mTexture->bind();
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-08 10:25:29 +00:00
|
|
|
Renderer::Vertex vertices[4];
|
|
|
|
|
2022-01-07 18:09:07 +00:00
|
|
|
// clang-format off
|
2022-01-08 10:25:29 +00:00
|
|
|
vertices[0] = {{0.0f, 0.0f }, {0.0f, 0.0f}, 0xFFFFFFFF};
|
|
|
|
vertices[1] = {{0.0f, mSize.y}, {0.0f, 1.0f}, 0xFFFFFFFF};
|
|
|
|
vertices[2] = {{mSize.x, 0.0f }, {1.0f, 0.0f}, 0xFFFFFFFF};
|
|
|
|
vertices[3] = {{mSize.x, mSize.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
|
2022-01-07 18:09:07 +00:00
|
|
|
// clang-format on
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-07 18:09:07 +00:00
|
|
|
// Round vertices.
|
|
|
|
for (int i = 0; i < 4; ++i)
|
2022-01-08 10:25:29 +00:00
|
|
|
vertices[i].pos = glm::round(vertices[i].pos);
|
2022-01-06 22:15:29 +00:00
|
|
|
|
2022-01-07 18:09:07 +00:00
|
|
|
#if defined(USE_OPENGL_21)
|
|
|
|
// Perform color space conversion from BGRA to RGBA.
|
2022-02-12 16:38:55 +00:00
|
|
|
vertices[0].opacity = mThemeOpacity;
|
2022-01-08 10:25:29 +00:00
|
|
|
vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA;
|
2022-01-07 18:09:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Render it.
|
2022-01-08 10:25:29 +00:00
|
|
|
Renderer::drawTriangleStrips(&vertices[0], 4, trans);
|
2022-01-07 18:09:07 +00:00
|
|
|
}
|
2022-01-06 23:30:31 +00:00
|
|
|
}
|