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

View File

@ -30,6 +30,7 @@ bool getEnableDiscordRPC();
bool getSeparateUpdateEnabled();
bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
std::string getLogFilter();
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 setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/path_util.h"
#include "game_grid_frame.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) {
int itemID = (row * this->columnCount()) + column;
QWidget* item = this->cellWidget(row, column);
if (item) {
QString pic1Path;
Common::FS::PathToQString(pic1Path, (*m_games_shared)[itemID].pic_path);
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();
if (!item) {
// handle case where no item was clicked
return;
}
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() {

View File

@ -167,26 +167,35 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
return;
}
QString pic1Path;
Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path);
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_game_info->m_games[item->row()].serial / "pic1.png";
QString blurredPic1PathQt;
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
const auto& game = m_game_info->m_games[item->row()];
const int opacity = Config::getBackgroundImageOpacity();
const auto cache_path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
game.serial / fmt::format("pic1_{}.png", opacity);
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_game_info->m_games[item->row()].serial;
std::filesystem::create_directories(img_path);
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
// qDebug() << "Error: Unable to save image.";
// Fast path - try to load cached version first
if (std::filesystem::exists(cache_path)) {
backgroundImage = QImage(QString::fromStdString(cache_path.string()));
if (!backgroundImage.isNull()) {
RefreshListBackgroundImage();
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");
});
}
}
RefreshListBackgroundImage();
}

View File

@ -202,16 +202,17 @@ public:
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
QImage result = image.convertToFormat(QImage::Format_ARGB32);
// Ensure opacity is between 0 and 1
opacity = std::clamp(opacity, 0.0f, 1.0f);
// Convert opacity to integer alpha value (0-255)
int alpha = static_cast<int>(opacity * 255);
// Process only the specified rectangle area
for (int y = rect.top(); y <= rect.bottom(); ++y) {
QRgb* line = reinterpret_cast<QRgb*>(result.scanLine(y));
@ -223,7 +224,20 @@ public:
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), newAlpha);
}
}
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,
[](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
{
@ -251,6 +255,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
#ifdef ENABLE_UPDATER
ui->updaterGroupBox->installEventFilter(this);
#endif
ui->GUIBackgroundImageGroupBox->installEventFilter(this);
ui->GUIMusicGroupBox->installEventFilter(this);
ui->disableTrophycheckBox->installEventFilter(this);
ui->enableCompatibilityCheckBox->installEventFilter(this);
@ -410,6 +415,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty());
ResetInstallFolders();
ui->backgroundImageOpacitySlider->setValue(Config::getBackgroundImageOpacity());
}
void SettingsDialog::InitializeEmulatorLanguages() {
@ -504,6 +510,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
} else if (elementName == "updaterGroupBox") {
text = tr("updaterGroupBox");
#endif
} else if (elementName == "GUIBackgroundImageGroupBox") {
text = tr("GUIBackgroundImageGroupBox");
} else if (elementName == "GUIMusicGroupBox") {
text = tr("GUIMusicGroupBox");
} else if (elementName == "disableTrophycheckBox") {