From ab30cca586a05506c3cfb502cee53957409242e8 Mon Sep 17 00:00:00 2001 From: Crystalwarrior Date: Sat, 14 Sep 2019 19:50:46 +0300 Subject: [PATCH 1/3] Use a ticker and QImageReader instead of QMovie Issue: absolutely fucking broken, needs shittons of fixing or ditching. Might not be that efficient, anyway. --- include/aocharmovie.h | 16 ++-- src/aocharmovie.cpp | 175 +++++++++++++++++------------------------- 2 files changed, 82 insertions(+), 109 deletions(-) diff --git a/include/aocharmovie.h b/include/aocharmovie.h index f54f510..65ab79f 100644 --- a/include/aocharmovie.h +++ b/include/aocharmovie.h @@ -1,7 +1,7 @@ #ifndef AOCHARMOVIE_H #define AOCHARMOVIE_H -#include +#include #include #include #include @@ -21,20 +21,24 @@ public: void play_talking(QString p_char, QString p_emote); void play_idle(QString p_char, QString p_emote); - void set_flipped(bool p_flipped) {m_flipped = p_flipped;} + void set_frame(QImage image); void stop(); + void set_flipped(bool p_flipped) {m_flipped = p_flipped;} + void move(int ax, int ay); void combo_resize(int w, int h); + private: AOApplication *ao_app; - QMovie *m_movie; - QVector movie_frames; + QImageReader *m_reader; QTimer *preanim_timer; + QTimer *ticker; + QString last_path; const int time_mod = 60; @@ -50,8 +54,8 @@ signals: void done(); private slots: - void frame_change(int n_frame); - void timer_done(); + void preanim_done(); + void movie_ticker(); }; #endif // AOCHARMOVIE_H diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index ac12f63..8004a54 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -7,14 +7,15 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) { ao_app = p_ao_app; - - m_movie = new QMovie(this); - preanim_timer = new QTimer(this); preanim_timer->setSingleShot(true); - connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); - connect(preanim_timer, SIGNAL(timeout()), this, SLOT(timer_done())); + ticker = new QTimer(this); + ticker->setSingleShot(true); + connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); + +// connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); + connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); } void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) @@ -37,126 +38,61 @@ void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) } } - m_movie->stop(); - m_movie->setFileName(emote_path); + this->clear(); + m_reader = new QImageReader(emote_path); - QImageReader *reader = new QImageReader(emote_path); - - movie_frames.clear(); - QImage f_image = reader->read(); - while (!f_image.isNull()) - { - if (m_flipped) - movie_frames.append(f_image.mirrored(true, false)); - else - movie_frames.append(f_image); - f_image = reader->read(); - } - - delete reader; + QImage f_image = m_reader->read(); +// while (!f_image.isNull()) +// { +// if (m_flipped) +// movie_frames.append(f_image.mirrored(true, false)); +// else +// movie_frames.append(f_image); +// f_image = reader->read(); +// } +// delete m_reader; this->show(); - m_movie->start(); + qDebug() << "Setting image to " << f_image; + this->set_frame(f_image); + if (m_reader->supportsAnimation()) + ticker->start(m_reader->nextImageDelay()); } void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) { - QString emote_path = ao_app->get_character_path(p_char, p_emote); - - m_movie->stop(); - this->clear(); - m_movie->setFileName(emote_path); - m_movie->jumpToFrame(0); - - int full_duration = duration * time_mod; - int real_duration = 0; - - play_once = false; - - for (int n_frame = 0 ; n_frame < m_movie->frameCount() ; ++n_frame) - { - qDebug() << "frame " << n_frame << " delay of " << m_movie->nextFrameDelay(); - real_duration += m_movie->nextFrameDelay(); - m_movie->jumpToFrame(n_frame + 1); - } - -#ifdef DEBUG_GIF - qDebug() << "full_duration: " << full_duration; - qDebug() << "real_duration: " << real_duration; -#endif - - double percentage_modifier = 100.0; - - if (real_duration != 0 && duration > 0) - { - double modifier = full_duration / static_cast(real_duration); - percentage_modifier = 100 / modifier; - - if (percentage_modifier > 100.0) - percentage_modifier = 100.0; - } - -#ifdef DEBUG_GIF - qDebug() << "% mod: " << percentage_modifier; -#endif - - if (full_duration == 0 || full_duration >= real_duration) - { - play_once = true; - } - else - { - play_once = false; - preanim_timer->start(full_duration); - } - - - m_movie->setSpeed(static_cast(percentage_modifier)); +// QString emote_path = ao_app->get_character_path(p_char, p_emote); + play_once = true; play(p_char, p_emote, ""); } void AOCharMovie::play_talking(QString p_char, QString p_emote) { play_once = false; - m_movie->setSpeed(100); play(p_char, p_emote, "(b)"); } void AOCharMovie::play_idle(QString p_char, QString p_emote) { play_once = false; - m_movie->setSpeed(100); play(p_char, p_emote, "(a)"); } void AOCharMovie::stop() { //for all intents and purposes, stopping is the same as hiding. at no point do we want a frozen gif to display - m_movie->stop(); + ticker->stop(); preanim_timer->stop(); this->hide(); } -void AOCharMovie::combo_resize(int w, int h) +void AOCharMovie::set_frame(QImage image) { - QSize f_size(w, h); - this->resize(f_size); - m_movie->setScaledSize(f_size); -} - -void AOCharMovie::move(int ax, int ay) -{ - x = ax; - y = ay; - QLabel::move(x, y); -} - -void AOCharMovie::frame_change(int n_frame) -{ - - if (movie_frames.size() > n_frame) - { - QPixmap f_pixmap = QPixmap::fromImage(movie_frames.at(n_frame)); + QPixmap f_pixmap; + if(m_flipped) + f_pixmap = QPixmap::fromImage(image.mirrored(true, false)); + else + f_pixmap = QPixmap::fromImage(image); auto aspect_ratio = Qt::KeepAspectRatio; if (f_pixmap.size().width() > f_pixmap.size().height()) @@ -168,17 +104,50 @@ void AOCharMovie::frame_change(int n_frame) this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::FastTransformation)); QLabel::move(x + (this->width() - this->pixmap()->width())/2, y); - } - - if (m_movie->frameCount() - 1 == n_frame && play_once) - { - preanim_timer->start(m_movie->nextFrameDelay()); - m_movie->stop(); - } } -void AOCharMovie::timer_done() +void AOCharMovie::combo_resize(int w, int h) { + QSize f_size(w, h); + this->resize(f_size); +// m_reader->setScaledSize(f_size); +} +void AOCharMovie::move(int ax, int ay) +{ + x = ax; + y = ay; + QLabel::move(x, y); +} + +void AOCharMovie::movie_ticker() +{ + if(m_reader->format() != "gif") + ticker->start(m_reader->nextImageDelay()); + if(m_reader->currentImageNumber() == -1) + { + if(play_once) + { + preanim_done(); + return; + } + else + m_reader->jumpToImage(0); //Loop back + } + QImage f_image = m_reader->read(); + if(f_image.isNull()) + { + qDebug() << "Animated image error on frame" << m_reader->currentImageNumber() << m_reader->errorString(); + stop(); + return; + } + this->set_frame(f_image); + qDebug() << m_reader->format() << "frame" << m_reader->nextImageDelay() << m_reader->currentImageNumber() << m_reader->imageCount(); + if(m_reader->format() == "gif") + ticker->start(m_reader->nextImageDelay()); +} + +void AOCharMovie::preanim_done() +{ done(); } From 938f1aeea15c6b68495bb1cb0a80467f4e8cf343 Mon Sep 17 00:00:00 2001 From: Crystalwarrior Date: Sat, 14 Sep 2019 22:35:02 +0300 Subject: [PATCH 2/3] Optimize the heck out of animated images. There is no lag after the image is loaded now. HOWEVER, there's lag when the image is first loaded due to the aspect ratio and sizing (at least with .apng's). --- include/aocharmovie.h | 11 +++++-- src/aocharmovie.cpp | 72 +++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/include/aocharmovie.h b/include/aocharmovie.h index 65ab79f..0921bc5 100644 --- a/include/aocharmovie.h +++ b/include/aocharmovie.h @@ -6,6 +6,7 @@ #include #include #include +#include class AOApplication; @@ -21,7 +22,8 @@ public: void play_talking(QString p_char, QString p_emote); void play_idle(QString p_char, QString p_emote); - void set_frame(QImage image); + QPixmap get_pixmap(QImage image); + void set_frame(QPixmap f_pixmap); void stop(); @@ -35,19 +37,22 @@ public: private: AOApplication *ao_app; - QImageReader *m_reader; + QVector movie_frames; + QVector movie_delays; QTimer *preanim_timer; QTimer *ticker; QString last_path; + QElapsedTimer actual_time; const int time_mod = 60; // These are the X and Y values before they are fixed based on the sprite's width. int x = 0; int y = 0; - bool m_flipped = false; + int frame = 0; + bool m_flipped = false; bool play_once = true; signals: diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index 8004a54..2d370ec 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -11,7 +11,8 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_ preanim_timer->setSingleShot(true); ticker = new QTimer(this); - ticker->setSingleShot(true); + ticker->setTimerType(Qt::PreciseTimer); + ticker->setSingleShot(false); connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); // connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); @@ -20,6 +21,7 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_ void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) { + actual_time.restart(); QString emote_path; QList pathlist; pathlist = { @@ -39,24 +41,29 @@ void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) } this->clear(); - m_reader = new QImageReader(emote_path); + ticker->stop(); + movie_frames.clear(); + movie_delays.clear(); + + QImageReader *m_reader = new QImageReader(emote_path); QImage f_image = m_reader->read(); -// while (!f_image.isNull()) -// { -// if (m_flipped) -// movie_frames.append(f_image.mirrored(true, false)); -// else -// movie_frames.append(f_image); -// f_image = reader->read(); -// } + while (!f_image.isNull()) + { + movie_frames.append(this->get_pixmap(f_image)); + movie_delays.append(m_reader->nextImageDelay()); + f_image = m_reader->read(); + } + + delete m_reader; -// delete m_reader; this->show(); - qDebug() << "Setting image to " << f_image; - this->set_frame(f_image); - if (m_reader->supportsAnimation()) - ticker->start(m_reader->nextImageDelay()); + qDebug() << "Setting image to " << emote_path << "Time taken to process image:" << actual_time.elapsed(); + frame = 0; + actual_time.restart(); + this->set_frame(movie_frames[frame]); + if (movie_frames.size() > 0) + ticker->start(movie_delays[frame]); } void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) @@ -86,7 +93,7 @@ void AOCharMovie::stop() this->hide(); } -void AOCharMovie::set_frame(QImage image) +QPixmap AOCharMovie::get_pixmap(QImage image) { QPixmap f_pixmap; if(m_flipped) @@ -99,10 +106,16 @@ void AOCharMovie::set_frame(QImage image) aspect_ratio = Qt::KeepAspectRatioByExpanding; if (f_pixmap.size().width() > this->size().width() || f_pixmap.size().height() > this->size().height()) - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::SmoothTransformation)); + f_pixmap = f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::SmoothTransformation); else - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::FastTransformation)); + f_pixmap = f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::FastTransformation); + return f_pixmap; +} + +void AOCharMovie::set_frame(QPixmap f_pixmap) +{ + this->setPixmap(f_pixmap); QLabel::move(x + (this->width() - this->pixmap()->width())/2, y); } @@ -122,29 +135,22 @@ void AOCharMovie::move(int ax, int ay) void AOCharMovie::movie_ticker() { - if(m_reader->format() != "gif") - ticker->start(m_reader->nextImageDelay()); - if(m_reader->currentImageNumber() == -1) + ++frame; + if(frame == movie_frames.size()) { if(play_once) { preanim_done(); + ticker->stop(); return; } else - m_reader->jumpToImage(0); //Loop back + frame = 0; } - QImage f_image = m_reader->read(); - if(f_image.isNull()) - { - qDebug() << "Animated image error on frame" << m_reader->currentImageNumber() << m_reader->errorString(); - stop(); - return; - } - this->set_frame(f_image); - qDebug() << m_reader->format() << "frame" << m_reader->nextImageDelay() << m_reader->currentImageNumber() << m_reader->imageCount(); - if(m_reader->format() == "gif") - ticker->start(m_reader->nextImageDelay()); +// qint64 difference = elapsed - movie_delays[frame]; + qDebug() << frame << movie_delays[frame] << "actual time taken from last frame:" << actual_time.restart(); + this->set_frame(movie_frames[frame]); + ticker->setInterval(movie_delays[frame]); } void AOCharMovie::preanim_done() From 37d192b4300efd1c2f3e35b2efb40e93ec99fa03 Mon Sep 17 00:00:00 2001 From: Crystalwarrior Date: Sun, 15 Sep 2019 02:14:40 +0300 Subject: [PATCH 3/3] Load frames as they're needed instead of loading everything at once, and cache them for optimization. The cache is cleared when a new animation is played. Resolve an issue where if a preanim transitions into (a)idle it would get stuck on the first frame of that (removed the ticker->stop();) --- include/aocharmovie.h | 2 ++ src/aocharmovie.cpp | 50 ++++++++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/include/aocharmovie.h b/include/aocharmovie.h index 0921bc5..c852bb3 100644 --- a/include/aocharmovie.h +++ b/include/aocharmovie.h @@ -42,6 +42,7 @@ private: QTimer *preanim_timer; QTimer *ticker; QString last_path; + QImageReader *m_reader = new QImageReader(); QElapsedTimer actual_time; const int time_mod = 60; @@ -51,6 +52,7 @@ private: int y = 0; int frame = 0; + int max_frames = 0; bool m_flipped = false; bool play_once = true; diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index 2d370ec..1251a20 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -21,7 +21,9 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_ void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) { +#ifdef DEBUG_CHARMOVIE actual_time.restart(); +#endif QString emote_path; QList pathlist; pathlist = { @@ -42,28 +44,31 @@ void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) this->clear(); ticker->stop(); + preanim_timer->stop(); movie_frames.clear(); movie_delays.clear(); - QImageReader *m_reader = new QImageReader(emote_path); - + m_reader->setFileName(emote_path); QImage f_image = m_reader->read(); - while (!f_image.isNull()) + int f_delay = m_reader->nextImageDelay(); + + frame = 0; + max_frames = m_reader->imageCount(); + +#ifdef DEBUG_CHARMOVIE + qDebug() << max_frames << "Setting image to " << emote_path << "Time taken to process image:" << actual_time.elapsed(); + + actual_time.restart(); +#endif + + this->set_frame(movie_frames[frame]); + this->show(); + if (max_frames > 1) { movie_frames.append(this->get_pixmap(f_image)); - movie_delays.append(m_reader->nextImageDelay()); - f_image = m_reader->read(); - } - - delete m_reader; - - this->show(); - qDebug() << "Setting image to " << emote_path << "Time taken to process image:" << actual_time.elapsed(); - frame = 0; - actual_time.restart(); - this->set_frame(movie_frames[frame]); - if (movie_frames.size() > 0) + movie_delays.append(f_delay); ticker->start(movie_delays[frame]); + } } void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) @@ -136,19 +141,30 @@ void AOCharMovie::move(int ax, int ay) void AOCharMovie::movie_ticker() { ++frame; - if(frame == movie_frames.size()) + if(frame == max_frames) { if(play_once) { preanim_done(); - ticker->stop(); return; } else frame = 0; } // qint64 difference = elapsed - movie_delays[frame]; + if(frame >= movie_frames.size()) + { + m_reader->jumpToImage(frame); + movie_frames.resize(frame + 1); + movie_frames[frame] = this->get_pixmap(m_reader->read()); + movie_delays.resize(frame + 1); + movie_delays[frame] = m_reader->nextImageDelay(); + } + +#ifdef DEBUG_CHARMOVIE qDebug() << frame << movie_delays[frame] << "actual time taken from last frame:" << actual_time.restart(); +#endif + this->set_frame(movie_frames[frame]); ticker->setInterval(movie_delays[frame]); }