From d27501313cae78b838c1e738ebfaeae4740a23b4 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sat, 5 Jun 2021 14:58:40 -0500 Subject: Finish mounting feature To pull this one off, a new class called VPath was created that denotes "virtual" paths that can exist anywhere among the list of mount points. It is functionally identical to QString, except that implicit conversion between QString and VPath is not allowed. This makes it easy to spot errors in path resolution at compile time, since get_real_path must be called to resolve a VPath into an absolute path that can be passed into a Qt function as a QString. Other functions, such as the get_*_suffix functions, also return an absolute path QString for convenience. As for the rest of the functions that return a VPath, you will need to call get_real_path yourself. Finally, a path resolution cache was added to try to avoid blowing up what is already a massive lookup cost for assets. The cache is invalidated when the mount path list is changed. Currently, this cache isn't bounded. Might need to write an LRU cache if issues arise. --- src/aolayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index f0d5779..27e7a65 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -137,7 +137,7 @@ void BackgroundLayer::load_image(QString p_filename) { play_once = false; cull_image = false; - QString design_path = ao_app->get_background_path("design.ini"); + VPath design_path = ao_app->get_background_path("design.ini"); transform_mode = ao_app->get_scaling(ao_app->read_design_ini("scaling", design_path)); stretch = ao_app->read_design_ini("stretch", design_path).startsWith("true"); -- cgit From ce94cd2d1e75738b53f7ab990a49e75a01b80394 Mon Sep 17 00:00:00 2001 From: in1tiate Date: Wed, 11 Aug 2021 09:20:00 -0500 Subject: preload next frame before ticking over --- src/aolayer.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 3791d66..7ec42b9 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -532,7 +532,11 @@ void CharLayer::movie_ticker() void AOLayer::movie_ticker() { ++frame; - if (frame >= max_frames) { + QFuture future; + if (frame >= movie_frames.size() && frame < max_frames) { // need to load the image + future = QtConcurrent::run(this, &AOLayer::load_next_frame); + } + else if (frame >= max_frames) { if (play_once) { if (cull_image) this->stop(); @@ -544,19 +548,21 @@ void AOLayer::movie_ticker() else frame = 0; } - // qint64 difference = elapsed - movie_delays[frame]; - if (frame >= movie_frames.size()) { - movie_frames.append(this->get_pixmap(m_reader.read())); - movie_delays.append(m_reader.nextImageDelay()); - } - #ifdef DEBUG_MOVIE qDebug() << frame << movie_delays[frame] << "actual time taken from last frame:" << actual_time.restart(); #endif - + future.waitForFinished(); // don't set the frame before we definitely have it in memory this->set_frame(movie_frames[frame]); ticker->setInterval(this->get_frame_delay(movie_delays[frame])); + if (frame + 1 >= movie_frames.size() && frame + 1 < max_frames) { // load the next frame before we tick again + future = QtConcurrent::run(this, &AOLayer::load_next_frame); + } +} + +void AOLayer::load_next_frame() { + movie_frames.append(this->get_pixmap(m_reader.read())); + movie_delays.append(m_reader.nextImageDelay()); } void CharLayer::preanim_done() -- cgit From 51698ca6ac57361ca3a5d9aa7d3b3a67374f8b49 Mon Sep 17 00:00:00 2001 From: in1tiate Date: Wed, 11 Aug 2021 09:28:59 -0500 Subject: debug_movie fixes --- src/aolayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 7ec42b9..075338b 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -344,7 +344,7 @@ void AOLayer::start_playback(QString p_image) if (duration > 0 && cull_image == true) shfx_timer->start(duration); #ifdef DEBUG_MOVIE - qDebug() << max_frames << "Setting image to " << image_path + qDebug() << max_frames << "Setting image to " << p_image << "Time taken to process image:" << actual_time.elapsed(); actual_time.restart(); @@ -548,11 +548,11 @@ void AOLayer::movie_ticker() else frame = 0; } + future.waitForFinished(); // don't set the frame before we definitely have it in memory #ifdef DEBUG_MOVIE qDebug() << frame << movie_delays[frame] << "actual time taken from last frame:" << actual_time.restart(); #endif - future.waitForFinished(); // don't set the frame before we definitely have it in memory this->set_frame(movie_frames[frame]); ticker->setInterval(this->get_frame_delay(movie_delays[frame])); if (frame + 1 >= movie_frames.size() && frame + 1 < max_frames) { // load the next frame before we tick again -- cgit From d84194871e50d86ba36d01029192ecf0b7665127 Mon Sep 17 00:00:00 2001 From: in1tiate Date: Wed, 11 Aug 2021 09:38:05 -0500 Subject: comment out debug_movie --- src/aolayer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 075338b..25873fe 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -320,10 +320,7 @@ void AOLayer::start_playback(QString p_image) for (int i = frame; i--;) { if (i <= -1) break; - QPixmap l_pixmap = this->get_pixmap(m_reader.read()); - int l_delay = m_reader.nextImageDelay(); - movie_frames.append(l_pixmap); - movie_delays.append(l_delay); + load_next_frame(); } } last_path = p_image; -- cgit From 2a18c1cdec54a324c6ba9834d6e6b05cc46f2535 Mon Sep 17 00:00:00 2001 From: in1tiate Date: Wed, 11 Aug 2021 20:14:35 -0500 Subject: wait for thread to finish before starting new one --- src/aolayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 25873fe..9952494 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -529,8 +529,8 @@ void CharLayer::movie_ticker() void AOLayer::movie_ticker() { ++frame; - QFuture future; if (frame >= movie_frames.size() && frame < max_frames) { // need to load the image + future.waitForFinished(); // Do Not want this to be running twice future = QtConcurrent::run(this, &AOLayer::load_next_frame); } else if (frame >= max_frames) { -- cgit From 3bcf01bb21c60c05d52bf53828bf8361b3234a2b Mon Sep 17 00:00:00 2001 From: in1tiate Date: Fri, 13 Aug 2021 03:23:12 -0500 Subject: asynchronously load animations for bigly performant gainz --- src/aolayer.cpp | 83 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 9952494..eb7853c 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -269,6 +269,9 @@ void CharLayer::start_playback(QString p_image) void AOLayer::start_playback(QString p_image) { + QMutexLocker locker(&mutex); + if (frame_loader.isRunning()) + exit_loop = true; // tell the loader to stop, we have a new image to load this->show(); if (!ao_app->is_continuous_enabled()) { @@ -276,7 +279,7 @@ void AOLayer::start_playback(QString p_image) force_continuous = true; } - if ((last_path == p_image) && (!force_continuous)) + if (((last_path == p_image) && (!force_continuous)) || p_image == "") return; #ifdef DEBUG_MOVIE @@ -296,7 +299,7 @@ void AOLayer::start_playback(QString p_image) stretch = stretch_override.startsWith("true"); #ifdef DEBUG_MOVIE - qDebug() << "stretch:" << stretch << "filename:" << p_image; + qDebug() << "[AOLayer::start_playback] Stretch:" << stretch << "Filename:" << p_image; #endif m_reader.setFileName(p_image); if (m_reader.loopCount() == 0) @@ -309,39 +312,22 @@ void AOLayer::start_playback(QString p_image) frame = 0; continuous = false; } - // CANTFIX: this causes a hitch - // The correct way of doing this would be to use QImageReader::jumpToImage() - // and populate missing data in the movie ticker when it's needed. This is - // unfortunately completely impossible, because QImageReader::jumpToImage() is - // not implemented in any image format AO2 is equipped to use. Instead, the - // default behavior is used - that is, absolutely nothing. - // This is why continuous playback can be toggled off. - if (continuous) { - for (int i = frame; i--;) { - if (i <= -1) - break; - load_next_frame(); - } - } + frame_loader = QtConcurrent::run(this, &AOLayer::populate_vectors); last_path = p_image; - QPixmap f_pixmap = this->get_pixmap(m_reader.read()); - int f_delay = m_reader.nextImageDelay(); + while (movie_frames.size() <= frame) + frameAdded.wait(&mutex); + this->set_frame(movie_frames[frame]); - this->set_frame(f_pixmap); - if (max_frames > 1) { - movie_frames.append(f_pixmap); - movie_delays.append(f_delay); - } - else if (max_frames <= 1) { + if (max_frames <= 1) { duration = static_duration; #ifdef DEBUG_MOVIE - qDebug() << "max_frames is <= 1, using static duration"; + qDebug() << "[AOLayer::start_playback] max_frames is <= 1, using static duration"; #endif } if (duration > 0 && cull_image == true) shfx_timer->start(duration); #ifdef DEBUG_MOVIE - qDebug() << max_frames << "Setting image to " << p_image + qDebug() << "[AOLayer::start_playback] Max frames:" << max_frames << "Setting image to " << p_image << "Time taken to process image:" << actual_time.elapsed(); actual_time.restart(); @@ -372,8 +358,12 @@ void AOLayer::play() else this->freeze(); } - else + else { + while (movie_delays.size() <= frame) { + frameAdded.wait(&mutex); + } ticker->start(this->get_frame_delay(movie_delays[frame])); + } } void AOLayer::set_play_once(bool p_play_once) { play_once = p_play_once; } @@ -451,7 +441,7 @@ void CharLayer::load_network_effects() // data, let's yank it in. effect += f_data; #ifdef DEBUG_MOVIE - qDebug() << effect << f_data << "frame" << f_frame << "for" + qDebug() << "[CharLayer::load_network_effects]" << effect << f_data << "frame" << f_frame << "for" << m_emote; #endif movie_effects[f_frame].append(effect); @@ -472,14 +462,14 @@ void CharLayer::play_frame_effect(int p_frame) if (effect == "shake") { shake(); #ifdef DEBUG_MOVIE - qDebug() << "Attempting to play shake on frame" << frame; + qDebug() << "[CharLayer::play_frame_effect] Attempting to play shake on frame" << frame; #endif } if (effect == "flash") { flash(); #ifdef DEBUG_MOVIE - qDebug() << "Attempting to play flash on frame" << frame; + qDebug() << "[CharLayer::play_frame_effect] Attempting to play flash on frame" << frame; #endif } @@ -487,7 +477,7 @@ void CharLayer::play_frame_effect(int p_frame) QString sfx = effect.section("^", 1); play_sfx(sfx); #ifdef DEBUG_MOVIE - qDebug() << "Attempting to play sfx" << sfx << "on frame" << frame; + qDebug() << "[CharLayer::play_frame_effect] Attempting to play sfx" << sfx << "on frame" << frame; #endif } } @@ -529,11 +519,12 @@ void CharLayer::movie_ticker() void AOLayer::movie_ticker() { ++frame; - if (frame >= movie_frames.size() && frame < max_frames) { // need to load the image - future.waitForFinished(); // Do Not want this to be running twice - future = QtConcurrent::run(this, &AOLayer::load_next_frame); + mutex.lock(); + while (frame >= movie_frames.size() && frame < max_frames) { // oops! our frame isn't ready yet + frameAdded.wait(&mutex); } - else if (frame >= max_frames) { + mutex.unlock(); + if (frame >= max_frames) { if (play_once) { if (cull_image) this->stop(); @@ -545,21 +536,31 @@ void AOLayer::movie_ticker() else frame = 0; } - future.waitForFinished(); // don't set the frame before we definitely have it in memory #ifdef DEBUG_MOVIE - qDebug() << frame << movie_delays[frame] - << "actual time taken from last frame:" << actual_time.restart(); + qDebug() << "[AOLayer::movie_ticker] Frame:" << frame << "Delay:" << movie_delays[frame] + << "Actual time taken from last frame:" << actual_time.restart(); #endif this->set_frame(movie_frames[frame]); ticker->setInterval(this->get_frame_delay(movie_delays[frame])); - if (frame + 1 >= movie_frames.size() && frame + 1 < max_frames) { // load the next frame before we tick again - future = QtConcurrent::run(this, &AOLayer::load_next_frame); - } +} + +void AOLayer::populate_vectors() { + while (movie_frames.size() < max_frames && !exit_loop) { + load_next_frame(); +#ifdef DEBUG_MOVIE + qDebug() << "[AOLayer::populate_vectors] Loaded frame" << movie_frames.size(); +#endif + } + exit_loop = false; } void AOLayer::load_next_frame() { + //QMutexLocker locker(&mutex); + mutex.lock(); movie_frames.append(this->get_pixmap(m_reader.read())); movie_delays.append(m_reader.nextImageDelay()); + mutex.unlock(); + frameAdded.wakeAll(); } void CharLayer::preanim_done() -- cgit From 8b82f5d592a9f76b1edbaa1efdf09366e41b266c Mon Sep 17 00:00:00 2001 From: Rosemary Witchaven <32779090+in1tiate@users.noreply.github.com> Date: Tue, 24 Aug 2021 19:37:13 -0500 Subject: Fix an infinite hang when loading the pair character, fix strange undesired behavior of the pair character (#586) * sanity check if the frame we're waiting on isnt nonexistent * consolidate conditionals --- src/aolayer.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index eb7853c..30ff652 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -304,11 +304,12 @@ void AOLayer::start_playback(QString p_image) m_reader.setFileName(p_image); if (m_reader.loopCount() == 0) play_once = true; - if (!continuous) - frame = 0; last_max_frames = max_frames; max_frames = m_reader.imageCount(); - if (((continuous) && (max_frames != last_max_frames)) || max_frames == 0) { + if (!continuous + || ((continuous) && (max_frames != last_max_frames)) + || max_frames == 0 + || frame >= max_frames) { frame = 0; continuous = false; } @@ -519,11 +520,6 @@ void CharLayer::movie_ticker() void AOLayer::movie_ticker() { ++frame; - mutex.lock(); - while (frame >= movie_frames.size() && frame < max_frames) { // oops! our frame isn't ready yet - frameAdded.wait(&mutex); - } - mutex.unlock(); if (frame >= max_frames) { if (play_once) { if (cull_image) @@ -536,6 +532,11 @@ void AOLayer::movie_ticker() else frame = 0; } + mutex.lock(); + while (frame >= movie_frames.size() && frame < max_frames) { // oops! our frame isn't ready yet + frameAdded.wait(&mutex); + } + mutex.unlock(); #ifdef DEBUG_MOVIE qDebug() << "[AOLayer::movie_ticker] Frame:" << frame << "Delay:" << movie_delays[frame] << "Actual time taken from last frame:" << actual_time.restart(); -- cgit From 7ce4dd6f618b64341c9d9520dfc98ede5a7500a7 Mon Sep 17 00:00:00 2001 From: Rosemary Witchaven <32779090+in1tiate@users.noreply.github.com> Date: Mon, 30 Aug 2021 21:23:11 -0500 Subject: Use event filters instead of subclassing QLineEdit and QPlainTextEdit (#587) * replace aolineedit and aotextedit with event filters * use a button to make evidence editable instead of double click --- src/aolayer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 30ff652..201c724 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -52,7 +52,7 @@ StickerLayer::StickerLayer(QWidget *p_parent, AOApplication *p_ao_app) QString AOLayer::find_image(QStringList p_list) { QString image_path; - for (QString path : p_list) { + for (const QString &path : p_list) { #ifdef DEBUG_MOVIE qDebug() << "checking path " << path; #endif @@ -461,14 +461,14 @@ void CharLayer::play_frame_effect(int p_frame) if (p_frame < max_frames) { foreach (QString effect, movie_effects[p_frame]) { if (effect == "shake") { - shake(); + emit shake(); #ifdef DEBUG_MOVIE qDebug() << "[CharLayer::play_frame_effect] Attempting to play shake on frame" << frame; #endif } if (effect == "flash") { - flash(); + emit flash(); #ifdef DEBUG_MOVIE qDebug() << "[CharLayer::play_frame_effect] Attempting to play flash on frame" << frame; #endif @@ -476,7 +476,7 @@ void CharLayer::play_frame_effect(int p_frame) if (effect.startsWith("sfx^")) { QString sfx = effect.section("^", 1); - play_sfx(sfx); + emit play_sfx(sfx); #ifdef DEBUG_MOVIE qDebug() << "[CharLayer::play_frame_effect] Attempting to play sfx" << sfx << "on frame" << frame; #endif @@ -576,7 +576,7 @@ void AOLayer::preanim_done() { ticker->stop(); preanim_timer->stop(); - done(); + emit done(); } void AOLayer::shfx_timer_done() @@ -586,5 +586,5 @@ void AOLayer::shfx_timer_done() qDebug() << "shfx timer signaled done"; #endif // signal connected to courtroom object, let it figure out what to do - done(); + emit done(); } -- cgit From b2a4a41fd707633a03ecdef8a484c0f16f13779e Mon Sep 17 00:00:00 2001 From: Rosemary Witchaven <32779090+in1tiate@users.noreply.github.com> Date: Sat, 18 Sep 2021 09:33:00 -0500 Subject: Fix background positions with no desk inheriting the previous position's desk (#598) * kill bglayer if file not exist * kill ALL layers if file not found --- src/aolayer.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 201c724..cf34999 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -145,7 +145,8 @@ void BackgroundLayer::load_image(QString p_filename) #ifdef DEBUG_MOVIE qDebug() << "[BackgroundLayer] BG loaded: " << p_filename; #endif - start_playback(ao_app->get_image_suffix(ao_app->get_background_path(p_filename))); + QString final_path = ao_app->get_image_suffix(ao_app->get_background_path(p_filename)); + start_playback(final_path); play(); } @@ -179,15 +180,16 @@ void CharLayer::load_image(QString p_filename, QString p_charname, last_emote = current_emote; last_prefix = prefix; is_preanim = p_is_preanim; - if ((p_filename.left(3) == "(a)") || (p_filename.left(3) == "(b)")) { - prefix = p_filename.left(3); + if ((p_filename.left(3) == "(a)") || (p_filename.left(3) == "(b)")) { // if we are playing an idle or talking animation + prefix = p_filename.left(3); // separate the prefix from the emote name current_emote = p_filename.mid(3, -1); } - else if ((duration > 0) || (p_filename.left(3) == "(c)")) { - if (p_filename.left(3) == "(c)") { - prefix = "(c)"; + else if ((duration > 0) || (p_filename.left(3) == "(c)")) { // else if we are playing a preanim or postanim + if (p_filename.left(3) == "(c)") { // if we are playing a postanim + prefix = "(c)"; // separate the prefix from the emote name current_emote = p_filename.mid(3, -1); } + // pre/postanim specific flags is_preanim = true; play_once = true; preanim_timer->start(duration); @@ -197,7 +199,7 @@ void CharLayer::load_image(QString p_filename, QString p_charname, << current_emote << " from character: " << p_charname << " continuous: " << continuous; #endif - QVector pathlist { + QVector pathlist { // cursed character path resolution vector ao_app->get_character_path( p_charname, prefix + current_emote), // Default path ao_app->get_character_path( @@ -232,7 +234,7 @@ void EffectLayer::load_image(QString p_filename, bool p_looping) play_once = true; continuous = false; force_continuous = true; - start_playback(p_filename); // handled in its own file before we see it + start_playback(p_filename); // path resolution is handled by the caller for EffectLayer objects play(); } @@ -269,6 +271,10 @@ void CharLayer::start_playback(QString p_image) void AOLayer::start_playback(QString p_image) { + if (p_image == "") {// image wasn't found by the path resolution function + this->kill(); + return; + } QMutexLocker locker(&mutex); if (frame_loader.isRunning()) exit_loop = true; // tell the loader to stop, we have a new image to load @@ -315,8 +321,8 @@ void AOLayer::start_playback(QString p_image) } frame_loader = QtConcurrent::run(this, &AOLayer::populate_vectors); last_path = p_image; - while (movie_frames.size() <= frame) - frameAdded.wait(&mutex); + while (movie_frames.size() <= frame) // if we haven't loaded the frame we need yet + frameAdded.wait(&mutex); // wait for the frame loader to add another frame, then check again this->set_frame(movie_frames[frame]); if (max_frames <= 1) { @@ -534,7 +540,7 @@ void AOLayer::movie_ticker() } mutex.lock(); while (frame >= movie_frames.size() && frame < max_frames) { // oops! our frame isn't ready yet - frameAdded.wait(&mutex); + frameAdded.wait(&mutex); // wait for a new frame to be added, then check again } mutex.unlock(); #ifdef DEBUG_MOVIE -- cgit From bc49b3e976e4df01e983a9c9daea28287b9bee75 Mon Sep 17 00:00:00 2001 From: Salanto <62221668+Salanto@users.noreply.github.com> Date: Sat, 11 Dec 2021 00:28:00 +0100 Subject: Update slots/signals to Qt5 connection style and eliminate some clazy warnings --- src/aolayer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/aolayer.cpp') diff --git a/src/aolayer.cpp b/src/aolayer.cpp index cf34999..dd1fe76 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -12,16 +12,16 @@ AOLayer::AOLayer(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) shfx_timer = new QTimer(this); shfx_timer->setTimerType(Qt::PreciseTimer); shfx_timer->setSingleShot(true); - connect(shfx_timer, SIGNAL(timeout()), this, SLOT(shfx_timer_done())); + connect(shfx_timer, &QTimer::timeout, this, &AOLayer::shfx_timer_done); ticker = new QTimer(this); ticker->setTimerType(Qt::PreciseTimer); ticker->setSingleShot(false); - connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); + connect(ticker, &QTimer::timeout, this, &AOLayer::movie_ticker); preanim_timer = new QTimer(this); preanim_timer->setSingleShot(true); - connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); + connect(preanim_timer, &QTimer::timeout, this, &AOLayer::preanim_done); } BackgroundLayer::BackgroundLayer(QWidget *p_parent, AOApplication *p_ao_app) -- cgit