370 lines
13 KiB
C++
370 lines
13 KiB
C++
#include "UI/LayerIcons.h"
|
|
#include "UI/ToolIcons.h"
|
|
#include <QPainterPath>
|
|
#include <cmath>
|
|
#include <numbers>
|
|
|
|
namespace {
|
|
void eye(QPainter &painter, bool slashed)
|
|
{
|
|
QPainterPath lid;
|
|
lid.moveTo(1.5, 9);
|
|
lid.cubicTo(5, 3, 13, 3, 16.5, 9);
|
|
lid.cubicTo(13, 15, 5, 15, 1.5, 9);
|
|
painter.drawPath(lid);
|
|
painter.drawEllipse(QPointF(9, 9), 2.6, 2.6);
|
|
if (slashed)
|
|
painter.drawLine(QPointF(3, 15), QPointF(15, 3));
|
|
}
|
|
|
|
void chevron(QPainter &painter, bool down)
|
|
{
|
|
if (down)
|
|
painter.drawPolyline(QPolygonF{QPointF(4.5, 6.5), QPointF(9, 11), QPointF(13.5, 6.5)});
|
|
else
|
|
painter.drawPolyline(QPolygonF{QPointF(6.5, 4.5), QPointF(11, 9), QPointF(6.5, 13.5)});
|
|
}
|
|
|
|
void folder(QPainter &painter, bool badged)
|
|
{
|
|
QPainterPath shape;
|
|
shape.moveTo(2, 4.5);
|
|
shape.lineTo(7, 4.5);
|
|
shape.lineTo(8.5, 6.5);
|
|
shape.lineTo(16, 6.5);
|
|
shape.lineTo(16, 14.5);
|
|
shape.lineTo(2, 14.5);
|
|
shape.closeSubpath();
|
|
painter.drawPath(shape);
|
|
if (badged) {
|
|
painter.drawLine(QPointF(9, 8.5), QPointF(9, 12.5));
|
|
painter.drawLine(QPointF(7, 10.5), QPointF(11, 10.5));
|
|
}
|
|
}
|
|
|
|
// Swift's chain turned 45 degrees: upright in a narrow gap.
|
|
void link(QPainter &painter)
|
|
{
|
|
painter.save();
|
|
painter.translate(9, 9);
|
|
painter.rotate(-45);
|
|
painter.drawRoundedRect(QRectF(-6.5, -2.2, 7.5, 4.4), 2.2, 2.2);
|
|
painter.drawRoundedRect(QRectF(-1, -2.2, 7.5, 4.4), 2.2, 2.2);
|
|
painter.restore();
|
|
}
|
|
|
|
void newLayer(QPainter &painter)
|
|
{
|
|
painter.drawRoundedRect(QRectF(2.5, 2.5, 13, 13), 2, 2);
|
|
painter.drawLine(QPointF(9, 6), QPointF(9, 12));
|
|
painter.drawLine(QPointF(6, 9), QPointF(12, 9));
|
|
}
|
|
|
|
void addMask(QPainter &painter)
|
|
{
|
|
painter.drawRoundedRect(QRectF(2.5, 3.5, 13, 11), 2, 2);
|
|
painter.fillRect(QRectF(5.5, 6.5, 7, 5), painter.pen().color());
|
|
}
|
|
|
|
void trash(QPainter &painter)
|
|
{
|
|
painter.drawLine(QPointF(3, 5), QPointF(15, 5));
|
|
painter.drawLine(QPointF(7, 3), QPointF(11, 3));
|
|
painter.drawPolyline(QPolygonF{QPointF(4.5, 5), QPointF(5.5, 15.5), QPointF(12.5, 15.5), QPointF(13.5, 5)});
|
|
painter.drawLine(QPointF(7.5, 8), QPointF(7.8, 13));
|
|
painter.drawLine(QPointF(10.5, 8), QPointF(10.2, 13));
|
|
}
|
|
|
|
// Swift's square.3.layers.3d: three tilted plates.
|
|
void layers(QPainter &painter)
|
|
{
|
|
for (int step = 0; step < 3; ++step) {
|
|
const double y = 4 + step * 4;
|
|
painter.drawPolygon(QPolygonF{QPointF(9, y - 3), QPointF(16, y), QPointF(9, y + 3), QPointF(2, y)});
|
|
}
|
|
}
|
|
|
|
// Swift's circle.lefthalf.filled: a ring, its left half solid.
|
|
void halfFilledCircle(QPainter &painter)
|
|
{
|
|
QPainterPath half;
|
|
half.moveTo(9, 2);
|
|
half.arcTo(QRectF(2, 2, 14, 14), 90, 180);
|
|
half.closeSubpath();
|
|
painter.fillPath(half, painter.pen().color());
|
|
painter.drawEllipse(QPointF(9, 9), 7, 7);
|
|
}
|
|
|
|
// Swift's circle.righthalf.filled: the other half, Invert's.
|
|
void rightHalfCircle(QPainter &painter)
|
|
{
|
|
QPainterPath half;
|
|
half.moveTo(9, 2);
|
|
half.arcTo(QRectF(2, 2, 14, 14), 90, -180);
|
|
half.closeSubpath();
|
|
painter.fillPath(half, painter.pen().color());
|
|
painter.drawEllipse(QPointF(9, 9), 7, 7);
|
|
}
|
|
|
|
// Swift's circle.filled.pattern.diagonalline.rectangle: Black & White's.
|
|
void hatchedCircle(QPainter &painter)
|
|
{
|
|
const QRectF frame(2, 4, 14, 10);
|
|
painter.drawRoundedRect(frame, 2, 2);
|
|
painter.save();
|
|
painter.setClipRect(frame);
|
|
for (double x = -6; x < 16; x += 3)
|
|
painter.drawLine(QPointF(x, 14), QPointF(x + 10, 4));
|
|
painter.restore();
|
|
painter.setBrush(painter.pen().color());
|
|
painter.drawEllipse(QPointF(9, 9), 3.2, 3.2);
|
|
}
|
|
|
|
// Swift's scale.3d: three axes from one corner, Color Balance's.
|
|
void axes(QPainter &painter)
|
|
{
|
|
const QPointF origin(7, 11);
|
|
const auto arrow = [&](QPointF tip, QPointF back1, QPointF back2) {
|
|
painter.drawLine(origin, tip);
|
|
painter.drawLine(tip, back1);
|
|
painter.drawLine(tip, back2);
|
|
};
|
|
arrow(QPointF(7, 2.5), QPointF(5.2, 4.5), QPointF(8.8, 4.5));
|
|
arrow(QPointF(16, 11), QPointF(14, 9.2), QPointF(14, 12.8));
|
|
arrow(QPointF(2.5, 15.5), QPointF(2.6, 12.9), QPointF(5.1, 15.4));
|
|
}
|
|
|
|
// Swift's slider.horizontal.3: three tracks, each with a knob.
|
|
void sliders(QPainter &painter)
|
|
{
|
|
for (const auto &[y, knob] : {std::pair{4.0, 12.0}, std::pair{9.0, 6.0}, std::pair{14.0, 10.5}}) {
|
|
painter.drawLine(QPointF(2, y), QPointF(knob - 2.8, y));
|
|
painter.drawLine(QPointF(knob + 2.8, y), QPointF(16, y));
|
|
painter.drawEllipse(QPointF(knob, y), 2, 2);
|
|
}
|
|
}
|
|
|
|
// Swift's point.topleft.down.to.point.bottomright.curvepath.
|
|
void curvePath(QPainter &painter)
|
|
{
|
|
QPainterPath curve;
|
|
curve.moveTo(5.5, 3.5);
|
|
curve.cubicTo(12, 3.5, 6, 14.5, 12.5, 14.5);
|
|
painter.drawPath(curve);
|
|
painter.setBrush(painter.pen().color());
|
|
painter.drawEllipse(QPointF(3.5, 3.5), 1.6, 1.6);
|
|
painter.drawEllipse(QPointF(14.5, 14.5), 1.6, 1.6);
|
|
}
|
|
|
|
// Swift's plusminus.circle.
|
|
void plusMinusCircle(QPainter &painter)
|
|
{
|
|
painter.drawEllipse(QPointF(9, 9), 7, 7);
|
|
painter.drawLine(QPointF(6.5, 7.5), QPointF(11.5, 7.5));
|
|
painter.drawLine(QPointF(9, 5), QPointF(9, 10));
|
|
painter.drawLine(QPointF(6.5, 12.2), QPointF(11.5, 12.2));
|
|
}
|
|
|
|
// Swift's paintpalette: a palette with a thumb notch and wells.
|
|
void paintPalette(QPainter &painter)
|
|
{
|
|
QPainterPath palette;
|
|
palette.moveTo(9, 2.5);
|
|
palette.cubicTo(13.5, 2.5, 16, 5.5, 16, 8.5);
|
|
palette.cubicTo(16, 11, 13.5, 11.5, 12, 11.2);
|
|
palette.cubicTo(10.8, 11, 10.2, 12.2, 10.8, 13.4);
|
|
palette.cubicTo(11.5, 15, 10.5, 15.8, 9, 15.8);
|
|
palette.cubicTo(5, 15.8, 2, 12.8, 2, 9);
|
|
palette.cubicTo(2, 5.2, 5, 2.5, 9, 2.5);
|
|
painter.drawPath(palette);
|
|
painter.setBrush(painter.pen().color());
|
|
for (const QPointF well : {QPointF(5.8, 6.8), QPointF(9.3, 5.4), QPointF(12.7, 7.3), QPointF(5.4, 10.6)})
|
|
painter.drawEllipse(well, 1.1, 1.1);
|
|
}
|
|
|
|
// Swift's circle.grid.3x3: nine dots.
|
|
void circleGrid(QPainter &painter)
|
|
{
|
|
painter.setBrush(painter.pen().color());
|
|
painter.setPen(Qt::NoPen);
|
|
for (const double y : {4.0, 9.0, 14.0}) {
|
|
for (const double x : {4.0, 9.0, 14.0})
|
|
painter.drawEllipse(QPointF(x, y), 1.7, 1.7);
|
|
}
|
|
}
|
|
|
|
// Swift's sparkles: a large four-pointed star, two small ones.
|
|
void sparkles(QPainter &painter)
|
|
{
|
|
const auto star = [&painter](QPointF centre, double radius) {
|
|
const double bend = radius * 0.18;
|
|
QPainterPath path;
|
|
path.moveTo(centre.x(), centre.y() - radius);
|
|
path.quadTo(centre.x() + bend, centre.y() - bend, centre.x() + radius, centre.y());
|
|
path.quadTo(centre.x() + bend, centre.y() + bend, centre.x(), centre.y() + radius);
|
|
path.quadTo(centre.x() - bend, centre.y() + bend, centre.x() - radius, centre.y());
|
|
path.quadTo(centre.x() - bend, centre.y() - bend, centre.x(), centre.y() - radius);
|
|
painter.fillPath(path, painter.pen().color());
|
|
};
|
|
star(QPointF(7.5, 10.5), 6.5);
|
|
star(QPointF(14, 4.5), 3);
|
|
star(QPointF(14.5, 14), 2);
|
|
}
|
|
|
|
// Swift's drop.fill: a filled teardrop, point up.
|
|
void drop(QPainter &painter)
|
|
{
|
|
QPainterPath path;
|
|
path.moveTo(9, 1.5);
|
|
path.cubicTo(11, 5, 14.5, 8.5, 14.5, 11.3);
|
|
path.cubicTo(14.5, 14.4, 12, 16.5, 9, 16.5);
|
|
path.cubicTo(6, 16.5, 3.5, 14.4, 3.5, 11.3);
|
|
path.cubicTo(3.5, 8.5, 7, 5, 9, 1.5);
|
|
painter.fillPath(path, painter.pen().color());
|
|
}
|
|
|
|
// Swift's wind: three strokes, the outer two curling back.
|
|
void wind(QPainter &painter)
|
|
{
|
|
QPainterPath top;
|
|
top.moveTo(1.5, 6);
|
|
top.lineTo(12, 6);
|
|
top.cubicTo(14.5, 6, 15, 2.5, 12.5, 2.5);
|
|
QPainterPath middle;
|
|
middle.moveTo(1.5, 9.5);
|
|
middle.lineTo(15, 9.5);
|
|
QPainterPath bottom;
|
|
bottom.moveTo(1.5, 13);
|
|
bottom.lineTo(10, 13);
|
|
bottom.cubicTo(12.5, 13, 13, 16.5, 10.5, 16.5);
|
|
for (const QPainterPath &path : {top, middle, bottom})
|
|
painter.drawPath(path);
|
|
}
|
|
|
|
// The lens toward the top left, its handle down right.
|
|
void magnifyingGlass(QPainter &painter, bool plus)
|
|
{
|
|
painter.drawEllipse(QPointF(7.5, 7.5), 5.5, 5.5);
|
|
painter.drawLine(QPointF(11.4, 11.4), QPointF(16, 16));
|
|
painter.drawLine(QPointF(5, 7.5), QPointF(10, 7.5));
|
|
if (plus)
|
|
painter.drawLine(QPointF(7.5, 5), QPointF(7.5, 10));
|
|
}
|
|
|
|
// Swift's circle.dotted: twelve dots round a ring.
|
|
void dottedCircle(QPainter &painter)
|
|
{
|
|
painter.setBrush(painter.pen().color());
|
|
painter.setPen(Qt::NoPen);
|
|
for (int index = 0; index < 12; ++index) {
|
|
const double angle = index * std::numbers::pi / 6;
|
|
painter.drawEllipse(QPointF(9 + 6.8 * std::cos(angle), 9 + 6.8 * std::sin(angle)), 1.1, 1.1);
|
|
}
|
|
}
|
|
|
|
// Swift's scope: a ring with four ticks toward its middle.
|
|
void scope(QPainter &painter)
|
|
{
|
|
painter.drawEllipse(QPointF(9, 9), 6, 6);
|
|
painter.drawLine(QPointF(9, 1.5), QPointF(9, 6));
|
|
painter.drawLine(QPointF(9, 12), QPointF(9, 16.5));
|
|
painter.drawLine(QPointF(1.5, 9), QPointF(6, 9));
|
|
painter.drawLine(QPointF(12, 9), QPointF(16.5, 9));
|
|
}
|
|
}
|
|
|
|
void LayerIcons::paint(QPainter &painter, LayerIcon icon, QPointF origin, double side, const QColor &colour)
|
|
{
|
|
painter.save();
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
painter.translate(origin);
|
|
painter.scale(side / 18, side / 18);
|
|
painter.setPen(QPen(colour, 1.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
|
painter.setBrush(Qt::NoBrush);
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
switch (icon) {
|
|
case LayerIcon::eye: eye(painter, false); break;
|
|
case LayerIcon::eyeSlash: eye(painter, true); break;
|
|
case LayerIcon::chevronRight: chevron(painter, false); break;
|
|
case LayerIcon::chevronDown: chevron(painter, true); break;
|
|
case LayerIcon::folder: folder(painter, false); break;
|
|
case LayerIcon::link: link(painter); break;
|
|
case LayerIcon::newLayer: newLayer(painter); break;
|
|
case LayerIcon::newFolder: folder(painter, true); break;
|
|
case LayerIcon::addMask: addMask(painter); break;
|
|
case LayerIcon::trash: trash(painter); break;
|
|
case LayerIcon::layers: layers(painter); break;
|
|
// Swift's textformat, as the rail draws it, 1.21 times up.
|
|
case LayerIcon::text: ToolIcons::paint(painter, NavigationTool::type, QPointF(3.6, 3.6), 10.8, colour); break;
|
|
case LayerIcon::halfFilledCircle: halfFilledCircle(painter); break;
|
|
case LayerIcon::sliders: sliders(painter); break;
|
|
case LayerIcon::curvePath: curvePath(painter); break;
|
|
case LayerIcon::plusMinusCircle: plusMinusCircle(painter); break;
|
|
case LayerIcon::paintPalette: paintPalette(painter); break;
|
|
case LayerIcon::circleGrid: circleGrid(painter); break;
|
|
case LayerIcon::sparkles: sparkles(painter); break;
|
|
case LayerIcon::rightHalfCircle: rightHalfCircle(painter); break;
|
|
case LayerIcon::hatchedCircle: hatchedCircle(painter); break;
|
|
case LayerIcon::axes: axes(painter); break;
|
|
case LayerIcon::scope: scope(painter); break;
|
|
case LayerIcon::lineDiagonal: painter.drawLine(QPointF(3, 15), QPointF(15, 3)); break;
|
|
case LayerIcon::drop: drop(painter); break;
|
|
case LayerIcon::wind: wind(painter); break;
|
|
case LayerIcon::dottedCircle: dottedCircle(painter); break;
|
|
case LayerIcon::plusMagnifyingGlass: magnifyingGlass(painter, true); break;
|
|
case LayerIcon::minusMagnifyingGlass: magnifyingGlass(painter, false); break;
|
|
// Swift's xmark at bold weight.
|
|
case LayerIcon::xmark:
|
|
painter.setPen(QPen(colour, 2.6, Qt::SolidLine, Qt::RoundCap));
|
|
painter.drawLine(QPointF(4, 4), QPointF(14, 14));
|
|
painter.drawLine(QPointF(14, 4), QPointF(4, 14));
|
|
break;
|
|
}
|
|
painter.restore();
|
|
}
|
|
|
|
QPixmap LayerIcons::pixmap(LayerIcon icon, double side, const QColor &colour, double ratio)
|
|
{
|
|
QPixmap result(QSize(int(side * ratio), int(side * ratio)));
|
|
result.setDevicePixelRatio(ratio);
|
|
result.fill(Qt::transparent);
|
|
QPainter painter(&result);
|
|
paint(painter, icon, QPointF(0, 0), side, colour);
|
|
return result;
|
|
}
|
|
|
|
LayerIcon LayerIcons::symbol(AdjustmentKind kind)
|
|
{
|
|
switch (kind) {
|
|
case AdjustmentKind::curves: return LayerIcon::curvePath;
|
|
case AdjustmentKind::levels: return LayerIcon::sliders;
|
|
case AdjustmentKind::hsv: return LayerIcon::halfFilledCircle;
|
|
case AdjustmentKind::exposure: return LayerIcon::plusMinusCircle;
|
|
case AdjustmentKind::gradientMap: return LayerIcon::paintPalette;
|
|
case AdjustmentKind::grain: return LayerIcon::circleGrid;
|
|
case AdjustmentKind::addNoise: return LayerIcon::dottedCircle;
|
|
case AdjustmentKind::gaussianBlur: return LayerIcon::drop;
|
|
case AdjustmentKind::motionBlur: return LayerIcon::wind;
|
|
case AdjustmentKind::invert: return LayerIcon::rightHalfCircle;
|
|
case AdjustmentKind::blackWhite: return LayerIcon::hatchedCircle;
|
|
case AdjustmentKind::colorBalance: return LayerIcon::axes;
|
|
}
|
|
throw std::logic_error("unknown adjustment kind");
|
|
}
|
|
|
|
QPixmap LayerIcons::adjustmentThumbnail(AdjustmentKind kind, const QColor &colour, double ratio)
|
|
{
|
|
QPixmap result(QSize(int(36 * ratio), int(36 * ratio)));
|
|
result.setDevicePixelRatio(ratio);
|
|
result.fill(Qt::transparent);
|
|
QPainter painter(&result);
|
|
// Swift turns the curve a quarter clockwise: a tone curve.
|
|
if (kind == AdjustmentKind::curves) {
|
|
painter.translate(18, 18);
|
|
painter.rotate(90);
|
|
painter.translate(-18, -18);
|
|
}
|
|
paint(painter, symbol(kind), QPointF(7.2, 7.2), 18 * 1.2, colour);
|
|
return result;
|
|
}
|