Move to pixmap generator

Took 8 minutes

Took 4 seconds
This commit is contained in:
Lukas Brübach 2026-08-24 18:43:21 +02:00
parent 127d31cf4e
commit 48d7417605
7 changed files with 89 additions and 102 deletions

View file

@ -3,6 +3,7 @@
#include <QApplication>
#include <QDomDocument>
#include <QFile>
#include <QImageReader>
#include <QPainter>
#include <QPalette>
#include <QSvgRenderer>
@ -418,6 +419,57 @@ QPixmap DropdownIconPixmapGenerator::generatePixmap(int height, bool expanded)
QMap<QString, QPixmap> DropdownIconPixmapGenerator::pmCache;
namespace
{
/// Longest side mana symbols are rendered at before being scaled to their final size.
constexpr int MASTER_ICON_SIZE = 128;
QString manaSymbolCacheKey(const QString &symbol, const QSize &size)
{
return symbol + QLatin1Char('|') + QString::number(size.width()) + QLatin1Char('x') +
QString::number(size.height());
}
} // namespace
const QPixmap &ManaSymbolPixmapGenerator::masterIcon(const QString &symbol)
{
auto it = masterCache.constFind(symbol);
if (it != masterCache.constEnd()) {
return it.value();
}
QImageReader reader("theme:icons/mana/" + symbol);
QSize sourceSize = reader.size();
if (!sourceSize.isEmpty()) {
sourceSize.scale(QSize(MASTER_ICON_SIZE, MASTER_ICON_SIZE), Qt::KeepAspectRatio);
reader.setScaledSize(sourceSize);
}
const QPixmap rendered = QPixmap::fromImageReader(&reader);
return masterCache.insert(symbol, rendered).value();
}
QPixmap ManaSymbolPixmapGenerator::generatePixmap(const QString &symbol, const QSize &size)
{
const QString key = manaSymbolCacheKey(symbol, size);
auto it = scaledCache.constFind(key);
if (it != scaledCache.constEnd()) {
return it.value();
}
const QPixmap &icon = masterIcon(symbol);
if (icon.isNull()) {
return {};
}
QPixmap scaled = icon.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
scaledCache.insert(key, scaled);
return scaled;
}
QHash<QString, QPixmap> ManaSymbolPixmapGenerator::masterCache;
QHash<QString, QPixmap> ManaSymbolPixmapGenerator::scaledCache;
QPixmap loadColorAdjustedPixmap(const QString &name)
{
if (qApp->palette().windowText().color().lightness() > 200) {

View file

@ -7,6 +7,7 @@
#ifndef PIXMAPGENERATOR_H
#define PIXMAPGENERATOR_H
#include <QHash>
#include <QIcon>
#include <QLoggingCategory>
#include <QMap>
@ -125,6 +126,34 @@ public:
}
};
class ManaSymbolPixmapGenerator
{
private:
static QHash<QString, QPixmap> masterCache;
static QHash<QString, QPixmap> scaledCache;
/**
* @brief Renders \a symbol once at a fixed moderate size, so repeated scalings never
* re-rasterize the source file (SVG sources can be very expensive to rasterize).
*/
static const QPixmap &masterIcon(const QString &symbol);
public:
/**
* @brief Returns a smooth-scaled rendering of the given mana symbol icon.
*
* Results are shared between all callers via a process-wide cache keyed by symbol
* and size, so scaling work is done once per distinct combination instead of once
* per widget creation or resize.
*/
static QPixmap generatePixmap(const QString &symbol, const QSize &size);
static void clear()
{
masterCache.clear();
scaledCache.clear();
}
};
QPixmap loadColorAdjustedPixmap(const QString &name);
#endif

View file

@ -1,57 +1,15 @@
#include "mana_symbol_widget.h"
#include "../../../../client/settings/cache_settings.h"
#include "../../../pixel_map_generator.h"
#include <QHash>
#include <QImageReader>
#include <QResizeEvent>
#include <libcockatrice/settings/visual_deck_storage_settings.h>
namespace
{
/// Longest side mana symbols are rendered at before being scaled to their final size.
constexpr int MASTER_ICON_SIZE = 128;
QHash<QString, QPixmap> &scaledIconCache()
{
static QHash<QString, QPixmap> cache;
return cache;
}
/**
* @brief Renders \a symbol once at a fixed moderate size, so repeated scalings never
* re-rasterize the source file (SVG sources can be very expensive to rasterize).
*/
const QPixmap &masterIcon(const QString &symbol)
{
static QHash<QString, QPixmap> cache;
auto it = cache.constFind(symbol);
if (it != cache.constEnd()) {
return it.value();
}
QImageReader reader("theme:icons/mana/" + symbol);
QSize sourceSize = reader.size();
if (!sourceSize.isEmpty()) {
sourceSize.scale(QSize(MASTER_ICON_SIZE, MASTER_ICON_SIZE), Qt::KeepAspectRatio);
reader.setScaledSize(sourceSize);
}
const QPixmap rendered = QPixmap::fromImageReader(&reader);
return cache.insert(symbol, rendered).value();
}
QString cacheKey(const QString &symbol, const QSize &size)
{
return symbol + QLatin1Char('|') + QString::number(size.width()) + QLatin1Char('x') +
QString::number(size.height());
}
} // namespace
ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isActive, bool _mayBeToggled)
: QLabel(parent), symbol(std::move(_symbol)), isActive(_isActive), mayBeToggled(_mayBeToggled)
{
setPixmap(getCachedScaledIcon(symbol, QSize(50, 50)));
setPixmap(ManaSymbolPixmapGenerator::generatePixmap(symbol, QSize(50, 50)));
setMaximumWidth(50);
// Initialize opacity effect
@ -64,24 +22,6 @@ ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isAct
&ManaSymbolWidget::updateOpacity);
}
QPixmap ManaSymbolWidget::getCachedScaledIcon(const QString &symbol, const QSize &size)
{
const QString key = cacheKey(symbol, size);
auto it = scaledIconCache().constFind(key);
if (it != scaledIconCache().constEnd()) {
return it.value();
}
const QPixmap &icon = masterIcon(symbol);
if (icon.isNull()) {
return {};
}
QPixmap scaled = icon.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
scaledIconCache().insert(key, scaled);
return scaled;
}
void ManaSymbolWidget::toggleSymbol()
{
setColorActive(!isActive);
@ -132,5 +72,5 @@ void ManaSymbolWidget::resizeEvent(QResizeEvent *event)
return;
}
setPixmap(getCachedScaledIcon(symbol, newSize));
setPixmap(ManaSymbolPixmapGenerator::generatePixmap(symbol, newSize));
}

View file

@ -33,15 +33,6 @@ public:
return symbol[0];
}
/**
* @brief Returns a smooth-scaled rendering of the given mana symbol icon.
*
* Results are shared between all instances via a process-wide cache keyed by symbol
* and size, so scaling work is done once per distinct combination instead of once per
* widget creation or resize.
*/
static QPixmap getCachedScaledIcon(const QString &symbol, const QSize &size);
public slots:
void resizeEvent(QResizeEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;

View file

@ -1,5 +1,6 @@
#include "card_completer_delegate.h"
#include "../../pixel_map_generator.h"
#include "../cards/additional_info/mana_cost_widget.h"
#include <QFontMetrics>
@ -84,7 +85,6 @@ QColor CardCompleterDelegate::accentForColors(const QString &colors)
CardCompleterDelegate::CardCompleterDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
symbolCache.setMaxCost(64);
setCodeCache.setMaxCost(64);
}
@ -108,36 +108,16 @@ QSize CardCompleterDelegate::sizeHint(const QStyleOptionViewItem &option, const
// Mana symbol painting
// ---------------------------------------------------------------------------
const QPixmap *CardCompleterDelegate::cachedSymbolPixmap(const QString &symbol, int size) const
{
const QString key = symbol + QString::number(size);
if (symbolCache.contains(key)) {
return symbolCache[key];
}
QPixmap src(QString("theme:icons/mana/%1").arg(symbol));
if (!src.isNull()) {
auto *pm = new QPixmap(src.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
symbolCache.insert(key, pm);
return pm;
}
return nullptr;
}
// ---------------------------------------------------------------------------
void CardCompleterDelegate::drawManaSymbol(QPainter *p, QPoint centre, const QString &symbol, int radius) const
{
const QRect pip(centre.x() - radius, centre.y() - radius, radius * 2, radius * 2);
const QPixmap *px = cachedSymbolPixmap(symbol, radius * 2);
const QPixmap px = ManaSymbolPixmapGenerator::generatePixmap(symbol, QSize(radius * 2, radius * 2));
if (px && !px->isNull()) {
p->drawPixmap(pip, *px);
if (!px.isNull()) {
p->drawPixmap(pip, px);
return;
}

View file

@ -31,9 +31,6 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
// Mana symbol pixmaps, loaded once and cached
mutable QCache<QString, QPixmap> symbolCache;
// Set short codes, resolved once per card name and cached
mutable QCache<QString, QString> setCodeCache;
@ -47,9 +44,6 @@ private:
// adventure costs ("1W // W") are drawn as separate groups. Returns the left-most x used
int drawManaCost(QPainter *p, const QRect &row, const QString &manaCost, int radius) const;
// Load (or return cached) a mana icon pixmap; falls back to painted circle
const QPixmap *cachedSymbolPixmap(const QString &symbol, int size) const;
// Resolve the preferred printing's set short code for a card
QString setCodeForCard(const QSharedPointer<CardInfo> &card) const;

View file

@ -390,6 +390,7 @@ int main(int argc, char *argv[])
PingPixmapGenerator::clear();
CountryPixmapGenerator::clear();
UserLevelPixmapGenerator::clear();
ManaSymbolPixmapGenerator::clear();
return ret;
}