chore: add TokenLens sources and ignore rules
This commit is contained in:
175
NativeTokenLens/Tools/GenerateAppIcon.swift
Normal file
175
NativeTokenLens/Tools/GenerateAppIcon.swift
Normal file
@@ -0,0 +1,175 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user