Files
TokenLens/NativeTokenLens/Tools/GenerateAppIcon.swift

176 lines
6.3 KiB
Swift

import AppKit
import Foundation
struct IconRenderer {
let size: CGFloat
func render(to url: URL) throws {
guard
let bitmap = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(size),
pixelsHigh: Int(size),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .deviceRGB,
bytesPerRow: 0,
bitsPerPixel: 0
),
let graphics = NSGraphicsContext(bitmapImageRep: bitmap)
else { return }
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = graphics
defer { NSGraphicsContext.restoreGraphicsState() }
let context = graphics.cgContext
context.setShouldAntialias(true)
context.setAllowsAntialiasing(true)
let rect = CGRect(x: 0, y: 0, width: size, height: size)
let scale = size / 1024
let radius = 226 * scale
let base = CGPath(
roundedRect: rect.insetBy(dx: 44 * scale, dy: 44 * scale),
cornerWidth: radius,
cornerHeight: radius,
transform: nil
)
context.addPath(base)
context.clip()
let background = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: [
NSColor(calibratedRed: 0.04, green: 0.12, blue: 0.24, alpha: 1).cgColor,
NSColor(calibratedRed: 0.12, green: 0.20, blue: 0.42, alpha: 1).cgColor,
NSColor(calibratedRed: 0.58, green: 0.16, blue: 0.88, alpha: 1).cgColor
] as CFArray,
locations: [0, 0.52, 1]
)!
context.drawLinearGradient(
background,
start: CGPoint(x: rect.minX, y: rect.maxY),
end: CGPoint(x: rect.maxX, y: rect.minY),
options: []
)
drawGlow(context, color: NSColor.systemBlue, center: CGPoint(x: 260 * scale, y: 760 * scale), radius: 330 * scale)
drawGlow(context, color: NSColor.systemPurple, center: CGPoint(x: 785 * scale, y: 285 * scale), radius: 430 * scale)
drawGlassHighlight(context, rect: rect, scale: scale)
drawBars(context, scale: scale)
drawLens(context, scale: scale)
context.resetClip()
context.addPath(base)
context.setStrokeColor(NSColor.white.withAlphaComponent(0.22).cgColor)
context.setLineWidth(12 * scale)
context.strokePath()
guard let png = bitmap.representation(using: .png, properties: [:]) else { return }
try png.write(to: url)
}
private func drawGlow(_ context: CGContext, color: NSColor, center: CGPoint, radius: CGFloat) {
let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: [
color.withAlphaComponent(0.55).cgColor,
color.withAlphaComponent(0.0).cgColor
] as CFArray,
locations: [0, 1]
)!
context.drawRadialGradient(
gradient,
startCenter: center,
startRadius: 0,
endCenter: center,
endRadius: radius,
options: []
)
}
private func drawGlassHighlight(_ context: CGContext, rect: CGRect, scale: CGFloat) {
let highlight = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: [
NSColor.white.withAlphaComponent(0.32).cgColor,
NSColor.white.withAlphaComponent(0.06).cgColor,
NSColor.white.withAlphaComponent(0.0).cgColor
] as CFArray,
locations: [0, 0.36, 1]
)!
let path = CGPath(
roundedRect: CGRect(x: 120 * scale, y: 610 * scale, width: 790 * scale, height: 268 * scale),
cornerWidth: 130 * scale,
cornerHeight: 130 * scale,
transform: nil
)
context.saveGState()
context.addPath(path)
context.clip()
context.drawLinearGradient(
highlight,
start: CGPoint(x: rect.minX, y: rect.maxY),
end: CGPoint(x: rect.maxX, y: rect.minY),
options: []
)
context.restoreGState()
}
private func drawBars(_ context: CGContext, scale: CGFloat) {
let bars = [
CGRect(x: 288, y: 286, width: 86, height: 300),
CGRect(x: 430, y: 212, width: 86, height: 448),
CGRect(x: 572, y: 330, width: 86, height: 252)
].map { CGRect(x: $0.minX * scale, y: $0.minY * scale, width: $0.width * scale, height: $0.height * scale) }
for (index, bar) in bars.enumerated() {
let color: NSColor = index == 1 ? .white : NSColor(calibratedWhite: 1, alpha: 0.88)
let path = CGPath(
roundedRect: bar,
cornerWidth: 44 * scale,
cornerHeight: 44 * scale,
transform: nil
)
context.addPath(path)
context.setFillColor(color.cgColor)
context.fillPath()
}
}
private func drawLens(_ context: CGContext, scale: CGFloat) {
let ring = CGRect(x: 682 * scale, y: 228 * scale, width: 170 * scale, height: 170 * scale)
context.setStrokeColor(NSColor.white.withAlphaComponent(0.92).cgColor)
context.setLineWidth(34 * scale)
context.strokeEllipse(in: ring)
context.setLineCap(.round)
context.move(to: CGPoint(x: 805 * scale, y: 275 * scale))
context.addLine(to: CGPoint(x: 892 * scale, y: 188 * scale))
context.strokePath()
}
}
let outputRoot = URL(fileURLWithPath: CommandLine.arguments.dropFirst().first ?? "NativeTokenLens/Assets/AppIcon.iconset")
try FileManager.default.createDirectory(at: outputRoot, withIntermediateDirectories: true)
let specs: [(String, CGFloat)] = [
("icon_16x16.png", 16),
("icon_16x16@2x.png", 32),
("icon_32x32.png", 32),
("icon_32x32@2x.png", 64),
("icon_128x128.png", 128),
("icon_128x128@2x.png", 256),
("icon_256x256.png", 256),
("icon_256x256@2x.png", 512),
("icon_512x512.png", 512),
("icon_512x512@2x.png", 1024)
]
for spec in specs {
try IconRenderer(size: spec.1).render(to: outputRoot.appendingPathComponent(spec.0))
}