#include "UI/LayerIcons.h" #include "UI/ToolIcons.h" #include #include #include 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; }