-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTextureGenerator.swift
More file actions
105 lines (83 loc) · 3.46 KB
/
TextureGenerator.swift
File metadata and controls
105 lines (83 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//
// TextureGenerator.swift
// asciiRender
//
// Created by Carl Villads Priisholm on 03/01/2026.
//
import AppKit
import CoreText
class TextureGenerator {
static var sortedCharCount: Float = 0.0
static func generateSortedAtlas() -> NSImage? {
// Standard printable ASCII characters
let scalars = (32...126).compactMap { UnicodeScalar($0) }
var chars = scalars.map { String($0) }
let font = NSFont(name: "Menlo-Bold", size: 64) ?? NSFont.monospacedSystemFont(ofSize: 64, weight: .bold)
chars.sort { density(of: $0, font: font) < density(of: $1, font: font) }
Self.sortedCharCount = Float(chars.count)
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: NSColor.white
]
let sample = "W" as NSString
let sampleSize = sample.size(withAttributes: attributes)
let charWidth = ceil(sampleSize.width)
let charHeight = ceil(sampleSize.height)
let imageSize = NSSize(width: charWidth * CGFloat(chars.count), height: charHeight)
let image = NSImage(size: imageSize)
image.lockFocus()
NSColor.clear.set()
NSRect(origin: .zero, size: imageSize).fill()
let baselineOffset: CGFloat = 0
for (index, char) in chars.enumerated() {
let str = char as NSString
let xPos = CGFloat(index) * charWidth
str.draw(at: NSPoint(x: xPos, y: baselineOffset), withAttributes: attributes)
}
image.unlockFocus()
if let darkest = chars.first, let brightest = chars.last {
print("Generated text atlas with \(chars.count) characters")
print("Darkest: '\(darkest)'. Brightest: '\(brightest)'")
print("Characters: \(chars)")
}
return image
}
private static func density(of char: String, font: NSFont) -> Double {
let size = CGSize(width: 64, height: 64)
let img = NSImage(size: size)
img.lockFocus()
NSColor.black.set()
NSRect(origin: .zero, size: size).fill()
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: NSColor.white
]
(char as NSString).draw(in: NSRect(origin: .zero, size: size), withAttributes: attributes)
img.unlockFocus()
guard let cgRef = img.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return 0 }
let width = cgRef.width
let height = cgRef.height
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
var rawData = [UInt8](repeating: 0, count: height * bytesPerRow)
guard let context = CGContext(
data: &rawData,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue
) else { return 0 }
context.draw(cgRef, in: CGRect(x: 0, y: 0, width: width, height: height))
var brightnessSum: Double = 0
for i in stride(from: 0, to: rawData.count, by: 4) {
let r = Double(rawData[i])
let g = Double(rawData[i + 1])
let b = Double(rawData[i + 2])
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
brightnessSum += luminance
}
return brightnessSum
}
}