Skip to content

Commit

Permalink
feat: add support for num_plays in animatedTexture (.aPNG) files (#398)
Browse files Browse the repository at this point in the history
Addition of new functionality to the software by introducing support for the 'num_plays' variable in 'animatedTexture' (.aPNG) files. This variable determines the number of times an animation should loop, and was not previously supported by the software. These changes affect multiple files and include the addition of a new function, as well as modifications to existing functions, in order to fully implement the new feature. The new feature allows for greater flexibility in using animated textures, such as using them for effects on widgets in addition to just backgrounds.
  • Loading branch information
mehah authored Jan 27, 2023
1 parent b8ab646 commit 6f453de
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 21 deletions.
19 changes: 12 additions & 7 deletions src/framework/graphics/animatedtexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

#include <utility>

AnimatedTexture::AnimatedTexture(const Size& size, const std::vector<ImagePtr>& frames, std::vector<int> framesDelay, bool buildMipmaps, bool compress)
AnimatedTexture::AnimatedTexture(const Size& size, const std::vector<ImagePtr>& frames, std::vector<uint16_t> framesDelay, uint16_t numPlays, bool buildMipmaps, bool compress)
{
if (!setupSize(size))
return;
Expand All @@ -39,13 +39,11 @@ AnimatedTexture::AnimatedTexture(const Size& size, const std::vector<ImagePtr>&

m_framesDelay = std::move(framesDelay);
m_hasMipmaps = buildMipmaps;
m_numPlays = numPlays;
m_id = m_frames[0]->getId();
m_animTimer.restart();
}

AnimatedTexture::~AnimatedTexture()
= default;

void AnimatedTexture::buildHardwareMipmaps()
{
if (m_hasMipmaps) return;
Expand All @@ -69,19 +67,26 @@ void AnimatedTexture::setRepeat(bool repeat)
frame->setRepeat(repeat);
}

void AnimatedTexture::updateAnimation()
void AnimatedTexture::update()
{
if (!m_animTimer.running())
return;

if (m_animTimer.ticksElapsed() < m_framesDelay[m_currentFrame])
return;

if (++m_currentFrame >= m_frames.size())
m_animTimer.restart(); // it is necessary to restart the animation before stop()

if (++m_currentFrame >= m_frames.size()) {
m_currentFrame = 0;
if (m_numPlays > 0 && ++m_currentPlay == m_numPlays)
m_animTimer.stop();
}

const auto& txt = m_frames[m_currentFrame];
txt->create();

m_id = txt->getId();
m_animTimer.restart();

g_app.repaint();
}
12 changes: 8 additions & 4 deletions src/framework/graphics/animatedtexture.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,25 @@
class AnimatedTexture : public Texture
{
public:
AnimatedTexture(const Size& size, const std::vector<ImagePtr>& frames, std::vector<int> framesDelay, bool buildMipmaps = false, bool compress = false);
~AnimatedTexture() override;
AnimatedTexture(const Size& size, const std::vector<ImagePtr>& frames, std::vector<uint16_t> framesDelay, uint16_t numPlays, bool buildMipmaps = false, bool compress = false);
~AnimatedTexture() override = default;

void buildHardwareMipmaps() override;

void setSmooth(bool smooth) override;
void setRepeat(bool repeat) override;

void updateAnimation();
void update();
void restart() { m_animTimer.restart(); m_currentPlay = 0; }

bool isAnimatedTexture() const override { return true; }

private:
std::vector<TexturePtr> m_frames;
std::vector<int> m_framesDelay;
std::vector<uint16_t> m_framesDelay;
uint32_t m_currentFrame{ 0 };
uint32_t m_currentPlay{ 0 };
uint32_t m_numPlays{ 0 };

Timer m_animTimer;
};
2 changes: 1 addition & 1 deletion src/framework/graphics/apngloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct apng_data
uint8_t coltype;
uint32_t num_frames;
uint32_t num_plays;
unsigned short* frames_delay; // each frame delay in ms
uint16_t* frames_delay; // each frame delay in ms
};

// returns -1 on error, 0 on success
Expand Down
16 changes: 8 additions & 8 deletions src/framework/graphics/texturemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void TextureManager::poll() const
lastUpdate = now;

for (const AnimatedTexturePtr& animatedTexture : m_animatedTextures)
animatedTexture->updateAnimation();
animatedTexture->update();
}

void TextureManager::clearCache()
Expand Down Expand Up @@ -141,21 +141,21 @@ TexturePtr TextureManager::loadTexture(std::stringstream& file)
{
TexturePtr texture;

if (apng_data apng; load_apng(file, &apng) == 0) {
apng_data apng;
if (load_apng(file, &apng) == 0) {
const Size imageSize(apng.width, apng.height);
if (apng.num_frames > 1) { // animated texture
std::vector<ImagePtr> frames;
std::vector<int> framesDelay;
std::vector<uint16_t> framesDelay;
for (uint32_t i = 0; i < apng.num_frames; ++i) {
uint8_t* frameData = apng.pdata + ((apng.first_frame + i) * imageSize.area() * apng.bpp);
int frameDelay = apng.frames_delay[i];

framesDelay.push_back(frameDelay);
framesDelay.push_back(apng.frames_delay[i]);
frames.emplace_back(std::make_shared<Image>(imageSize, apng.bpp, frameData));
}
const auto& animatedTexture = std::make_shared<AnimatedTexture>(imageSize, frames, framesDelay);
m_animatedTextures.emplace_back(animatedTexture);
texture = animatedTexture;

const auto& animatedTexture = std::make_shared<AnimatedTexture>(imageSize, frames, framesDelay, apng.num_plays);
texture = m_animatedTextures.emplace_back(animatedTexture);
} else {
const auto& image = std::make_shared<Image>(imageSize, apng.bpp, apng.pdata);
texture = std::make_shared<Texture>(image, false, false);
Expand Down
6 changes: 5 additions & 1 deletion src/framework/ui/uiwidgetimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <framework/core/eventdispatcher.h>
#include <framework/graphics/painter.h>
#include <framework/graphics/animatedtexture.h>
#include <framework/graphics/texture.h>
#include <framework/graphics/texturemanager.h>
#include "uiwidget.h"
Expand Down Expand Up @@ -181,8 +182,11 @@ void UIWidget::setImageSource(const std::string_view source)
}

m_imageTexture = g_textures.getTexture(m_imageSource = source);
if (m_imageTexture->isAnimatedTexture())

This comment has been minimized.

Copy link
@xmish

xmish Jan 29, 2023

It crashes if used with classic theme which uses some hack.
m_imageTexture is NULL in this case:

 Item
    id: textItem
    virtual: true
    anchors.top: parent.top
    anchors.left: parent.left
    image-source: /images/ui/noimage

Where noimage is empty file
Fix: replace with 1x1 transparent png

std::static_pointer_cast<AnimatedTexture>(m_imageTexture)->restart();

if (!m_rect.isValid() || m_imageAutoResize) {
const Size imageSize = m_imageTexture->getSize();
const auto& imageSize = m_imageTexture->getSize();

Size size = getSize();
if (size.width() <= 0 || m_imageAutoResize)
Expand Down

0 comments on commit 6f453de

Please sign in to comment.