tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tone_mapping.h (7036B)


      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 #ifndef LIB_JXL_CMS_TONE_MAPPING_H_
      7 #define LIB_JXL_CMS_TONE_MAPPING_H_
      8 
      9 #include <algorithm>
     10 #include <cmath>
     11 #include <cstddef>
     12 #include <utility>
     13 
     14 #include "lib/jxl/base/common.h"
     15 #include "lib/jxl/base/compiler_specific.h"
     16 #include "lib/jxl/base/matrix_ops.h"
     17 #include "lib/jxl/cms/transfer_functions.h"
     18 
     19 namespace jxl {
     20 
     21 class Rec2408ToneMapperBase {
     22 public:
     23  explicit Rec2408ToneMapperBase(std::pair<float, float> source_range,
     24                                 std::pair<float, float> target_range,
     25                                 const Vector3& primaries_luminances)
     26      : source_range_(std::move(source_range)),
     27        target_range_(std::move(target_range)),
     28        red_Y_(primaries_luminances[0]),
     29        green_Y_(primaries_luminances[1]),
     30        blue_Y_(primaries_luminances[2]) {}
     31 
     32  // TODO(eustas): test me
     33  void ToneMap(Color& rgb) const {
     34    const float luminance =
     35        source_range_.second *
     36        (red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]);
     37    const float normalized_pq =
     38        std::min(1.f, (InvEOTF(luminance) - pq_mastering_min_) *
     39                          inv_pq_mastering_range_);
     40    const float e2 = (normalized_pq < ks_) ? normalized_pq : P(normalized_pq);
     41    const float one_minus_e2 = 1 - e2;
     42    const float one_minus_e2_2 = one_minus_e2 * one_minus_e2;
     43    const float one_minus_e2_4 = one_minus_e2_2 * one_minus_e2_2;
     44    const float e3 = min_lum_ * one_minus_e2_4 + e2;
     45    const float e4 = e3 * pq_mastering_range_ + pq_mastering_min_;
     46    const float d4 =
     47        TF_PQ_Base::DisplayFromEncoded(/*display_intensity_target=*/1.0, e4);
     48    const float new_luminance = Clamp1(d4, 0.f, target_range_.second);
     49    const float min_luminance = 1e-6f;
     50    const bool use_cap = (luminance <= min_luminance);
     51    const float ratio = new_luminance / std::max(luminance, min_luminance);
     52    const float cap = new_luminance * inv_target_peak_;
     53    const float multiplier = ratio * normalizer_;
     54    for (size_t idx : {0, 1, 2}) {
     55      rgb[idx] = use_cap ? cap : rgb[idx] * multiplier;
     56    }
     57  }
     58 
     59 protected:
     60  static float InvEOTF(const float luminance) {
     61    return TF_PQ_Base::EncodedFromDisplay(/*display_intensity_target=*/1.0,
     62                                          luminance);
     63  }
     64  float T(const float a) const { return (a - ks_) * inv_one_minus_ks_; }
     65  float P(const float b) const {
     66    const float t_b = T(b);
     67    const float t_b_2 = t_b * t_b;
     68    const float t_b_3 = t_b_2 * t_b;
     69    return (2 * t_b_3 - 3 * t_b_2 + 1) * ks_ +
     70           (t_b_3 - 2 * t_b_2 + t_b) * (1 - ks_) +
     71           (-2 * t_b_3 + 3 * t_b_2) * max_lum_;
     72  }
     73 
     74  const std::pair<float, float> source_range_;
     75  const std::pair<float, float> target_range_;
     76  const float red_Y_;
     77  const float green_Y_;
     78  const float blue_Y_;
     79 
     80  const float pq_mastering_min_ = InvEOTF(source_range_.first);
     81  const float pq_mastering_max_ = InvEOTF(source_range_.second);
     82  const float pq_mastering_range_ = pq_mastering_max_ - pq_mastering_min_;
     83  const float inv_pq_mastering_range_ = 1.0f / pq_mastering_range_;
     84  // TODO(eustas): divide instead of inverse-multiply?
     85  const float min_lum_ = (InvEOTF(target_range_.first) - pq_mastering_min_) *
     86                         inv_pq_mastering_range_;
     87  // TODO(eustas): divide instead of inverse-multiply?
     88  const float max_lum_ = (InvEOTF(target_range_.second) - pq_mastering_min_) *
     89                         inv_pq_mastering_range_;
     90  const float ks_ = 1.5f * max_lum_ - 0.5f;
     91 
     92  const float inv_one_minus_ks_ = 1.0f / std::max(1e-6f, 1.0f - ks_);
     93 
     94  const float normalizer_ = source_range_.second / target_range_.second;
     95  const float inv_target_peak_ = 1.f / target_range_.second;
     96 };
     97 
     98 class HlgOOTF_Base {
     99 public:
    100  explicit HlgOOTF_Base(float source_luminance, float target_luminance,
    101                        const Vector3& primaries_luminances)
    102      : HlgOOTF_Base(/*gamma=*/std::pow(1.111f, std::log2(target_luminance /
    103                                                          source_luminance)),
    104                     primaries_luminances) {}
    105 
    106  // TODO(eustas): test me
    107  void Apply(Color& rgb) const {
    108    if (!apply_ootf_) return;
    109    const float luminance =
    110        red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2];
    111    const float ratio = std::min<float>(powf(luminance, exponent_), 1e9);
    112    rgb[0] *= ratio;
    113    rgb[1] *= ratio;
    114    rgb[2] *= ratio;
    115  }
    116 
    117 protected:
    118  explicit HlgOOTF_Base(float gamma, const Vector3& luminances)
    119      : exponent_(gamma - 1),
    120        red_Y_(luminances[0]),
    121        green_Y_(luminances[1]),
    122        blue_Y_(luminances[2]) {}
    123  const float exponent_;
    124  const bool apply_ootf_ = exponent_ < -0.01f || 0.01f < exponent_;
    125  const float red_Y_;
    126  const float green_Y_;
    127  const float blue_Y_;
    128 };
    129 
    130 static JXL_MAYBE_UNUSED void GamutMapScalar(Color& rgb,
    131                                            const Vector3& primaries_luminances,
    132                                            float preserve_saturation = 0.1f) {
    133  const float luminance = primaries_luminances[0] * rgb[0] +
    134                          primaries_luminances[1] * rgb[1] +
    135                          primaries_luminances[2] * rgb[2];
    136 
    137  // Desaturate out-of-gamut pixels. This is done by mixing each pixel
    138  // with just enough gray of the target luminance to make all
    139  // components non-negative.
    140  // - For saturation preservation, if a component is still larger than
    141  // 1 then the pixel is normalized to have a maximum component of 1.
    142  // That will reduce its luminance.
    143  // - For luminance preservation, getting all components below 1 is
    144  // done by mixing in yet more gray. That will desaturate it further.
    145  float gray_mix_saturation = 0.0f;
    146  float gray_mix_luminance = 0.0f;
    147  for (size_t idx : {0, 1, 2}) {
    148    const float& val = rgb[idx];
    149    const float val_minus_gray = val - luminance;
    150    const float inv_val_minus_gray =
    151        1.0f / ((val_minus_gray == 0.0f) ? 1.0f : val_minus_gray);
    152    const float val_over_val_minus_gray = val * inv_val_minus_gray;
    153    gray_mix_saturation =
    154        (val_minus_gray >= 0.0f)
    155            ? gray_mix_saturation
    156            : std::max(gray_mix_saturation, val_over_val_minus_gray);
    157    gray_mix_luminance =
    158        std::max(gray_mix_luminance,
    159                 (val_minus_gray <= 0.0f)
    160                     ? gray_mix_saturation
    161                     : (val_over_val_minus_gray - inv_val_minus_gray));
    162  }
    163  const float gray_mix =
    164      Clamp1((preserve_saturation * (gray_mix_saturation - gray_mix_luminance) +
    165              gray_mix_luminance),
    166             0.0f, 1.0f);
    167  for (size_t idx : {0, 1, 2}) {
    168    float& val = rgb[idx];
    169    val = gray_mix * (luminance - val) + val;
    170  }
    171  const float max_clr = std::max({1.0f, rgb[0], rgb[1], rgb[2]});
    172  const float normalizer = 1.0f / max_clr;
    173  for (size_t idx : {0, 1, 2}) {
    174    rgb[idx] *= normalizer;
    175  }
    176 }
    177 
    178 }  // namespace jxl
    179 
    180 #endif  // LIB_JXL_CMS_TONE_MAPPING_H_