import Foundation /// Wie Untertitel aussehen sollen. /// /// **Warum hier und nicht im App-Ziel.** Weil es prüfbar sein muss: die /// Übersetzung nach AVFoundation ist eine Tabelle aus Schlüsseln, bei der ein /// Tippfehler lautlos dazu führt, dass **gar nichts** gesetzt wird. Ein Test /// findet das, ein Blick auf den Bildschirm nicht — dort sieht „keine Regel" /// genauso aus wie „Regel ohne Wirkung". public struct SubtitleStyle: Sendable, Equatable { /// Eine Farbe, wie CoreMedia sie versteht: vier Anteile von 0 bis 1. public struct RGBA: Sendable, Equatable { public let red: Double public let green: Double public let blue: Double public let alpha: Double public init(red: Double, green: Double, blue: Double, alpha: Double = 1) { self.red = red self.green = green self.blue = blue self.alpha = alpha } static let white = RGBA(red: 1, green: 1, blue: 1) static let black = RGBA(red: 0, green: 0, blue: 0) static let yellow = RGBA(red: 1, green: 0.85, blue: 0.2) static let clear = RGBA(red: 0, green: 0, blue: 0, alpha: 0) /// Nicht ganz deckend: ein voll schwarzer Kasten wirkt wie ein Loch /// im Bild. static let box = RGBA(red: 0, green: 0, blue: 0, alpha: 0.75) } /// Die Grösse, **relativ** zur Vorgabe des Players. /// /// Ein Prozentwert, keine Punktgrösse: hundert heisst unverändert. So /// bleibt die Einstellung richtig, egal wie gross das Bild ist. public enum Size: Int, CaseIterable, Identifiable, Sendable, Codable { case small = 75 case normal = 100 case large = 130 case huge = 175 public var id: Int { rawValue } } /// Schrift und Kasten als **Paar**, nicht als zwei Regler. /// /// Unabhängige Achsen liessen unlesbare Mischungen zu — Schwarz auf /// durchsichtig über einer Nachtszene ist eine gültige Einstellung und /// eine wertlose. Diese fünf decken ab, was man tatsächlich will. public enum Palette: String, CaseIterable, Identifiable, Sendable, Codable { case whiteOnClear case whiteOnBlack case blackOnWhite case yellowOnClear case yellowOnBlack public var id: String { rawValue } } public var size: Size public var palette: Palette public init(size: Size = .normal, palette: Palette = .whiteOnClear) { self.size = size self.palette = palette } public var foreground: RGBA { switch palette { case .whiteOnClear, .whiteOnBlack: .white case .blackOnWhite: .black case .yellowOnClear, .yellowOnBlack: .yellow } } public var background: RGBA { switch palette { case .whiteOnClear, .yellowOnClear: .clear case .whiteOnBlack, .yellowOnBlack: .box case .blackOnWhite: RGBA(red: 1, green: 1, blue: 1, alpha: 0.85) } } /// Ob ein Schlagschatten nötig ist. /// /// Ohne Kasten steht die Schrift unmittelbar im Bild, und Weiss auf einer /// hellen Szene ist dort unlesbar. Mit Kasten wäre der Schatten nur /// Unruhe. public var wantsEdgeShadow: Bool { switch palette { case .whiteOnClear, .yellowOnClear: true case .whiteOnBlack, .blackOnWhite, .yellowOnBlack: false } } }