From ff0f163de50760b5884ec0eb7c61eba86e2bcad0 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 17 Jan 2023 18:35:46 +0100 Subject: [PATCH] Added a maxSize property to GIFAnimComponent and LottieAnimComponent. Also refactored and cleaned up the code in general. --- es-core/src/ThemeData.cpp | 2 +- es-core/src/components/GIFAnimComponent.cpp | 127 ++++++++++------- es-core/src/components/GIFAnimComponent.h | 4 +- .../src/components/LottieAnimComponent.cpp | 131 +++++++++++------- es-core/src/components/LottieAnimComponent.h | 4 +- 5 files changed, 162 insertions(+), 106 deletions(-) diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index fa216afc7..a06e78a9e 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -327,6 +327,7 @@ std::map> {"animation", {{"pos", NORMALIZED_PAIR}, {"size", NORMALIZED_PAIR}, + {"maxSize", NORMALIZED_PAIR}, {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, @@ -334,7 +335,6 @@ std::map> {"path", PATH}, {"speed", FLOAT}, {"direction", STRING}, - {"keepAspectRatio", BOOLEAN}, {"interpolation", STRING}, {"brightness", FLOAT}, {"opacity", FLOAT}, diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp index e6f39f9a6..2a121e358 100644 --- a/es-core/src/components/GIFAnimComponent.cpp +++ b/es-core/src/components/GIFAnimComponent.cpp @@ -21,6 +21,7 @@ GIFAnimComponent::GIFAnimComponent() : mRenderer {Renderer::getInstance()} + , mTargetSize {0.0f, 0.0f} , mFrameSize {0} , mAnimFile {nullptr} , mAnimation {nullptr} @@ -41,7 +42,7 @@ GIFAnimComponent::GIFAnimComponent() , mPause {false} , mExternalPause {false} , mAlternate {false} - , mKeepAspectRatio {true} + , mTargetIsMax {false} { // Get an empty texture for rendering the animation. mTexture = TextureResource::get(""); @@ -54,8 +55,8 @@ GIFAnimComponent::GIFAnimComponent() // Set component defaults. setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f); setPosition(Renderer::getScreenWidth() * 0.3f, Renderer::getScreenHeight() * 0.3f); - setDefaultZIndex(10.0f); - setZIndex(10.0f); + setDefaultZIndex(35.0f); + setZIndex(35.0f); mTexture->setLinearMagnify(false); } @@ -154,11 +155,6 @@ void GIFAnimComponent::setAnimation(const std::string& path) return; } - if (!mKeepAspectRatio && (mSize.x == 0.0f || mSize.y == 0.0f)) { - LOG(LogWarning) << "GIFAnimComponent: Width or height auto sizing is incompatible with " - "disabling of so ignoring this setting"; - } - size_t width {0}; size_t height {0}; @@ -170,10 +166,36 @@ void GIFAnimComponent::setAnimation(const std::string& path) mFileHeight = FreeImage_GetHeight(mFrame); filePitch = FreeImage_GetPitch(mFrame); - if (mSize.x == 0.0f || mSize.y == 0.0f) { - double sizeRatio {static_cast(mFileWidth) / static_cast(mFileHeight)}; + if (mTargetIsMax || mSize.x == 0.0f || mSize.y == 0.0f) { + const double sizeRatio {static_cast(mFileWidth) / static_cast(mFileHeight)}; - if (mSize.x == 0) { + if (mTargetIsMax) { + // Just a precaution if FreeImage would return zero for some reason. + if (mFileWidth == 0) + mFileWidth = 1; + if (mFileHeight == 0) + mFileHeight = 1; + + mSize.x = static_cast(mFileWidth); + mSize.y = static_cast(mFileHeight); + + // Preserve aspect ratio. + const glm::vec2 resizeScale {mTargetSize.x / mSize.x, mTargetSize.y / mSize.y}; + + if (resizeScale.x < resizeScale.y) { + mSize.x *= resizeScale.x; + mSize.y = std::min(mSize.y * resizeScale.x, mTargetSize.y); + } + else { + mSize.y *= resizeScale.y; + mSize.x = std::min((mSize.y / static_cast(mFileHeight)) * + static_cast(mFileWidth), + mTargetSize.x); + } + width = static_cast(mSize.x); + height = static_cast(mSize.y); + } + else if (mSize.x == 0) { width = static_cast(static_cast(mSize.y) * sizeRatio); height = static_cast(mSize.y); } @@ -190,6 +212,9 @@ void GIFAnimComponent::setAnimation(const std::string& path) mSize.x = static_cast(width); mSize.y = static_cast(height); + if (!mTargetIsMax) + mTargetSize = mSize; + FreeImage_PreMultiplyWithAlpha(mFrame); mPictureRGBA.resize(mFileWidth * mFileHeight * 4); @@ -256,34 +281,40 @@ void GIFAnimComponent::applyTheme(const std::shared_ptr& theme, unsigned int properties) { using namespace ThemeFlags; + GuiComponent::applyTheme(theme, view, element, properties ^ ThemeFlags::SIZE); + const ThemeData::ThemeElement* elem {theme->getElement(view, element, "animation")}; + if (!elem) + return; + + const glm::vec2 scale {glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight())}; if (elem->has("size")) { - glm::vec2 size = elem->get("size"); - if (size.x == 0.0f && size.y == 0.0f) { - LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, defined as \"" - << size.x << " " << size.y << "\""; - return; + glm::vec2 animationSize {elem->get("size")}; + if (animationSize == glm::vec2 {0.0f, 0.0f}) { + LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, property " + "\"size\" for element \"" + << element.substr(10) << "\" is set to zero"; + animationSize = {0.01f, 0.01f}; } + if (animationSize.x > 0.0f) + animationSize.x = glm::clamp(animationSize.x, 0.01f, 1.0f); + if (animationSize.y > 0.0f) + animationSize.y = glm::clamp(animationSize.y, 0.01f, 1.0f); + setSize(animationSize * scale); + } + else if (elem->has("maxSize")) { + const glm::vec2 animationMaxSize {glm::clamp(elem->get("maxSize"), 0.01f, 1.0f)}; + setSize(animationMaxSize * scale); + mTargetIsMax = true; + mTargetSize = mSize; } if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; - if (elem->has("speed")) { - const float speed {elem->get("speed")}; - if (speed < 0.2f || speed > 3.0f) { - LOG(LogWarning) - << "GIFAnimComponent: Invalid theme configuration, defined as \"" - << std::fixed << std::setprecision(1) << speed << "\""; - } - else { - mSpeedModifier = speed; - } - } - - if (elem->has("keepAspectRatio")) - mKeepAspectRatio = elem->get("keepAspectRatio"); + if (elem->has("speed")) + mSpeedModifier = glm::clamp(elem->get("speed"), 0.2f, 3.0f); if (elem->has("direction")) { const std::string& direction {elem->get("direction")}; @@ -304,9 +335,9 @@ void GIFAnimComponent::applyTheme(const std::shared_ptr& theme, mAlternate = true; } else { - LOG(LogWarning) - << "GIFAnimComponent: Invalid theme configuration, defined as \"" - << direction << "\""; + LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, property " + "\"direction\" for element \"" + << element.substr(10) << "\" defined as \"" << direction << "\""; mStartDirection = "normal"; mAlternate = false; } @@ -322,25 +353,14 @@ void GIFAnimComponent::applyTheme(const std::shared_ptr& theme, } else { mTexture->setLinearMagnify(false); - LOG(LogWarning) - << "GIFAnimComponent::applyTheme(): Invalid theme configuration, property " - " defined as \"" - << interpolation << "\""; + LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, property " + "\"interpolation\" for element \"" + << element.substr(10) << "\" defined as \"" << interpolation << "\""; } } - GuiComponent::applyTheme(theme, view, element, properties); - - if (elem->has("path")) { - const std::string& path {elem->get("path")}; - if (path != "") { - setAnimation(path); - } - } - else { - LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, not set"; - return; - } + if (elem->has("path")) + setAnimation(elem->get("path")); } void GIFAnimComponent::update(int deltaTime) @@ -476,8 +496,15 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans) mRenderer->setMatrix(trans); - if (Settings::getInstance()->getBool("DebugImage")) + if (Settings::getInstance()->getBool("DebugImage")) { + if (mTargetIsMax) { + const glm::vec2 targetSizePos { + glm::round((mTargetSize - mSize) * mOrigin * glm::vec2 {-1.0f})}; + mRenderer->drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y, + 0xFF000033, 0xFF000033); + } mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + } if (mTexture->getSize().x != 0.0f) { mTexture->bind(); diff --git a/es-core/src/components/GIFAnimComponent.h b/es-core/src/components/GIFAnimComponent.h index 27ee14b8a..cf525bb8c 100644 --- a/es-core/src/components/GIFAnimComponent.h +++ b/es-core/src/components/GIFAnimComponent.h @@ -25,7 +25,6 @@ public: ~GIFAnimComponent(); void setAnimation(const std::string& path); - void setKeepAspectRatio(bool value) { mKeepAspectRatio = value; } void setPauseAnimation(bool state) { mExternalPause = state; } void resetFileAnimation() override; @@ -70,6 +69,7 @@ private: } Renderer* mRenderer; + glm::vec2 mTargetSize; std::shared_ptr mTexture; std::vector mPictureRGBA; size_t mFrameSize; @@ -100,7 +100,7 @@ private: bool mPause; bool mExternalPause; bool mAlternate; - bool mKeepAspectRatio; + bool mTargetIsMax; }; #endif // ES_CORE_COMPONENTS_GIF_ANIM_COMPONENT_H diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp index 2c673478c..076e488d3 100644 --- a/es-core/src/components/LottieAnimComponent.cpp +++ b/es-core/src/components/LottieAnimComponent.cpp @@ -17,6 +17,7 @@ LottieAnimComponent::LottieAnimComponent() : mRenderer {Renderer::getInstance()} + , mTargetSize {0.0f, 0.0f} , mCacheFrames {true} , mMaxCacheSize {0} , mCacheSize {0} @@ -36,7 +37,7 @@ LottieAnimComponent::LottieAnimComponent() , mPause {false} , mExternalPause {false} , mAlternate {false} - , mKeepAspectRatio {true} + , mTargetIsMax {false} { // Get an empty texture for rendering the animation. mTexture = TextureResource::get(""); @@ -55,8 +56,8 @@ LottieAnimComponent::LottieAnimComponent() // Set component defaults. setSize(mRenderer->getScreenWidth() * 0.2f, mRenderer->getScreenHeight() * 0.2f); setPosition(mRenderer->getScreenWidth() * 0.3f, mRenderer->getScreenHeight() * 0.3f); - setDefaultZIndex(10.0f); - setZIndex(10.0f); + setDefaultZIndex(35.0f); + setZIndex(35.0f); } LottieAnimComponent::~LottieAnimComponent() @@ -117,22 +118,44 @@ void LottieAnimComponent::setAnimation(const std::string& path) return; } - if (!mKeepAspectRatio && (mSize.x == 0.0f || mSize.y == 0.0f)) { - LOG(LogWarning) << "LottieAnimComponent: Width or height auto sizing is incompatible with " - "disabling of so ignoring this setting"; - } - size_t width {0}; size_t height {0}; - if (mSize.x == 0.0f || mSize.y == 0.0f) { + if (mTargetIsMax || mSize.x == 0.0f || mSize.y == 0.0f) { size_t viewportWidth {0}; size_t viewportHeight {0}; mAnimation->size(viewportWidth, viewportHeight); - double sizeRatio = static_cast(viewportWidth) / static_cast(viewportHeight); + const double sizeRatio {static_cast(viewportWidth) / + static_cast(viewportHeight)}; - if (mSize.x == 0) { + if (mTargetIsMax) { + // Just a precaution if rlottie::Animation::size() would return zero for some reason. + if (viewportWidth == 0) + viewportWidth = 1; + if (viewportHeight == 0) + viewportHeight = 1; + + mSize.x = static_cast(viewportWidth); + mSize.y = static_cast(viewportHeight); + + // Preserve aspect ratio. + const glm::vec2 resizeScale {mTargetSize.x / mSize.x, mTargetSize.y / mSize.y}; + + if (resizeScale.x < resizeScale.y) { + mSize.x *= resizeScale.x; + mSize.y = std::min(mSize.y * resizeScale.x, mTargetSize.y); + } + else { + mSize.y *= resizeScale.y; + mSize.x = std::min((mSize.y / static_cast(viewportHeight)) * + static_cast(viewportWidth), + mTargetSize.x); + } + width = static_cast(mSize.x); + height = static_cast(mSize.y); + } + else if (mSize.x == 0.0f) { width = static_cast(static_cast(mSize.y) * sizeRatio); height = static_cast(mSize.y); } @@ -149,6 +172,9 @@ void LottieAnimComponent::setAnimation(const std::string& path) mSize.x = static_cast(width); mSize.y = static_cast(height); + if (!mTargetIsMax) + mTargetSize = mSize; + mPictureRGBA.resize(width * height * 4); mSurface = std::make_unique(reinterpret_cast(&mPictureRGBA[0]), width, height, width * sizeof(uint32_t)); @@ -207,7 +233,7 @@ void LottieAnimComponent::resetFileAnimation() if (mAnimation != nullptr) { if (mFuture.valid()) mFuture.get(); - mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio); + mFuture = mAnimation->render(mFrameNum, *mSurface, false); mLastRenderedFrame = static_cast(mFrameNum); } } @@ -225,35 +251,40 @@ void LottieAnimComponent::applyTheme(const std::shared_ptr& theme, unsigned int properties) { using namespace ThemeFlags; + GuiComponent::applyTheme(theme, view, element, properties ^ ThemeFlags::SIZE); + const ThemeData::ThemeElement* elem {theme->getElement(view, element, "animation")}; + if (!elem) + return; + + const glm::vec2 scale {glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight())}; if (elem->has("size")) { - glm::vec2 size {elem->get("size")}; - if (size.x == 0.0f && size.y == 0.0f) { - LOG(LogWarning) - << "LottieAnimComponent: Invalid theme configuration, defined as \"" - << size.x << " " << size.y << "\""; - return; + glm::vec2 animationSize {elem->get("size")}; + if (animationSize == glm::vec2 {0.0f, 0.0f}) { + LOG(LogWarning) << "LottieAnimComponent: Invalid theme configuration, property " + "\"size\" for element \"" + << element.substr(10) << "\" is set to zero"; + animationSize = {0.01f, 0.01f}; } + if (animationSize.x > 0.0f) + animationSize.x = glm::clamp(animationSize.x, 0.01f, 1.0f); + if (animationSize.y > 0.0f) + animationSize.y = glm::clamp(animationSize.y, 0.01f, 1.0f); + setSize(animationSize * scale); + } + else if (elem->has("maxSize")) { + const glm::vec2 animationMaxSize {glm::clamp(elem->get("maxSize"), 0.01f, 1.0f)}; + setSize(animationMaxSize * scale); + mTargetIsMax = true; + mTargetSize = mSize; } if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; - if (elem->has("speed")) { - const float speed {elem->get("speed")}; - if (speed < 0.2f || speed > 3.0f) { - LOG(LogWarning) - << "LottieAnimComponent: Invalid theme configuration, defined as \"" - << std::fixed << std::setprecision(1) << speed << "\""; - } - else { - mSpeedModifier = speed; - } - } - - if (elem->has("keepAspectRatio")) - mKeepAspectRatio = elem->get("keepAspectRatio"); + if (elem->has("speed")) + mSpeedModifier = glm::clamp(elem->get("speed"), 0.2f, 3.0f); if (elem->has("direction")) { const std::string& direction {elem->get("direction")}; @@ -274,26 +305,16 @@ void LottieAnimComponent::applyTheme(const std::shared_ptr& theme, mAlternate = true; } else { - LOG(LogWarning) - << "LottieAnimComponent: Invalid theme configuration, defined as \"" - << direction << "\""; + LOG(LogWarning) << "LottieAnimComponent: Invalid theme configuration, property " + "\"direction\" for element \"" + << element.substr(10) << "\" defined as \"" << direction << "\""; mStartDirection = "normal"; mAlternate = false; } } - GuiComponent::applyTheme(theme, view, element, properties); - - if (elem->has("path")) { - std::string path {elem->get("path")}; - if (path != "") { - setAnimation(path); - } - } - else { - LOG(LogWarning) << "LottieAnimComponent: Invalid theme configuration, not set"; - return; - } + if (elem->has("path")) + setAnimation(elem->get("path")); } void LottieAnimComponent::update(int deltaTime) @@ -403,7 +424,7 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans) mAnimationStartTime = std::chrono::system_clock::now(); } - bool renderNextFrame = false; + bool renderNextFrame {false}; if (mFuture.valid()) { if (mFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) { @@ -458,15 +479,23 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans) } if (renderNextFrame && !mHoldFrame) { - mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio); + mFuture = mAnimation->render(mFrameNum, *mSurface, false); mLastRenderedFrame = static_cast(mFrameNum); } } mRenderer->setMatrix(trans); - if (Settings::getInstance()->getBool("DebugImage")) + if (Settings::getInstance()->getBool("DebugImage")) { + + if (mTargetIsMax) { + const glm::vec2 targetSizePos { + glm::round((mTargetSize - mSize) * mOrigin * glm::vec2 {-1.0f})}; + mRenderer->drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y, + 0xFF000033, 0xFF000033); + } mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + } if (mTexture->getSize().x != 0.0f) { mTexture->bind(); @@ -481,7 +510,7 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans) // clang-format on // Round vertices. - for (int i = 0; i < 4; ++i) + for (int i {0}; i < 4; ++i) vertices[i].position = glm::round(vertices[i].position); vertices->brightness = mBrightness; diff --git a/es-core/src/components/LottieAnimComponent.h b/es-core/src/components/LottieAnimComponent.h index dcf06c46f..e2600f0f0 100644 --- a/es-core/src/components/LottieAnimComponent.h +++ b/es-core/src/components/LottieAnimComponent.h @@ -32,7 +32,6 @@ public: ~LottieAnimComponent(); void setAnimation(const std::string& path); - void setKeepAspectRatio(bool value) { mKeepAspectRatio = value; } void setFrameCaching(bool value) { mCacheFrames = value; } void setMaxCacheSize(int value) { @@ -54,6 +53,7 @@ private: void render(const glm::mat4& parentTrans) override; Renderer* mRenderer; + glm::vec2 mTargetSize; std::shared_ptr mTexture; std::vector mPictureRGBA; std::unordered_map> mFrameCache; @@ -86,7 +86,7 @@ private: bool mPause; bool mExternalPause; bool mAlternate; - bool mKeepAspectRatio; + bool mTargetIsMax; }; #endif // ES_CORE_COMPONENTS_LOTTIE_ANIM_COMPONENT_H