From f5fbf3a1f5590d3a0d2714310c0799a2f447aabf Mon Sep 17 00:00:00 2001 From: boludoz Date: Fri, 6 Oct 2023 00:06:05 -0300 Subject: [PATCH] @DanielSvoboda ICO conversion multiplataform code --- src/yuzu/main.cpp | 20 ++++-- src/yuzu/util/util.cpp | 138 ++++++++++++++++++++++------------------- src/yuzu/util/util.h | 2 +- 3 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e39cd28109..386816ba74 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3128,7 +3128,21 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const QString& game_p } else { LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); } -#endif // __linux__ +#elif defined(_WIN32) + // ICO is only for Windows + + // Convert QImage to QPixmap + QPixmap pixmap = QPixmap::fromImage(icon_jpeg); + + // Save the QPixmap as an .ico file + QList pixmaps; + pixmaps.append(pixmap); + if (!savePixmapsToICO(pixmaps, QString::fromStdWString(Common::FS::ToWString(icon_path.u8string())))) { + LOG_ERROR(Frontend, "Could not write icon as ICO to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } +#endif #if defined(__linux__) || defined(__FreeBSD__) QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No; @@ -3177,10 +3191,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const QString& game_p const std::filesystem::path shortcut_path = target_directory / (sanitized_title); - bool is_icon_ok = SaveIconToFile(icon_path, icon_jpeg); - if (!is_icon_ok) { - LOG_ERROR(Frontend, "Could not write icon as ICO to file"); - } const std::u8string keywords = u8"Switch;Nintendo;"; const std::u8string categories = u8"Game;Emulator;Qt;"; #else diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 5ef0950edb..9c40532852 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -1,14 +1,19 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#if defined(WIN32) -#include -#endif #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + #include "yuzu/util/util.h" QFont GetMonospaceFont() { @@ -44,79 +49,84 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { } #if defined(WIN32) -#pragma pack(push, 2) -struct ICONDIR { - WORD idReserved; - WORD idType; - WORD idCount; -}; +template +void write(QFile& f, const T t) { + f.write((const char*)&t, sizeof(t)); +} -struct ICONDIRENTRY { - BYTE bWidth; - BYTE bHeight; - BYTE bColorCount; - BYTE bReserved; - WORD wPlanes; - WORD wBitCount; - DWORD dwBytesInRes; - DWORD dwImageOffset; -}; +bool savePixmapsToICO(const QList& pixmaps, const QString& path) { + static_assert(sizeof(short) == 2, "short int is not 2 bytes"); + static_assert(sizeof(int) == 4, "int is not 4 bytes"); -#pragma pack(pop) - -bool SaveIconToFile(const std::filesystem::path IconPath, const QImage image) { - - QImage sourceImage = image.convertToFormat(QImage::Format_RGB32); - - const int bytesPerPixel = 4; - const int imageSize = sourceImage.width() * sourceImage.height() * bytesPerPixel; - - BITMAPINFOHEADER bmih = {}; - bmih.biSize = sizeof(BITMAPINFOHEADER); - bmih.biWidth = sourceImage.width(); - bmih.biHeight = sourceImage.height() * 2; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - - // Create an ICO header - ICONDIR iconDir; - iconDir.idReserved = 0; - iconDir.idType = 1; - iconDir.idCount = 1; - - // Create an ICONDIRENTRY - ICONDIRENTRY iconEntry; - iconEntry.bWidth = sourceImage.width(); - iconEntry.bHeight = sourceImage.height() * 2; - iconEntry.bColorCount = 0; - iconEntry.bReserved = 0; - iconEntry.wPlanes = 1; - iconEntry.wBitCount = 32; - iconEntry.dwBytesInRes = sizeof(BITMAPINFOHEADER) + imageSize; - iconEntry.dwImageOffset = sizeof(ICONDIR) + sizeof(ICONDIRENTRY); - - // Save the icon data to a file - std::ofstream iconFile(IconPath, std::ios::binary | std::ios::trunc); - if (iconFile.fail()) + QFile f(path); + if (!f.open(QFile::OpenModeFlag::WriteOnly)) return false; - iconFile.write((char*)&iconDir, sizeof(ICONDIR)); - iconFile.write((char*)&iconEntry, sizeof(ICONDIRENTRY)); - iconFile.write((char*)&bmih, sizeof(BITMAPINFOHEADER)); + // Header + write(f, 0); + write(f, 1); + write(f, pixmaps.count()); - for (int y = 0; y < image.height(); y++) { - auto line = (char*)sourceImage.scanLine(sourceImage.height() - 1 - y); - iconFile.write(line, sourceImage.width() * 4); + QList images_size; + for (int ii = 0; ii < pixmaps.count(); ++ii) { + QTemporaryFile temp; + temp.setAutoRemove(true); + if (!temp.open()) + return false; + + const auto& pixmap = pixmaps[ii]; + pixmap.save(&temp, "PNG"); + + + temp.close(); + + images_size.push_back(QFileInfo(temp).size()); } - iconFile.close(); + // Images directory + constexpr unsigned int entry_size = sizeof(char) + sizeof(char) + sizeof(char) + sizeof(char) + + sizeof(short) + sizeof(short) + sizeof(unsigned int) + + sizeof(unsigned int); + static_assert(entry_size == 16, "wrong entry size"); + + unsigned int offset = 3 * sizeof(short) + pixmaps.count() * entry_size; + for (int ii = 0; ii < pixmaps.count(); ++ii) { + const auto& pixmap = pixmaps[ii]; + if (pixmap.width() > 256 || pixmap.height() > 256) + continue; + + write(f, pixmap.width() == 256 ? 0 : pixmap.width()); + write(f, pixmap.height() == 256 ? 0 : pixmap.height()); + write(f, 0); // palette size + write(f, 0); // reserved + write(f, 1); // color planes + write(f, pixmap.depth()); // bits-per-pixel + write(f, images_size[ii]); // size of image in bytes + write(f, offset); // offset + offset += images_size[ii]; + } + + for (int ii = 0; ii < pixmaps.count(); ++ii) { + const auto& pixmap = pixmaps[ii]; + if (pixmap.width() > 256 || pixmap.height() > 256) + continue; + pixmap.save(&f, "PNG"); + } + + // Close the file before renaming it + f.close(); + + // Remove the .png extension Add the .ico extension and Rename the file + QString qPath = path; + qPath.chop(3); + qPath = qPath % QString::fromStdString("ico"); + QFile::rename(path, qPath); return true; } #else -bool SaveAsIco(QImage image) { +bool SaveAsIco(const QList& pixmaps, const QString& path) { return false; } #endif \ No newline at end of file diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 338b23c5d8..1a750d61c6 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -31,4 +31,4 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color); * @return QPixmap circle pixmap */ -bool SaveIconToFile(const std::filesystem::path IconPath, QImage image); +bool savePixmapsToICO(const QList& pixmaps, const QString& path);