blob: c2372fe82885a97b0204fc00ad5c0d5ae43530f2 [file] [log] [blame]
Alec Mouri465b2962021-10-08 16:22:21 -07001/*
2 * Copyright 2021 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#include <tonemap/tonemap.h>
18
Alec Mouri4049b532021-10-15 20:59:33 -070019#include <algorithm>
Alec Mouri465b2962021-10-08 16:22:21 -070020#include <cstdint>
21#include <mutex>
22#include <type_traits>
23
24namespace android::tonemap {
25
26namespace {
27
28// Flag containing the variant of tone map algorithm to use.
29enum class ToneMapAlgorithm {
Alec Mouri5184f412021-10-14 18:13:49 -070030 AndroidO, // Default algorithm in place since Android O,
31 Android13, // Algorithm used in Android 13.
Alec Mouri465b2962021-10-08 16:22:21 -070032};
33
Alec Mouri5184f412021-10-14 18:13:49 -070034static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13;
Alec Mouri465b2962021-10-08 16:22:21 -070035
36static const constexpr auto kTransferMask =
37 static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK);
38static const constexpr auto kTransferST2084 =
39 static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084);
40static const constexpr auto kTransferHLG =
41 static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG);
42
43template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
44std::vector<uint8_t> buildUniformValue(T value) {
45 std::vector<uint8_t> result;
46 result.resize(sizeof(value));
47 std::memcpy(result.data(), &value, sizeof(value));
48 return result;
49}
50
51class ToneMapperO : public ToneMapper {
52public:
53 std::string generateTonemapGainShaderSkSL(
54 aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
55 aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
56 const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
57 const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
58
59 std::string program;
60 // Define required uniforms
61 program.append(R"(
62 uniform float in_libtonemap_displayMaxLuminance;
63 uniform float in_libtonemap_inputMaxLuminance;
64 )");
65 switch (sourceDataspaceInt & kTransferMask) {
66 case kTransferST2084:
67 case kTransferHLG:
68 switch (destinationDataspaceInt & kTransferMask) {
69 case kTransferST2084:
70 program.append(R"(
71 float libtonemap_ToneMapTargetNits(vec3 xyz) {
72 return xyz.y;
73 }
74 )");
75 break;
76 case kTransferHLG:
77 // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
78 // we'll clamp the luminance range in case we're mapping from PQ input to
79 // HLG output.
80 program.append(R"(
81 float libtonemap_ToneMapTargetNits(vec3 xyz) {
82 return clamp(xyz.y, 0.0, 1000.0);
83 }
84 )");
85 break;
86 default:
87 // Here we're mapping from HDR to SDR content, so interpolate using a
88 // Hermitian polynomial onto the smaller luminance range.
89 program.append(R"(
90 float libtonemap_ToneMapTargetNits(vec3 xyz) {
91 float maxInLumi = in_libtonemap_inputMaxLuminance;
92 float maxOutLumi = in_libtonemap_displayMaxLuminance;
93
94 float nits = xyz.y;
95
96 // if the max input luminance is less than what we can
97 // output then no tone mapping is needed as all color
98 // values will be in range.
99 if (maxInLumi <= maxOutLumi) {
100 return xyz.y;
101 } else {
102
103 // three control points
104 const float x0 = 10.0;
105 const float y0 = 17.0;
106 float x1 = maxOutLumi * 0.75;
107 float y1 = x1;
108 float x2 = x1 + (maxInLumi - x1) / 2.0;
109 float y2 = y1 + (maxOutLumi - y1) * 0.75;
110
111 // horizontal distances between the last three
112 // control points
113 float h12 = x2 - x1;
114 float h23 = maxInLumi - x2;
115 // tangents at the last three control points
116 float m1 = (y2 - y1) / h12;
117 float m3 = (maxOutLumi - y2) / h23;
118 float m2 = (m1 + m3) / 2.0;
119
120 if (nits < x0) {
121 // scale [0.0, x0] to [0.0, y0] linearly
122 float slope = y0 / x0;
123 return nits * slope;
124 } else if (nits < x1) {
125 // scale [x0, x1] to [y0, y1] linearly
126 float slope = (y1 - y0) / (x1 - x0);
127 nits = y0 + (nits - x0) * slope;
128 } else if (nits < x2) {
129 // scale [x1, x2] to [y1, y2] using Hermite interp
130 float t = (nits - x1) / h12;
131 nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) *
132 (1.0 - t) * (1.0 - t) +
133 (y2 * (3.0 - 2.0 * t) +
134 h12 * m2 * (t - 1.0)) * t * t;
135 } else {
136 // scale [x2, maxInLumi] to [y2, maxOutLumi] using
137 // Hermite interp
138 float t = (nits - x2) / h23;
139 nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) *
140 (1.0 - t) * (1.0 - t) + (maxOutLumi *
141 (3.0 - 2.0 * t) + h23 * m3 *
142 (t - 1.0)) * t * t;
143 }
144 }
145
146 return nits;
147 }
148 )");
149 break;
150 }
151 break;
152 default:
153 switch (destinationDataspaceInt & kTransferMask) {
154 case kTransferST2084:
155 case kTransferHLG:
156 // Map from SDR onto an HDR output buffer
157 // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
158 // [0, maxOutLumi] which is hard-coded to be 3000 nits.
159 program.append(R"(
160 float libtonemap_ToneMapTargetNits(vec3 xyz) {
161 const float maxOutLumi = 3000.0;
162
163 const float x0 = 5.0;
164 const float y0 = 2.5;
165 float x1 = in_libtonemap_displayMaxLuminance * 0.7;
166 float y1 = maxOutLumi * 0.15;
167 float x2 = in_libtonemap_displayMaxLuminance * 0.9;
168 float y2 = maxOutLumi * 0.45;
169 float x3 = in_libtonemap_displayMaxLuminance;
170 float y3 = maxOutLumi;
171
172 float c1 = y1 / 3.0;
173 float c2 = y2 / 2.0;
174 float c3 = y3 / 1.5;
175
176 float nits = xyz.y;
177
178 if (nits <= x0) {
179 // scale [0.0, x0] to [0.0, y0] linearly
180 float slope = y0 / x0;
181 return nits * slope;
182 } else if (nits <= x1) {
183 // scale [x0, x1] to [y0, y1] using a curve
184 float t = (nits - x0) / (x1 - x0);
185 nits = (1.0 - t) * (1.0 - t) * y0 +
186 2.0 * (1.0 - t) * t * c1 + t * t * y1;
187 } else if (nits <= x2) {
188 // scale [x1, x2] to [y1, y2] using a curve
189 float t = (nits - x1) / (x2 - x1);
190 nits = (1.0 - t) * (1.0 - t) * y1 +
191 2.0 * (1.0 - t) * t * c2 + t * t * y2;
192 } else {
193 // scale [x2, x3] to [y2, y3] using a curve
194 float t = (nits - x2) / (x3 - x2);
195 nits = (1.0 - t) * (1.0 - t) * y2 +
196 2.0 * (1.0 - t) * t * c3 + t * t * y3;
197 }
198
199 return nits;
200 }
201 )");
202 break;
203 default:
204 // For completeness, this is tone-mapping from SDR to SDR, where this is
205 // just a no-op.
206 program.append(R"(
207 float libtonemap_ToneMapTargetNits(vec3 xyz) {
208 return xyz.y;
209 }
210 )");
211 break;
212 }
213 break;
214 }
215
216 program.append(R"(
217 float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
218 if (xyz.y <= 0.0) {
219 return 1.0;
220 }
221 return libtonemap_ToneMapTargetNits(xyz) / xyz.y;
222 }
223 )");
224 return program;
225 }
226
227 std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
228 std::vector<ShaderUniform> uniforms;
229
230 uniforms.reserve(2);
231
232 uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
233 .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
234 uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
235 .value = buildUniformValue<float>(metadata.contentMaxLuminance)});
Alec Mouri5184f412021-10-14 18:13:49 -0700236 return uniforms;
237 }
Alec Mouri4049b532021-10-15 20:59:33 -0700238
239 double lookupTonemapGain(
240 aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
241 aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
242 vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override {
243 if (xyz.y <= 0.0) {
244 return 1.0;
245 }
246 const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
247 const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
248
249 double targetNits = 0.0;
250 switch (sourceDataspaceInt & kTransferMask) {
251 case kTransferST2084:
252 case kTransferHLG:
253 switch (destinationDataspaceInt & kTransferMask) {
254 case kTransferST2084:
255 targetNits = xyz.y;
256 break;
257 case kTransferHLG:
258 // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
259 // we'll clamp the luminance range in case we're mapping from PQ input to
260 // HLG output.
261 targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
262 break;
263 default:
264 // Here we're mapping from HDR to SDR content, so interpolate using a
265 // Hermitian polynomial onto the smaller luminance range.
266
267 targetNits = xyz.y;
268 // if the max input luminance is less than what we can output then
269 // no tone mapping is needed as all color values will be in range.
270 if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
271 // three control points
272 const double x0 = 10.0;
273 const double y0 = 17.0;
274 double x1 = metadata.displayMaxLuminance * 0.75;
275 double y1 = x1;
276 double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
277 double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
278
279 // horizontal distances between the last three control points
280 double h12 = x2 - x1;
281 double h23 = metadata.contentMaxLuminance - x2;
282 // tangents at the last three control points
283 double m1 = (y2 - y1) / h12;
284 double m3 = (metadata.displayMaxLuminance - y2) / h23;
285 double m2 = (m1 + m3) / 2.0;
286
287 if (targetNits < x0) {
288 // scale [0.0, x0] to [0.0, y0] linearly
289 double slope = y0 / x0;
290 targetNits *= slope;
291 } else if (targetNits < x1) {
292 // scale [x0, x1] to [y0, y1] linearly
293 double slope = (y1 - y0) / (x1 - x0);
294 targetNits = y0 + (targetNits - x0) * slope;
295 } else if (targetNits < x2) {
296 // scale [x1, x2] to [y1, y2] using Hermite interp
297 double t = (targetNits - x1) / h12;
298 targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) *
299 (1.0 - t) +
300 (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
301 } else {
302 // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
303 double t = (targetNits - x2) / h23;
304 targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
305 (1.0 - t) +
306 (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
307 h23 * m3 * (t - 1.0)) *
308 t * t;
309 }
310 }
311 break;
312 }
313 break;
314 default:
315 // source is SDR
316 switch (destinationDataspaceInt & kTransferMask) {
317 case kTransferST2084:
318 case kTransferHLG: {
319 // Map from SDR onto an HDR output buffer
320 // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
321 // [0, maxOutLumi] which is hard-coded to be 3000 nits.
322 const double maxOutLumi = 3000.0;
323
324 double x0 = 5.0;
325 double y0 = 2.5;
326 double x1 = metadata.displayMaxLuminance * 0.7;
327 double y1 = maxOutLumi * 0.15;
328 double x2 = metadata.displayMaxLuminance * 0.9;
329 double y2 = maxOutLumi * 0.45;
330 double x3 = metadata.displayMaxLuminance;
331 double y3 = maxOutLumi;
332
333 double c1 = y1 / 3.0;
334 double c2 = y2 / 2.0;
335 double c3 = y3 / 1.5;
336
337 targetNits = xyz.y;
338
339 if (targetNits <= x0) {
340 // scale [0.0, x0] to [0.0, y0] linearly
341 double slope = y0 / x0;
342 targetNits *= slope;
343 } else if (targetNits <= x1) {
344 // scale [x0, x1] to [y0, y1] using a curve
345 double t = (targetNits - x0) / (x1 - x0);
346 targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
347 t * t * y1;
348 } else if (targetNits <= x2) {
349 // scale [x1, x2] to [y1, y2] using a curve
350 double t = (targetNits - x1) / (x2 - x1);
351 targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 +
352 t * t * y2;
353 } else {
354 // scale [x2, x3] to [y2, y3] using a curve
355 double t = (targetNits - x2) / (x3 - x2);
356 targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
357 t * t * y3;
358 }
359 } break;
360 default:
361 // For completeness, this is tone-mapping from SDR to SDR, where this is
362 // just a no-op.
363 targetNits = xyz.y;
364 break;
365 }
366 }
367
368 return targetNits / xyz.y;
369 }
Alec Mouri5184f412021-10-14 18:13:49 -0700370};
Alec Mouri465b2962021-10-08 16:22:21 -0700371
Alec Mouri5184f412021-10-14 18:13:49 -0700372class ToneMapper13 : public ToneMapper {
Alec Mouri4049b532021-10-15 20:59:33 -0700373private:
374 double OETF_ST2084(double nits) {
375 nits = nits / 10000.0;
376 double m1 = (2610.0 / 4096.0) / 4.0;
377 double m2 = (2523.0 / 4096.0) * 128.0;
378 double c1 = (3424.0 / 4096.0);
379 double c2 = (2413.0 / 4096.0) * 32.0;
380 double c3 = (2392.0 / 4096.0) * 32.0;
381
382 double tmp = std::pow(nits, m1);
383 tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
384 return std::pow(tmp, m2);
385 }
386
387 double OETF_HLG(double nits) {
388 nits = nits / 1000.0;
389 const double a = 0.17883277;
390 const double b = 0.28466892;
391 const double c = 0.55991073;
392 return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c;
393 }
394
Alec Mouri5184f412021-10-14 18:13:49 -0700395public:
396 std::string generateTonemapGainShaderSkSL(
397 aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
398 aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
399 const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
400 const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
401
402 std::string program;
403 // Input uniforms
404 program.append(R"(
405 uniform float in_libtonemap_displayMaxLuminance;
406 uniform float in_libtonemap_inputMaxLuminance;
407 )");
408 switch (sourceDataspaceInt & kTransferMask) {
409 case kTransferST2084:
410 case kTransferHLG:
411 switch (destinationDataspaceInt & kTransferMask) {
412 case kTransferST2084:
413 program.append(R"(
414 float libtonemap_ToneMapTargetNits(float maxRGB) {
415 return maxRGB;
416 }
417 )");
418 break;
419 case kTransferHLG:
420 // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
421 // we'll clamp the luminance range in case we're mapping from PQ input to
422 // HLG output.
423 program.append(R"(
424 float libtonemap_ToneMapTargetNits(float maxRGB) {
425 return clamp(maxRGB, 0.0, 1000.0);
426 }
427 )");
428 break;
429
430 default:
431 switch (sourceDataspaceInt & kTransferMask) {
432 case kTransferST2084:
433 program.append(R"(
434 float libtonemap_OETFTone(float channel) {
435 channel = channel / 10000.0;
436 float m1 = (2610.0 / 4096.0) / 4.0;
437 float m2 = (2523.0 / 4096.0) * 128.0;
438 float c1 = (3424.0 / 4096.0);
439 float c2 = (2413.0 / 4096.0) * 32.0;
440 float c3 = (2392.0 / 4096.0) * 32.0;
441
442 float tmp = pow(channel, float(m1));
443 tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
444 return pow(tmp, float(m2));
445 }
446 )");
447 break;
448 case kTransferHLG:
449 program.append(R"(
450 float libtonemap_OETFTone(float channel) {
451 channel = channel / 1000.0;
452 const float a = 0.17883277;
453 const float b = 0.28466892;
454 const float c = 0.55991073;
455 return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
456 a * log(12.0 * channel - b) + c;
457 }
458 )");
459 break;
460 }
461 // Here we're mapping from HDR to SDR content, so interpolate using a
462 // Hermitian polynomial onto the smaller luminance range.
463 program.append(R"(
464 float libtonemap_ToneMapTargetNits(float maxRGB) {
465 float maxInLumi = in_libtonemap_inputMaxLuminance;
466 float maxOutLumi = in_libtonemap_displayMaxLuminance;
467
468 float nits = maxRGB;
469
470 float x1 = maxOutLumi * 0.65;
471 float y1 = x1;
472
473 float x3 = maxInLumi;
474 float y3 = maxOutLumi;
475
476 float x2 = x1 + (x3 - x1) * 4.0 / 17.0;
477 float y2 = maxOutLumi * 0.9;
478
479 float greyNorm1 = libtonemap_OETFTone(x1);
480 float greyNorm2 = libtonemap_OETFTone(x2);
481 float greyNorm3 = libtonemap_OETFTone(x3);
482
483 float slope1 = 0;
484 float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
485 float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2);
486
487 if (nits < x1) {
488 return nits;
489 }
490
491 if (nits > maxInLumi) {
492 return maxOutLumi;
493 }
494
495 float greyNits = libtonemap_OETFTone(nits);
496
497 if (greyNits <= greyNorm2) {
498 nits = (greyNits - greyNorm2) * slope2 + y2;
499 } else if (greyNits <= greyNorm3) {
500 nits = (greyNits - greyNorm3) * slope3 + y3;
501 } else {
502 nits = maxOutLumi;
503 }
504
505 return nits;
506 }
507 )");
508 break;
509 }
510 break;
511 default:
512 // Inverse tone-mapping and SDR-SDR mapping is not supported.
513 program.append(R"(
514 float libtonemap_ToneMapTargetNits(float maxRGB) {
515 return maxRGB;
516 }
517 )");
518 break;
519 }
520
521 program.append(R"(
522 float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
523 float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b));
524 if (maxRGB <= 0.0) {
525 return 1.0;
526 }
527 return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB;
528 }
529 )");
530 return program;
531 }
532
533 std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
534 // Hardcode the max content luminance to a "reasonable" level
535 static const constexpr float kContentMaxLuminance = 4000.f;
536 std::vector<ShaderUniform> uniforms;
537 uniforms.reserve(2);
538 uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
539 .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
540 uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
541 .value = buildUniformValue<float>(kContentMaxLuminance)});
Alec Mouri465b2962021-10-08 16:22:21 -0700542 return uniforms;
543 }
Alec Mouri4049b532021-10-15 20:59:33 -0700544
545 double lookupTonemapGain(
546 aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
547 aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
548 vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override {
549 double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
550
551 if (maxRGB <= 0.0) {
552 return 1.0;
553 }
554
555 const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
556 const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
557
558 double targetNits = 0.0;
559 switch (sourceDataspaceInt & kTransferMask) {
560 case kTransferST2084:
561 case kTransferHLG:
562 switch (destinationDataspaceInt & kTransferMask) {
563 case kTransferST2084:
564 targetNits = maxRGB;
565 break;
566 case kTransferHLG:
567 // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
568 // we'll clamp the luminance range in case we're mapping from PQ input to
569 // HLG output.
570 targetNits = std::clamp(maxRGB, 0.0, 1000.0);
571 break;
572 default:
573 // Here we're mapping from HDR to SDR content, so interpolate using a
574 // Hermitian polynomial onto the smaller luminance range.
575
576 double maxInLumi = 4000;
577 double maxOutLumi = metadata.displayMaxLuminance;
578
579 targetNits = maxRGB;
580
581 double x1 = maxOutLumi * 0.65;
582 double y1 = x1;
583
584 double x3 = maxInLumi;
585 double y3 = maxOutLumi;
586
587 double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
588 double y2 = maxOutLumi * 0.9;
589
590 double greyNorm1 = 0.0;
591 double greyNorm2 = 0.0;
592 double greyNorm3 = 0.0;
593
594 if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
595 greyNorm1 = OETF_ST2084(x1);
596 greyNorm2 = OETF_ST2084(x2);
597 greyNorm3 = OETF_ST2084(x3);
598 } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
599 greyNorm1 = OETF_HLG(x1);
600 greyNorm2 = OETF_HLG(x2);
601 greyNorm3 = OETF_HLG(x3);
602 }
603
604 double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
605 double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
606
607 if (targetNits < x1) {
608 break;
609 }
610
611 if (targetNits > maxInLumi) {
612 targetNits = maxOutLumi;
613 break;
614 }
615
616 double greyNits = 0.0;
617 if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
618 greyNits = OETF_ST2084(targetNits);
619 } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
620 greyNits = OETF_HLG(targetNits);
621 }
622
623 if (greyNits <= greyNorm2) {
624 targetNits = (greyNits - greyNorm2) * slope2 + y2;
625 } else if (greyNits <= greyNorm3) {
626 targetNits = (greyNits - greyNorm3) * slope3 + y3;
627 } else {
628 targetNits = maxOutLumi;
629 }
630 break;
631 }
632 break;
633 default:
634 switch (destinationDataspaceInt & kTransferMask) {
635 case kTransferST2084:
636 case kTransferHLG:
637 default:
638 targetNits = maxRGB;
639 break;
640 }
641 break;
642 }
643
644 return targetNits / maxRGB;
645 }
Alec Mouri465b2962021-10-08 16:22:21 -0700646};
647
648} // namespace
649
650ToneMapper* getToneMapper() {
651 static std::once_flag sOnce;
652 static std::unique_ptr<ToneMapper> sToneMapper;
653
654 std::call_once(sOnce, [&] {
655 switch (kToneMapAlgorithm) {
656 case ToneMapAlgorithm::AndroidO:
657 sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO());
658 break;
Alec Mouri5184f412021-10-14 18:13:49 -0700659 case ToneMapAlgorithm::Android13:
660 sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13());
Alec Mouri465b2962021-10-08 16:22:21 -0700661 }
662 });
663
664 return sToneMapper.get();
665}
Alec Mouri465b2962021-10-08 16:22:21 -0700666} // namespace android::tonemap