From e7e4ab768b53a6c2138544ab2125fd372b32d048 Mon Sep 17 00:00:00 2001 From: hemirt Date: Sun, 8 Oct 2017 15:18:47 +0200 Subject: [PATCH] Fix image loading slowness Images are now loaded in another thread --- chatterino.pro | 6 +- src/messages/imageloadermanager.cpp | 98 +++++++++++++++++++++++++++++ src/messages/imageloadermanager.hpp | 48 ++++++++++++++ src/messages/lazyloadedimage.cpp | 78 ++++++----------------- src/messages/lazyloadedimage.hpp | 7 ++- 5 files changed, 176 insertions(+), 61 deletions(-) create mode 100644 src/messages/imageloadermanager.cpp create mode 100644 src/messages/imageloadermanager.hpp diff --git a/chatterino.pro b/chatterino.pro index 328a10b1..8a61d34b 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -99,7 +99,8 @@ SOURCES += \ src/twitch/twitchchannel.cpp \ src/widgets/rippleeffectlabel.cpp \ src/widgets/rippleeffectbutton.cpp \ - src/messages/messagecolor.cpp + src/messages/messagecolor.cpp \ + src/messages/imageloadermanager.cpp HEADERS += \ src/asyncexec.hpp \ @@ -165,7 +166,8 @@ HEADERS += \ src/widgets/emotepopup.hpp \ src/messages/messagecolor.hpp \ src/util/nativeeventhelper.hpp \ - src/debug/log.hpp + src/debug/log.hpp \ + src/messages/imageloadermanager.hpp PRECOMPILED_HEADER = diff --git a/src/messages/imageloadermanager.cpp b/src/messages/imageloadermanager.cpp new file mode 100644 index 00000000..a1ed63d1 --- /dev/null +++ b/src/messages/imageloadermanager.cpp @@ -0,0 +1,98 @@ +#include "messages/imageloadermanager.hpp" +#include "emotemanager.hpp" +#include "messages/lazyloadedimage.hpp" +#include "windowmanager.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chatterino { +namespace messages { + +ImageLoaderManager::ImageLoaderManager() + : NaM(new QNetworkAccessManager) +{ + qDebug() << "imageloaderthread" << QThread::currentThread(); + qDebug() << "workerthread: " << &this->workerThread; + this->NaM->moveToThread(&this->workerThread); + this->workerThread.start(); +} + +ImageLoaderManager::~ImageLoaderManager() +{ + this->workerThread.quit(); + this->workerThread.wait(); +} + +void ImageLoaderWorker::handleRequest(LazyLoadedImage *lli, QNetworkAccessManager *nam) +{ + QNetworkRequest request; + request.setUrl(QUrl(lli->getUrl())); + qDebug() << "handleRequest: " << lli->getUrl(); + QNetworkReply *reply = nam->get(request); + QObject::connect(reply, &QNetworkReply::finished, + [lli, reply, this]() { this->handleLoad(lli, reply); }); + +} + +void ImageLoaderManager::queue(chatterino::messages::LazyLoadedImage *lli) +{ + ImageLoaderRequester requester; + ImageLoaderWorker *workerer = new ImageLoaderWorker; + workerer->moveToThread(&this->workerThread); + QObject::connect(&requester, &ImageLoaderRequester::request, workerer, + &ImageLoaderWorker::handleRequest); + emit requester.request(lli, this->NaM); + qDebug() << lli->getUrl(); +} + +void ImageLoaderWorker::handleLoad(chatterino::messages::LazyLoadedImage *lli, QNetworkReply *reply) +{ + qDebug() << "Received emote " << lli->url; + qDebug() << QThread::currentThread(); + QByteArray array = reply->readAll(); + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); + + QImage image; + QImageReader reader(&buffer); + + bool first = true; + + for (int index = 0; index < reader.imageCount(); ++index) { + if (reader.read(&image)) { + auto pixmap = new QPixmap(QPixmap::fromImage(image)); + + if (first) { + first = false; + lli->currentPixmap = pixmap; + } + + LazyLoadedImage::FrameData data; + data.duration = std::max(20, reader.nextImageDelay()); + data.image = pixmap; + + lli->allFrames.push_back(data); + } + } + + if (lli->allFrames.size() > 1) { + lli->animated = true; + } + + lli->emoteManager.incGeneration(); + lli->windowManager.layoutVisibleChatWidgets(); + + delete reply; + delete this; +} + +} // namespace messages +} // namespace chatterino diff --git a/src/messages/imageloadermanager.hpp b/src/messages/imageloadermanager.hpp new file mode 100644 index 00000000..6024cade --- /dev/null +++ b/src/messages/imageloadermanager.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace chatterino { +namespace messages { + +class LazyLoadedImage; + +class ImageLoaderWorker : public QObject +{ + Q_OBJECT +public: +public slots: + void handleRequest(chatterino::messages::LazyLoadedImage *lli, QNetworkAccessManager *nam); + void handleLoad(LazyLoadedImage *lli, QNetworkReply *reply); +}; + +class ImageLoaderRequester : public QObject +{ + Q_OBJECT +signals: + void request(chatterino::messages::LazyLoadedImage *lli, QNetworkAccessManager *nam); +}; + +class ImageLoaderManager : public QObject +{ + Q_OBJECT + QThread workerThread; + QNetworkAccessManager *NaM; + +public: + ImageLoaderManager(); + ~ImageLoaderManager(); + void queue(chatterino::messages::LazyLoadedImage *lli); +}; + +} // namespace messages +} // namespace chatterino diff --git a/src/messages/lazyloadedimage.cpp b/src/messages/lazyloadedimage.cpp index 1d84d026..d48f9e8e 100644 --- a/src/messages/lazyloadedimage.cpp +++ b/src/messages/lazyloadedimage.cpp @@ -2,16 +2,17 @@ #include "asyncexec.hpp" #include "emotemanager.hpp" #include "ircmanager.hpp" +#include "messages/imageloadermanager.hpp" #include "util/urlfetch.hpp" #include "windowmanager.hpp" -#include #include #include #include #include #include #include +#include #include @@ -51,70 +52,31 @@ LazyLoadedImage::LazyLoadedImage(EmoteManager &_emoteManager, WindowManager &_wi void LazyLoadedImage::loadImage() { - std::thread([=] () { - QNetworkRequest request; - request.setUrl(QUrl(this->url)); - QNetworkAccessManager NaM; - QEventLoop eventLoop; - QNetworkReply *reply = NaM.get(request); - QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit); - eventLoop.exec(); // Wait until response is read. + static ImageLoaderManager imageLoader; + imageLoader.queue(this); - qDebug() << "Received emote " << this->url; - QByteArray array = reply->readAll(); - QBuffer buffer(&array); - buffer.open(QIODevice::ReadOnly); - - QImage image; - QImageReader reader(&buffer); - - bool first = true; - - for (int index = 0; index < reader.imageCount(); ++index) { - if (reader.read(&image)) { - auto pixmap = new QPixmap(QPixmap::fromImage(image)); - - if (first) { - first = false; - this->currentPixmap = pixmap; - } - - FrameData data; - data.duration = std::max(20, reader.nextImageDelay()); - data.image = pixmap; - - this->allFrames.push_back(data); - } - } - - if (this->allFrames.size() > 1) { - this->animated = true; - } - - this->emoteManager.incGeneration(); - this->windowManager.layoutVisibleChatWidgets(); - - delete reply; - }).detach(); - this->emoteManager.getGifUpdateSignal().connect([=] () { this->gifUpdateTimout(); }); // For some reason when Boost signal is in thread scope and thread deletes the signal doesn't work, so this is the fix. + this->emoteManager.getGifUpdateSignal().connect([=]() { + this->gifUpdateTimout(); + }); // For some reason when Boost signal is in thread scope and thread deletes the signal + // doesn't work, so this is the fix. } void LazyLoadedImage::gifUpdateTimout() { - if (animated) { - this->currentFrameOffset += GIF_FRAME_LENGTH; + if (animated) { + this->currentFrameOffset += GIF_FRAME_LENGTH; - while (true) { - if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) { - this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration; - this->currentFrame = (this->currentFrame + 1) % this->allFrames.size(); - } else { - break; - } - } + while (true) { + if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) { + this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration; + this->currentFrame = (this->currentFrame + 1) % this->allFrames.size(); + } else { + break; + } + } - this->currentPixmap = this->allFrames[this->currentFrame].image; - } + this->currentPixmap = this->allFrames[this->currentFrame].image; + } } const QPixmap *LazyLoadedImage::getPixmap() diff --git a/src/messages/lazyloadedimage.hpp b/src/messages/lazyloadedimage.hpp index c6c06125..59e8cc41 100644 --- a/src/messages/lazyloadedimage.hpp +++ b/src/messages/lazyloadedimage.hpp @@ -10,7 +10,9 @@ class WindowManager; namespace messages { -class LazyLoadedImage : QObject +class ImageLoaderManager; + +class LazyLoadedImage : public QObject { public: LazyLoadedImage() = delete; @@ -64,6 +66,9 @@ private: void loadImage(); void gifUpdateTimout(); + + //static ImageLoaderManager imageLoader; + friend class ImageLoaderWorker; }; } // namespace messages