Neue California-Fahrzeuggrafik einbauen, eigene Heckansicht ergänzt

Seiten- und Heckansicht kamen als weiß gefüllte Vorlage ohne Alphakanal
(Tools/California Side.png, Tools/California Back.png). Dafür ein neues
Werkzeug ergänzt, das den weißen Grund in echte Transparenz übersetzt
(mit weichem statt hartem Schwellwert, um Kanten nicht auszufransen),
danach wie gewohnt durch make-vehicle-art.swift geschickt.

California hatte bisher keine eigene Heckansicht und griff ersatzweise
auf die von Vanster zurück – jetzt eigenes CaliforniaRear-Imageset,
VehicleGraphicStyle.rearImageName entsprechend angepasst. Seiten- und
Heckbild sind quadratisch (1254×1254), passend zu den bestehenden
Seitenverhältnissen (sideAspect/rearAspect = 1) – keine Anpassung dort
nötig.

Build und Tests grün.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
fototeddy
2026-09-07 21:31:42 +02:00
co-authored by Claude Sonnet 5
parent 8d58ea6e49
commit 0b3bf5e815
6 changed files with 93 additions and 5 deletions
+62
View File
@@ -0,0 +1,62 @@
import AppKit
import CoreGraphics
import Foundation
// Vorlagen kommen manchmal auf weißem statt transparentem Grund. Ohne
// Alphakanal hielte `make-vehicle-art.swift` die ganze Fläche für Zeichnung
// und färbte den Hintergrund mit ein. Dieses Werkzeug macht daraus einen
// echten Alphakanal: reines Weiß wird voll durchsichtig, alles andere bleibt
// stehen. Ein weicher Übergang statt einer harten Schwelle verhindert
// gezackte Kanten an den (leicht kantengeglätteten) Linien.
let arguments = CommandLine.arguments
guard arguments.count >= 3,
let source = NSImage(contentsOfFile: arguments[1]),
let image = source.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
print("Quelle nicht lesbar"); exit(1)
}
let width = image.width, height = image.height
let bytesPerRow = width * 4
var pixels = [UInt8](repeating: 0, count: bytesPerRow * height)
guard let context = CGContext(data: &pixels, width: width, height: height,
bitsPerComponent: 8, bytesPerRow: bytesPerRow,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
print("Puffer nicht anlegbar"); exit(1)
}
context.draw(image, in: CGRect(x: 0, y: 0, width: width, height: height))
/// Unterhalb davon gilt ein Pixel als reiner Hintergrund, oberhalb als
/// sicher Zeichnung. Dazwischen wird linear zwischen 0 und voller Deckkraft
/// überblendet, damit Kanten nicht ausgefranst wirken.
let whiteFloor = 230.0
let whiteCeiling = 250.0
for index in stride(from: 0, to: pixels.count, by: 4) {
// Quelle ist opak (kein Alphakanal), also stehen hier die Rohfarben.
let r = Double(pixels[index])
let g = Double(pixels[index + 1])
let b = Double(pixels[index + 2])
let whiteness = min(r, g, b)
let t = min(1, max(0, (whiteness - whiteFloor) / (whiteCeiling - whiteFloor)))
let alpha = 1 - t
guard alpha > 0.004 else {
pixels[index] = 0; pixels[index + 1] = 0; pixels[index + 2] = 0; pixels[index + 3] = 0
continue
}
// Premultipliziert ablegen.
pixels[index] = UInt8(r * alpha)
pixels[index + 1] = UInt8(g * alpha)
pixels[index + 2] = UInt8(b * alpha)
pixels[index + 3] = UInt8(alpha * 255)
}
guard let result = context.makeImage() else { print("Bild leer"); exit(1) }
let rep = NSBitmapImageRep(cgImage: result)
guard let png = rep.representation(using: .png, properties: [:]) else {
print("PNG nicht erzeugbar"); exit(1)
}
try png.write(to: URL(fileURLWithPath: arguments[2]))
print("\(arguments[2]): fertig")