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