• 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 
24 #include "ultrahdr_api.h"
25 #include "ultrahdr/ultrahdrcommon.h"
26 #include "ultrahdr/ultrahdr.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 CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
34 
35 namespace ultrahdr {
36 
37 ////////////////////////////////////////////////////////////////////////////////
38 // Framework
39 
40 // This aligns with the suggested default reference diffuse white from
41 // ISO/TS 22028-5
42 const float kSdrWhiteNits = 203.0f;
43 const float kHlgMaxNits = 1000.0f;
44 const float kPqMaxNits = 10000.0f;
45 
46 static const float kMaxPixelFloat = 1.0f;
47 
48 // Describes the tone-mapping operation & gain-map encoding parameters.
49 const float kHlgHeadroom = 1000.0f / 203.0f;
50 
51 struct Color {
52   union {
53     struct {
54       float r;
55       float g;
56       float b;
57     };
58     struct {
59       float y;
60       float u;
61       float v;
62     };
63   };
64 };
65 
66 typedef Color (*ColorTransformFn)(Color);
67 typedef float (*ColorCalculationFn)(Color);
68 
clampPixelFloat(float value)69 static inline float clampPixelFloat(float value) {
70   return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
71 }
clampPixelFloat(Color e)72 static inline Color clampPixelFloat(Color e) {
73   return {{{clampPixelFloat(e.r), clampPixelFloat(e.g), clampPixelFloat(e.b)}}};
74 }
75 
76 // A transfer function mapping encoded values to linear values,
77 // represented by this 7-parameter piecewise function:
78 //
79 //   linear = sign(encoded) *  (c*|encoded| + f)       , 0 <= |encoded| < d
80 //          = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
81 //
82 // (A simple gamma transfer function sets g to gamma and a to 1.)
83 typedef struct TransferFunction {
84   float g, a, b, c, d, e, f;
85 } TransferFunction;
86 
87 static constexpr TransferFunction kSRGB_TransFun = {
88     2.4f, (float)(1 / 1.055), (float)(0.055 / 1.055), (float)(1 / 12.92), 0.04045f, 0.0f, 0.0f};
89 
90 static constexpr TransferFunction kLinear_TransFun = {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
91 
92 inline Color operator+=(Color& lhs, const Color& rhs) {
93   lhs.r += rhs.r;
94   lhs.g += rhs.g;
95   lhs.b += rhs.b;
96   return lhs;
97 }
98 inline Color operator-=(Color& lhs, const Color& rhs) {
99   lhs.r -= rhs.r;
100   lhs.g -= rhs.g;
101   lhs.b -= rhs.b;
102   return lhs;
103 }
104 
105 inline Color operator+(const Color& lhs, const Color& rhs) {
106   Color temp = lhs;
107   return temp += rhs;
108 }
109 inline Color operator-(const Color& lhs, const Color& rhs) {
110   Color temp = lhs;
111   return temp -= rhs;
112 }
113 
114 inline Color operator+=(Color& lhs, const float rhs) {
115   lhs.r += rhs;
116   lhs.g += rhs;
117   lhs.b += rhs;
118   return lhs;
119 }
120 inline Color operator-=(Color& lhs, const float rhs) {
121   lhs.r -= rhs;
122   lhs.g -= rhs;
123   lhs.b -= rhs;
124   return lhs;
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 inline Color operator/=(Color& lhs, const float rhs) {
133   lhs.r /= rhs;
134   lhs.g /= rhs;
135   lhs.b /= rhs;
136   return lhs;
137 }
138 
139 inline Color operator+(const Color& lhs, const float rhs) {
140   Color temp = lhs;
141   return temp += rhs;
142 }
143 inline Color operator-(const Color& lhs, const float rhs) {
144   Color temp = lhs;
145   return temp -= rhs;
146 }
147 inline Color operator*(const Color& lhs, const float rhs) {
148   Color temp = lhs;
149   return temp *= rhs;
150 }
151 inline Color operator/(const Color& lhs, const float rhs) {
152   Color temp = lhs;
153   return temp /= rhs;
154 }
155 
156 union FloatUIntUnion {
157   uint32_t fUInt;
158   float fFloat;
159 };
160 
floatToHalf(float f)161 inline uint16_t floatToHalf(float f) {
162   FloatUIntUnion floatUnion;
163   floatUnion.fFloat = f;
164   // round-to-nearest-even: add last bit after truncated mantissa
165   const uint32_t b = floatUnion.fUInt + 0x00001000;
166 
167   const int32_t e = (b & 0x7F800000) >> 23;  // exponent
168   const uint32_t m = b & 0x007FFFFF;         // mantissa
169 
170   // sign : normalized : denormalized : saturate
171   return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) |
172          ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
173          (e > 143) * 0x7FFF;
174 }
175 
176 constexpr int32_t kGainFactorPrecision = 10;
177 constexpr int32_t kGainFactorNumEntries = 1 << kGainFactorPrecision;
178 struct GainLUT {
GainLUTGainLUT179   GainLUT(ultrahdr_metadata_ptr metadata) {
180     for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) {
181       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
182       float logBoost = log2(metadata->minContentBoost) * (1.0f - value) +
183                        log2(metadata->maxContentBoost) * value;
184       mGainTable[idx] = exp2(logBoost);
185     }
186   }
187 
GainLUTGainLUT188   GainLUT(ultrahdr_metadata_ptr metadata, float displayBoost) {
189     float boostFactor = displayBoost > 0 ? displayBoost / metadata->maxContentBoost : 1.0f;
190     for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) {
191       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
192       float logBoost = log2(metadata->minContentBoost) * (1.0f - value) +
193                        log2(metadata->maxContentBoost) * value;
194       mGainTable[idx] = exp2(logBoost * boostFactor);
195     }
196   }
197 
~GainLUTGainLUT198   ~GainLUT() {}
199 
getGainFactorGainLUT200   float getGainFactor(float gain) {
201     int32_t idx = static_cast<int32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
202     // TODO() : Remove once conversion modules have appropriate clamping in place
203     idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
204     return mGainTable[idx];
205   }
206 
207  private:
208   float mGainTable[kGainFactorNumEntries];
209 };
210 
211 struct ShepardsIDW {
ShepardsIDWShepardsIDW212   ShepardsIDW(int mapScaleFactor) : mMapScaleFactor{mapScaleFactor} {
213     const int size = mMapScaleFactor * mMapScaleFactor * 4;
214     mWeights = new float[size];
215     mWeightsNR = new float[size];
216     mWeightsNB = new float[size];
217     mWeightsC = new float[size];
218     fillShepardsIDW(mWeights, 1, 1);
219     fillShepardsIDW(mWeightsNR, 0, 1);
220     fillShepardsIDW(mWeightsNB, 1, 0);
221     fillShepardsIDW(mWeightsC, 0, 0);
222   }
~ShepardsIDWShepardsIDW223   ~ShepardsIDW() {
224     delete[] mWeights;
225     delete[] mWeightsNR;
226     delete[] mWeightsNB;
227     delete[] mWeightsC;
228   }
229 
230   int mMapScaleFactor;
231   // Image :-
232   // p00 p01 p02 p03 p04 p05 p06 p07
233   // p10 p11 p12 p13 p14 p15 p16 p17
234   // p20 p21 p22 p23 p24 p25 p26 p27
235   // p30 p31 p32 p33 p34 p35 p36 p37
236   // p40 p41 p42 p43 p44 p45 p46 p47
237   // p50 p51 p52 p53 p54 p55 p56 p57
238   // p60 p61 p62 p63 p64 p65 p66 p67
239   // p70 p71 p72 p73 p74 p75 p76 p77
240 
241   // Gain Map (for 4 scale factor) :-
242   // m00 p01
243   // m10 m11
244 
245   // Gain sample of curr 4x4, right 4x4, bottom 4x4, bottom right 4x4 are used during
246   // reconstruction. hence table weight size is 4.
247   float* mWeights;
248   // TODO: check if its ok to mWeights at places
249   float* mWeightsNR;  // no right
250   float* mWeightsNB;  // no bottom
251   float* mWeightsC;   // no right & bottom
252 
253   float euclideanDistance(float x1, float x2, float y1, float y2);
254   void fillShepardsIDW(float* weights, int incR, int incB);
255 };
256 
257 ////////////////////////////////////////////////////////////////////////////////
258 // sRGB transformations
259 // NOTE: sRGB has the same color primaries as BT.709, but different transfer
260 // function. For this reason, all sRGB transformations here apply to BT.709,
261 // except for those concerning transfer functions.
262 
263 /*
264  * Calculate the luminance of a linear RGB sRGB pixel, according to
265  * IEC 61966-2-1/Amd 1:2003.
266  *
267  * [0.0, 1.0] range in and out.
268  */
269 float srgbLuminance(Color e);
270 
271 /*
272  * Convert from OETF'd srgb RGB to YUV, according to ITU-R BT.709-6.
273  *
274  * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
275  */
276 Color srgbRgbToYuv(Color e_gamma);
277 
278 /*
279  * Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6.
280  *
281  * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
282  */
283 Color srgbYuvToRgb(Color e_gamma);
284 
285 /*
286  * Convert from srgb to linear, according to IEC 61966-2-1/Amd 1:2003.
287  *
288  * [0.0, 1.0] range in and out.
289  */
290 float srgbInvOetf(float e_gamma);
291 Color srgbInvOetf(Color e_gamma);
292 float srgbInvOetfLUT(float e_gamma);
293 Color srgbInvOetfLUT(Color e_gamma);
294 
295 /*
296  * Convert from linear to srgb, according to IEC 61966-2-1/Amd 1:2003.
297  *
298  * [0.0, 1.0] range in and out.
299  */
300 float srgbOetf(float e);
301 Color srgbOetf(Color e);
302 
303 constexpr int32_t kSrgbInvOETFPrecision = 10;
304 constexpr int32_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision;
305 
306 ////////////////////////////////////////////////////////////////////////////////
307 // Display-P3 transformations
308 
309 /*
310  * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1.
311  *
312  * [0.0, 1.0] range in and out.
313  */
314 float p3Luminance(Color e);
315 
316 /*
317  * Convert from OETF'd P3 RGB to YUV, according to ITU-R BT.601-7.
318  *
319  * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
320  */
321 Color p3RgbToYuv(Color e_gamma);
322 
323 /*
324  * Convert from OETF'd P3 YUV to RGB, according to ITU-R BT.601-7.
325  *
326  * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
327  */
328 Color p3YuvToRgb(Color e_gamma);
329 
330 ////////////////////////////////////////////////////////////////////////////////
331 // BT.2100 transformations - according to ITU-R BT.2100-2
332 
333 /*
334  * Calculate the luminance of a linear RGB BT.2100 pixel.
335  *
336  * [0.0, 1.0] range in and out.
337  */
338 float bt2100Luminance(Color e);
339 
340 /*
341  * Convert from OETF'd BT.2100 RGB to YUV, according to ITU-R BT.2100-2.
342  *
343  * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
344  */
345 Color bt2100RgbToYuv(Color e_gamma);
346 
347 /*
348  * Convert from OETF'd BT.2100 YUV to RGB, according to ITU-R BT.2100-2.
349  *
350  * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
351  */
352 Color bt2100YuvToRgb(Color e_gamma);
353 
354 /*
355  * Convert from scene luminance to HLG.
356  *
357  * [0.0, 1.0] range in and out.
358  */
359 float hlgOetf(float e);
360 Color hlgOetf(Color e);
361 float hlgOetfLUT(float e);
362 Color hlgOetfLUT(Color e);
363 
364 constexpr int32_t kHlgOETFPrecision = 16;
365 constexpr int32_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
366 
367 /*
368  * Convert from HLG to scene luminance.
369  *
370  * [0.0, 1.0] range in and out.
371  */
372 float hlgInvOetf(float e_gamma);
373 Color hlgInvOetf(Color e_gamma);
374 float hlgInvOetfLUT(float e_gamma);
375 Color hlgInvOetfLUT(Color e_gamma);
376 
377 constexpr int32_t kHlgInvOETFPrecision = 12;
378 constexpr int32_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
379 
380 /*
381  * Convert from scene luminance to PQ.
382  *
383  * [0.0, 1.0] range in and out.
384  */
385 float pqOetf(float e);
386 Color pqOetf(Color e);
387 float pqOetfLUT(float e);
388 Color pqOetfLUT(Color e);
389 
390 constexpr int32_t kPqOETFPrecision = 16;
391 constexpr int32_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
392 
393 /*
394  * Convert from PQ to scene luminance in nits.
395  *
396  * [0.0, 1.0] range in and out.
397  */
398 float pqInvOetf(float e_gamma);
399 Color pqInvOetf(Color e_gamma);
400 float pqInvOetfLUT(float e_gamma);
401 Color pqInvOetfLUT(Color e_gamma);
402 
403 constexpr int32_t kPqInvOETFPrecision = 12;
404 constexpr int32_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 // Color space conversions
408 
409 /*
410  * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
411  *
412  * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
413  * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
414  * always the inverse of the RGB gamut to XYZ matrix.
415  */
416 Color bt709ToP3(Color e);
417 Color bt709ToBt2100(Color e);
418 Color p3ToBt709(Color e);
419 Color p3ToBt2100(Color e);
420 Color bt2100ToBt709(Color e);
421 Color bt2100ToP3(Color e);
422 
423 /*
424  * Identity conversion.
425  */
identityConversion(Color e)426 inline Color identityConversion(Color e) { return e; }
427 
428 /*
429  * Get the conversion to apply to the HDR image for gain map generation
430  */
431 ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut);
432 
433 /*
434  * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2.
435  *
436  * Bt.709 and Bt.2100 have well-defined YUV encodings; Display-P3's is less well defined, but is
437  * treated as Bt.601 by DataSpace, hence we do the same.
438  */
439 extern const std::array<float, 9> kYuvBt709ToBt601;
440 extern const std::array<float, 9> kYuvBt709ToBt2100;
441 extern const std::array<float, 9> kYuvBt601ToBt709;
442 extern const std::array<float, 9> kYuvBt601ToBt2100;
443 extern const std::array<float, 9> kYuvBt2100ToBt709;
444 extern const std::array<float, 9> kYuvBt2100ToBt601;
445 
446 Color yuvColorGamutConversion(Color e_gamma, const std::array<float, 9>& coeffs);
447 
448 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
449 
450 extern const int16_t kYuv709To601_coeffs_neon[8];
451 extern const int16_t kYuv709To2100_coeffs_neon[8];
452 extern const int16_t kYuv601To709_coeffs_neon[8];
453 extern const int16_t kYuv601To2100_coeffs_neon[8];
454 extern const int16_t kYuv2100To709_coeffs_neon[8];
455 extern const int16_t kYuv2100To601_coeffs_neon[8];
456 
457 /*
458  * The Y values are provided at half the width of U & V values to allow use of the widening
459  * arithmetic instructions.
460  */
461 int16x8x3_t yuvConversion_neon(uint8x8_t y, int16x8_t u, int16x8_t v, int16x8_t coeffs);
462 
463 void transformYuv420_neon(jr_uncompressed_ptr image, const int16_t* coeffs_ptr);
464 
465 status_t convertYuv_neon(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
466                          ultrahdr_color_gamut dst_encoding);
467 #endif
468 
469 /*
470  * Performs a color gamut transformation on an entire YUV420 image.
471  *
472  * Apply the transformation by determining transformed YUV for each of the 4 Y + 1 UV; each Y gets
473  * this result, and UV gets the averaged result.
474  *
475  * The chroma channels should be less than or equal to half the image's width and height
476  * respectively, since input is 4:2:0 subsampled.
477  */
478 void transformYuv420(jr_uncompressed_ptr image, const std::array<float, 9>& coeffs);
479 
480 ////////////////////////////////////////////////////////////////////////////////
481 // Gain map calculations
482 
483 /*
484  * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
485  * luminances in linear space, and the hdr ratio to encode against.
486  *
487  * Note: since this library always uses gamma of 1.0, offsetSdr of 0.0, and
488  * offsetHdr of 0.0, this function doesn't handle different metadata values for
489  * these fields.
490  */
491 uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata);
492 uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
493                    float log2MinContentBoost, float log2MaxContentBoost);
494 
495 /*
496  * Calculates the linear luminance in nits after applying the given gain
497  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
498  *
499  * Note: similar to encodeGain(), this function only supports gamma 1.0,
500  * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to
501  * gainMapMax, as this library encodes.
502  */
503 Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata);
504 Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost);
505 Color applyGainLUT(Color e, float gain, GainLUT& gainLUT);
506 
507 /*
508  * Apply gain in R, G and B channels, with the given hdr ratio, to the given sdr input
509  * in the range [0, 1].
510  *
511  * Note: similar to encodeGain(), this function only supports gamma 1.0,
512  * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to
513  * gainMapMax, as this library encodes.
514  */
515 Color applyGain(Color e, Color gain, ultrahdr_metadata_ptr metadata);
516 Color applyGain(Color e, Color gain, ultrahdr_metadata_ptr metadata, float displayBoost);
517 Color applyGainLUT(Color e, Color gain, GainLUT& gainLUT);
518 
519 /*
520  * Helper for sampling from YUV 420 images.
521  */
522 Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
523 
524 /*
525  * Helper for sampling from P010 images.
526  *
527  * Expect narrow-range image data for P010.
528  */
529 Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
530 
531 /*
532  * Sample the image at the provided location, with a weighting based on nearby
533  * pixels and the map scale factor.
534  */
535 Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
536 
537 /*
538  * Sample the image at the provided location, with a weighting based on nearby
539  * pixels and the map scale factor.
540  *
541  * Expect narrow-range image data for P010.
542  */
543 Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
544 
545 /*
546  * Sample the gain value for the map from a given x,y coordinate on a scale
547  * that is map scale factor larger than the map size.
548  */
549 float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y);
550 float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y,
551                 ShepardsIDW& weightTables);
552 Color sampleMap3Channel(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y,
553                         bool has_alpha);
554 Color sampleMap3Channel(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y,
555                         ShepardsIDW& weightTables, bool has_alpha);
556 
557 /*
558  * Convert from Color to RGBA1010102.
559  *
560  * Alpha always set to 1.0.
561  */
562 uint32_t colorToRgba1010102(Color e_gamma);
563 
564 /*
565  * Convert from Color to F16.
566  *
567  * Alpha always set to 1.0.
568  */
569 uint64_t colorToRgbaF16(Color e_gamma);
570 
571 /*
572  * Helper for preparing encoder raw inputs for encoding
573  */
574 std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr(uhdr_raw_image_t* src);
575 
576 /*
577  * Helper for converting float to fraction
578  */
579 bool floatToSignedFraction(float v, int32_t* numerator, uint32_t* denominator);
580 bool floatToUnsignedFraction(float v, uint32_t* numerator, uint32_t* denominator);
581 
582 }  // namespace ultrahdr
583 
584 #endif  // ULTRAHDR_GAINMAPMATH_H
585