Added play direction support to LottieComponent.

Also fixed some bugs and cleaned up the code a bit.
This commit is contained in:
Leon Styhre 2022-01-09 18:17:23 +01:00
parent e730d24da0
commit cd7cfe36ce
3 changed files with 104 additions and 31 deletions

View file

@ -157,7 +157,6 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> The
{"speed", FLOAT},
{"direction", STRING},
{"keepAspectRatio", BOOLEAN},
{"delay", FLOAT},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"badges",

View file

@ -26,14 +26,17 @@ LottieComponent::LottieComponent(Window* window)
, mFrameSize{0}
, mAnimation{nullptr}
, mSurface{nullptr}
, mStartDirection{"normal"}
, mTotalFrames{0}
, mFrameNum{0}
, mFrameRate{0.0}
, mSpeedModifier{1.0f}
, mTargetPacing{0}
, mTimeAccumulator{0}
, mSkippedFrames{0}
, mHoldFrame{false}
, mDroppedFrames{0}
, mSpeedModifier{1.0f}
, mPause{false}
, mAlternate{false}
, mKeepAspectRatio{true}
{
// Get an empty texture for rendering the animation.
@ -65,8 +68,10 @@ LottieComponent::LottieComponent(Window* window)
LottieComponent::~LottieComponent()
{
// This is required as rlottie could otherwise crash on application shutdown.
if (mFuture.valid())
mFuture.get();
mTotalFrameCache -= mCacheSize;
}
@ -147,10 +152,6 @@ void LottieComponent::setAnimation(const std::string& path)
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();
@ -158,6 +159,11 @@ void LottieComponent::setAnimation(const std::string& path)
mFrameSize = width * height * 4;
mTargetPacing = static_cast<int>((1000.0 / mFrameRate) / static_cast<double>(mSpeedModifier));
mDirection = mStartDirection;
if (mDirection == "reverse")
mFrameNum = mTotalFrames - 1;
if (DEBUG_ANIMATION) {
LOG(LogDebug) << "LottieComponent::setAnimation(): Rasterized width: " << mSize.x;
LOG(LogDebug) << "LottieComponent::setAnimation(): Rasterized height: " << mSize.y;
@ -181,6 +187,15 @@ void LottieComponent::setAnimation(const std::string& path)
}
}
void LottieComponent::resetFileAnimation()
{
mTimeAccumulator = 0;
mFrameNum = mStartDirection == "reverse" ? mTotalFrames - 1 : 0;
if (mAnimation != nullptr)
mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);
}
void LottieComponent::onSizeChanged()
{
// Setting the animation again will completely reinitialize it.
@ -221,6 +236,32 @@ void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
mKeepAspectRatio = elem->get<bool>("keepAspectRatio");
}
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;
}
}
GuiComponent::applyTheme(theme, view, element, properties);
if (elem->has("path")) {
@ -255,7 +296,7 @@ void LottieComponent::update(int deltaTime)
if (mTimeAccumulator > deltaTime * 200)
mTimeAccumulator = 0;
// Keep animation speed from going too quickly.
// Prevent animation from playing too quickly.
if (mTimeAccumulator + deltaTime < mTargetPacing) {
mHoldFrame = true;
mTimeAccumulator += deltaTime;
@ -272,8 +313,13 @@ void LottieComponent::update(int deltaTime)
<< "LottieComponent::update(): Skipped frame, mTimeAccumulator / mTargetPacing: "
<< mTimeAccumulator - deltaTime << " / " << mTargetPacing;
}
if (mDirection == "reverse")
--mFrameNum;
else
++mFrameNum;
++mDroppedFrames;
++mSkippedFrames;
mTimeAccumulator -= mTargetPacing;
}
}
@ -294,24 +340,48 @@ void LottieComponent::render(const glm::mat4& parentTrans)
static_cast<size_t>(mSize.y));
}
// Don't render any new frames if paused or if a menu is open.
if (!mPause && mWindow->getGuiStackSize() < 2) {
if (mFrameNum >= mTotalFrames) {
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)) {
if (DEBUG_ANIMATION) {
LOG(LogError) << "Dropped frames: " << mDroppedFrames;
LOG(LogDebug) << "LottieComponent::render(): Skipped frames: " << mSkippedFrames;
LOG(LogDebug) << "LottieComponent::render(): Actual duration: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - mAnimationStartTime)
.count()
<< " ms";
}
if (mAlternate) {
if (mDirection == "normal")
mDirection = "reverse";
else
mDirection = "normal";
}
mTimeAccumulator = 0;
mSkippedFrames = 0;
if (mDirection == "reverse")
mFrameNum = mTotalFrames - 1;
else
mFrameNum = 0;
mDroppedFrames = 0;
}
if (DEBUG_ANIMATION) {
if (mFrameNum == 0)
if ((mDirection == "normal" && mFrameNum == 0) ||
(mDirection == "reverse" && mFrameNum == mTotalFrames - 1))
mAnimationStartTime = std::chrono::system_clock::now();
}
@ -336,6 +406,9 @@ void LottieComponent::render(const glm::mat4& parentTrans)
mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y));
if (mDirection == "reverse")
--mFrameNum;
else
++mFrameNum;
if (mFrameNum == mTotalFrames)
@ -350,6 +423,10 @@ void LottieComponent::render(const glm::mat4& parentTrans)
mTexture->initFromPixels(&mFrameCache[mFrameNum][0],
static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y));
if (mDirection == "reverse")
--mFrameNum;
else
++mFrameNum;
}
}

View file

@ -34,12 +34,7 @@ public:
mMaxCacheSize = static_cast<size_t>(glm::clamp(value, 0, 1024) * 1024 * 1024);
}
void resetFileAnimation() override
{
mTimeAccumulator = 0;
mFrameNum = 0;
}
void resetFileAnimation() override;
void onSizeChanged() override;
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
@ -62,24 +57,26 @@ private:
size_t mCacheSize;
size_t mFrameSize;
std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime;
std::unique_ptr<rlottie::Animation> mAnimation;
std::unique_ptr<rlottie::Surface> mSurface;
std::future<rlottie::Surface> mFuture;
std::string mPath;
std::string mStartDirection;
std::string mDirection;
size_t mTotalFrames;
size_t mFrameNum;
double mFrameRate;
float mSpeedModifier;
int mTargetPacing;
int mTimeAccumulator;
int mSkippedFrames;
bool mHoldFrame;
int mDroppedFrames;
bool mPause;
float mSpeedModifier;
bool mAlternate;
bool mKeepAspectRatio;
std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime;
};
#endif // ES_CORE_COMPONENTS_LOTTIE_COMPONENT_H