Use std::shared_ptrfor AlbumCoverLoaderResult

Reduces memory fragmentation with Qt 6
This commit is contained in:
Jonas Kvinge
2023-04-06 23:18:10 +02:00
parent 962536bc83
commit b660287779
38 changed files with 363 additions and 294 deletions

View File

@@ -21,6 +21,8 @@
#include "config.h"
#include <memory>
#include <QtGlobal>
#include <QObject>
#include <QStandardPaths>
@@ -81,8 +83,9 @@ void AlbumCoverLoader::Exit() {
void AlbumCoverLoader::CancelTask(const quint64 id) {
QMutexLocker l(&mutex_load_image_async_);
for (QQueue<Task>::iterator it = tasks_.begin(); it != tasks_.end(); ++it) {
if (it->id == id) {
for (QQueue<TaskPtr>::iterator it = tasks_.begin(); it != tasks_.end(); ++it) {
TaskPtr task = *it;
if (task->id == id) {
tasks_.erase(it); // clazy:exclude=strict-iterators
break;
}
@@ -93,8 +96,9 @@ void AlbumCoverLoader::CancelTask(const quint64 id) {
void AlbumCoverLoader::CancelTasks(const QSet<quint64> &ids) {
QMutexLocker l(&mutex_load_image_async_);
for (QQueue<Task>::iterator it = tasks_.begin(); it != tasks_.end();) {
if (ids.contains(it->id)) {
for (QQueue<TaskPtr>::iterator it = tasks_.begin(); it != tasks_.end();) {
TaskPtr task = *it;
if (ids.contains(task->id)) {
it = tasks_.erase(it); // clazy:exclude=strict-iterators
}
else {
@@ -106,10 +110,11 @@ void AlbumCoverLoader::CancelTasks(const QSet<quint64> &ids) {
quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options, const Song &song) {
Task task;
task.options = options;
task.song = song;
task.state = State::Manual;
TaskPtr task = std::make_shared<Task>();
task->options = options;
task->song = song;
task->state = State::Manual;
task->album_cover = std::make_shared<AlbumCoverImageResult>();
return EnqueueTask(task);
@@ -122,20 +127,21 @@ quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options,
song.set_art_automatic(art_automatic);
song.set_art_manual(art_manual);
Task task;
task.options = options;
task.song = song;
task.state = State::Manual;
TaskPtr task = std::make_shared<Task>();
task->options = options;
task->song = song;
task->state = State::Manual;
task->album_cover = std::make_shared<AlbumCoverImageResult>();
return EnqueueTask(task);
}
quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options, const AlbumCoverImageResult &album_cover) {
quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options, AlbumCoverImageResultPtr album_cover) {
Task task;
task.options = options;
task.album_cover = album_cover;
TaskPtr task = std::make_shared<Task>();
task->options = options;
task->album_cover = album_cover;
return EnqueueTask(task);
@@ -143,25 +149,26 @@ quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options,
quint64 AlbumCoverLoader::LoadImageAsync(const AlbumCoverLoaderOptions &options, const QImage &image) {
Task task;
task.options = options;
task.album_cover.image = image;
TaskPtr task = std::make_shared<Task>();
task->options = options;
task->album_cover = std::make_shared<AlbumCoverImageResult>();
task->album_cover->image = image;
return EnqueueTask(task);
}
quint64 AlbumCoverLoader::EnqueueTask(Task &task) {
quint64 AlbumCoverLoader::EnqueueTask(TaskPtr task) {
{
QMutexLocker l(&mutex_load_image_async_);
task.id = load_image_async_id_++;
task->id = load_image_async_id_++;
tasks_.enqueue(task);
}
QMetaObject::invokeMethod(this, "ProcessTasks", Qt::QueuedConnection);
return task.id;
return task->id;
}
@@ -169,19 +176,19 @@ void AlbumCoverLoader::ProcessTasks() {
while (!stop_requested_) {
// Get the next task
Task task;
TaskPtr task;
{
QMutexLocker l(&mutex_load_image_async_);
if (tasks_.isEmpty()) return;
task = tasks_.dequeue();
}
ProcessTask(&task);
ProcessTask(task);
}
}
void AlbumCoverLoader::ProcessTask(Task *task) {
void AlbumCoverLoader::ProcessTask(TaskPtr task) {
TryLoadResult result = TryLoadImage(task);
if (result.started_async) {
@@ -190,16 +197,16 @@ void AlbumCoverLoader::ProcessTask(Task *task) {
}
if (result.loaded_success) {
result.album_cover.mime_type = Utilities::MimeTypeFromData(result.album_cover.image_data);
result.album_cover->mime_type = Utilities::MimeTypeFromData(result.album_cover->image_data);
QImage image_scaled;
QImage image_thumbnail;
if (task->options.get_image_ && task->options.scale_output_image_) {
image_scaled = ImageUtils::ScaleAndPad(result.album_cover.image, task->options.scale_output_image_, task->options.pad_output_image_, task->options.desired_height_);
image_scaled = ImageUtils::ScaleAndPad(result.album_cover->image, task->options.scale_output_image_, task->options.pad_output_image_, task->options.desired_height_);
}
if (task->options.get_image_ && task->options.create_thumbnail_) {
image_thumbnail = ImageUtils::CreateThumbnail(result.album_cover.image, task->options.pad_thumbnail_image_, task->options.thumbnail_size_);
image_thumbnail = ImageUtils::CreateThumbnail(result.album_cover->image, task->options.pad_thumbnail_image_, task->options.thumbnail_size_);
}
emit AlbumCoverLoaded(task->id, AlbumCoverLoaderResult(result.loaded_success, result.type, result.album_cover, image_scaled, image_thumbnail, task->art_updated));
emit AlbumCoverLoaded(task->id, std::make_shared<AlbumCoverLoaderResult>(result.loaded_success, result.type, result.album_cover, image_scaled, image_thumbnail, task->art_updated));
return;
}
@@ -207,7 +214,7 @@ void AlbumCoverLoader::ProcessTask(Task *task) {
}
void AlbumCoverLoader::NextState(Task *task) {
void AlbumCoverLoader::NextState(TaskPtr task) {
if (task->state == State::Manual) {
// Try the automatic one next
@@ -216,15 +223,15 @@ void AlbumCoverLoader::NextState(Task *task) {
}
else {
// Give up
emit AlbumCoverLoaded(task->id, AlbumCoverLoaderResult(false, AlbumCoverLoaderResult::Type::None, AlbumCoverImageResult(task->options.default_output_image_), task->options.default_scaled_image_, task->options.default_thumbnail_image_, task->art_updated));
emit AlbumCoverLoaded(task->id, std::make_shared<AlbumCoverLoaderResult>(false, AlbumCoverLoaderResult::Type::None, std::make_shared<AlbumCoverImageResult>(task->options.default_output_image_), task->options.default_scaled_image_, task->options.default_thumbnail_image_, task->art_updated));
}
}
AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(Task *task) {
AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(TaskPtr task) {
// Only scale and pad.
if (task->album_cover.is_valid()) {
if (task->album_cover->is_valid()) {
return TryLoadResult(false, true, AlbumCoverLoaderResult::Type::Embedded, task->album_cover);
}
@@ -263,17 +270,17 @@ AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(Task *task) {
if (!cover_url.isEmpty() && !cover_url.path().isEmpty()) {
if (cover_url.path() == Song::kManuallyUnsetCover) {
return TryLoadResult(false, true, AlbumCoverLoaderResult::Type::ManuallyUnset, AlbumCoverImageResult(cover_url, QString(), QByteArray(), task->options.default_output_image_));
return TryLoadResult(false, true, AlbumCoverLoaderResult::Type::ManuallyUnset, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), QByteArray(), task->options.default_output_image_));
}
else if (cover_url.path() == Song::kEmbeddedCover && task->song.url().isLocalFile()) {
QByteArray image_data = TagReaderClient::Instance()->LoadEmbeddedArtBlocking(task->song.url().toLocalFile());
if (!image_data.isEmpty()) {
QImage image;
if (!image_data.isEmpty() && task->options.get_image_ && image.loadFromData(image_data)) {
return TryLoadResult(false, !image.isNull(), AlbumCoverLoaderResult::Type::Embedded, AlbumCoverImageResult(cover_url, QString(), image_data, image));
return TryLoadResult(false, !image.isNull(), AlbumCoverLoaderResult::Type::Embedded, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image));
}
else {
return TryLoadResult(false, !image_data.isEmpty(), AlbumCoverLoaderResult::Type::Embedded, AlbumCoverImageResult(cover_url, QString(), image_data, image));
return TryLoadResult(false, !image_data.isEmpty(), AlbumCoverLoaderResult::Type::Embedded, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image));
}
}
}
@@ -286,10 +293,10 @@ AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(Task *task) {
file.close();
QImage image;
if (!image_data.isEmpty() && task->options.get_image_ && image.loadFromData(image_data)) {
return TryLoadResult(false, !image.isNull(), type, AlbumCoverImageResult(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
return TryLoadResult(false, !image.isNull(), type, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
}
else {
return TryLoadResult(false, !image_data.isEmpty(), type, AlbumCoverImageResult(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
return TryLoadResult(false, !image_data.isEmpty(), type, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
}
}
else {
@@ -308,10 +315,10 @@ AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(Task *task) {
file.close();
QImage image;
if (!image_data.isEmpty() && task->options.get_image_ && image.loadFromData(image_data)) {
return TryLoadResult(false, !image.isNull(), type, AlbumCoverImageResult(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
return TryLoadResult(false, !image.isNull(), type, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
}
else {
return TryLoadResult(false, !image_data.isEmpty(), type, AlbumCoverImageResult(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
return TryLoadResult(false, !image_data.isEmpty(), type, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), image_data, image.isNull() ? task->options.default_output_image_ : image));
}
}
else {
@@ -329,12 +336,12 @@ AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(Task *task) {
QNetworkReply *reply = network_->get(request);
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, cover_url]() { RemoteFetchFinished(reply, cover_url); });
remote_tasks_.insert(reply, *task);
return TryLoadResult(true, false, type, AlbumCoverImageResult(cover_url));
remote_tasks_.insert(reply, task);
return TryLoadResult(true, false, type, std::make_shared<AlbumCoverImageResult>(cover_url));
}
}
return TryLoadResult(false, false, AlbumCoverLoaderResult::Type::None, AlbumCoverImageResult(cover_url, QString(), QByteArray(), task->options.default_output_image_));
return TryLoadResult(false, false, AlbumCoverLoaderResult::Type::None, std::make_shared<AlbumCoverImageResult>(cover_url, QString(), QByteArray(), task->options.default_output_image_));
}
@@ -343,12 +350,12 @@ void AlbumCoverLoader::RemoteFetchFinished(QNetworkReply *reply, const QUrl &cov
reply->deleteLater();
if (!remote_tasks_.contains(reply)) return;
Task task = remote_tasks_.take(reply);
TaskPtr task = remote_tasks_.take(reply);
// Handle redirects.
QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirect.isValid()) {
if (++task.redirects > kMaxRedirects) {
if (++task->redirects > kMaxRedirects) {
return; // Give up.
}
QNetworkRequest request = reply->request();
@@ -369,9 +376,9 @@ void AlbumCoverLoader::RemoteFetchFinished(QNetworkReply *reply, const QUrl &cov
if (image.loadFromData(image_data)) {
QImage image_scaled;
QImage image_thumbnail;
if (task.options.scale_output_image_) image_scaled = ImageUtils::ScaleAndPad(image, task.options.scale_output_image_, task.options.pad_output_image_, task.options.desired_height_);
if (task.options.create_thumbnail_) image_thumbnail = ImageUtils::CreateThumbnail(image, task.options.pad_thumbnail_image_, task.options.thumbnail_size_);
emit AlbumCoverLoaded(task.id, AlbumCoverLoaderResult(true, task.type, AlbumCoverImageResult(cover_url, mime_type, (task.options.get_image_data_ ? image_data : QByteArray()), image), image_scaled, image_thumbnail, task.art_updated));
if (task->options.scale_output_image_) image_scaled = ImageUtils::ScaleAndPad(image, task->options.scale_output_image_, task->options.pad_output_image_, task->options.desired_height_);
if (task->options.create_thumbnail_) image_thumbnail = ImageUtils::CreateThumbnail(image, task->options.pad_thumbnail_image_, task->options.thumbnail_size_);
emit AlbumCoverLoaded(task->id, std::make_shared<AlbumCoverLoaderResult>(true, task->type, std::make_shared<AlbumCoverImageResult>(cover_url, mime_type, (task->options.get_image_data_ ? image_data : QByteArray()), image), image_scaled, image_thumbnail, task->art_updated));
return;
}
else {
@@ -382,7 +389,7 @@ void AlbumCoverLoader::RemoteFetchFinished(QNetworkReply *reply, const QUrl &cov
qLog(Error) << "Unable to get album cover" << cover_url << reply->error() << reply->errorString();
}
NextState(&task);
NextState(task);
}