Qt: Add AsyncPixmapLoader class

pull/3715/head
Stenzek 5 months ago
parent 1260891e4f
commit d618413c5d
No known key found for this signature in database

@ -17,6 +17,8 @@ set(SRCS
advancedsettingswidget.cpp
advancedsettingswidget.h
advancedsettingswidget.ui
asyncpixmaploader.cpp
asyncpixmaploader.h
audiosettingswidget.cpp
audiosettingswidget.h
audiosettingswidget.ui

@ -0,0 +1,118 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "asyncpixmaploader.h"
#include "qtutils.h"
#include "core/host.h"
#include "util/http_cache.h"
#include "util/object_archive.h"
#include "common/error.h"
#include "common/heap_array.h"
#include "common/log.h"
#include "common/path.h"
#include "common/small_string.h"
#include "moc_asyncpixmaploader.cpp"
LOG_CHANNEL(Host);
AsyncPixmapLoader::AsyncPixmapLoader(QObject* parent /*= nullptr*/) : QObject(parent)
{
}
AsyncPixmapLoader::~AsyncPixmapLoader() = default;
bool AsyncPixmapLoader::isQueueNeeded(std::string_view url_or_path)
{
if (!HTTPCache::IsHTTPURL(url_or_path))
return false;
// Don't try to async load when we don't have cache.
const auto cache = HTTPCache::GetCacheArchive();
return (cache->IsOpen() && !cache->Contains(url_or_path));
}
static std::string_view GetExtensionFromURL(std::string_view url)
{
return Path::GetExtension(HTTPCache::GetURLFilename(url));
}
static QPixmap LoadPixmapFromSpan(std::string_view extension, std::span<const u8> data)
{
QPixmap ret;
TinyString extension_cstr(extension);
if (!ret.loadFromData(data.data(), static_cast<uint>(data.size()),
extension_cstr.empty() ? nullptr : extension_cstr.c_str()))
{
ERROR_LOG("Failed to load pixmap from data for extension '{}'", extension);
}
return ret;
}
QPixmap AsyncPixmapLoader::load(std::string_view url_or_path)
{
if (!HTTPCache::IsHTTPURL(url_or_path))
return QPixmap(QtUtils::StringViewToQString(url_or_path));
Error error;
const HTTPCache::LookupResult result = HTTPCache::Lookup(url_or_path, &error);
if (result.status() != HTTPCache::LookupStatus::Hit)
{
ERROR_LOG("Failed to lookup pixmap for URL '{}': {}", url_or_path, error.GetDescription());
return QPixmap();
}
return LoadPixmapFromSpan(GetExtensionFromURL(url_or_path), result->cspan());
}
void AsyncPixmapLoader::enqueue(std::string_view url_or_path)
{
if (!HTTPCache::IsHTTPURL(url_or_path))
{
QPixmap pm(QtUtils::StringViewToQString(url_or_path));
emit pixmapLoaded(pm);
deleteLater();
return;
}
Error error;
HTTPCache::LookupResult result = HTTPCache::Lookup(url_or_path, &error);
// gotta load it, this is the yuck part.
if (result.status() == HTTPCache::LookupStatus::Miss)
{
m_extension = GetExtensionFromURL(url_or_path);
result = HTTPCache::LookupOrFetch(url_or_path, &error, [this](std::span<const u8> data) mutable {
if (!data.empty())
m_data.assign(data);
QMetaObject::invokeMethod(this, &AsyncPixmapLoader::finishLoad, Qt::QueuedConnection);
});
}
if (result.status() == HTTPCache::LookupStatus::Hit)
{
QPixmap pm = LoadPixmapFromSpan(GetExtensionFromURL(url_or_path), result->cspan());
emit pixmapLoaded(pm);
deleteLater();
return;
}
else if (result.status() != HTTPCache::LookupStatus::Miss)
{
ERROR_LOG("Failed to lookup pixmap for URL '{}': {}", url_or_path, error.GetDescription());
QPixmap pm;
emit pixmapLoaded(pm);
deleteLater();
return;
}
}
void AsyncPixmapLoader::finishLoad()
{
QPixmap pm = LoadPixmapFromSpan(m_extension, m_data);
emit pixmapLoaded(pm);
deleteLater();
}

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include "common/heap_array.h"
#include <string>
#include <string_view>
#include <QtCore/QObject>
class QPixmap;
class AsyncPixmapLoader final : public QObject
{
Q_OBJECT
public:
explicit AsyncPixmapLoader(QObject* parent = nullptr);
~AsyncPixmapLoader() override;
static bool isQueueNeeded(std::string_view url_or_path);
static QPixmap load(std::string_view url_or_path);
void enqueue(std::string_view url_or_path);
Q_SIGNALS:
void pixmapLoaded(QPixmap& pixmap);
private:
void finishLoad();
std::string m_extension;
DynamicHeapArray<u8> m_data;
};

@ -6,6 +6,7 @@
<ClCompile Include="achievementsettingswidget.cpp" />
<ClCompile Include="achievementlogindialog.cpp" />
<ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="asyncpixmaploader.cpp" />
<ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="autoupdaterdialog.cpp" />
<ClCompile Include="biossettingswidget.cpp" />
@ -82,6 +83,7 @@
<QtMoc Include="controllersettingswindow.h" />
<QtMoc Include="colorpickerbutton.h" />
<QtMoc Include="capturesettingswidget.h" />
<QtMoc Include="asyncpixmaploader.h" />
<ClInclude Include="controllersettingwidgetbinder.h" />
<QtMoc Include="memoryeditorwindow.h" />
<QtMoc Include="memoryviewwidget.h" />

@ -52,6 +52,7 @@
<ClCompile Include="qtwindowinfo.cpp" />
<ClCompile Include="osdsettingswidget.cpp" />
<ClCompile Include="capturesettingswidget.cpp" />
<ClCompile Include="asyncpixmaploader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qtutils.h" />
@ -116,6 +117,7 @@
<QtMoc Include="memoryviewwidget.h" />
<QtMoc Include="osdsettingswidget.h" />
<QtMoc Include="capturesettingswidget.h" />
<QtMoc Include="asyncpixmaploader.h" />
</ItemGroup>
<ItemGroup>
<QtUi Include="consolesettingswidget.ui" />

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "qtutils.h"
#include "asyncpixmaploader.h"
#include "qthost.h"
#include "core/core.h"
@ -350,6 +351,28 @@ QMenu* QtUtils::NewPopupMenu(QWidget* parent, bool delete_on_close /*= true*/)
return menu;
}
void QtUtils::SetLabelPixmapPathOrURL(QLabel* label, std::string_view path_or_url, bool ignore_if_invalid)
{
if (!AsyncPixmapLoader::isQueueNeeded(path_or_url))
{
const QPixmap pm = AsyncPixmapLoader::load(path_or_url);
if (!pm.isNull() || !ignore_if_invalid)
label->setPixmap(pm);
return;
}
AsyncPixmapLoader* loader = new AsyncPixmapLoader();
QObject::connect(loader, &AsyncPixmapLoader::pixmapLoaded, label, [label, ignore_if_invalid](QPixmap& pm) {
if (pm.isNull() && ignore_if_invalid)
return;
pm.setDevicePixelRatio(label->devicePixelRatio());
label->setPixmap(pm);
});
loader->enqueue(path_or_url);
}
QMessageBox::StandardButton QtUtils::MessageBoxInformation(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)

@ -152,6 +152,9 @@ void StyleChildMenus(QWidget* widget);
/// Creates a new popup menu, styled for the current theme.
QMenu* NewPopupMenu(QWidget* parent, bool delete_on_close = true);
/// Helper function to load a QPixmap from a path or URL.
void SetLabelPixmapPathOrURL(QLabel* label, std::string_view path_or_url, bool ignore_if_invalid);
/// Returns icon for language.
QIcon GetIconForTranslationLanguage(std::string_view language_name);

Loading…
Cancel
Save