Created proper program structure for LottieComponent.

This commit is contained in:
Leon Styhre 2022-01-08 11:25:29 +01:00
parent 8d648867e1
commit 3f629b6c1e
2 changed files with 76 additions and 35 deletions

View file

@ -25,6 +25,7 @@ LottieComponent::LottieComponent(Window* window)
, mTargetPacing{0} , mTargetPacing{0}
, mTimeAccumulator{0} , mTimeAccumulator{0}
, mHoldFrame{false} , mHoldFrame{false}
, mKeepAspectRatio{true}
{ {
// Get an empty texture for rendering the animation. // Get an empty texture for rendering the animation.
mTexture = TextureResource::get(""); mTexture = TextureResource::get("");
@ -34,37 +35,45 @@ LottieComponent::LottieComponent(Window* window)
mTexture->setFormat(Renderer::Texture::BGRA); mTexture->setFormat(Renderer::Texture::BGRA);
#endif #endif
// TODO: Temporary test files. // Set component defaults.
std::string filePath{":/animations/a_mountain.json"}; setOrigin(0.5f, 0.5f);
// std::string filePath{":/animations/bell.json"}; setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f);
setPosition(Renderer::getScreenWidth() * 0.3f, Renderer::getScreenHeight() * 0.3f);
setDefaultZIndex(30.0f);
setZIndex(30.0f);
}
if (filePath.empty()) { void LottieComponent::setAnimation(const std::string& path)
{
if (mAnimation != nullptr) {
if (mFuture.valid())
mFuture.get();
mSurface.reset();
mAnimation.reset();
mPictureRGBA.clear();
}
mPath = path;
if (mPath.empty()) {
LOG(LogError) << "Path to Lottie animation is empty"; LOG(LogError) << "Path to Lottie animation is empty";
return; return;
} }
if (filePath.front() == ':') if (mPath.front() == ':')
filePath = ResourceManager::getInstance().getResourcePath(filePath); mPath = ResourceManager::getInstance().getResourcePath(mPath);
else else
filePath = Utils::FileSystem::expandHomePath(filePath); mPath = Utils::FileSystem::expandHomePath(mPath);
if (!(Utils::FileSystem::isRegularFile(filePath) || Utils::FileSystem::isSymlink(filePath))) { if (!(Utils::FileSystem::isRegularFile(mPath) || Utils::FileSystem::isSymlink(mPath))) {
LOG(LogError) << "Couldn't open Lottie animation file \"" << filePath << "\""; LOG(LogError) << "Couldn't open Lottie animation file \"" << mPath << "\"";
return; return;
} }
// TODO: Only meant for development, to be replaced with proper theming support. mAnimation = rlottie::Animation::loadFromFile(mPath);
setOrigin(0.5f, 0.5f);
setSize(500.0f, 500.0f);
setPosition(mSize.x * 0.35f, mSize.y * 0.5f);
setDefaultZIndex(70.0f);
setZIndex(70.0f);
setVisible(true);
mAnimation = rlottie::Animation::loadFromFile(filePath);
if (mAnimation == nullptr) { if (mAnimation == nullptr) {
LOG(LogError) << "Couldn't parse Lottie animation file \"" << filePath << "\""; LOG(LogError) << "Couldn't parse Lottie animation file \"" << mPath << "\"";
return; return;
} }
@ -72,13 +81,21 @@ LottieComponent::LottieComponent(Window* window)
size_t height = static_cast<size_t>(mSize.y); size_t height = static_cast<size_t>(mSize.y);
mPictureRGBA.resize(width * height * 4); mPictureRGBA.resize(width * height * 4);
mSurface = std::make_unique<rlottie::Surface>(reinterpret_cast<uint32_t*>(&mPictureRGBA[0]), mSurface = std::make_unique<rlottie::Surface>(reinterpret_cast<uint32_t*>(&mPictureRGBA[0]),
width, height, width * sizeof(uint32_t)); width, height, width * sizeof(uint32_t));
// Animation time in seconds. if (mSurface == nullptr) {
double duration = mAnimation->duration(); LOG(LogError) << "Couldn't create Lottie surface for file \"" << mPath << "\"";
mAnimation.reset();
return;
}
// Render the first frame as a type of preload to decrease the chance of seeing a blank
// texture when first entering a view that uses this animation.
mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);
// Some statistics for the file.
double duration = mAnimation->duration();
mTotalFrames = mAnimation->totalFrame(); mTotalFrames = mAnimation->totalFrame();
mFrameRate = mAnimation->frameRate(); mFrameRate = mAnimation->frameRate();
@ -89,6 +106,12 @@ LottieComponent::LottieComponent(Window* window)
LOG(LogDebug) << "Duration: " << duration; LOG(LogDebug) << "Duration: " << duration;
} }
void LottieComponent::onSizeChanged()
{
// Setting the animation again will completely reinitialize it.
setAnimation(mPath);
}
void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& view,
const std::string& element, const std::string& element,
@ -100,6 +123,9 @@ void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
void LottieComponent::update(int deltaTime) void LottieComponent::update(int deltaTime)
{ {
if (mAnimation == nullptr)
return;
if (mTimeAccumulator < mTargetPacing) { if (mTimeAccumulator < mTargetPacing) {
mHoldFrame = true; mHoldFrame = true;
mTimeAccumulator += deltaTime; mTimeAccumulator += deltaTime;
@ -115,6 +141,11 @@ void LottieComponent::render(const glm::mat4& parentTrans)
if (!isVisible()) if (!isVisible())
return; return;
if (mAnimation == nullptr)
return;
glm::mat4 trans{parentTrans * getTransform()};
if (mFrameNum >= mTotalFrames) if (mFrameNum >= mTotalFrames)
mFrameNum = 0; mFrameNum = 0;
@ -135,33 +166,37 @@ void LottieComponent::render(const glm::mat4& parentTrans)
else { else {
renderNextFrame = true; renderNextFrame = true;
} }
if (renderNextFrame && !mHoldFrame) if (renderNextFrame && !mHoldFrame)
mFuture = mAnimation->render(mFrameNum, *mSurface); mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);
Renderer::setMatrix(trans);
if (Settings::getInstance()->getBool("DebugImage"))
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
if (mTexture->getSize().x != 0.0f) { if (mTexture->getSize().x != 0.0f) {
mTexture->bind(); mTexture->bind();
Renderer::Vertex vertices[4];
// clang-format off // clang-format off
mVertices[0] = {{0.0f, 0.0f }, {0.0f, 0.0f}, 0xFFFFFFFF}; vertices[0] = {{0.0f, 0.0f }, {0.0f, 0.0f}, 0xFFFFFFFF};
mVertices[1] = {{0.0f, mSize.y}, {0.0f, 1.0f}, 0xFFFFFFFF}; vertices[1] = {{0.0f, mSize.y}, {0.0f, 1.0f}, 0xFFFFFFFF};
mVertices[2] = {{mSize.x, 0.0f }, {1.0f, 0.0f}, 0xFFFFFFFF}; vertices[2] = {{mSize.x, 0.0f }, {1.0f, 0.0f}, 0xFFFFFFFF};
mVertices[3] = {{mSize.x, mSize.y}, {1.0f, 1.0f}, 0xFFFFFFFF}; vertices[3] = {{mSize.x, mSize.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
// clang-format on // clang-format on
// Round vertices. // Round vertices.
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
mVertices[i].pos = glm::round(mVertices[i].pos); vertices[i].pos = glm::round(vertices[i].pos);
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
// Perform color space conversion from BGRA to RGBA. // Perform color space conversion from BGRA to RGBA.
mVertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA; vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA;
#endif #endif
// Render it. // Render it.
glm::mat4 trans{parentTrans * getTransform()}; Renderer::drawTriangleStrips(&vertices[0], 4, trans);
Renderer::setMatrix(trans);
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
} }
if (!mHoldFrame && mFrameNum == mTotalFrames - 1) { if (!mHoldFrame && mFrameNum == mTotalFrames - 1) {

View file

@ -23,6 +23,11 @@ class LottieComponent : public GuiComponent
public: public:
LottieComponent(Window* window); LottieComponent(Window* window);
void setAnimation(const std::string& path);
void setKeepAspectRatio(bool value) { mKeepAspectRatio = value; }
void onSizeChanged() override;
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& view,
const std::string& element, const std::string& element,
@ -35,11 +40,10 @@ private:
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
std::vector<uint8_t> mPictureRGBA; std::vector<uint8_t> mPictureRGBA;
Renderer::Vertex mVertices[4];
std::unique_ptr<rlottie::Animation> mAnimation; std::unique_ptr<rlottie::Animation> mAnimation;
std::unique_ptr<rlottie::Surface> mSurface; std::unique_ptr<rlottie::Surface> mSurface;
std::future<rlottie::Surface> mFuture; std::future<rlottie::Surface> mFuture;
std::string mPath;
size_t mTotalFrames; size_t mTotalFrames;
size_t mFrameNum; size_t mFrameNum;
double mFrameRate; double mFrameRate;
@ -47,6 +51,8 @@ private:
int mTimeAccumulator; int mTimeAccumulator;
bool mHoldFrame; bool mHoldFrame;
bool mKeepAspectRatio;
std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime; std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime;
std::chrono::time_point<std::chrono::system_clock> mAnimationEndTime; std::chrono::time_point<std::chrono::system_clock> mAnimationEndTime;
}; };