libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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_