From 298d5e984881ab064778c1de076e955e8347f37e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 4 Jun 2025 22:32:34 -0400 Subject: [PATCH] Add color utilities. --- libs/core/utils.h | 174 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/libs/core/utils.h b/libs/core/utils.h index 81acd20..ed50b2f 100644 --- a/libs/core/utils.h +++ b/libs/core/utils.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include 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(hash[0]); + int g = static_cast(hash[1]); + int b = static_cast(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