Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23e96615e | ||
|
|
0b3bf5e815 |
@@ -26,11 +26,10 @@ enum VehicleGraphicStyle: String, CaseIterable, Identifiable, Codable, Sendable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Für California liegt noch keine eigene Heckansicht vor, deshalb
|
|
||||||
/// vorerst die von Vanster.
|
|
||||||
var rearImageName: String {
|
var rearImageName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .vanster, .california: return "VansterRear"
|
case .vanster: return "VansterRear"
|
||||||
|
case .california: return "CaliforniaRear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,5 +38,22 @@ sips --resampleHeight 600 heckansicht.png --out /tmp/rear.png
|
|||||||
Jedes Fahrzeug-Grafikset (siehe `VehicleGraphicStyle`) hat sein eigenes
|
Jedes Fahrzeug-Grafikset (siehe `VehicleGraphicStyle`) hat sein eigenes
|
||||||
Bildpaar `<Name>Side`/`<Name>Rear`. Hat die Vorlage schon einen
|
Bildpaar `<Name>Side`/`<Name>Rear`. Hat die Vorlage schon einen
|
||||||
transparenten Hintergrund, geht die Quelldatei direkt ins Skript. Liegt sie
|
transparenten Hintergrund, geht die Quelldatei direkt ins Skript. Liegt sie
|
||||||
auf weißem Grund, muss der Rand vorher weg – sonst wird die ganze Fläche
|
auf weißem Grund, muss der Rand vorher weg – siehe `remove-white-background.swift`.
|
||||||
mitgefärbt.
|
|
||||||
|
## remove-white-background.swift
|
||||||
|
|
||||||
|
Vorstufe zu `make-vehicle-art.swift`, wenn die Vorlage auf weißem statt
|
||||||
|
transparentem Grund liegt (kein Alphakanal). Ohne diesen Schritt hielte
|
||||||
|
`make-vehicle-art` die ganze Fläche für Zeichnung und färbte den Hintergrund
|
||||||
|
mit ein.
|
||||||
|
|
||||||
|
Reines Weiß wird voll durchsichtig, alles andere bleibt stehen; ein weicher
|
||||||
|
Übergang statt einer harten Schwelle vermeidet gezackte Kanten an
|
||||||
|
kantengeglätteten Linien.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
swiftc -O -o /tmp/remove-white-background Tools/remove-white-background.swift
|
||||||
|
swiftc -O -o /tmp/make-vehicle-art Tools/make-vehicle-art.swift
|
||||||
|
/tmp/remove-white-background seitenansicht.png /tmp/seite-transparent.png
|
||||||
|
/tmp/make-vehicle-art /tmp/seite-transparent.png VanControl/Assets.xcassets/CaliforniaSide.imageset/CaliforniaSide.png
|
||||||
|
```
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -496,8 +496,10 @@
|
|||||||
AA0000000000000000000014 /* Debug */ = {
|
AA0000000000000000000014 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-App2-Testflight";
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-App1-Testflight";
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
@@ -529,8 +531,10 @@
|
|||||||
AA0000000000000000000015 /* Release */ = {
|
AA0000000000000000000015 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-App2-Testflight";
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-App1-Testflight";
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIcon-App1-Testflight-1024.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : { "author" : "xcode", "version" : 1 }
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.0 MiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIcon-App1-1024.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : { "author" : "xcode", "version" : 1 }
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIcon-App2-Testflight-1024.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : { "author" : "xcode", "version" : 1 }
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIcon-App2-1024.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : { "author" : "xcode", "version" : 1 }
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 326 KiB |
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "CaliforniaRear.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : { "author" : "xcode", "version" : 1 },
|
||||||
|
"properties" : { "template-rendering-intent" : "template" }
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 341 KiB After Width: | Height: | Size: 323 KiB |