Qt: Use widget-local device pixel ratio for game list

Fixes blurry icons in mixed DPI environments.

At least on Windows.
pull/3547/head
Stenzek 2 months ago
parent 019636b71f
commit 0867decc87
No known key found for this signature in database

@ -132,8 +132,9 @@ const char* GameListModel::getColumnName(Column col)
return s_column_names[static_cast<int>(col)];
}
GameListModel::GameListModel(QObject* parent)
: QAbstractTableModel(parent), m_memcard_pixmap_cache(MIN_COVER_CACHE_SIZE)
GameListModel::GameListModel(GameListWidget* parent)
: QAbstractTableModel(parent), m_device_pixel_ratio(QtUtils::GetDevicePixelRatioForWidget(parent)),
m_memcard_pixmap_cache(MIN_COVER_CACHE_SIZE)
{
m_cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f);
m_icon_size = Host::GetBaseFloatSettingValue("UI", "GameListIconSize", MIN_ICON_SIZE);
@ -222,18 +223,16 @@ void GameListModel::updateCoverScale()
{
m_cover_pixmap_cache.Clear();
const qreal dpr = qApp->devicePixelRatio();
QImage loading_image;
if (loading_image.load(QStringLiteral("%1/images/placeholder.png").arg(QtHost::GetResourcesBasePath())))
{
loading_image.setDevicePixelRatio(dpr);
loading_image.setDevicePixelRatio(m_device_pixel_ratio);
resizeAndPadImage(&loading_image, getCoverArtSize(), getCoverArtSize(), false);
}
else
{
loading_image = QImage(getCoverArtSize(), getCoverArtSize(), QImage::Format_RGB32);
loading_image.setDevicePixelRatio(dpr);
loading_image.setDevicePixelRatio(m_device_pixel_ratio);
loading_image.fill(QColor(0, 0, 0, 0));
}
m_loading_pixmap = QPixmap::fromImage(loading_image);
@ -241,13 +240,13 @@ void GameListModel::updateCoverScale()
m_placeholder_image = QImage();
if (m_placeholder_image.load(QStringLiteral("%1/images/cover-placeholder.png").arg(QtHost::GetResourcesBasePath())))
{
m_placeholder_image.setDevicePixelRatio(dpr);
m_placeholder_image.setDevicePixelRatio(m_device_pixel_ratio);
resizeAndPadImage(&m_placeholder_image, getCoverArtSize(), getCoverArtSize(), false);
}
else
{
m_placeholder_image = QImage(getCoverArtSize(), getCoverArtSize(), QImage::Format_RGB32);
m_placeholder_image.setDevicePixelRatio(dpr);
m_placeholder_image.setDevicePixelRatio(m_device_pixel_ratio);
m_placeholder_image.fill(QColor(0, 0, 0, 0));
}
@ -270,6 +269,20 @@ void GameListModel::updateCacheSize(int num_rows, int num_columns)
m_cover_pixmap_cache.SetMaxCapacity(static_cast<int>(std::max(num_items, MIN_COVER_CACHE_SIZE)));
}
void GameListModel::setDevicePixelRatio(qreal dpr)
{
if (m_device_pixel_ratio == dpr)
return;
WARNING_LOG("NEW DPR {}", dpr);
m_device_pixel_ratio = dpr;
m_placeholder_image.setDevicePixelRatio(dpr);
m_loading_pixmap.setDevicePixelRatio(dpr);
loadCommonImages();
refreshCovers();
refreshIcons();
}
void GameListModel::reloadThemeSpecificImages()
{
loadSizeDependentPixmaps();
@ -281,8 +294,7 @@ void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
QtAsyncTask::create(this, [path = ge->path, serial = ge->serial, save_title = std::string(ge->GetSaveTitle()),
display_title = QtUtils::StringViewToQString(ge->GetDisplayTitle(m_show_localized_titles)),
placeholder_image = m_placeholder_image, list = this, width = getCoverArtSize(),
height = getCoverArtSize(), scale = m_cover_scale,
dpr = qApp->devicePixelRatio()]() mutable {
height = getCoverArtSize(), scale = m_cover_scale, dpr = m_device_pixel_ratio]() mutable {
QImage image;
loadOrGenerateCover(image, placeholder_image, width, height, scale, dpr, path, serial, save_title, display_title);
return [path = std::move(path), image = std::move(image), list, scale]() { list->coverLoaded(path, image, scale); };
@ -425,7 +437,19 @@ const QPixmap& GameListModel::getIconPixmapForEntry(const GameList::Entry* ge) c
QPixmap pm;
if (!path.empty() && pm.load(QString::fromStdString(path)))
{
const_cast<GameListModel*>(this)->fixIconPixmapSize(pm);
const int pm_width = pm.width();
const int pm_height = pm.height();
const qreal scale =
(static_cast<qreal>(m_icon_size) / static_cast<qreal>(MEMORY_CARD_ICON_SIZE)) * m_device_pixel_ratio;
const int scaled_pm_width = static_cast<int>(static_cast<qreal>(pm_width) * scale);
const int scaled_pm_height = static_cast<int>(static_cast<qreal>(pm_height) * scale);
if (pm_width != scaled_pm_width || pm_height != scaled_pm_height)
QtUtils::ResizeSharpBilinear(pm, std::max(scaled_pm_width, scaled_pm_height), MEMORY_CARD_ICON_SIZE);
pm.setDevicePixelRatio(m_device_pixel_ratio);
return *m_memcard_pixmap_cache.Insert(ge->serial, std::move(pm));
}
@ -484,22 +508,6 @@ QIcon GameListModel::getIconForGame(const QString& path)
return ret;
}
void GameListModel::fixIconPixmapSize(QPixmap& pm)
{
const float dpr = qApp->devicePixelRatio();
const int width = static_cast<int>(static_cast<qreal>(pm.width()));
const int height = static_cast<int>(static_cast<qreal>(pm.height()));
const qreal scale = (static_cast<float>(m_icon_size) / static_cast<float>(MEMORY_CARD_ICON_SIZE)) * dpr;
const int new_width = static_cast<int>(static_cast<float>(width) * scale);
const int new_height = static_cast<int>(static_cast<float>(height) * scale);
if (width != new_width || height != new_height)
QtUtils::ResizeSharpBilinear(pm, std::max(new_width, new_height), MEMORY_CARD_ICON_SIZE);
pm.setDevicePixelRatio(dpr);
}
int GameListModel::getCoverArtSize() const
{
return std::max(static_cast<int>(static_cast<float>(COVER_ART_SIZE) * m_cover_scale), 1);
@ -1009,9 +1017,12 @@ bool GameListModel::lessThan(const GameList::Entry* left, const GameList::Entry*
void GameListModel::loadSizeDependentPixmaps()
{
// nasty magic number here, +8 gets us a height of 24 at 16 icon size, which looks good.
const int icon_height = m_icon_size + 8;
const QSize icon_size = QSize(m_icon_size + 8, m_icon_size + 8);
for (u32 i = 0; i < static_cast<u32>(GameList::EntryType::MaxCount); i++)
m_type_pixmaps[i] = QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(i)).pixmap(icon_height);
{
m_type_pixmaps[i] =
QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(i)).pixmap(icon_size, m_device_pixel_ratio);
}
}
void GameListModel::loadCommonImages()
@ -1020,18 +1031,18 @@ void GameListModel::loadCommonImages()
for (u32 i = 0; i < static_cast<u32>(GameDatabase::CompatibilityRating::Count); i++)
{
m_compatibility_pixmaps[i] =
QtUtils::GetIconForCompatibility(static_cast<GameDatabase::CompatibilityRating>(i)).pixmap(96, 24);
m_compatibility_pixmaps[i] = QtUtils::GetIconForCompatibility(static_cast<GameDatabase::CompatibilityRating>(i))
.pixmap(QSize(96, 24), m_device_pixel_ratio);
}
constexpr int ACHIEVEMENT_ICON_SIZE = 16;
constexpr QSize ACHIEVEMENT_ICON_SIZE(16, 16);
m_no_achievements_pixmap = QIcon(QString::fromStdString(QtHost::GetResourcePath("images/trophy-icon-gray.svg", true)))
.pixmap(ACHIEVEMENT_ICON_SIZE);
.pixmap(ACHIEVEMENT_ICON_SIZE, m_device_pixel_ratio);
m_has_achievements_pixmap = QIcon(QString::fromStdString(QtHost::GetResourcePath("images/trophy-icon.svg", true)))
.pixmap(ACHIEVEMENT_ICON_SIZE);
.pixmap(ACHIEVEMENT_ICON_SIZE, m_device_pixel_ratio);
m_mastered_achievements_pixmap =
QIcon(QString::fromStdString(QtHost::GetResourcePath("images/trophy-icon-star.svg", true)))
.pixmap(ACHIEVEMENT_ICON_SIZE);
.pixmap(ACHIEVEMENT_ICON_SIZE, m_device_pixel_ratio);
}
void GameListModel::setColumnDisplayNames()
@ -1679,10 +1690,15 @@ void GameListWidget::onIconSizeChanged(int size)
onScaleChanged();
}
void GameListWidget::resizeEvent(QResizeEvent* event)
bool GameListWidget::event(QEvent* e)
{
QWidget::resizeEvent(event);
updateBackground(false);
const QEvent::Type type = e->type();
if (type == QEvent::Resize)
updateBackground(false);
else if (type == QEvent::DevicePixelRatioChange)
m_model->setDevicePixelRatio(QtUtils::GetDevicePixelRatioForWidget(this));
return QWidget::event(e);
}
const GameList::Entry* GameListWidget::getSelectedEntry() const

@ -62,7 +62,7 @@ public:
static std::optional<Column> getColumnIdForName(std::string_view name);
static const char* getColumnName(Column col);
explicit GameListModel(QObject* parent);
explicit GameListModel(GameListWidget* parent);
~GameListModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -108,6 +108,8 @@ public:
void refreshCovers();
void updateCacheSize(int num_rows, int num_columns);
void setDevicePixelRatio(qreal dpr);
Q_SIGNALS:
void coverScaleChanged(float scale);
void iconSizeChanged(int size);
@ -132,7 +134,8 @@ private:
const QPixmap& getIconPixmapForEntry(const GameList::Entry* ge) const;
const QPixmap& getFlagPixmapForEntry(const GameList::Entry* ge) const;
void fixIconPixmapSize(QPixmap& pm);
qreal m_device_pixel_ratio = 1.0;
std::optional<GameList::EntryList> m_taken_entries;
@ -282,7 +285,7 @@ public Q_SLOTS:
void focusSearchWidget();
protected:
void resizeEvent(QResizeEvent* event);
bool event(QEvent* e) override;
private:
void setViewMode(int stack_index);

Loading…
Cancel
Save