mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-12 21:10:01 -08:00
[Icons] Add a sticker icon treatment
The pre-#7595 add, remove and delete icons were filled shapes with a black outline and no disc. A tint cannot bring that back, and the badge disc is a different icon language, so add a third value to the existing style axis. IconStyle::Sticker draws the sentinel glyph twice: a near-black rim at the band width plus one rim on each side, then the tone pass clipped to the glyph and filled with a vertical gradient. Both treatments keep the disabled fallback to the line glyph. The fill rule moves into theme_icon_fill: the semantic tones are tuned for text, so a filled shape gives its tone a saturation floor, and a dark-scheme tone is pulled down rather than up. IconTint::resolve() applies the same floor to line glyphs, and the badge disc uses the same fill, so a line, a badge and a sticker next to each other carry the same green or red instead of a pale line beside a saturated glyph. The badge's glyph ink follows the scheme: white on light-scheme tones, as in the pre-#7595 art, and the saturated tone darkened to an outline-dark ink on dark-scheme tones, where the shared saturated disc is light enough that white would sit near 2.7:1. The new test pins both cases so the glyph cannot silently flip. resolveSvgStrokeWidth() moves the stroke-width rewrite into libcockatrice_utility so the badge and the sticker share it; the badge's local copy is gone. The sticker is opted into at 17 call sites: increment, decrement and remove_row in the deck editor, settings pages, token dialog, server dialogs and replay/deck storage tabs. The visual database's clear-filter button keeps its trash can (icons/delete) in the line treatment: that glyph is new in #7595, but it is the clearer metaphor, and a sticker rim closes its lid gap at toolbar sizes. The preview generators also gain a semantic-tint sheet for #7775 and a line/badge/sticker family strip, so the three PR descriptions can show the same rendered colors.
This commit is contained in:
parent
717f64a88b
commit
876d66ffc9
25 changed files with 1027 additions and 127 deletions
87
cockatrice/src/interface/theme_icon_sticker.cpp
Normal file
87
cockatrice/src/interface/theme_icon_sticker.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#include "theme_icon_sticker.h"
|
||||
|
||||
#include "libcockatrice/utility/svg_recolor.h"
|
||||
#include "theme_icon_fill.h"
|
||||
|
||||
#include <QLinearGradient>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QSvgRenderer>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace
|
||||
{
|
||||
// The pre-#7595 add/remove art filled about 80% of the canvas: the sentinel
|
||||
// paths are padded inside their 24-unit viewBox, so the drawing box has to be
|
||||
// slightly larger than the canvas for the ink to reach that far.
|
||||
constexpr qreal STICKER_SCALE = 1.06;
|
||||
// Old arm thickness was about a fifth of the canvas, with a rim near 5%. In the
|
||||
// 24-unit viewBox that is a 4.5-unit band with a 0.9-unit rim on each side.
|
||||
constexpr qreal STICKER_STROKE_WIDTH = 4.5;
|
||||
constexpr qreal STICKER_OUTLINE_WIDTH = 0.9;
|
||||
// The old art's outline was black. Darkening by 300 reads as black on a light
|
||||
// scheme (the shipped tones are already dark there) while staying a visible dark
|
||||
// rim on a dark one, where a neutral black would vanish into the window.
|
||||
constexpr int OUTLINE_DARKER = 300;
|
||||
} // namespace
|
||||
|
||||
namespace ThemeIconSticker
|
||||
{
|
||||
QColor outlineColor(const QColor &tone)
|
||||
{
|
||||
return ThemeIconFill::saturated(tone).darker(OUTLINE_DARKER);
|
||||
}
|
||||
|
||||
QPixmap renderSticker(const QByteArray &svg, const QColor &tone, const QSize &pixelSize)
|
||||
{
|
||||
if (svg.isEmpty() || !tone.isValid() || pixelSize.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QPixmap pixmap(pixelSize);
|
||||
pixmap.fill(Qt::transparent);
|
||||
|
||||
const qreal side = qMin(pixelSize.width(), pixelSize.height());
|
||||
const qreal box = side * STICKER_SCALE;
|
||||
const QRectF glyphRect(QPointF((pixelSize.width() - box) / 2.0, (pixelSize.height() - box) / 2.0),
|
||||
QSizeF(box, box));
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// Outline first, at the band width plus one rim on each side; the fill pass
|
||||
// then covers the middle and leaves only the rim visible.
|
||||
const QByteArray outlineSvg =
|
||||
resolveSvgStrokeWidth(resolveSvgColorSentinel(svg, outlineColor(tone).name(QColor::HexRgb).toLatin1()),
|
||||
STICKER_STROKE_WIDTH + 2.0 * STICKER_OUTLINE_WIDTH);
|
||||
QSvgRenderer outlineRenderer(outlineSvg);
|
||||
if (outlineRenderer.isValid()) {
|
||||
outlineRenderer.render(&painter, glyphRect);
|
||||
}
|
||||
|
||||
// The fill needs its own pass so the gradient can be clipped to the glyph:
|
||||
// painting it onto the composited pixmap would tint the outline as well.
|
||||
QPixmap fillPixmap(pixelSize);
|
||||
fillPixmap.fill(Qt::transparent);
|
||||
{
|
||||
QPainter fillPainter(&fillPixmap);
|
||||
fillPainter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
const QByteArray fillSvg = resolveSvgStrokeWidth(
|
||||
resolveSvgColorSentinel(svg, tone.name(QColor::HexRgb).toLatin1()), STICKER_STROKE_WIDTH);
|
||||
QSvgRenderer fillRenderer(fillSvg);
|
||||
if (fillRenderer.isValid()) {
|
||||
fillRenderer.render(&fillPainter, glyphRect);
|
||||
fillPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
QLinearGradient gradient(glyphRect.topLeft(), glyphRect.bottomLeft());
|
||||
gradient.setColorAt(0.0, ThemeIconFill::topColor(tone));
|
||||
gradient.setColorAt(1.0, ThemeIconFill::bottomColor(tone));
|
||||
fillPainter.fillRect(QRectF(QPointF(0, 0), QSizeF(pixelSize)), gradient);
|
||||
}
|
||||
}
|
||||
painter.drawPixmap(0, 0, fillPixmap);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
} // namespace ThemeIconSticker
|
||||
Loading…
Add table
Add a link
Reference in a new issue