Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

slab/yergersnap: paint icon from a Julia Yerger work

Adds make-icon-from-image.swift which crops a square painting source
to remove its frame, applies the macOS app-icon corner radius, and
slices to AppIcon.icns. Icon now uses one of Julia's digital paintings
on paper (JY17.05.05, from the 2018 Vogt show) instead of the
generated camera-aperture default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+100
slab/yergersnap/AppIcon.icns

This is a binary file and will not be displayed.

+100
slab/yergersnap/make-icon-from-image.swift
··· 1 + #!/usr/bin/env swift 2 + // 3 + // make-icon-from-image.swift — turn a source image (e.g. a painting 4 + // photo) into AppIcon.icns. Square-center-crops with an optional 5 + // frame inset, scales to 1024, applies the macOS app-icon corner 6 + // radius, slices an iconset, compiles to .icns. 7 + // 8 + // Usage: 9 + // swift make-icon-from-image.swift <path> [inset-fraction] 10 + // 11 + // inset-fraction defaults to 0.08 (trims 8% off each side, useful 12 + // for gallery photos where there's a visible frame + matte). 13 + 14 + import Cocoa 15 + 16 + let args = CommandLine.arguments 17 + guard args.count >= 2, !args[1].isEmpty else { 18 + fputs("usage: make-icon-from-image.swift <path> [inset-fraction]\n", stderr) 19 + exit(64) 20 + } 21 + let srcPath = args[1] 22 + let inset: CGFloat = args.count >= 3 23 + ? CGFloat(Double(args[2]) ?? 0.08) 24 + : 0.08 25 + 26 + guard let image = NSImage(contentsOfFile: srcPath) else { 27 + fputs("couldn't load image: \(srcPath)\n", stderr) 28 + exit(1) 29 + } 30 + 31 + let srcSize = image.size 32 + let side = min(srcSize.width, srcSize.height) 33 + let croppedSide = side * (1.0 - 2 * inset) 34 + let cropX = (srcSize.width - croppedSide) / 2 35 + let cropY = (srcSize.height - croppedSide) / 2 36 + let cropRect = NSRect(x: cropX, y: cropY, 37 + width: croppedSide, height: croppedSide) 38 + 39 + let canvas: CGFloat = 1024 40 + let cornerRadius = canvas * 0.2237 // matches macOS app-icon squircle 41 + 42 + let master = NSImage(size: NSSize(width: canvas, height: canvas)) 43 + master.lockFocus() 44 + NSBezierPath(roundedRect: NSRect(x: 0, y: 0, width: canvas, height: canvas), 45 + xRadius: cornerRadius, 46 + yRadius: cornerRadius).addClip() 47 + NSGraphicsContext.current?.imageInterpolation = .high 48 + image.draw(in: NSRect(x: 0, y: 0, width: canvas, height: canvas), 49 + from: cropRect, 50 + operation: .sourceOver, 51 + fraction: 1) 52 + master.unlockFocus() 53 + 54 + // Save iconset slices. 55 + let outIconset = "icon.iconset" 56 + let outIcns = "AppIcon.icns" 57 + let fm = FileManager.default 58 + try? fm.removeItem(atPath: outIconset) 59 + try fm.createDirectory(atPath: outIconset, withIntermediateDirectories: true) 60 + 61 + let slices: [(String, Int)] = [ 62 + ("icon_16x16", 16), 63 + ("icon_16x16@2x", 32), 64 + ("icon_32x32", 32), 65 + ("icon_32x32@2x", 64), 66 + ("icon_128x128", 128), 67 + ("icon_128x128@2x", 256), 68 + ("icon_256x256", 256), 69 + ("icon_256x256@2x", 512), 70 + ("icon_512x512", 512), 71 + ("icon_512x512@2x", 1024), 72 + ] 73 + for (name, px) in slices { 74 + let scaled = NSImage(size: NSSize(width: px, height: px)) 75 + scaled.lockFocus() 76 + NSGraphicsContext.current?.imageInterpolation = .high 77 + master.draw(in: NSRect(x: 0, y: 0, width: px, height: px), 78 + from: .zero, operation: .sourceOver, fraction: 1) 79 + scaled.unlockFocus() 80 + guard let cg = scaled.cgImage(forProposedRect: nil, context: nil, 81 + hints: nil) else { 82 + fputs("cgImage failed for \(name)\n", stderr); exit(1) 83 + } 84 + let bm = NSBitmapImageRep(cgImage: cg) 85 + guard let data = bm.representation(using: .png, properties: [:]) else { 86 + fputs("png encode failed for \(name)\n", stderr); exit(1) 87 + } 88 + try data.write(to: URL(fileURLWithPath: "\(outIconset)/\(name).png")) 89 + } 90 + 91 + let task = Process() 92 + task.executableURL = URL(fileURLWithPath: "/usr/bin/iconutil") 93 + task.arguments = ["-c", "icns", "-o", outIcns, outIconset] 94 + try task.run() 95 + task.waitUntilExit() 96 + if task.terminationStatus != 0 { 97 + fputs("iconutil failed\n", stderr); exit(1) 98 + } 99 + 100 + print("✓ wrote \(outIcns) from \(srcPath) (inset \(inset))")