/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 (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 | 7.72G | #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 | 1.94G | inline Color operator+=(Color& lhs, const Color& rhs) { |
82 | 1.94G | lhs.r += rhs.r; |
83 | 1.94G | lhs.g += rhs.g; |
84 | 1.94G | lhs.b += rhs.b; |
85 | 1.94G | return lhs; |
86 | 1.94G | } |
87 | | |
88 | 0 | inline Color operator-=(Color& lhs, const Color& rhs) { |
89 | 0 | lhs.r -= rhs.r; |
90 | 0 | lhs.g -= rhs.g; |
91 | 0 | lhs.b -= rhs.b; |
92 | 0 | return lhs; |
93 | 0 | } |
94 | | |
95 | 1.10G | inline Color operator+(const Color& lhs, const Color& rhs) { |
96 | 1.10G | Color temp = lhs; |
97 | 1.10G | return temp += rhs; |
98 | 1.10G | } |
99 | | |
100 | 0 | inline Color operator-(const Color& lhs, const Color& rhs) { |
101 | 0 | Color temp = lhs; |
102 | 0 | return temp -= rhs; |
103 | 0 | } |
104 | | |
105 | 302M | inline Color operator+=(Color& lhs, const float rhs) { |
106 | 302M | lhs.r += rhs; |
107 | 302M | lhs.g += rhs; |
108 | 302M | lhs.b += rhs; |
109 | 302M | return lhs; |
110 | 302M | } |
111 | | |
112 | 147M | inline Color operator-=(Color& lhs, const float rhs) { |
113 | 147M | lhs.r -= rhs; |
114 | 147M | lhs.g -= rhs; |
115 | 147M | lhs.b -= rhs; |
116 | 147M | return lhs; |
117 | 147M | } |
118 | | |
119 | 2.38G | inline Color operator*=(Color& lhs, const float rhs) { |
120 | 2.38G | lhs.r *= rhs; |
121 | 2.38G | lhs.g *= rhs; |
122 | 2.38G | lhs.b *= rhs; |
123 | 2.38G | return lhs; |
124 | 2.38G | } |
125 | | |
126 | 1.00G | inline Color operator/=(Color& lhs, const float rhs) { |
127 | 1.00G | lhs.r /= rhs; |
128 | 1.00G | lhs.g /= rhs; |
129 | 1.00G | lhs.b /= rhs; |
130 | 1.00G | return lhs; |
131 | 1.00G | } |
132 | | |
133 | 148M | inline Color operator+(const Color& lhs, const float rhs) { |
134 | 148M | Color temp = lhs; |
135 | 148M | return temp += rhs; |
136 | 148M | } |
137 | | |
138 | 147M | inline Color operator-(const Color& lhs, const float rhs) { |
139 | 147M | Color temp = lhs; |
140 | 147M | return temp -= rhs; |
141 | 147M | } |
142 | | |
143 | 2.22G | inline Color operator*(const Color& lhs, const float rhs) { |
144 | 2.22G | Color temp = lhs; |
145 | 2.22G | return temp *= rhs; |
146 | 2.22G | } |
147 | | |
148 | 999M | inline Color operator/(const Color& lhs, const float rhs) { |
149 | 999M | Color temp = lhs; |
150 | 999M | return temp /= rhs; |
151 | 999M | } |
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 |
166 | 531M | inline uint16_t floatToHalf(float f) { |
167 | 531M | FloatUIntUnion floatUnion; |
168 | 531M | floatUnion.mFloat = f; |
169 | | // round-to-nearest-even: add last bit after truncated mantissa |
170 | 531M | const uint32_t b = floatUnion.mUInt + 0x00001000; |
171 | | |
172 | 531M | const int32_t e = (b & 0x7F800000) >> 23; // exponent |
173 | 531M | const uint32_t m = b & 0x007FFFFF; // mantissa |
174 | | |
175 | | // sign : normalized : denormalized : saturate |
176 | 531M | return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) | |
177 | 531M | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | |
178 | 531M | (e > 143) * 0x7FFF; |
179 | 531M | } |
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 |
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/ |
193 | 0 | inline uint16_t halfMantissa(uint16_t h) { return h & 0x03ff; } |
194 | | |
195 | 0 | inline uint16_t halfExponent(uint16_t h) { return (h >> 10) & 0x001f; } |
196 | | |
197 | 0 | inline uint16_t halfSign(uint16_t h) { return h >> 15; } |
198 | | |
199 | 0 | inline float halfToFloat(uint16_t bits) { |
200 | 0 | static const FloatUIntUnion magic = {126 << 23}; |
201 | 0 | FloatUIntUnion o; |
202 | |
|
203 | 0 | if (halfExponent(bits) == 0) { |
204 | | // Zero / Denormal |
205 | 0 | o.mUInt = magic.mUInt + halfMantissa(bits); |
206 | 0 | o.mFloat -= magic.mFloat; |
207 | 0 | } else { |
208 | | // Set mantissa |
209 | 0 | o.mUInt = halfMantissa(bits) << 13; |
210 | | // Set exponent |
211 | 0 | if (halfExponent(bits) == 0x1f) { |
212 | | // Inf/NaN |
213 | 0 | o.mUInt |= (255 << 23); |
214 | 0 | } else { |
215 | 0 | o.mUInt |= ((127 - 15 + halfExponent(bits)) << 23); |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | // Set sign |
220 | 0 | o.mUInt |= (halfSign(bits) << 31); |
221 | 0 | return o.mFloat; |
222 | 0 | } |
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 { |
229 | 2.65k | ShepardsIDW(int mapScaleFactor) : mMapScaleFactor{mapScaleFactor} { |
230 | 2.65k | const int size = mMapScaleFactor * mMapScaleFactor * 4; |
231 | 2.65k | mWeights = new float[size]; |
232 | 2.65k | mWeightsNR = new float[size]; |
233 | 2.65k | mWeightsNB = new float[size]; |
234 | 2.65k | mWeightsC = new float[size]; |
235 | 2.65k | fillShepardsIDW(mWeights, 1, 1); |
236 | 2.65k | fillShepardsIDW(mWeightsNR, 0, 1); |
237 | 2.65k | fillShepardsIDW(mWeightsNB, 1, 0); |
238 | 2.65k | fillShepardsIDW(mWeightsC, 0, 0); |
239 | 2.65k | } |
240 | | |
241 | 2.65k | ~ShepardsIDW() { |
242 | 2.65k | delete[] mWeights; |
243 | 2.65k | delete[] mWeightsNR; |
244 | 2.65k | delete[] mWeightsNB; |
245 | 2.65k | delete[] mWeightsC; |
246 | 2.65k | } |
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); |
326 | 333M | 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: |
353 | 8 | LookUpTable(size_t numEntries, std::function<float(float)> computeFunc) { |
354 | 272k | for (size_t idx = 0; idx < numEntries; idx++) { |
355 | 272k | float value = static_cast<float>(idx) / static_cast<float>(numEntries - 1); |
356 | 272k | table.push_back(computeFunc(value)); |
357 | 272k | } |
358 | 8 | } |
359 | 3.07G | 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 | | |
408 | 1.18G | 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 { |
459 | 2.65k | GainLUT(uhdr_gainmap_metadata_ext_t* metadata, float gainmapWeight) { |
460 | 2.65k | bool isSingleChannel = metadata->are_all_channels_identical(); |
461 | 5.46k | for (int i = 0; i < (isSingleChannel ? 1 : 3); i++) { |
462 | 2.80k | mGainTable[i] = memory[i] = new float[kGainFactorNumEntries]; |
463 | 2.80k | this->mGammaInv[i] = 1.0f / metadata->gamma[i]; |
464 | 2.87M | for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) { |
465 | 2.87M | float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1); |
466 | 2.87M | float logBoost = log2(metadata->min_content_boost[i]) * (1.0f - value) + |
467 | 2.87M | log2(metadata->max_content_boost[i]) * value; |
468 | 2.87M | mGainTable[i][idx] = exp2(logBoost * gainmapWeight); |
469 | 2.87M | } |
470 | 2.80k | } |
471 | 2.65k | if (isSingleChannel) { |
472 | 2.58k | memory[1] = memory[2] = nullptr; |
473 | 2.58k | mGammaInv[1] = mGammaInv[2] = mGammaInv[0]; |
474 | 2.58k | mGainTable[1] = mGainTable[2] = mGainTable[0]; |
475 | 2.58k | } |
476 | 2.65k | } |
477 | | |
478 | 0 | GainLUT(uhdr_gainmap_metadata_ext_t* metadata) : GainLUT(metadata, 1.0f) {} |
479 | | |
480 | 2.65k | ~GainLUT() { |
481 | 10.6k | for (int i = 0; i < 3; i++) { |
482 | 7.96k | if (memory[i]) { |
483 | 2.80k | delete[] memory[i]; |
484 | 2.80k | memory[i] = nullptr; |
485 | 2.80k | } |
486 | 7.96k | } |
487 | 2.65k | } |
488 | | |
489 | 1.20G | float getGainFactor(float gain, int index) { |
490 | 1.20G | if (mGammaInv[index] != 1.0f) gain = pow(gain, mGammaInv[index]); |
491 | 1.20G | int32_t idx = static_cast<int32_t>(gain * (kGainFactorNumEntries - 1) + 0.5); |
492 | | // TODO() : Remove once conversion modules have appropriate clamping in place |
493 | 1.20G | idx = CLIP3(idx, 0, kGainFactorNumEntries - 1); |
494 | 1.20G | return mGainTable[index][idx]; |
495 | 1.20G | } |
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 | | |
558 | 984M | 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 | 558 | 984M | 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) |
559 | | |
560 | 339M | static inline Color clipNegatives(Color e) { |
561 | 339M | return {{{clipNegatives(e.r), clipNegatives(e.g), clipNegatives(e.b)}}}; |
562 | 339M | } 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 | 560 | 339M | static inline Color clipNegatives(Color e) { | 561 | 339M | return {{{clipNegatives(e.r), clipNegatives(e.g), clipNegatives(e.b)}}}; | 562 | 339M | } |
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) |
563 | | |
564 | | // maximum limit of normalized pixel value in float representation |
565 | | static const float kMaxPixelFloat = 1.0f; |
566 | | |
567 | 5.33G | static inline float clampPixelFloat(float value) { |
568 | 5.33G | return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value; |
569 | 5.33G | } Unexecuted instantiation: ultrahdr_api.cpp:ultrahdr::clampPixelFloat(float) Unexecuted instantiation: editorhelper.cpp:ultrahdr::clampPixelFloat(float) gainmapmath.cpp:ultrahdr::clampPixelFloat(float) Line | Count | Source | 567 | 3.57G | static inline float clampPixelFloat(float value) { | 568 | 3.57G | return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value; | 569 | 3.57G | } |
jpegr.cpp:ultrahdr::clampPixelFloat(float) Line | Count | Source | 567 | 1.76G | static inline float clampPixelFloat(float value) { | 568 | 1.76G | return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value; | 569 | 1.76G | } |
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) |
570 | | |
571 | 604M | static inline Color clampPixelFloat(Color e) { |
572 | 604M | return {{{clampPixelFloat(e.r), clampPixelFloat(e.g), clampPixelFloat(e.b)}}}; |
573 | 604M | } 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 | 571 | 604M | static inline Color clampPixelFloat(Color e) { | 572 | 604M | return {{{clampPixelFloat(e.r), clampPixelFloat(e.g), clampPixelFloat(e.b)}}}; | 573 | 604M | } |
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) |
574 | | |
575 | | // maximum limit of pixel value for linear hdr intent raw resource |
576 | | static const float kMaxPixelFloatHdrLinear = 10000.0f / 203.0f; |
577 | | |
578 | 426M | static inline float clampPixelFloatLinear(float value) { |
579 | 426M | return CLIP3(value, 0.0f, kMaxPixelFloatHdrLinear); |
580 | 426M | } 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 | 578 | 426M | static inline float clampPixelFloatLinear(float value) { | 579 | 426M | return CLIP3(value, 0.0f, kMaxPixelFloatHdrLinear); | 580 | 426M | } |
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) |
581 | | |
582 | 145M | static inline Color clampPixelFloatLinear(Color e) { |
583 | 145M | return {{{clampPixelFloatLinear(e.r), clampPixelFloatLinear(e.g), clampPixelFloatLinear(e.b)}}}; |
584 | 145M | } 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 | 582 | 145M | static inline Color clampPixelFloatLinear(Color e) { | 583 | 145M | return {{{clampPixelFloatLinear(e.r), clampPixelFloatLinear(e.g), clampPixelFloatLinear(e.b)}}}; | 584 | 145M | } |
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) |
585 | | |
586 | 0 | static float mapNonFiniteFloats(float val) { |
587 | 0 | if (std::isinf(val)) { |
588 | 0 | return val > 0 ? kMaxPixelFloatHdrLinear : 0.0f; |
589 | 0 | } |
590 | | // nan |
591 | 0 | return 0.0f; |
592 | 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) |
593 | | |
594 | 0 | static inline Color sanitizePixel(Color e) { |
595 | 0 | float r = std::isfinite(e.r) ? clampPixelFloatLinear(e.r) : mapNonFiniteFloats(e.r); |
596 | 0 | float g = std::isfinite(e.g) ? clampPixelFloatLinear(e.g) : mapNonFiniteFloats(e.g); |
597 | 0 | float b = std::isfinite(e.b) ? clampPixelFloatLinear(e.b) : mapNonFiniteFloats(e.b); |
598 | 0 | return {{{r, g, b}}}; |
599 | 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) |
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 |