Something went wrong. Try again.
macOS menu bar app for Restic
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105// Renders Snapshot's app icon at every size an .iconset needs.//// Drawn rather than designed so the icon is reproducible from source and has no// binary asset to keep in the repository. SF Symbols are deliberately not used:// Apple's licence forbids them in app icons.//// The mark is three cards stacked along a diagonal — successive snapshots of the// same thing, receding into the past. Cards rather than horizontal bars: three// equal bars are the universal hamburger-menu glyph and read as a menu, not a// backup. The front card is fully opaque so it occludes the ones behind cleanly// instead of blending into mush at small sizes.import AppKitimport CoreGraphicsimport Foundationimport ImageIOimport UniformTypeIdentifiers
let sizes: [(name: String, pixels: Int)] = [ ("icon_16x16", 16), ("icon_16x16@2x", 32), ("icon_32x32", 32), ("icon_32x32@2x", 64), ("icon_128x128", 128), ("icon_128x128@2x", 256), ("icon_256x256", 256), ("icon_256x256@2x", 512), ("icon_512x512", 512), ("icon_512x512@2x", 1024),]
func draw(pixels: Int) -> CGImage? { let side = CGFloat(pixels) let scale = side / 1024 guard let context = CGContext( data: nil, width: pixels, height: pixels, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpace(name: CGColorSpace.sRGB)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return nil }
context.setShouldAntialias(true) context.interpolationQuality = .high
// macOS icons sit inset within the canvas rather than filling it. let inset: CGFloat = 100 * scale let plate = CGRect(x: inset, y: inset, width: side - inset * 2, height: side - inset * 2) let radius = plate.width * 0.2246 // the Big Sur corner ratio
context.saveGState() context.addPath(CGPath(roundedRect: plate, cornerWidth: radius, cornerHeight: radius, transform: nil)) context.clip() let gradient = CGGradient( colorsSpace: CGColorSpace(name: CGColorSpace.sRGB)!, colors: [ CGColor(red: 0.30, green: 0.72, blue: 0.76, alpha: 1), CGColor(red: 0.16, green: 0.36, blue: 0.68, alpha: 1), ] as CFArray, locations: [0, 1])! context.drawLinearGradient( gradient, start: CGPoint(x: plate.midX, y: plate.maxY), end: CGPoint(x: plate.midX, y: plate.minY), options: []) context.restoreGState()
let card: CGFloat = 400 * scale let cornerRadius = card * 0.22 let step: CGFloat = 78 * scale // Three positions along a diagonal, centred as a group on the plate. let spread = step * 2 let originX = plate.midX - card / 2 - spread / 2 let originY = plate.midY - card / 2 + spread / 2
// Back to front, so the opaque front card covers the ones behind it. for (index, opacity) in [0.45, 0.70, 1.0].enumerated() { let offset = CGFloat(2 - index) let rect = CGRect( x: originX + step * offset, y: originY - step * offset, width: card, height: card) context.setFillColor(CGColor(red: 1, green: 1, blue: 1, alpha: opacity)) context.addPath( CGPath( roundedRect: rect, cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)) context.fillPath() }
return context.makeImage()}
let output = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true)try? FileManager.default.createDirectory(at: output, withIntermediateDirectories: true)
for entry in sizes { guard let image = draw(pixels: entry.pixels) else { FileHandle.standardError.write(Data("could not render \(entry.name)\n".utf8)) exit(1) } let url = output.appendingPathComponent("\(entry.name).png") guard let destination = CGImageDestinationCreateWithURL( url as CFURL, UTType.png.identifier as CFString, 1, nil) else { exit(1) } CGImageDestinationAddImage(destination, image, nil) CGImageDestinationFinalize(destination)}print("rendered \(sizes.count) icon sizes")