149 lines
5.0 KiB
Swift
149 lines
5.0 KiB
Swift
// SPDX-FileCopyrightText: 2026 John Burwell and contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import AppKit
|
|
import Foundation
|
|
|
|
let fileManager = FileManager.default
|
|
let currentDirectoryURL = URL(fileURLWithPath: fileManager.currentDirectoryPath, isDirectory: true)
|
|
let iconsetURL = currentDirectoryURL
|
|
.appendingPathComponent("World Manager for Minecraft/MinecraftVoxelDocument.iconset", isDirectory: true)
|
|
let singleIconURL = currentDirectoryURL
|
|
.appendingPathComponent("World Manager for Minecraft/MinecraftVoxelDocument.png")
|
|
|
|
let outputSizes: [(name: String, pixels: Int)] = [
|
|
("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)
|
|
]
|
|
|
|
try? fileManager.removeItem(at: iconsetURL)
|
|
try fileManager.createDirectory(at: iconsetURL, withIntermediateDirectories: true)
|
|
|
|
for output in outputSizes {
|
|
let image = try makeDocumentIcon(pixels: output.pixels)
|
|
let destinationURL = iconsetURL.appendingPathComponent(output.name)
|
|
try writePNG(image: image, to: destinationURL)
|
|
}
|
|
|
|
let primaryIcon = try makeDocumentIcon(pixels: 1024)
|
|
try writePNG(image: primaryIcon, to: singleIconURL)
|
|
|
|
func makeDocumentIcon(pixels: Int) throws -> NSImage {
|
|
guard let bitmap = NSBitmapImageRep(
|
|
bitmapDataPlanes: nil,
|
|
pixelsWide: pixels,
|
|
pixelsHigh: pixels,
|
|
bitsPerSample: 8,
|
|
samplesPerPixel: 4,
|
|
hasAlpha: true,
|
|
isPlanar: false,
|
|
colorSpaceName: .deviceRGB,
|
|
bytesPerRow: 0,
|
|
bitsPerPixel: 0
|
|
) else {
|
|
throw CocoaError(.fileWriteUnknown)
|
|
}
|
|
|
|
bitmap.size = NSSize(width: pixels, height: pixels)
|
|
|
|
NSGraphicsContext.saveGraphicsState()
|
|
defer { NSGraphicsContext.restoreGraphicsState() }
|
|
|
|
guard let context = NSGraphicsContext(bitmapImageRep: bitmap) else {
|
|
throw CocoaError(.fileWriteUnknown)
|
|
}
|
|
|
|
NSGraphicsContext.current = context
|
|
|
|
let size = CGFloat(pixels)
|
|
let rect = CGRect(origin: .zero, size: CGSize(width: size, height: size))
|
|
drawBackground(in: rect)
|
|
drawVoxelBadge(in: rect.insetBy(dx: size * 0.20, dy: size * 0.18))
|
|
drawBorder(in: rect)
|
|
context.flushGraphics()
|
|
|
|
let image = NSImage(size: NSSize(width: pixels, height: pixels))
|
|
image.addRepresentation(bitmap)
|
|
return image
|
|
}
|
|
|
|
func drawBackground(in rect: CGRect) {
|
|
let path = NSBezierPath(
|
|
roundedRect: rect.insetBy(dx: 1, dy: 1),
|
|
xRadius: rect.width * 0.20,
|
|
yRadius: rect.height * 0.20
|
|
)
|
|
|
|
let gradient = NSGradient(colors: [
|
|
NSColor(calibratedRed: 0.18, green: 0.48, blue: 0.25, alpha: 1),
|
|
NSColor(calibratedRed: 0.47, green: 0.75, blue: 0.31, alpha: 1)
|
|
])
|
|
gradient?.draw(in: path, angle: -45)
|
|
}
|
|
|
|
func drawVoxelBadge(in rect: CGRect) {
|
|
let topFace = NSBezierPath()
|
|
topFace.move(to: CGPoint(x: rect.midX, y: rect.maxY))
|
|
topFace.line(to: CGPoint(x: rect.maxX, y: rect.maxY - rect.height * 0.18))
|
|
topFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
|
|
topFace.line(to: CGPoint(x: rect.minX, y: rect.maxY - rect.height * 0.18))
|
|
topFace.close()
|
|
NSColor(calibratedRed: 0.78, green: 0.94, blue: 0.63, alpha: 1).setFill()
|
|
topFace.fill()
|
|
|
|
let leftFace = NSBezierPath()
|
|
leftFace.move(to: CGPoint(x: rect.minX, y: rect.maxY - rect.height * 0.18))
|
|
leftFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
|
|
leftFace.line(to: CGPoint(x: rect.midX, y: rect.minY))
|
|
leftFace.line(to: CGPoint(x: rect.minX, y: rect.minY + rect.height * 0.18))
|
|
leftFace.close()
|
|
NSColor(calibratedRed: 0.34, green: 0.61, blue: 0.24, alpha: 1).setFill()
|
|
leftFace.fill()
|
|
|
|
let rightFace = NSBezierPath()
|
|
rightFace.move(to: CGPoint(x: rect.maxX, y: rect.maxY - rect.height * 0.18))
|
|
rightFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
|
|
rightFace.line(to: CGPoint(x: rect.midX, y: rect.minY))
|
|
rightFace.line(to: CGPoint(x: rect.maxX, y: rect.minY + rect.height * 0.18))
|
|
rightFace.close()
|
|
NSColor(calibratedRed: 0.14, green: 0.34, blue: 0.13, alpha: 1).setFill()
|
|
rightFace.fill()
|
|
|
|
NSColor.black.withAlphaComponent(0.18).setStroke()
|
|
[topFace, leftFace, rightFace].forEach {
|
|
$0.lineWidth = max(1, rect.width * 0.02)
|
|
$0.stroke()
|
|
}
|
|
}
|
|
|
|
func drawBorder(in rect: CGRect) {
|
|
let path = NSBezierPath(
|
|
roundedRect: rect.insetBy(dx: 1, dy: 1),
|
|
xRadius: rect.width * 0.20,
|
|
yRadius: rect.height * 0.20
|
|
)
|
|
NSColor.black.withAlphaComponent(0.10).setStroke()
|
|
path.lineWidth = max(1, rect.width * 0.01)
|
|
path.stroke()
|
|
}
|
|
|
|
func writePNG(image: NSImage, to url: URL) throws {
|
|
guard
|
|
let tiffData = image.tiffRepresentation,
|
|
let bitmap = NSBitmapImageRep(data: tiffData),
|
|
let pngData = bitmap.representation(using: .png, properties: [:])
|
|
else {
|
|
throw CocoaError(.fileWriteUnknown)
|
|
}
|
|
|
|
try pngData.write(to: url)
|
|
}
|