transfer_functions.h (4338B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 // Transfer functions for color encodings. 7 8 #ifndef LIB_JXL_CMS_TRANSFER_FUNCTIONS_H_ 9 #define LIB_JXL_CMS_TRANSFER_FUNCTIONS_H_ 10 11 #include <algorithm> 12 #include <cmath> 13 14 #include "lib/jxl/base/status.h" 15 16 namespace jxl { 17 18 // Definitions for BT.2100-2 transfer functions (used inside/outside SIMD): 19 // "display" is linear light (nits) normalized to [0, 1]. 20 // "encoded" is a nonlinear encoding (e.g. PQ) in [0, 1]. 21 // "scene" is a linear function of photon counts, normalized to [0, 1]. 22 23 // Despite the stated ranges, we need unbounded transfer functions: see 24 // http://www.littlecms.com/CIC18_UnboundedCMM.pdf. Inputs can be negative or 25 // above 1 due to chromatic adaptation. To avoid severe round-trip errors caused 26 // by clamping, we mirror negative inputs via copysign (f(-x) = -f(x), see 27 // https://developer.apple.com/documentation/coregraphics/cgcolorspace/1644735-extendedsrgb) 28 // and extend the function domains above 1. 29 30 // Hybrid Log-Gamma. 31 class TF_HLG_Base { 32 public: 33 // EOTF. e = encoded. 34 static double DisplayFromEncoded(const double e) { return OOTF(InvOETF(e)); } 35 36 // Inverse EOTF. d = display. 37 static double EncodedFromDisplay(const double d) { return OETF(InvOOTF(d)); } 38 39 private: 40 // OETF (defines the HLG approach). s = scene, returns encoded. 41 static double OETF(double s) { 42 if (s == 0.0) return 0.0; 43 const double original_sign = s; 44 s = std::abs(s); 45 46 if (s <= kDiv12) return copysignf(std::sqrt(3.0 * s), original_sign); 47 48 const double e = kA * std::log(12 * s - kB) + kC; 49 JXL_ASSERT(e > 0.0); 50 return copysignf(e, original_sign); 51 } 52 53 // e = encoded, returns scene. 54 static double InvOETF(double e) { 55 if (e == 0.0) return 0.0; 56 const double original_sign = e; 57 e = std::abs(e); 58 59 if (e <= 0.5) return copysignf(e * e * (1.0 / 3), original_sign); 60 61 const double s = (std::exp((e - kC) * kRA) + kB) * kDiv12; 62 JXL_ASSERT(s >= 0); 63 return copysignf(s, original_sign); 64 } 65 66 // s = scene, returns display. 67 static double OOTF(const double s) { 68 // The actual (red channel) OOTF is RD = alpha * YS^(gamma-1) * RS, where 69 // YS = 0.2627 * RS + 0.6780 * GS + 0.0593 * BS. Let alpha = 1 so we return 70 // "display" (normalized [0, 1]) instead of nits. Our transfer function 71 // interface does not allow a dependency on YS. Fortunately, the system 72 // gamma at 334 nits is 1.0, so this reduces to RD = RS. 73 return s; 74 } 75 76 // d = display, returns scene. 77 static double InvOOTF(const double d) { 78 return d; // see OOTF(). 79 } 80 81 protected: 82 static constexpr double kA = 0.17883277; 83 static constexpr double kRA = 1.0 / kA; 84 static constexpr double kB = 1 - 4 * kA; 85 static constexpr double kC = 0.5599107295; 86 static constexpr double kDiv12 = 1.0 / 12; 87 }; 88 89 // Perceptual Quantization 90 class TF_PQ_Base { 91 public: 92 static double DisplayFromEncoded(float display_intensity_target, double e) { 93 if (e == 0.0) return 0.0; 94 const double original_sign = e; 95 e = std::abs(e); 96 97 const double xp = std::pow(e, 1.0 / kM2); 98 const double num = std::max(xp - kC1, 0.0); 99 const double den = kC2 - kC3 * xp; 100 JXL_DASSERT(den != 0.0); 101 const double d = std::pow(num / den, 1.0 / kM1); 102 JXL_DASSERT(d >= 0.0); // Equal for e ~= 1E-9 103 return copysignf(d * (10000.0f / display_intensity_target), original_sign); 104 } 105 106 // Inverse EOTF. d = display. 107 static double EncodedFromDisplay(float display_intensity_target, double d) { 108 if (d == 0.0) return 0.0; 109 const double original_sign = d; 110 d = std::abs(d); 111 112 const double xp = 113 std::pow(d * (display_intensity_target * (1.0f / 10000.0f)), kM1); 114 const double num = kC1 + xp * kC2; 115 const double den = 1.0 + xp * kC3; 116 const double e = std::pow(num / den, kM2); 117 JXL_DASSERT(e > 0.0); 118 return copysignf(e, original_sign); 119 } 120 121 protected: 122 static constexpr double kM1 = 2610.0 / 16384; 123 static constexpr double kM2 = (2523.0 / 4096) * 128; 124 static constexpr double kC1 = 3424.0 / 4096; 125 static constexpr double kC2 = (2413.0 / 4096) * 32; 126 static constexpr double kC3 = (2392.0 / 4096) * 32; 127 }; 128 129 } // namespace jxl 130 131 #endif // LIB_JXL_CMS_TRANSFER_FUNCTIONS_H_