Merge pull request #582 from AttorneyOnline/async-anim-load
Fully asynchronous animation loading
This commit is contained in:
		
						commit
						9d2e00ee11
					
				@ -8,6 +8,8 @@
 | 
				
			|||||||
#include <QTimer>
 | 
					#include <QTimer>
 | 
				
			||||||
#include <QBitmap>
 | 
					#include <QBitmap>
 | 
				
			||||||
#include <QtConcurrent/QtConcurrentRun>
 | 
					#include <QtConcurrent/QtConcurrentRun>
 | 
				
			||||||
 | 
					#include <QMutex>
 | 
				
			||||||
 | 
					#include <QWaitCondition>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AOApplication;
 | 
					class AOApplication;
 | 
				
			||||||
class VPath;
 | 
					class VPath;
 | 
				
			||||||
@ -140,11 +142,17 @@ protected:
 | 
				
			|||||||
  // Center the QLabel in the viewport based on the dimensions of f_pixmap
 | 
					  // Center the QLabel in the viewport based on the dimensions of f_pixmap
 | 
				
			||||||
  void center_pixmap(QPixmap f_pixmap);
 | 
					  void center_pixmap(QPixmap f_pixmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Populates the frame and delay vectors with the next frame's data.
 | 
					private:
 | 
				
			||||||
  void load_next_frame();
 | 
					  // Populates the frame and delay vectors.
 | 
				
			||||||
 | 
					  void populate_vectors();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // used in populate_vectors
 | 
				
			||||||
 | 
					  void load_next_frame();
 | 
				
			||||||
 | 
					  bool exit_loop; //awful solution but i'm not fucking using QThread
 | 
				
			||||||
 | 
					  QFuture<void> frame_loader;
 | 
				
			||||||
 | 
					  QMutex mutex;
 | 
				
			||||||
 | 
					  QWaitCondition frameAdded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // used in load_next_frame
 | 
					 | 
				
			||||||
  QFuture<void> future;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
  void done();
 | 
					  void done();
 | 
				
			||||||
@ -243,4 +251,5 @@ public:
 | 
				
			|||||||
  StickerLayer(QWidget *p_parent, AOApplication *p_ao_app);
 | 
					  StickerLayer(QWidget *p_parent, AOApplication *p_ao_app);
 | 
				
			||||||
  void load_image(QString p_charname);
 | 
					  void load_image(QString p_charname);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // AOLAYER_H
 | 
					#endif // AOLAYER_H
 | 
				
			||||||
 | 
				
			|||||||
@ -269,6 +269,9 @@ void CharLayer::start_playback(QString p_image)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void AOLayer::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();
 | 
					  this->show();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!ao_app->is_continuous_enabled()) {
 | 
					  if (!ao_app->is_continuous_enabled()) {
 | 
				
			||||||
@ -276,7 +279,7 @@ void AOLayer::start_playback(QString p_image)
 | 
				
			|||||||
    force_continuous = true;
 | 
					    force_continuous = true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if ((last_path == p_image) && (!force_continuous))
 | 
					  if (((last_path == p_image) && (!force_continuous)) || p_image == "")
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
@ -296,7 +299,7 @@ void AOLayer::start_playback(QString p_image)
 | 
				
			|||||||
    stretch = stretch_override.startsWith("true");
 | 
					    stretch = stretch_override.startsWith("true");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
  qDebug() << "stretch:" << stretch << "filename:" << p_image;
 | 
					  qDebug() << "[AOLayer::start_playback] Stretch:" << stretch << "Filename:" << p_image;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  m_reader.setFileName(p_image);
 | 
					  m_reader.setFileName(p_image);
 | 
				
			||||||
  if (m_reader.loopCount() == 0)
 | 
					  if (m_reader.loopCount() == 0)
 | 
				
			||||||
@ -309,39 +312,22 @@ void AOLayer::start_playback(QString p_image)
 | 
				
			|||||||
    frame = 0;
 | 
					    frame = 0;
 | 
				
			||||||
    continuous = false;
 | 
					    continuous = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // CANTFIX: this causes a hitch
 | 
					  frame_loader = QtConcurrent::run(this, &AOLayer::populate_vectors);
 | 
				
			||||||
  // 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();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  last_path = p_image;
 | 
					  last_path = p_image;
 | 
				
			||||||
  QPixmap f_pixmap = this->get_pixmap(m_reader.read());
 | 
					  while (movie_frames.size() <= frame)
 | 
				
			||||||
  int f_delay = m_reader.nextImageDelay();
 | 
					    frameAdded.wait(&mutex);
 | 
				
			||||||
 | 
					  this->set_frame(movie_frames[frame]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->set_frame(f_pixmap);
 | 
					  if (max_frames <= 1) {
 | 
				
			||||||
  if (max_frames > 1) {
 | 
					 | 
				
			||||||
    movie_frames.append(f_pixmap);
 | 
					 | 
				
			||||||
    movie_delays.append(f_delay);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  else if (max_frames <= 1) {
 | 
					 | 
				
			||||||
    duration = static_duration;
 | 
					    duration = static_duration;
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
    qDebug() << "max_frames is <= 1, using static duration";
 | 
					    qDebug() << "[AOLayer::start_playback] max_frames is <= 1, using static duration";
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (duration > 0 && cull_image == true)
 | 
					  if (duration > 0 && cull_image == true)
 | 
				
			||||||
    shfx_timer->start(duration);
 | 
					    shfx_timer->start(duration);
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#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();
 | 
					           << "Time taken to process image:" << actual_time.elapsed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  actual_time.restart();
 | 
					  actual_time.restart();
 | 
				
			||||||
@ -372,8 +358,12 @@ void AOLayer::play()
 | 
				
			|||||||
    else
 | 
					    else
 | 
				
			||||||
      this->freeze();
 | 
					      this->freeze();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else
 | 
					  else {
 | 
				
			||||||
 | 
					      while (movie_delays.size() <= frame) {
 | 
				
			||||||
 | 
					          frameAdded.wait(&mutex);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    ticker->start(this->get_frame_delay(movie_delays[frame]));
 | 
					    ticker->start(this->get_frame_delay(movie_delays[frame]));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AOLayer::set_play_once(bool p_play_once) { play_once = p_play_once; }
 | 
					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.
 | 
					                                // data, let's yank it in.
 | 
				
			||||||
            effect += f_data;
 | 
					            effect += f_data;
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#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;
 | 
					                   << m_emote;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
          movie_effects[f_frame].append(effect);
 | 
					          movie_effects[f_frame].append(effect);
 | 
				
			||||||
@ -472,14 +462,14 @@ void CharLayer::play_frame_effect(int p_frame)
 | 
				
			|||||||
      if (effect == "shake") {
 | 
					      if (effect == "shake") {
 | 
				
			||||||
        shake();
 | 
					        shake();
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
        qDebug() << "Attempting to play shake on frame" << frame;
 | 
					        qDebug() << "[CharLayer::play_frame_effect] Attempting to play shake on frame" << frame;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (effect == "flash") {
 | 
					      if (effect == "flash") {
 | 
				
			||||||
        flash();
 | 
					        flash();
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
        qDebug() << "Attempting to play flash on frame" << frame;
 | 
					        qDebug() << "[CharLayer::play_frame_effect] Attempting to play flash on frame" << frame;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -487,7 +477,7 @@ void CharLayer::play_frame_effect(int p_frame)
 | 
				
			|||||||
        QString sfx = effect.section("^", 1);
 | 
					        QString sfx = effect.section("^", 1);
 | 
				
			||||||
        play_sfx(sfx);
 | 
					        play_sfx(sfx);
 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#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
 | 
					#endif
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -529,11 +519,12 @@ void CharLayer::movie_ticker()
 | 
				
			|||||||
void AOLayer::movie_ticker()
 | 
					void AOLayer::movie_ticker()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  ++frame;
 | 
					  ++frame;
 | 
				
			||||||
  if (frame >= movie_frames.size() && frame < max_frames) { // need to load the image
 | 
					  mutex.lock();
 | 
				
			||||||
      future.waitForFinished(); // Do Not want this to be running twice
 | 
					  while (frame >= movie_frames.size() && frame < max_frames) { // oops! our frame isn't ready yet
 | 
				
			||||||
      future = QtConcurrent::run(this, &AOLayer::load_next_frame);
 | 
					      frameAdded.wait(&mutex);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else if (frame >= max_frames) {
 | 
					  mutex.unlock();
 | 
				
			||||||
 | 
					  if (frame >= max_frames) {
 | 
				
			||||||
    if (play_once) {
 | 
					    if (play_once) {
 | 
				
			||||||
      if (cull_image)
 | 
					      if (cull_image)
 | 
				
			||||||
        this->stop();
 | 
					        this->stop();
 | 
				
			||||||
@ -545,21 +536,31 @@ void AOLayer::movie_ticker()
 | 
				
			|||||||
    else
 | 
					    else
 | 
				
			||||||
      frame = 0;
 | 
					      frame = 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  future.waitForFinished(); // don't set the frame before we definitely have it in memory
 | 
					 | 
				
			||||||
#ifdef DEBUG_MOVIE
 | 
					#ifdef DEBUG_MOVIE
 | 
				
			||||||
  qDebug() << frame << movie_delays[frame]
 | 
					  qDebug() << "[AOLayer::movie_ticker] Frame:" << frame << "Delay:" << movie_delays[frame]
 | 
				
			||||||
           << "actual time taken from last frame:" << actual_time.restart();
 | 
					           << "Actual time taken from last frame:" << actual_time.restart();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  this->set_frame(movie_frames[frame]);
 | 
					  this->set_frame(movie_frames[frame]);
 | 
				
			||||||
  ticker->setInterval(this->get_frame_delay(movie_delays[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() {
 | 
					void AOLayer::load_next_frame() {
 | 
				
			||||||
 | 
					    //QMutexLocker locker(&mutex);
 | 
				
			||||||
 | 
					    mutex.lock();
 | 
				
			||||||
    movie_frames.append(this->get_pixmap(m_reader.read()));
 | 
					    movie_frames.append(this->get_pixmap(m_reader.read()));
 | 
				
			||||||
    movie_delays.append(m_reader.nextImageDelay());
 | 
					    movie_delays.append(m_reader.nextImageDelay());
 | 
				
			||||||
 | 
					    mutex.unlock();
 | 
				
			||||||
 | 
					    frameAdded.wakeAll();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CharLayer::preanim_done()
 | 
					void CharLayer::preanim_done()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user