mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-08 07:14:31 +00:00
Qt: Use widget-local device pixel ratio for game list
Fixes blurry icons in mixed DPI environments. At least on Windows.
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user