|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +PATH=${PWD%%/test*}/bin:$PATH |
| 4 | + |
| 5 | +die() { (( $# > 0 )) && echo "ERROR: $*" >&2; exit 1; } |
| 6 | + |
| 7 | +tmpDir=$(mktemp -d -t XXXX) |
| 8 | +trap 'rm -rf "$tmpDir"' EXIT |
| 9 | + |
| 10 | +mkdir -p "$tmpDir/modcache" |
| 11 | + |
| 12 | +cat > "$tmpDir/create-image.swift" <<'SWIFT' |
| 13 | +import AppKit |
| 14 | +import Foundation |
| 15 | +
|
| 16 | +let outURL = URL(fileURLWithPath: CommandLine.arguments[1]) |
| 17 | +
|
| 18 | +let image = NSImage(size: NSSize(width: 400, height: 200)) |
| 19 | +image.lockFocus() |
| 20 | +NSColor.clear.set() |
| 21 | +NSRect(x: 0, y: 0, width: 400, height: 200).fill() |
| 22 | +NSColor.black.set() |
| 23 | +NSRect(x: 0, y: 0, width: 400, height: 200).fill() |
| 24 | +image.unlockFocus() |
| 25 | +
|
| 26 | +guard |
| 27 | + let tiff = image.tiffRepresentation, |
| 28 | + let rep = NSBitmapImageRep(data: tiff), |
| 29 | + let png = rep.representation(using: .png, properties: [:]) |
| 30 | +else { |
| 31 | + fatalError("Failed to create test image.") |
| 32 | +} |
| 33 | +
|
| 34 | +try png.write(to: outURL) |
| 35 | +SWIFT |
| 36 | + |
| 37 | +CLANG_MODULE_CACHE_PATH="$tmpDir/modcache" swift "$tmpDir/create-image.swift" "$tmpDir/non-square.png" || die "Failed to create test image." |
| 38 | +touch "$tmpDir/target" || die |
| 39 | + |
| 40 | +fileicon set "$tmpDir/target" "$tmpDir/non-square.png" || die "Failed to set icon." |
| 41 | +fileicon get -f "$tmpDir/target" "$tmpDir/out.icns" || die "Failed to extract icon." |
| 42 | +sips -s format png "$tmpDir/out.icns" --out "$tmpDir/out.png" >/dev/null || die "Failed to convert icon." |
| 43 | + |
| 44 | +cat > "$tmpDir/measure-bounds.swift" <<'SWIFT' |
| 45 | +import AppKit |
| 46 | +import Foundation |
| 47 | +
|
| 48 | +let imageURL = URL(fileURLWithPath: CommandLine.arguments[1]) |
| 49 | +let data = try Data(contentsOf: imageURL) |
| 50 | +guard let rep = NSBitmapImageRep(data: data) else { |
| 51 | + fatalError("Failed to open rendered icon.") |
| 52 | +} |
| 53 | +
|
| 54 | +let width = rep.pixelsWide |
| 55 | +let height = rep.pixelsHigh |
| 56 | +var minX = width |
| 57 | +var minY = height |
| 58 | +var maxX = -1 |
| 59 | +var maxY = -1 |
| 60 | +
|
| 61 | +for y in 0..<height { |
| 62 | + for x in 0..<width { |
| 63 | + guard let color = rep.colorAt(x: x, y: y) else { continue } |
| 64 | + if color.alphaComponent > 0.01 { |
| 65 | + minX = min(minX, x) |
| 66 | + minY = min(minY, y) |
| 67 | + maxX = max(maxX, x) |
| 68 | + maxY = max(maxY, y) |
| 69 | + } |
| 70 | + } |
| 71 | +} |
| 72 | +
|
| 73 | +guard maxX >= minX, maxY >= minY else { |
| 74 | + fatalError("Rendered icon has no visible pixels.") |
| 75 | +} |
| 76 | +
|
| 77 | +print("\(maxX - minX + 1) \(maxY - minY + 1)") |
| 78 | +SWIFT |
| 79 | + |
| 80 | +read -r opaqueWidth opaqueHeight < <(CLANG_MODULE_CACHE_PATH="$tmpDir/modcache" swift "$tmpDir/measure-bounds.swift" "$tmpDir/out.png") || die "Failed to measure rendered icon." |
| 81 | + |
| 82 | +(( opaqueWidth > opaqueHeight )) || die "Expected a wide rendered icon, got ${opaqueWidth}x${opaqueHeight}." |
| 83 | + |
| 84 | +ratio=$(awk -v w="$opaqueWidth" -v h="$opaqueHeight" 'BEGIN { printf "%.3f", w / h }') |
| 85 | +awk -v ratio="$ratio" 'BEGIN { exit !(ratio > 1.7 && ratio < 2.3) }' || die "Expected aspect ratio near 2:1, got ${opaqueWidth}x${opaqueHeight} (${ratio}:1)." |
0 commit comments