• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
401 extern const std::array<float, 9> kBt709ToP3;
402 extern const std::array<float, 9> kBt709ToBt2100;
403 extern const std::array<float, 9> kP3ToBt709;
404 extern const std::array<float, 9> kP3ToBt2100;
405 extern const std::array<float, 9> kBt2100ToBt709;
406 extern const std::array<float, 9> kBt2100ToP3;
407 
identityConversion(Color e)408 inline Color identityConversion(Color e) { return e; }
409 Color bt709ToP3(Color e);
410 Color bt709ToBt2100(Color e);
411 Color p3ToBt709(Color e);
412 Color p3ToBt2100(Color e);
413 Color bt2100ToBt709(Color e);
414 Color bt2100ToP3(Color e);
415 
416 // convert between yuv encodings
417 extern const std::array<float, 9> kYuvBt709ToBt601;
418 extern const std::array<float, 9> kYuvBt709ToBt2100;
419 extern const std::array<float, 9> kYuvBt601ToBt709;
420 extern const std::array<float, 9> kYuvBt601ToBt2100;
421 extern const std::array<float, 9> kYuvBt2100ToBt709;
422 extern const std::array<float, 9> kYuvBt2100ToBt601;
423 
424 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
425 
426 extern const int16_t kYuv709To601_coeffs_neon[8];
427 extern const int16_t kYuv709To2100_coeffs_neon[8];
428 extern const int16_t kYuv601To709_coeffs_neon[8];
429 extern const int16_t kYuv601To2100_coeffs_neon[8];
430 extern const int16_t kYuv2100To709_coeffs_neon[8];
431 extern const int16_t kYuv2100To601_coeffs_neon[8];
432 
433 /*
434  * The Y values are provided at half the width of U & V values to allow use of the widening
435  * arithmetic instructions.
436  */
437 int16x8x3_t yuvConversion_neon(uint8x8_t y, int16x8_t u, int16x8_t v, int16x8_t coeffs);
438 
439 void transformYuv420_neon(uhdr_raw_image_t* image, const int16_t* coeffs_ptr);
440 
441 void transformYuv444_neon(uhdr_raw_image_t* image, const int16_t* coeffs_ptr);
442 
443 uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t src_encoding,
444                                   uhdr_color_gamut_t dst_encoding);
445 #endif
446 
447 // Performs a color gamut transformation on an yuv image.
448 Color yuvColorGamutConversion(Color e_gamma, const std::array<float, 9>& coeffs);
449 void transformYuv420(uhdr_raw_image_t* image, const std::array<float, 9>& coeffs);
450 void transformYuv444(uhdr_raw_image_t* image, const std::array<float, 9>& coeffs);
451 
452 ////////////////////////////////////////////////////////////////////////////////
453 // Gain map calculations
454 
455 constexpr int32_t kGainFactorPrecision = 10;
456 constexpr int32_t kGainFactorNumEntries = 1 << kGainFactorPrecision;
457 
458 struct GainLUT {
GainLUTGainLUT459   GainLUT(uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight) {
460     bool isSingleChannel = metadata->are_all_channels_identical();
461     for (int i = 0; i < (isSingleChannel ? 1 : 3); i++) {
462       mGainTable[i] = memory[i] = new float[kGainFactorNumEntries];
463       this->mGammaInv[i] = 1.0f / metadata->gamma[i];
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[i]) * (1.0f - value) +
467                          log2(metadata->max_content_boost[i]) * value;
468         mGainTable[i][idx] = exp2(logBoost * gainmapWeight);
469       }
470     }
471     if (isSingleChannel) {
472       memory[1] = memory[2] = nullptr;
473       mGammaInv[1] = mGammaInv[2] = mGammaInv[0];
474       mGainTable[1] = mGainTable[2] = mGainTable[0];
475     }
476   }
477 
GainLUTGainLUT478   GainLUT(uhdr_gainmap_metadata_ext_t* metadata) : GainLUT(metadata, 1.0f) {}
479 
~GainLUTGainLUT480   ~GainLUT() {
481     for (int i = 0; i < 3; i++) {
482       if (memory[i]) {
483         delete[] memory[i];
484         memory[i] = nullptr;
485       }
486     }
487   }
488 
getGainFactorGainLUT489   float getGainFactor(float gain, int index) {
490     if (mGammaInv[index] != 1.0f) gain = pow(gain, mGammaInv[index]);
491     int32_t idx = static_cast<int32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
492     // TODO() : Remove once conversion modules have appropriate clamping in place
493     idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
494     return mGainTable[index][idx];
495   }
496 
497  private:
498   float* memory[3]{};
499   float* mGainTable[3]{};
500   float mGammaInv[3]{};
501 };
502 
503 /*
504  * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
505  * luminances in linear space and gainmap metadata fields.
506  */
507 uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata, int index);
508 uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata,
509                    float log2MinContentBoost, float log2MaxContentBoost, int index);
510 float computeGain(float sdr, float hdr);
511 uint8_t affineMapGain(float gainlog2, float mingainlog2, float maxgainlog2, float gamma);
512 
513 /*
514  * Calculates the linear luminance in nits after applying the given gain
515  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
516  */
517 Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata);
518 Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight);
519 Color applyGainLUT(Color e, float gain, GainLUT& gainLUT, uhdr_gainmap_metadata_ext_t* metadata);
520 
521 /*
522  * Apply gain in R, G and B channels, with the given hdr ratio, to the given sdr input
523  * in the range [0, 1].
524  */
525 Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata);
526 Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight);
527 Color applyGainLUT(Color e, Color gain, GainLUT& gainLUT, uhdr_gainmap_metadata_ext_t* metadata);
528 
529 /*
530  * Sample the gain value for the map from a given x,y coordinate on a scale
531  * that is map scale factor larger than the map size.
532  */
533 float sampleMap(uhdr_raw_image_t* map, float map_scale_factor, size_t x, size_t y);
534 float sampleMap(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y,
535                 ShepardsIDW& weightTables);
536 Color sampleMap3Channel(uhdr_raw_image_t* map, float map_scale_factor, size_t x, size_t y,
537                         bool has_alpha);
538 Color sampleMap3Channel(uhdr_raw_image_t* map, size_t map_scale_factor, size_t x, size_t y,
539                         ShepardsIDW& weightTables, bool has_alpha);
540 
541 ////////////////////////////////////////////////////////////////////////////////
542 // function selectors
543 
544 ColorTransformFn getGamutConversionFn(uhdr_color_gamut_t dst_gamut, uhdr_color_gamut_t src_gamut);
545 ColorTransformFn getYuvToRgbFn(uhdr_color_gamut_t gamut);
546 LuminanceFn getLuminanceFn(uhdr_color_gamut_t gamut);
547 ColorTransformFn getInverseOetfFn(uhdr_color_transfer_t transfer);
548 SceneToDisplayLuminanceFn getOotfFn(uhdr_color_transfer_t transfer);
549 GetPixelFn getPixelFn(uhdr_img_fmt_t format);
550 SamplePixelFn getSamplePixelFn(uhdr_img_fmt_t format);
551 PutPixelFn putPixelFn(uhdr_img_fmt_t format);
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 // common utils
555 static const float kHdrOffset = 1e-7f;
556 static const float kSdrOffset = 1e-7f;
557 
clipNegatives(float value)558 static inline float clipNegatives(float value) { return (value < 0.0f) ? 0.0f : value; }
559 
clipNegatives(Color e)560 static inline Color clipNegatives(Color e) {
561   return {{{clipNegatives(e.r), clipNegatives(e.g), clipNegatives(e.b)}}};
562 }
563 
564 // maximum limit of normalized pixel value in float representation
565 static const float kMaxPixelFloat = 1.0f;
566 
clampPixelFloat(float value)567 static inline float clampPixelFloat(float value) {
568   return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
569 }
570 
clampPixelFloat(Color e)571 static inline Color clampPixelFloat(Color e) {
572   return {{{clampPixelFloat(e.r), clampPixelFloat(e.g), clampPixelFloat(e.b)}}};
573 }
574 
575 // maximum limit of pixel value for linear hdr intent raw resource
576 static const float kMaxPixelFloatHdrLinear = 10000.0f / 203.0f;
577 
clampPixelFloatLinear(float value)578 static inline float clampPixelFloatLinear(float value) {
579   return CLIP3(value, 0.0f, kMaxPixelFloatHdrLinear);
580 }
581 
clampPixelFloatLinear(Color e)582 static inline Color clampPixelFloatLinear(Color e) {
583   return {{{clampPixelFloatLinear(e.r), clampPixelFloatLinear(e.g), clampPixelFloatLinear(e.b)}}};
584 }
585 
mapNonFiniteFloats(float val)586 static float mapNonFiniteFloats(float val) {
587   if (std::isinf(val)) {
588     return val > 0 ? kMaxPixelFloatHdrLinear : 0.0f;
589   }
590   // nan
591   return 0.0f;
592 }
593 
sanitizePixel(Color e)594 static inline Color sanitizePixel(Color e) {
595   float r = std::isfinite(e.r) ? clampPixelFloatLinear(e.r) : mapNonFiniteFloats(e.r);
596   float g = std::isfinite(e.g) ? clampPixelFloatLinear(e.g) : mapNonFiniteFloats(e.g);
597   float b = std::isfinite(e.b) ? clampPixelFloatLinear(e.b) : mapNonFiniteFloats(e.b);
598   return {{{r, g, b}}};
599 }
600 
601 bool isPixelFormatRgb(uhdr_img_fmt_t format);
602 
603 uint32_t colorToRgba1010102(Color e_gamma);
604 uint64_t colorToRgbaF16(Color e_gamma);
605 
606 std::unique_ptr<uhdr_raw_image_ext_t> copy_raw_image(uhdr_raw_image_t* src);
607 
608 uhdr_error_info_t copy_raw_image(uhdr_raw_image_t* src, uhdr_raw_image_t* dst);
609 
610 std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr(
611     uhdr_raw_image_t* src, bool chroma_sampling_enabled = false);
612 
613 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
614 std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src);
615 #endif
616 
617 bool floatToSignedFraction(float v, int32_t* numerator, uint32_t* denominator);
618 bool floatToUnsignedFraction(float v, uint32_t* numerator, uint32_t* denominator);
619 
620 }  // namespace ultrahdr
621 
622 #endif  // ULTRAHDR_GAINMAPMATH_H
623