mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
loading images on their own thread
This commit is contained in:
parent
bb76a632f4
commit
6344fa6b23
|
@ -98,8 +98,6 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
||||||
for (int i = snapshotLength - 1; i >= end; --i) {
|
for (int i = snapshotLength - 1; i >= end; --i) {
|
||||||
auto &s = snapshot[i];
|
auto &s = snapshot[i];
|
||||||
|
|
||||||
qDebug() << s->parseTime << minimumTime;
|
|
||||||
|
|
||||||
if (s->parseTime < minimumTime) {
|
if (s->parseTime < minimumTime) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDebug>
|
#include "debug/Log.hpp"
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <boost/current_function.hpp>
|
#include <boost/current_function.hpp>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
@ -29,8 +30,7 @@ public:
|
||||||
|
|
||||||
~BenchmarkGuard()
|
~BenchmarkGuard()
|
||||||
{
|
{
|
||||||
qDebug() << this->name << float(timer.nsecsElapsed()) / 1000000.0f
|
Log("{} {} ms", this->name, float(timer.nsecsElapsed()) / 1000000.0f);
|
||||||
<< "ms";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal getElapsedMs()
|
qreal getElapsedMs()
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "common/NetworkRequest.hpp"
|
#include "common/NetworkRequest.hpp"
|
||||||
#include "debug/AssertInGuiThread.hpp"
|
#include "debug/AssertInGuiThread.hpp"
|
||||||
|
#include "debug/Benchmark.hpp"
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "singletons/WindowManager.hpp"
|
#include "singletons/WindowManager.hpp"
|
||||||
|
@ -15,7 +16,6 @@
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -81,11 +81,11 @@ const QPixmap *Frames::first() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
std::vector<Frame> readFrames(QImageReader &reader, const Url &url)
|
std::vector<ParseFrame> readFrames(QImageReader &reader, const Url &url)
|
||||||
{
|
{
|
||||||
std::vector<Frame> frames;
|
std::vector<ParseFrame> frames;
|
||||||
|
|
||||||
if (reader.imageCount() <= 0) {
|
if (reader.imageCount() == 0) {
|
||||||
Log("Error while reading image {}: '{}'", url.string,
|
Log("Error while reading image {}: '{}'", url.string,
|
||||||
reader.errorString());
|
reader.errorString());
|
||||||
return frames;
|
return frames;
|
||||||
|
@ -94,14 +94,14 @@ std::vector<Frame> readFrames(QImageReader &reader, const Url &url)
|
||||||
QImage image;
|
QImage image;
|
||||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||||
if (reader.read(&image)) {
|
if (reader.read(&image)) {
|
||||||
auto pixmap = std::make_unique<QPixmap>(QPixmap::fromImage(image));
|
QPixmap::fromImage(image);
|
||||||
|
|
||||||
int duration = std::max(20, reader.nextImageDelay());
|
int duration = std::max(20, reader.nextImageDelay());
|
||||||
frames.push_back(Frame{std::move(pixmap), duration});
|
frames.push_back(ParseFrame{image, duration});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frames.size() != 0) {
|
if (frames.size() == 0) {
|
||||||
Log("Error while reading image {}: '{}'", url.string,
|
Log("Error while reading image {}: '{}'", url.string,
|
||||||
reader.errorString());
|
reader.errorString());
|
||||||
}
|
}
|
||||||
|
@ -123,11 +123,65 @@ void queueLoadedEvent()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parsed
|
||||||
|
template <typename Assign>
|
||||||
|
void asd(std::queue<std::pair<Assign, std::vector<Frame>>> &queued,
|
||||||
|
std::mutex &mutex, std::atomic_bool &loadedEventQueued)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (!queued.empty()) {
|
||||||
|
queued.front().first(std::move(queued.front().second));
|
||||||
|
queued.pop();
|
||||||
|
|
||||||
|
if (++i > 50) {
|
||||||
|
QTimer::singleShot(3,
|
||||||
|
[&] { asd(queued, mutex, loadedEventQueued); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getApp()->windows->forceLayoutChannelViews();
|
||||||
|
loadedEventQueued = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Assign>
|
||||||
|
auto makeConvertCallback(std::vector<ParseFrame> parsed, Assign assign)
|
||||||
|
{
|
||||||
|
return [parsed = std::move(parsed), assign] {
|
||||||
|
// BenchmarkGuard guard("convert image");
|
||||||
|
|
||||||
|
// convert to pixmap
|
||||||
|
auto frames = std::vector<Frame>();
|
||||||
|
std::transform(parsed.begin(), parsed.end(), std::back_inserter(frames),
|
||||||
|
[](auto &frame) {
|
||||||
|
return Frame{std::make_unique<QPixmap>(
|
||||||
|
QPixmap::fromImage(frame.image)),
|
||||||
|
frame.duration};
|
||||||
|
});
|
||||||
|
|
||||||
|
// put into stack
|
||||||
|
static std::queue<std::pair<Assign, std::vector<Frame>>> queued;
|
||||||
|
static std::mutex mutex;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
queued.emplace(assign, std::move(frames));
|
||||||
|
|
||||||
|
static std::atomic_bool loadedEventQueued{false};
|
||||||
|
|
||||||
|
if (!loadedEventQueued) {
|
||||||
|
loadedEventQueued = true;
|
||||||
|
|
||||||
|
QTimer::singleShot(100,
|
||||||
|
[=] { asd(queued, mutex, loadedEventQueued); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// IMAGE2
|
// IMAGE2
|
||||||
std::atomic<bool> Image::loadedEventQueued{false};
|
|
||||||
|
|
||||||
ImagePtr Image::fromUrl(const Url &url, qreal scale)
|
ImagePtr Image::fromUrl(const Url &url, qreal scale)
|
||||||
{
|
{
|
||||||
static std::unordered_map<Url, std::weak_ptr<Image>> cache;
|
static std::unordered_map<Url, std::weak_ptr<Image>> cache;
|
||||||
|
@ -249,25 +303,36 @@ void Image::load()
|
||||||
NetworkRequest req(this->url().string);
|
NetworkRequest req(this->url().string);
|
||||||
req.setCaller(&this->object_);
|
req.setCaller(&this->object_);
|
||||||
req.setUseQuickLoadCache(true);
|
req.setUseQuickLoadCache(true);
|
||||||
req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome {
|
req.onSuccess([that = this, weak = weakOf(this)](auto result) -> Outcome {
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
|
||||||
auto shared = weak.lock();
|
auto shared = weak.lock();
|
||||||
if (!shared) return Failure;
|
if (!shared) return Failure;
|
||||||
|
|
||||||
// const cast since we are only reading from it
|
static auto parseThread = [] {
|
||||||
QBuffer buffer(const_cast<QByteArray *>(&result.getData()));
|
auto thread = std::make_unique<QThread>();
|
||||||
buffer.open(QIODevice::ReadOnly);
|
thread->start();
|
||||||
QImageReader reader(&buffer);
|
return thread;
|
||||||
|
}();
|
||||||
|
|
||||||
this->frames_ = readFrames(reader, this->url());
|
postToThread(
|
||||||
|
[data = result.getData(), weak, that] {
|
||||||
|
// BenchmarkGuard guard("parse image");
|
||||||
|
|
||||||
|
// const cast since we are only reading from it
|
||||||
|
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
QImageReader reader(&buffer);
|
||||||
|
auto parsed = readFrames(reader, that->url());
|
||||||
|
|
||||||
|
postToThread(
|
||||||
|
makeConvertCallback(std::move(parsed), [weak](auto frames) {
|
||||||
|
if (auto shared = weak.lock())
|
||||||
|
shared->frames_ = std::move(frames);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
parseThread.get());
|
||||||
|
|
||||||
if (!loadedEventQueued) {
|
|
||||||
QTimer::singleShot(150, [] {
|
|
||||||
getApp()->windows->forceLayoutChannelViews();
|
|
||||||
loadedEventQueued = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Success;
|
return Success;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QThread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
@ -19,6 +20,10 @@ struct Frame {
|
||||||
Pixmap pixmap;
|
Pixmap pixmap;
|
||||||
int duration;
|
int duration;
|
||||||
};
|
};
|
||||||
|
struct ParseFrame {
|
||||||
|
QImage image;
|
||||||
|
int duration;
|
||||||
|
};
|
||||||
class Frames
|
class Frames
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -77,7 +82,5 @@ private:
|
||||||
bool shouldLoad_{false};
|
bool shouldLoad_{false};
|
||||||
Frames frames_{};
|
Frames frames_{};
|
||||||
QObject object_{};
|
QObject object_{};
|
||||||
|
|
||||||
static std::atomic<bool> loadedEventQueued;
|
|
||||||
};
|
};
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -205,7 +205,7 @@ void ChannelView::layoutMessages()
|
||||||
|
|
||||||
void ChannelView::actuallyLayoutMessages(bool causedByScrollbar)
|
void ChannelView::actuallyLayoutMessages(bool causedByScrollbar)
|
||||||
{
|
{
|
||||||
// BenchmarkGuard benchmark("layout messages");
|
// BenchmarkGuard benchmark("layout");
|
||||||
|
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ void ChannelView::updatePauseStatus()
|
||||||
|
|
||||||
void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
||||||
{
|
{
|
||||||
// BenchmarkGuard benchmark("paint event");
|
// BenchmarkGuard benchmark("paint");
|
||||||
|
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue