#!/usr/bin/env bash # # Пересборка всех иконок лаунчера из одного исходника master/icon_master.png # (квадрат 1024x1024, арт нарисован «в край» холста). # # Размеры у систем разные не по прихоти: macOS сама не масштабирует иконку в # доке, поэтому поля закладываются в сам файл — по сетке Apple арт занимает # 824x824 внутри 1024x1024, а углы скруглены радиусом 185.4. Windows и Linux, # наоборот, ждут картинку на весь холст и добавляют отступы сами, так что для # них тот же арт идёт без полей. # # Нужны ImageMagick (magick) и iconutil из состава macOS. # set -euo pipefail cd "$(dirname "$0")" MASTER="master/icon_master.png" [ -f "$MASTER" ] || { echo "нет исходника $MASTER" >&2; exit 1; } command -v magick >/dev/null || { echo "нужен ImageMagick: brew install imagemagick" >&2; exit 1; } # Сетка Apple для иконки-«скруглённого квадрата», доли от холста 1024. readonly APPLE_ART=824 readonly APPLE_RADIUS=185.4 readonly CANVAS=1024 echo "==> растры без полей (Windows, Linux, иконка окна Qt)" # 24 и 48 нужны панели задач Windows и темам значков Linux, 64 — HiDPI-вариант 32. for size in 16 24 32 48 64 128 256 512; do magick "$MASTER" -filter Lanczos -resize "${size}x${size}" -strip "PNG32:icon_${size}x${size}.png" done # Те же кадры под именами @2x: Qt по такому имени берёт растр для HiDPI-экрана. for base in 16 32 128 256 512; do magick "$MASTER" -filter Lanczos -resize "$((base * 2))x$((base * 2))" -strip "PNG32:icon_${base}x${base}@2x.png" done echo "==> icon.ico (Windows 10/11)" # Кадры от 16 до 256: проводник, панель задач и Alt+Tab берут свой размер сами, # промежуточные 20, 40 и 96 нужны масштабам 125%, 150% и 200%. Без явного списка # ImageMagick кладёт в .ico только исходные 1024x1024, и система их мылит. magick "$MASTER" -filter Lanczos \ -define icon:auto-resize=256,128,96,64,48,40,32,24,20,16 \ -strip icon.ico echo "==> icon.icns (macOS)" # Маска рисуется в четыре раза крупнее и ужимается — так края скругления # получаются сглаженными, а не ступенькой. SS=4 MASK="$(mktemp -t icnsmask).png" PADDED="$(mktemp -t icnspad).png" trap 'rm -f "$MASK" "$PADDED"' EXIT mask_px=$((APPLE_ART * SS)) mask_radius=$(awk -v r="$APPLE_RADIUS" -v ss="$SS" 'BEGIN { printf "%d", r * ss }') # Префикс PNG32: обязателен: без него ImageMagick сохранит белое-на-прозрачном # как серую картинку без альфы, и маска вырежет вообще всё. magick -size "${mask_px}x${mask_px}" xc:none -fill white -antialias \ -draw "roundrectangle 0,0,$((mask_px - 1)),$((mask_px - 1)),${mask_radius},${mask_radius}" \ -filter Lanczos -resize "${APPLE_ART}x${APPLE_ART}" "PNG32:$MASK" # DstIn оставляет картинку только там, где непрозрачна маска: арт ужимается до # 824 и обрезается по «яблочному» скруглению. Дальше -compose Copy обязателен: # -extent кладёт результат на прозрачный холст 1024 текущим оператором, а с # оставшимся DstIn холст вычистил бы всю картинку. magick "$MASTER" -filter Lanczos -resize "${APPLE_ART}x${APPLE_ART}" \ "$MASK" -compose DstIn -composite \ -compose Copy -background none -gravity center -extent "${CANVAS}x${CANVAS}" \ -strip "PNG32:$PADDED" rm -rf icon.iconset mkdir icon.iconset for base in 16 32 128 256 512; do magick "$PADDED" -filter Lanczos -resize "${base}x${base}" -strip "PNG32:icon.iconset/icon_${base}x${base}.png" magick "$PADDED" -filter Lanczos -resize "$((base * 2))x$((base * 2))" -strip "PNG32:icon.iconset/icon_${base}x${base}@2x.png" done iconutil -c icns icon.iconset -o icon.icns # Промежуточный .iconset больше не нужен: всё его содержимое лежит в .icns. rm -rf icon.iconset echo "готово"