tone_mapping-inl.h (7008B)
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 #if defined(LIB_JXL_CMS_TONE_MAPPING_INL_H_) == defined(HWY_TARGET_TOGGLE) 7 #ifdef LIB_JXL_CMS_TONE_MAPPING_INL_H_ 8 #undef LIB_JXL_CMS_TONE_MAPPING_INL_H_ 9 #else 10 #define LIB_JXL_CMS_TONE_MAPPING_INL_H_ 11 #endif 12 13 #include <hwy/highway.h> 14 15 #include "lib/jxl/cms/tone_mapping.h" 16 #include "lib/jxl/cms/transfer_functions-inl.h" 17 18 HWY_BEFORE_NAMESPACE(); 19 namespace jxl { 20 namespace HWY_NAMESPACE { 21 namespace { 22 23 // These templates are not found via ADL. 24 using hwy::HWY_NAMESPACE::Clamp; 25 using hwy::HWY_NAMESPACE::Max; 26 using hwy::HWY_NAMESPACE::ZeroIfNegative; 27 28 template <typename D> 29 class Rec2408ToneMapper : Rec2408ToneMapperBase { 30 private: 31 using V = hwy::HWY_NAMESPACE::Vec<D>; 32 33 public: 34 using Rec2408ToneMapperBase::Rec2408ToneMapperBase; 35 36 void ToneMap(V* red, V* green, V* blue) const { 37 const V luminance = Mul(Set(df_, source_range_.second), 38 (MulAdd(Set(df_, red_Y_), *red, 39 MulAdd(Set(df_, green_Y_), *green, 40 Mul(Set(df_, blue_Y_), *blue))))); 41 const V pq_mastering_min = Set(df_, pq_mastering_min_); 42 const V inv_pq_mastering_range = Set(df_, inv_pq_mastering_range_); 43 const V normalized_pq = Min( 44 Set(df_, 1.f), 45 Mul(Sub(InvEOTF(luminance), pq_mastering_min), inv_pq_mastering_range)); 46 const V ks = Set(df_, ks_); 47 const V e2 = 48 IfThenElse(Lt(normalized_pq, ks), normalized_pq, P(normalized_pq)); 49 const V one_minus_e2 = Sub(Set(df_, 1), e2); 50 const V one_minus_e2_2 = Mul(one_minus_e2, one_minus_e2); 51 const V one_minus_e2_4 = Mul(one_minus_e2_2, one_minus_e2_2); 52 const V b = Set(df_, min_lum_); 53 const V e3 = MulAdd(b, one_minus_e2_4, e2); 54 const V pq_mastering_range = Set(df_, pq_mastering_range_); 55 const V e4 = MulAdd(e3, pq_mastering_range, pq_mastering_min); 56 const V new_luminance = 57 Min(Set(df_, target_range_.second), 58 ZeroIfNegative(tf_pq_.DisplayFromEncoded(df_, e4))); 59 const V min_luminance = Set(df_, 1e-6f); 60 const auto use_cap = Le(luminance, min_luminance); 61 const V ratio = Div(new_luminance, Max(luminance, min_luminance)); 62 const V cap = Mul(new_luminance, Set(df_, inv_target_peak_)); 63 const V normalizer = Set(df_, normalizer_); 64 const V multiplier = Mul(ratio, normalizer); 65 for (V* const val : {red, green, blue}) { 66 *val = IfThenElse(use_cap, cap, Mul(*val, multiplier)); 67 } 68 } 69 70 private: 71 V InvEOTF(const V luminance) const { 72 return tf_pq_.EncodedFromDisplay(df_, luminance); 73 } 74 V T(const V a) const { 75 const V ks = Set(df_, ks_); 76 const V inv_one_minus_ks = Set(df_, inv_one_minus_ks_); 77 return Mul(Sub(a, ks), inv_one_minus_ks); 78 } 79 V P(const V b) const { 80 const V t_b = T(b); 81 const V t_b_2 = Mul(t_b, t_b); 82 const V t_b_3 = Mul(t_b_2, t_b); 83 const V ks = Set(df_, ks_); 84 const V max_lum = Set(df_, max_lum_); 85 return MulAdd( 86 MulAdd(Set(df_, 2), t_b_3, MulAdd(Set(df_, -3), t_b_2, Set(df_, 1))), 87 ks, 88 MulAdd(Add(t_b_3, MulAdd(Set(df_, -2), t_b_2, t_b)), 89 Sub(Set(df_, 1), ks), 90 Mul(MulAdd(Set(df_, -2), t_b_3, Mul(Set(df_, 3), t_b_2)), 91 max_lum))); 92 } 93 94 D df_; 95 const TF_PQ tf_pq_ = TF_PQ(/*display_intensity_target=*/1.0); 96 }; 97 98 class HlgOOTF : HlgOOTF_Base { 99 public: 100 using HlgOOTF_Base::HlgOOTF_Base; 101 102 static HlgOOTF FromSceneLight(float display_luminance, 103 const float primaries_luminances[3]) { 104 return HlgOOTF(/*gamma=*/1.2f * 105 std::pow(1.111f, std::log2(display_luminance / 1000.f)), 106 primaries_luminances); 107 } 108 109 static HlgOOTF ToSceneLight(float display_luminance, 110 const float primaries_luminances[3]) { 111 return HlgOOTF( 112 /*gamma=*/(1 / 1.2f) * 113 std::pow(1.111f, -std::log2(display_luminance / 1000.f)), 114 primaries_luminances); 115 } 116 117 template <typename V> 118 void Apply(V* red, V* green, V* blue) const { 119 hwy::HWY_NAMESPACE::DFromV<V> df; 120 if (!apply_ootf_) return; 121 const V luminance = 122 MulAdd(Set(df, red_Y_), *red, 123 MulAdd(Set(df, green_Y_), *green, Mul(Set(df, blue_Y_), *blue))); 124 const V ratio = 125 Min(FastPowf(df, luminance, Set(df, exponent_)), Set(df, 1e9)); 126 *red = Mul(*red, ratio); 127 *green = Mul(*green, ratio); 128 *blue = Mul(*blue, ratio); 129 } 130 131 bool WarrantsGamutMapping() const { return apply_ootf_ && exponent_ < 0; } 132 }; 133 134 template <typename V> 135 void GamutMap(V* red, V* green, V* blue, const float primaries_luminances[3], 136 float preserve_saturation = 0.1f) { 137 hwy::HWY_NAMESPACE::DFromV<V> df; 138 const V luminance = 139 MulAdd(Set(df, primaries_luminances[0]), *red, 140 MulAdd(Set(df, primaries_luminances[1]), *green, 141 Mul(Set(df, primaries_luminances[2]), *blue))); 142 143 // Desaturate out-of-gamut pixels. This is done by mixing each pixel 144 // with just enough gray of the target luminance to make all 145 // components non-negative. 146 // - For saturation preservation, if a component is still larger than 147 // 1 then the pixel is normalized to have a maximum component of 1. 148 // That will reduce its luminance. 149 // - For luminance preservation, getting all components below 1 is 150 // done by mixing in yet more gray. That will desaturate it further. 151 const V zero = Zero(df); 152 const V one = Set(df, 1); 153 V gray_mix_saturation = zero; 154 V gray_mix_luminance = zero; 155 for (const V* ch : {red, green, blue}) { 156 const V& val = *ch; 157 const V val_minus_gray = Sub(val, luminance); 158 const V inv_val_minus_gray = 159 Div(one, IfThenElse(Eq(val_minus_gray, zero), one, val_minus_gray)); 160 const V val_over_val_minus_gray = Mul(val, inv_val_minus_gray); 161 gray_mix_saturation = 162 IfThenElse(Ge(val_minus_gray, zero), gray_mix_saturation, 163 Max(gray_mix_saturation, val_over_val_minus_gray)); 164 gray_mix_luminance = 165 Max(gray_mix_luminance, 166 IfThenElse(Le(val_minus_gray, zero), gray_mix_saturation, 167 Sub(val_over_val_minus_gray, inv_val_minus_gray))); 168 } 169 const V gray_mix = Clamp( 170 MulAdd(Set(df, preserve_saturation), 171 Sub(gray_mix_saturation, gray_mix_luminance), gray_mix_luminance), 172 zero, one); 173 for (V* const ch : {red, green, blue}) { 174 V& val = *ch; 175 val = MulAdd(gray_mix, Sub(luminance, val), val); 176 } 177 const V max_clr = Max(Max(one, *red), Max(*green, *blue)); 178 const V normalizer = Div(one, max_clr); 179 for (V* const ch : {red, green, blue}) { 180 V& val = *ch; 181 val = Mul(val, normalizer); 182 } 183 } 184 185 } // namespace 186 // NOLINTNEXTLINE(google-readability-namespace-comments) 187 } // namespace HWY_NAMESPACE 188 } // namespace jxl 189 HWY_AFTER_NAMESPACE(); 190 191 #endif // LIB_JXL_CMS_TONE_MAPPING_INL_H_