Added slider to control background image opacity

This commit is contained in:
Pablo Santana 2025-02-02 19:08:58 +01:00
parent bd4cd9632c
commit 681c1bbf64
6 changed files with 103 additions and 45 deletions

View File

@ -95,6 +95,7 @@ std::vector<std::string> m_pkg_viewer;
std::vector<std::string> m_elf_viewer; std::vector<std::string> m_elf_viewer;
std::vector<std::string> m_recent_files; std::vector<std::string> m_recent_files;
std::string emulator_language = "en"; std::string emulator_language = "en";
static int backgroundImageOpacity = 50;
// Language // Language
u32 m_language = 1; // english u32 m_language = 1; // english
@ -611,6 +612,14 @@ u32 GetLanguage() {
return m_language; return m_language;
} }
int getBackgroundImageOpacity() {
return backgroundImageOpacity;
}
void setBackgroundImageOpacity(int opacity) {
backgroundImageOpacity = std::clamp(opacity, 0, 100);
}
void load(const std::filesystem::path& path) { void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return // If the configuration file does not exist, create it and return
std::error_code error; std::error_code error;
@ -655,6 +664,7 @@ void load(const std::filesystem::path& path) {
checkCompatibilityOnStartup = checkCompatibilityOnStartup =
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false); toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release"); chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release");
backgroundImageOpacity = toml::find_or<int>(general, "backgroundImageOpacity", 50);
} }
if (data.contains("Input")) { if (data.contains("Input")) {
@ -783,6 +793,7 @@ void save(const std::filesystem::path& path) {
data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["separateUpdateEnabled"] = separateupdatefolder;
data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["General"]["backgroundImageOpacity"] = backgroundImageOpacity;
data["Input"]["cursorState"] = cursorState; data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
data["Input"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["backButtonBehavior"] = backButtonBehavior;
@ -914,6 +925,7 @@ void setDefaultValues() {
separateupdatefolder = false; separateupdatefolder = false;
compatibilityData = false; compatibilityData = false;
checkCompatibilityOnStartup = false; checkCompatibilityOnStartup = false;
backgroundImageOpacity = 50;
} }
constexpr std::string_view GetDefaultKeyboardConfig() { constexpr std::string_view GetDefaultKeyboardConfig() {

View File

@ -30,6 +30,7 @@ bool getEnableDiscordRPC();
bool getSeparateUpdateEnabled(); bool getSeparateUpdateEnabled();
bool getCompatibilityEnabled(); bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup(); bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
std::string getLogFilter(); std::string getLogFilter();
std::string getLogType(); std::string getLogType();
@ -88,6 +89,7 @@ void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_insta
void setSaveDataPath(const std::filesystem::path& path); void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use); void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use); void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity);
void setCursorState(s16 cursorState); void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout); void setCursorHideTimeout(int newcursorHideTimeout);

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/path_util.h" #include "common/path_util.h"
#include "game_grid_frame.h" #include "game_grid_frame.h"
#include "qt_gui/compatibility_info.h" #include "qt_gui/compatibility_info.h"
@ -153,32 +155,43 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
} }
void GameGridFrame::SetGridBackgroundImage(int row, int column) { void GameGridFrame::SetGridBackgroundImage(int row, int column) {
int itemID = (row * this->columnCount()) + column; int itemID = (row * this->columnCount()) + column;
QWidget* item = this->cellWidget(row, column); QWidget* item = this->cellWidget(row, column);
if (item) { if (!item) {
QString pic1Path; // handle case where no item was clicked
Common::FS::PathToQString(pic1Path, (*m_games_shared)[itemID].pic_path); return;
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
(*m_games_shared)[itemID].serial / "pic1.png";
QString blurredPic1PathQt;
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
backgroundImage = QImage(blurredPic1PathQt);
if (backgroundImage.isNull()) {
QImage image(pic1Path);
backgroundImage = m_game_list_utils.ChangeImageOpacity(image, image.rect(), 0.5);
std::filesystem::path img_path =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
(*m_games_shared)[itemID].serial;
std::filesystem::create_directories(img_path);
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
// qDebug() << "Error: Unable to save image.";
}
}
RefreshGridBackgroundImage();
} }
const auto& game = (*m_games_shared)[itemID];
const int opacity = Config::getBackgroundImageOpacity();
const auto cache_path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
game.serial / fmt::format("pic1_{}.png", opacity);
// Fast path - try to load cached version first
if (std::filesystem::exists(cache_path)) {
backgroundImage = QImage(QString::fromStdString(cache_path.string()));
if (!backgroundImage.isNull()) {
RefreshGridBackgroundImage();
return;
}
}
// Cache miss - generate and store
m_game_list_utils.CleanupOldOpacityImages(cache_path.parent_path());
QImage original_image(QString::fromStdString(game.pic_path.string()));
if (!original_image.isNull()) {
std::filesystem::create_directories(cache_path.parent_path());
backgroundImage = m_game_list_utils.ChangeImageOpacity(
original_image, original_image.rect(), opacity / 100.0f);
if (!backgroundImage.isNull()) {
// Save the image to the cache asynchronously
QFuture<void> future = QtConcurrent::run([this, cache_path]() {
backgroundImage.save(QString::fromStdString(cache_path.string()), "PNG");
});
}
}
RefreshGridBackgroundImage();
} }
void GameGridFrame::RefreshGridBackgroundImage() { void GameGridFrame::RefreshGridBackgroundImage() {

View File

@ -167,26 +167,35 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
return; return;
} }
QString pic1Path; const auto& game = m_game_info->m_games[item->row()];
Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path); const int opacity = Config::getBackgroundImageOpacity();
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / const auto cache_path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_game_info->m_games[item->row()].serial / "pic1.png"; game.serial / fmt::format("pic1_{}.png", opacity);
QString blurredPic1PathQt;
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
backgroundImage = QImage(blurredPic1PathQt); // Fast path - try to load cached version first
if (backgroundImage.isNull()) { if (std::filesystem::exists(cache_path)) {
QImage image(pic1Path); backgroundImage = QImage(QString::fromStdString(cache_path.string()));
backgroundImage = m_game_list_utils.ChangeImageOpacity(image, image.rect(), 0.5); if (!backgroundImage.isNull()) {
RefreshListBackgroundImage();
std::filesystem::path img_path = return;
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_game_info->m_games[item->row()].serial;
std::filesystem::create_directories(img_path);
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
// qDebug() << "Error: Unable to save image.";
} }
} }
// Cache miss - generate and store
m_game_list_utils.CleanupOldOpacityImages(cache_path.parent_path());
QImage original_image(QString::fromStdString(game.pic_path.string()));
if (!original_image.isNull()) {
std::filesystem::create_directories(cache_path.parent_path());
backgroundImage = m_game_list_utils.ChangeImageOpacity(
original_image, original_image.rect(), opacity / 100.0f);
if (!backgroundImage.isNull()) {
// Save the image to the cache asynchronously
QFuture<void> future = QtConcurrent::run([this, cache_path]() {
backgroundImage.save(QString::fromStdString(cache_path.string()), "PNG");
});
}
}
RefreshListBackgroundImage(); RefreshListBackgroundImage();
} }

View File

@ -202,16 +202,17 @@ public:
return result; return result;
} }
QImage ChangeImageOpacity(const QImage& image, const QRect& rect, float opacity) { // Opacity is a float between 0 and 1
static QImage ChangeImageOpacity(const QImage& image, const QRect& rect, float opacity) {
// Convert to ARGB32 format to ensure alpha channel support // Convert to ARGB32 format to ensure alpha channel support
QImage result = image.convertToFormat(QImage::Format_ARGB32); QImage result = image.convertToFormat(QImage::Format_ARGB32);
// Ensure opacity is between 0 and 1 // Ensure opacity is between 0 and 1
opacity = std::clamp(opacity, 0.0f, 1.0f); opacity = std::clamp(opacity, 0.0f, 1.0f);
// Convert opacity to integer alpha value (0-255) // Convert opacity to integer alpha value (0-255)
int alpha = static_cast<int>(opacity * 255); int alpha = static_cast<int>(opacity * 255);
// Process only the specified rectangle area // Process only the specified rectangle area
for (int y = rect.top(); y <= rect.bottom(); ++y) { for (int y = rect.top(); y <= rect.bottom(); ++y) {
QRgb* line = reinterpret_cast<QRgb*>(result.scanLine(y)); QRgb* line = reinterpret_cast<QRgb*>(result.scanLine(y));
@ -223,7 +224,20 @@ public:
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), newAlpha); line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), newAlpha);
} }
} }
return result; return result;
} }
void CleanupOldOpacityImages(const std::filesystem::path& dir) {
if (!std::filesystem::exists(dir)) {
return;
}
for (const auto& entry : std::filesystem::directory_iterator(dir)) {
const auto& path = entry.path();
if (path.filename().string().starts_with("pic1") && path.extension() == ".png") {
std::filesystem::remove(path);
}
}
}
}; };

View File

@ -173,6 +173,10 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
{ {
connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this, connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this,
[](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); }); [](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); });
// Add background image opacity slider connection
connect(ui->backgroundImageOpacitySlider, &QSlider::valueChanged, this,
[](int value) { Config::setBackgroundImageOpacity(value); });
} }
// Input TAB // Input TAB
{ {
@ -251,6 +255,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
ui->updaterGroupBox->installEventFilter(this); ui->updaterGroupBox->installEventFilter(this);
#endif #endif
ui->GUIBackgroundImageGroupBox->installEventFilter(this);
ui->GUIMusicGroupBox->installEventFilter(this); ui->GUIMusicGroupBox->installEventFilter(this);
ui->disableTrophycheckBox->installEventFilter(this); ui->disableTrophycheckBox->installEventFilter(this);
ui->enableCompatibilityCheckBox->installEventFilter(this); ui->enableCompatibilityCheckBox->installEventFilter(this);
@ -410,6 +415,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty());
ResetInstallFolders(); ResetInstallFolders();
ui->backgroundImageOpacitySlider->setValue(Config::getBackgroundImageOpacity());
} }
void SettingsDialog::InitializeEmulatorLanguages() { void SettingsDialog::InitializeEmulatorLanguages() {
@ -504,6 +510,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
} else if (elementName == "updaterGroupBox") { } else if (elementName == "updaterGroupBox") {
text = tr("updaterGroupBox"); text = tr("updaterGroupBox");
#endif #endif
} else if (elementName == "GUIBackgroundImageGroupBox") {
text = tr("GUIBackgroundImageGroupBox");
} else if (elementName == "GUIMusicGroupBox") { } else if (elementName == "GUIMusicGroupBox") {
text = tr("GUIMusicGroupBox"); text = tr("GUIMusicGroupBox");
} else if (elementName == "disableTrophycheckBox") { } else if (elementName == "disableTrophycheckBox") {