1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
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_
10 #define LIB_JXL_CMS_TONE_MAPPING_INL_H_
13 #include <hwy/highway.h>
15 #include "lib/jxl/cms/tone_mapping.h"
16 #include "lib/jxl/cms/transfer_functions-inl.h"
18 HWY_BEFORE_NAMESPACE();
20 namespace HWY_NAMESPACE {
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;
29 class Rec2408ToneMapper : Rec2408ToneMapperBase {
31 using V = hwy::HWY_NAMESPACE::Vec<D>;
34 using Rec2408ToneMapperBase::Rec2408ToneMapperBase;
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(
45 Mul(Sub(InvEOTF(luminance), pq_mastering_min), inv_pq_mastering_range));
46 const V ks = Set(df_, ks_);
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));
71 V InvEOTF(const V luminance) const {
72 return tf_pq_.EncodedFromDisplay(df_, luminance);
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);
79 V P(const V b) const {
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_);
86 MulAdd(Set(df_, 2), t_b_3, MulAdd(Set(df_, -3), t_b_2, Set(df_, 1))),
88 MulAdd(Add(t_b_3, MulAdd(Set(df_, -2), t_b_2, t_b)),
90 Mul(MulAdd(Set(df_, -2), t_b_3, Mul(Set(df_, 3), t_b_2)),
95 const TF_PQ tf_pq_ = TF_PQ(/*display_intensity_target=*/1.0);
98 class HlgOOTF : HlgOOTF_Base {
100 using HlgOOTF_Base::HlgOOTF_Base;
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);
109 static HlgOOTF ToSceneLight(float display_luminance,
110 const float primaries_luminances[3]) {
112 /*gamma=*/(1 / 1.2f) *
113 std::pow(1.111f, -std::log2(display_luminance / 1000.f)),
114 primaries_luminances);
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;
122 MulAdd(Set(df, red_Y_), *red,
123 MulAdd(Set(df, green_Y_), *green, Mul(Set(df, blue_Y_), *blue)));
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);
131 bool WarrantsGamutMapping() const { return apply_ootf_ && exponent_ < 0; }
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;
139 MulAdd(Set(df, primaries_luminances[0]), *red,
140 MulAdd(Set(df, primaries_luminances[1]), *green,
141 Mul(Set(df, primaries_luminances[2]), *blue)));
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}) {
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));
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)));
169 const V gray_mix = Clamp(
170 MulAdd(Set(df, preserve_saturation),
171 Sub(gray_mix_saturation, gray_mix_luminance), gray_mix_luminance),
173 for (V* const ch : {red, green, blue}) {
175 val = MulAdd(gray_mix, Sub(luminance, val), val);
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}) {
181 val = Mul(val, normalizer);
186 // NOLINTNEXTLINE(google-readability-namespace-comments)
187 } // namespace HWY_NAMESPACE
189 HWY_AFTER_NAMESPACE();
191 #endif // LIB_JXL_CMS_TONE_MAPPING_INL_H_