xref: /aosp_15_r20/external/libultrahdr/lib/include/ultrahdr/gainmapmath.h (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ULTRAHDR_GAINMAPMATH_H
18 #define ULTRAHDR_GAINMAPMATH_H
19 
20 #include <array>
21 #include <cmath>
22 #include <cstring>
23 #include <functional>
24 
25 #include "ultrahdr_api.h"
26 #include "ultrahdr/ultrahdrcommon.h"
27 #include "ultrahdr/jpegr.h"
28 
29 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
30 #include <arm_neon.h>
31 #endif
32 
33 #define USE_SRGB_INVOETF_LUT 1
34 #define USE_HLG_OETF_LUT 1
35 #define USE_PQ_OETF_LUT 1
36 #define USE_HLG_INVOETF_LUT 1
37 #define USE_PQ_INVOETF_LUT 1
38 #define USE_APPLY_GAIN_LUT 1
39 
40 #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
41 
42 namespace ultrahdr {
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 // Framework
46 
47 // nominal {SDR, HLG, PQ} peak display luminance
48 // This aligns with the suggested default reference diffuse white from ISO/TS 22028-5
49 // sdr white
50 static const float kSdrWhiteNits = 203.0f;
51 // hlg peak white. 75% of hlg peak white maps to reference diffuse white
52 static const float kHlgMaxNits = 1000.0f;
53 // pq peak white. 58% of pq peak white maps to reference diffuse white
54 static const float kPqMaxNits = 10000.0f;
55 
56 float getReferenceDisplayPeakLuminanceInNits(uhdr_color_transfer_t transfer);
57 
58 // Image pixel descriptor
59 struct Color {
60   union {
61     struct {
62       float r;
63       float g;
64       float b;
65     };
66     struct {
67       float y;
68       float u;
69       float v;
70     };
71   };
72 };
73 
74 typedef Color (*ColorTransformFn)(Color);
75 typedef float (*LuminanceFn)(Color);
76 typedef Color (*SceneToDisplayLuminanceFn)(Color, LuminanceFn);
77 typedef Color (*GetPixelFn)(uhdr_raw_image_t*, size_t, size_t);
78 typedef Color (*SamplePixelFn)(uhdr_raw_image_t*, size_t, size_t, size_t);
79 typedef void (*PutPixelFn)(uhdr_raw_image_t*, size_t, size_t, Color&);
80 
81 inline Color operator+=(Color& lhs, const Color& rhs) {
82   lhs.r += rhs.r;
83   lhs.g += rhs.g;
84   lhs.b += rhs.b;
85   return lhs;
86 }
87 
88 inline Color operator-=(Color& lhs, const Color& rhs) {
89   lhs.r -= rhs.r;
90   lhs.g -= rhs.g;
91   lhs.b -= rhs.b;
92   return lhs;
93 }
94 
95 inline Color operator+(const Color& lhs, const Color& rhs) {
96   Color temp = lhs;
97   return temp += rhs;
98 }
99 
100 inline Color operator-(const Color& lhs, const Color& rhs) {
101   Color temp = lhs;
102   return temp -= rhs;
103 }
104 
105 inline Color operator+=(Color& lhs, const float rhs) {
106   lhs.r += rhs;
107   lhs.g += rhs;
108   lhs.b += rhs;
109   return lhs;
110 }
111 
112 inline Color operator-=(Color& lhs, const float rhs) {
113   lhs.r -= rhs;
114   lhs.g -= rhs;
115   lhs.b -= rhs;
116   return lhs;
117 }
118 
119 inline Color operator*=(Color& lhs, const float rhs) {
120   lhs.r *= rhs;
121   lhs.g *= rhs;
122   lhs.b *= rhs;
123   return lhs;
124 }
125 
126 inline Color operator/=(Color& lhs, const float rhs) {
127   lhs.r /= rhs;
128   lhs.g /= rhs;
129   lhs.b /= rhs;
130   return lhs;
131 }
132 
133 inline Color operator+(const Color& lhs, const float rhs) {
134   Color temp = lhs;
135   return temp += rhs;
136 }
137 
138 inline Color operator-(const Color& lhs, const float rhs) {
139   Color temp = lhs;
140   return temp -= rhs;
141 }
142 
143 inline Color operator*(const Color& lhs, const float rhs) {
144   Color temp = lhs;
145   return temp *= rhs;
146 }
147 
148 inline Color operator/(const Color& lhs, const float rhs) {
149   Color temp = lhs;
150   return temp /= rhs;
151 }
152 
153 ////////////////////////////////////////////////////////////////////////////////
154 // Float to Half and Half to Float conversions
155 union FloatUIntUnion {
156   uint32_t mUInt;
157   float mFloat;
158 };
159 
160 // FIXME: The shift operations in this function are causing UBSAN (Undefined-shift) errors
161 // Precisely,
162 // runtime error: left shift of negative value -112
163 // runtime error : shift exponent 125 is too large for 32 - bit type 'uint32_t'(aka 'unsigned int')
164 // These need to be addressed. Until then, disable ubsan analysis for this function
165 UHDR_NO_SANITIZE_UNDEFINED
floatToHalf(float f)166 inline uint16_t floatToHalf(float f) {
167   FloatUIntUnion floatUnion;
168   floatUnion.mFloat = f;
169   // round-to-nearest-even: add last bit after truncated mantissa
170   const uint32_t b = floatUnion.mUInt + 0x00001000;
171 
172   const int32_t e = (b & 0x7F800000) >> 23;  // exponent
173   const uint32_t m = b & 0x007FFFFF;         // mantissa
174 
175   // sign : normalized : denormalized : saturate
176   return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) |
177          ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
178          (e > 143) * 0x7FFF;
179 }
180 
181 // Taken from frameworks/base/libs/hwui/jni/android_graphics_ColorSpace.cpp
182 
183 #if defined(__ANDROID__)  // __fp16 is not defined on non-Android builds
halfToFloat(uint16_t bits)184 inline float halfToFloat(uint16_t bits) {
185   __fp16 h;
186   memcpy(&h, &bits, 2);
187   return (float)h;
188 }
189 #else
190 // This is Skia's implementation of SkHalfToFloat, which is
191 // based on Fabien Giesen's half_to_float_fast2()
192 // see https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
halfMantissa(uint16_t h)193 inline uint16_t halfMantissa(uint16_t h) { return h & 0x03ff; }
194 
halfExponent(uint16_t h)195 inline uint16_t halfExponent(uint16_t h) { return (h >> 10) & 0x001f; }
196 
halfSign(uint16_t h)197 inline uint16_t halfSign(uint16_t h) { return h >> 15; }
198 
halfToFloat(uint16_t bits)199 inline float halfToFloat(uint16_t bits) {
200   static const FloatUIntUnion magic = {126 << 23};
201   FloatUIntUnion o;
202 
203   if (halfExponent(bits) == 0) {
204     // Zero / Denormal
205     o.mUInt = magic.mUInt + halfMantissa(bits);
206     o.mFloat -= magic.mFloat;
207   } else {
208     // Set mantissa
209     o.mUInt = halfMantissa(bits) << 13;
210     // Set exponent
211     if (halfExponent(bits) == 0x1f) {
212       // Inf/NaN
213       o.mUInt |= (255 << 23);
214     } else {
215       o.mUInt |= ((127 - 15 + halfExponent(bits)) << 23);
216     }
217   }
218 
219   // Set sign
220   o.mUInt |= (halfSign(bits) << 31);
221   return o.mFloat;
222 }
223 #endif  // defined(__ANDROID__)
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 // Use Shepard's method for inverse distance weighting. For more information:
227 // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
228 struct ShepardsIDW {
ShepardsIDWShepardsIDW229   ShepardsIDW(int mapScaleFactor) : mMapScaleFactor{mapScaleFactor} {
230     const int size = mMapScaleFactor * mMapScaleFactor * 4;
231     mWeights = new float[size];
232     mWeightsNR = new float[size];
233     mWeightsNB = new float[size];
234     mWeightsC = new float[size];
235     fillShepardsIDW(mWeights, 1, 1);
236     fillShepardsIDW(mWeightsNR, 0, 1);
237     fillShepardsIDW(mWeightsNB, 1, 0);
238     fillShepardsIDW(mWeightsC, 0, 0);
239   }
240 
~ShepardsIDWShepardsIDW241   ~ShepardsIDW() {
242     delete[] mWeights;
243     delete[] mWeightsNR;
244     delete[] mWeightsNB;
245     delete[] mWeightsC;
246   }
247 
248   int mMapScaleFactor;
249   // curr, right, bottom, bottom-right are used during interpolation. hence table weight size is 4.
250   float* mWeights;    // default
251   float* mWeightsNR;  // no right
252   float* mWeightsNB;  // no bottom
253   float* mWeightsC;   // no right & bottom
254 
255   float euclideanDistance(float x1, float x2, float y1, float y2);
256   void fillShepardsIDW(float* weights, int incR, int incB);
257 };
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 // sRGB transformations.
261 // for all functions range in and out [0.0, 1.0]
262 
263 // sRGB luminance
264 float srgbLuminance(Color e);
265 
266 // sRGB rgb <-> yuv  conversion
267 Color srgbRgbToYuv(Color e_gamma);
268 Color srgbYuvToRgb(Color e_gamma);
269 
270 // sRGB eotf
271 float srgbInvOetf(float e_gamma);
272 Color srgbInvOetf(Color e_gamma);
273 float srgbInvOetfLUT(float e_gamma);
274 Color srgbInvOetfLUT(Color e_gamma);
275 
276 // sRGB oetf
277 float srgbOetf(float e);
278 Color srgbOetf(Color e);
279 
280 constexpr int32_t kSrgbInvOETFPrecision = 10;
281 constexpr int32_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision;
282 
283 ////////////////////////////////////////////////////////////////////////////////
284 // Display-P3 transformations
285 // for all functions range in and out [0.0, 1.0]
286 
287 // DispP3 luminance
288 float p3Luminance(Color e);
289 
290 // DispP3 rgb <-> yuv  conversion
291 Color p3RgbToYuv(Color e_gamma);
292 Color p3YuvToRgb(Color e_gamma);
293 
294 ////////////////////////////////////////////////////////////////////////////////
295 // BT.2100 transformations
296 // for all functions range in and out [0.0, 1.0]
297 
298 // bt2100 luminance
299 float bt2100Luminance(Color e);
300 
301 // bt2100 rgb <-> yuv  conversion
302 Color bt2100RgbToYuv(Color e_gamma);
303 Color bt2100YuvToRgb(Color e_gamma);
304 
305 // hlg oetf (normalized)
306 float hlgOetf(float e);
307 Color hlgOetf(Color e);
308 float hlgOetfLUT(float e);
309 Color hlgOetfLUT(Color e);
310 
311 constexpr int32_t kHlgOETFPrecision = 16;
312 constexpr int32_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
313 
314 // hlg inverse oetf (normalized)
315 float hlgInvOetf(float e_gamma);
316 Color hlgInvOetf(Color e_gamma);
317 float hlgInvOetfLUT(float e_gamma);
318 Color hlgInvOetfLUT(Color e_gamma);
319 
320 constexpr int32_t kHlgInvOETFPrecision = 12;
321 constexpr int32_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
322 
323 // hlg ootf (normalized)
324 Color hlgOotf(Color e, LuminanceFn luminance);
325 Color hlgOotfApprox(Color e, [[maybe_unused]] LuminanceFn luminance);
identityOotf(Color e,LuminanceFn)326 inline Color identityOotf(Color e, [[maybe_unused]] LuminanceFn) { return e; }
327 
328 // hlg inverse ootf (normalized)
329 Color hlgInverseOotf(Color e, LuminanceFn luminance);
330 Color hlgInverseOotfApprox(Color e);
331 
332 // pq oetf
333 float pqOetf(float e);
334 Color pqOetf(Color e);
335 float pqOetfLUT(float e);
336 Color pqOetfLUT(Color e);
337 
338 constexpr int32_t kPqOETFPrecision = 16;
339 constexpr int32_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
340 
341 // pq inverse oetf
342 float pqInvOetf(float e_gamma);
343 Color pqInvOetf(Color e_gamma);
344 float pqInvOetfLUT(float e_gamma);
345 Color pqInvOetfLUT(Color e_gamma);
346 
347 constexpr int32_t kPqInvOETFPrecision = 12;
348 constexpr int32_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
349 
350 // util class to prepare look up tables for oetf/eotf functions
351 class LookUpTable {
352  public:
LookUpTable(size_t numEntries,std::function<float (float)> computeFunc)353   LookUpTable(size_t numEntries, std::function<float(float)> computeFunc) {
354     for (size_t idx = 0; idx < numEntries; idx++) {
355       float value = static_cast<float>(idx) / static_cast<float>(numEntries - 1);
356       table.push_back(computeFunc(value));
357     }
358   }
getTable()359   const std::vector<float>& getTable() const { return table; }
360 
361  private:
362   std::vector<float> table;
363 };
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 // Color access functions
367 
368 // Get pixel from the image at the provided location.
369 Color getYuv444Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
370 Color getYuv422Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
371 Color getYuv420Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
372 Color getYuv400Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
373 Color getP010Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
374 Color getYuv444Pixel10bit(uhdr_raw_image_t* image, size_t x, size_t y);
375 Color getRgb888Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
376 Color getRgba8888Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
377 Color getRgba1010102Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
378 Color getRgbaF16Pixel(uhdr_raw_image_t* image, size_t x, size_t y);
379 
380 // Sample the image at the provided location, with a weighting based on nearby pixels and the map
381 // scale factor.
382 Color sampleYuv444(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y);
383 Color sampleYuv422(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y);
384 Color sampleYuv420(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y);
385 Color sampleP010(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y);
386 Color sampleYuv44410bit(uhdr_raw_image_t* image, size_t map_scale_factor, size_t x, size_t y);
387 Color sampleRgba8888(uhdr_raw_image_t* image, size_t map_scale_factor, size_t x, size_t y);
388 Color sampleRgba1010102(uhdr_raw_image_t* image, size_t map_scale_factor, size_t x, size_t y);
389 Color sampleRgbaF16(uhdr_raw_image_t* image, size_t map_scale_factor, size_t x, size_t y);
390 
391 // Put pixel in the image at the provided location.
392 void putRgba8888Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel);
393 void putRgb888Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel);
394 void putYuv400Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel);
395 void putYuv444Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel);
396 
397 ////////////////////////////////////////////////////////////////////////////////
398 // Color space conversions
399 
400 // color gamut conversion (rgb) functions
identityConversion(Color e)401 inline Color identityConversion(Color e) { return e; }
402 Color bt709ToP3(Color e);
403 Color bt709ToBt2100(Color e);
404 Color p3ToBt709(Color e);
405 Color p3ToBt2100(Color e);
406 Color bt2100ToBt709(Color e);
407 Color bt2100ToP3(Color e);
408 
409 // convert between yuv encodings
410 extern const std::array<float, 9> kYuvBt709ToBt601;
411 extern const std::array<float, 9> kYuvBt709ToBt2100;
412 extern const std::array<float, 9> kYuvBt601ToBt709;
413 extern const std::array<float, 9> kYuvBt601ToBt2100;
414 extern const std::array<float, 9> kYuvBt2100ToBt709;
415 extern const std::array<float, 9> kYuvBt2100ToBt601;
416 
417 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
418 
419 extern const int16_t kYuv709To601_coeffs_neon[8];
420 extern const int16_t kYuv709To2100_coeffs_neon[8];
421 extern const int16_t kYuv601To709_coeffs_neon[8];
422 extern const int16_t kYuv601To2100_coeffs_neon[8];
423 extern const int16_t kYuv2100To709_coeffs_neon[8];
424 extern const int16_t kYuv2100To601_coeffs_neon[8];
425 
426 /*
427  * The Y values are provided at half the width of U & V values to allow use of the widening
428  * arithmetic instructions.
429  */
430 int16x8x3_t yuvConversion_neon(uint8x8_t y, int16x8_t u, int16x8_t v, int16x8_t coeffs);
431 
432 void transformYuv420_neon(uhdr_raw_image_t* image, const int16_t* coeffs_ptr);
433 
434 void transformYuv444_neon(uhdr_raw_image_t* image, const int16_t* coeffs_ptr);
435 
436 uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t src_encoding,
437                                   uhdr_color_gamut_t dst_encoding);
438 #endif
439 
440 // Performs a color gamut transformation on an yuv image.
441 Color yuvColorGamutConversion(Color e_gamma, const std::array<float, 9>& coeffs);
442 void transformYuv420(uhdr_raw_image_t* image, const std::array<float, 9>& coeffs);
443 void transformYuv444(uhdr_raw_image_t* image, const std::array<float, 9>& coeffs);
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 // Gain map calculations
447 
448 constexpr int32_t kGainFactorPrecision = 10;
449 constexpr int32_t kGainFactorNumEntries = 1 << kGainFactorPrecision;
450 
451 struct GainLUT {
GainLUTGainLUT452   GainLUT(uhdr_gainmap_metadata_ext_t* metadata) {
453     this->mGammaInv = 1.0f / metadata->gamma;
454     for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) {
455       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
456       float logBoost = log2(metadata->min_content_boost) * (1.0f - value) +
457                        log2(metadata->max_content_boost) * value;
458       mGainTable[idx] = exp2(logBoost);
459     }
460   }
461 
GainLUTGainLUT462   GainLUT(uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight) {
463     this->mGammaInv = 1.0f / metadata->gamma;
464     for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) {
465       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
466       float logBoost = log2(metadata->min_content_boost) * (1.0f - value) +
467                        log2(metadata->max_content_boost) * value;
468       mGainTable[idx] = exp2(logBoost * gainmapWeight);
469     }
470   }
471 
~GainLUTGainLUT472   ~GainLUT() {}
473 
getGainFactorGainLUT474   float getGainFactor(float gain) {
475     if (mGammaInv != 1.0f) gain = pow(gain, mGammaInv);
476     int32_t idx = static_cast<int32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
477     // TODO() : Remove once conversion modules have appropriate clamping in place
478     idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
479     return mGainTable[idx];
480   }
481 
482  private:
483   float mGainTable[kGainFactorNumEntries];
484   float mGammaInv;
485 };
486 
487 /*
488  * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
489  * luminances in linear space and gainmap metadata fields.
490  */
491 uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata);
492 uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata,
493                    float log2MinContentBoost, float log2MaxContentBoost);
494 float computeGain(float sdr, float hdr);
495 uint8_t affineMapGain(float gainlog2, float mingainlog2, float maxgainlog2, float gamma);
496 
497 /*
498  * Calculates the linear luminance in nits after applying the given gain
499  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
500  */
501 Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata);
502 Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight);
503 Color applyGainLUT(Color e, float gain, GainLUT& gainLUT, uhdr_gainmap_metadata_ext_t* metadata);
504 
505 /*
506  * Apply gain in R, G and B channels, with the given hdr ratio, to the given sdr input
507  * in the range [0, 1].
508  */
509 Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata);
510 Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight);
511 Color applyGainLUT(Color e, Color gain, GainLUT& gainLUT, uhdr_gainmap_metadata_ext_t* metadata);
512 
513 /*
514  * Sample the gain value for the map from a given x,y coordinate on a scale
515  * that is map scale factor larger than the map size.
516  */
517 float sampleMap(uhdr_raw_image_t* map, float map_scale_factor, size_t x, size_t y);
518 float sampleMap(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y,
519                 ShepardsIDW& weightTables);
520 Color sampleMap3Channel(uhdr_raw_image_t* map, float map_scale_factor, size_t x, size_t y,
521                         bool has_alpha);
522 Color sampleMap3Channel(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y,
523                         ShepardsIDW& weightTables, bool has_alpha);
524 
525 ////////////////////////////////////////////////////////////////////////////////
526 // function selectors
527 
528 ColorTransformFn getGamutConversionFn(uhdr_color_gamut_t dst_gamut, uhdr_color_gamut_t src_gamut);
529 ColorTransformFn getYuvToRgbFn(uhdr_color_gamut_t gamut);
530 LuminanceFn getLuminanceFn(uhdr_color_gamut_t gamut);
531 ColorTransformFn getInverseOetfFn(uhdr_color_transfer_t transfer);
532 SceneToDisplayLuminanceFn getOotfFn(uhdr_color_transfer_t transfer);
533 GetPixelFn getPixelFn(uhdr_img_fmt_t format);
534 SamplePixelFn getSamplePixelFn(uhdr_img_fmt_t format);
535 PutPixelFn putPixelFn(uhdr_img_fmt_t format);
536 
537 ////////////////////////////////////////////////////////////////////////////////
538 // common utils
539 
540 // maximum limit of normalized pixel value in float representation
541 static const float kMaxPixelFloat = 1.0f;
542 
clampPixelFloat(float value)543 static inline float clampPixelFloat(float value) {
544   return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
545 }
546 
clampPixelFloat(Color e)547 static inline Color clampPixelFloat(Color e) {
548   return {{{clampPixelFloat(e.r), clampPixelFloat(e.g), clampPixelFloat(e.b)}}};
549 }
550 
551 // maximum limit of pixel value for linear hdr intent raw resource
552 static const float kMaxPixelFloatHdrLinear = 10000.0f / 203.0f;
553 
clampPixelFloatLinear(float value)554 static inline float clampPixelFloatLinear(float value) {
555   return CLIP3(value, 0.0f, kMaxPixelFloatHdrLinear);
556 }
557 
clampPixelFloatLinear(Color e)558 static inline Color clampPixelFloatLinear(Color e) {
559   return {{{clampPixelFloatLinear(e.r), clampPixelFloatLinear(e.g), clampPixelFloatLinear(e.b)}}};
560 }
561 
mapNonFiniteFloats(float val)562 static float mapNonFiniteFloats(float val) {
563   if (std::isinf(val)) {
564     return val > 0 ? kMaxPixelFloatHdrLinear : 0.0f;
565   }
566   // nan
567   return 0.0f;
568 }
569 
sanitizePixel(Color e)570 static inline Color sanitizePixel(Color e) {
571   float r = std::isfinite(e.r) ? clampPixelFloatLinear(e.r) : mapNonFiniteFloats(e.r);
572   float g = std::isfinite(e.g) ? clampPixelFloatLinear(e.g) : mapNonFiniteFloats(e.g);
573   float b = std::isfinite(e.b) ? clampPixelFloatLinear(e.b) : mapNonFiniteFloats(e.b);
574   return {{{r, g, b}}};
575 }
576 
577 bool isPixelFormatRgb(uhdr_img_fmt_t format);
578 
579 uint32_t colorToRgba1010102(Color e_gamma);
580 uint64_t colorToRgbaF16(Color e_gamma);
581 
582 std::unique_ptr<uhdr_raw_image_ext_t> copy_raw_image(uhdr_raw_image_t* src);
583 
584 uhdr_error_info_t copy_raw_image(uhdr_raw_image_t* src, uhdr_raw_image_t* dst);
585 
586 std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr(
587     uhdr_raw_image_t* src, bool chroma_sampling_enabled = false);
588 
589 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
590 std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src);
591 #endif
592 
593 bool floatToSignedFraction(float v, int32_t* numerator, uint32_t* denominator);
594 bool floatToUnsignedFraction(float v, uint32_t* numerator, uint32_t* denominator);
595 
596 }  // namespace ultrahdr
597 
598 #endif  // ULTRAHDR_GAINMAPMATH_H
599