Add color utilities.

This commit is contained in:
= 2025-06-04 22:32:34 -04:00
parent 3ac572edeb
commit 298d5e9848

View File

@ -7,6 +7,8 @@
#include <QtZlib/zlib.h>
#include <QFileDialog>
#include <QMessageBox>
#include <QPainter>
#include <QCryptographicHash>
class Utils {
public:
@ -22,6 +24,178 @@ public:
testFile.close();
return true;
}
static quint8 ReverseBits(quint8 b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
static QIcon CreateAssetIcon(const QString& name, QColor color = QColor()) {
constexpr int iconSize = 32;
constexpr int padding = 4;
QImage result(iconSize, iconSize, QImage::Format_ARGB32);
result.fill(Qt::transparent);
if (!color.isValid()) {
color = Utils::StringToColor(name);
}
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::TextAntialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
// Draw background
QBrush brush(color);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(color);
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(0, 0, iconSize, iconSize, 4, 4);
// Set base font
QFont font("Heavitas");
font.setPixelSize(iconSize); // Start large
painter.setFont(font);
// Adjust font size to fit text width (only reduce, not increase)
QFontMetrics fm(font);
int textWidth = fm.horizontalAdvance(name);
while (textWidth > iconSize - 2 * padding && font.pixelSize() > 1) {
font.setPixelSize(font.pixelSize() - 1);
painter.setFont(font);
fm = QFontMetrics(font);
textWidth = fm.horizontalAdvance(name);
}
// Calculate vertical scaling factor
qreal scaleY = 1.2 * iconSize / fm.height();
// Apply transform: scale vertically, center align
painter.save();
painter.translate(iconSize / 2.0, iconSize / 2.0);
painter.scale(1.0, scaleY);
painter.translate(-iconSize / 2.0, -iconSize / 2.0);
QRect textRect(0, 0, iconSize, iconSize);
// Draw stroke
painter.setPen(Qt::black);
for (int dx = 0; dx <= 1; ++dx) {
for (int dy = 0; dy <= 1; ++dy) {
if (dx || dy)
painter.drawText(textRect.translated(dx, dy), Qt::AlignCenter, name);
}
}
// Draw main text
painter.setPen(Qt::white);
painter.drawText(textRect, Qt::AlignCenter, name);
painter.restore();
// Debug output
QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
result.save(QDir().absoluteFilePath(".") + "/icons/" + name + ".png");
return QIcon(QPixmap::fromImage(result));
}
static QIcon CreateGameIcon(const int gameNum, QColor color = QColor()) {
constexpr int size = 32;
constexpr int padding = 4; // pixels of padding on all sides
const int contentSize = size - 2 * padding;
QImage result(size, size, QImage::Format_ARGB32);
result.fill(Qt::transparent);
if (!color.isValid()) {
color = Utils::StringToColor(QString("COD%1").arg(gameNum));
}
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setRenderHint(QPainter::TextAntialiasing, false);
painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
// Draw background
painter.setBrush(color);
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(0, 0, size, size, 4, 4);
// === Font Setup ===
QFont codFont("Heavitas");
codFont.setPixelSize(contentSize * 0.40);
QFont numFont("Heavitas");
numFont.setPixelSize(contentSize);
// === Layout Areas (inside padding) ===
const QRect contentRect(padding, padding, contentSize, contentSize);
const int col1 = contentRect.left();
const int col2 = contentRect.left() + contentRect.width() / 3;
const int thirdH = contentRect.height() / 3;
const QRect codRects[] = {
QRect(col1, 2 + contentRect.top(), contentRect.width() / 3, thirdH),
QRect(col1, contentRect.top() + thirdH, contentRect.width() / 3, thirdH),
QRect(col1, -2 + contentRect.top() + 2 * thirdH, contentRect.width() / 3, thirdH),
};
const QRect numRect(col2, contentRect.top(), contentRect.width() * 2 / 3, contentRect.height());
const QString codLetters[] = { "C", "O", "D" };
const QString numText = QString::number(gameNum);
// === Stroke pass ===
for (int dx = -1; dx <= 2; ++dx) {
for (int dy = -1; dy <= 2; ++dy) {
if (dx == 0 && dy == 0) continue;
painter.setPen(Qt::black);
painter.setFont(codFont);
for (int i = 0; i < 3; ++i)
painter.drawText(codRects[i].translated(dx, dy), Qt::AlignCenter, codLetters[i]);
painter.setFont(numFont);
painter.drawText(numRect.translated(dx, dy), Qt::AlignCenter, numText);
}
}
// === Fill pass ===
painter.setPen(Qt::white);
painter.setFont(codFont);
for (int i = 0; i < 3; ++i)
painter.drawText(codRects[i], Qt::AlignCenter, codLetters[i]);
painter.setFont(numFont);
painter.drawText(numRect, Qt::AlignCenter, numText);
// Save & return icon
QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
result.save(QDir().absoluteFilePath(".") + QString("/icons/COD%1.png").arg(gameNum));
return QIcon(QPixmap::fromImage(result));
}
static QColor StringToColor(const QString& str) {
// 1. Hash the string using Qt's built-in hash (MD5, SHA1, or any)
QByteArray hash = QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5);
// 2. Use first 3 bytes of hash for RGB
// This guarantees same string = same color every time
int r = static_cast<unsigned char>(hash[0]);
int g = static_cast<unsigned char>(hash[1]);
int b = static_cast<unsigned char>(hash[2]);
// 3. Optionally adjust brightness or saturation to avoid too dark/light colors
QColor color(r, g, b);
if (color.value() < 128) { // brighten if too dark
color = color.lighter(150);
}
return color;
}
static bool ReadUntilString(QDataStream* stream, const QString& targetString) {
if (!stream || targetString.isEmpty()) {
return false; // Invalid input