import Foundation /// Camera log transfer functions. Constants from vendor whitepapers: /// - ARRI LogC4 (ALEXA 35), ARRI LogC4 spec 2022 /// - RED Log3G10 v3, RED IPP2 whitepaper /// - Sony S-Log3, Sony technical summary public enum TransferFunction: String, Sendable, CaseIterable, Codable { case logC4 case log3G10 case sLog3 case linear /// Linear scene value -> encoded log signal (0..1 nominal). public func encode(_ x: Float) -> Float { switch self { case .linear: return x case .logC4: let c = LogC4.self if x < c.t { return (x - c.t) / c.s } return (log2(c.a * x + 64) - 6) / 14 * c.b + c.c case .log3G10: let c = Log3G10.self let y = x + c.c if y < 0 { return y * c.g } return c.a * log10(y * c.b + 1) case .sLog3: let c = SLog3.self if x >= c.linBreak { return (420 + log10((x + 0.01) / 0.19) * 261.5) / 1023 } return (x * (171.2102946929 - 95) / c.linBreak + 95) / 1023 } } /// Encoded log signal -> linear scene value. public func decode(_ y: Float) -> Float { switch self { case .linear: return y case .logC4: let c = LogC4.self if y < 0 { return y * c.s + c.t } return (exp2(14 * (y - c.c) / c.b + 6) - 64) / c.a case .log3G10: let c = Log3G10.self if y < 0 { return y / c.g - c.c } return (pow(10, y / c.a) - 1) / c.b - c.c case .sLog3: let c = SLog3.self if y >= 171.2102946929 / 1023 { return pow(10, (y * 1023 - 420) / 261.5) * 0.19 - 0.01 } return (y * 1023 - 95) * c.linBreak / (171.2102946929 - 95) } } // MARK: Constants private enum LogC4 { static let a: Float = (exp2(18) - 16) / 117.45 static let b: Float = (1023 - 95) / 1023 static let c: Float = 95.0 / 1023.0 static let s: Float = (7 * log(2.0 as Float) * exp2(7 - 14 * c / b)) / (a * b) static let t: Float = (exp2(14 * (-c / b) + 6) - 64) / a } private enum Log3G10 { static let a: Float = 0.224282 static let b: Float = 155.975327 static let c: Float = 0.01 static let g: Float = 15.1927 } private enum SLog3 { static let linBreak: Float = 0.01125000 } }