Loading libs/renderengine/tests/RenderEngineTest.cpp +8 −2 Original line number Diff line number Diff line Loading @@ -1562,15 +1562,21 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB; const vec3 scaledXYZ = scaleOotf(xyz, kCurrentLuminanceNits); const double gain = const auto gains = tonemap::getToneMapper() ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: Dataspace>(sourceDataspace), static_cast<aidl::android::hardware::graphics::common:: Dataspace>( ui::Dataspace::DISPLAY_P3), scaleOotf(linearRGB, kCurrentLuminanceNits), scaledXYZ, {tonemap:: Color{.linearRGB = scaleOotf(linearRGB, kCurrentLuminanceNits), .xyz = scaledXYZ}}, metadata); EXPECT_EQ(1, gains.size()); const double gain = gains.front(); const vec3 normalizedXYZ = scaledXYZ * gain / metadata.displayMaxLuminance; const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * normalizedXYZ) * 255; Loading libs/tonemap/include/tonemap/tonemap.h +12 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ struct Metadata { float contentMaxLuminance = 0.0; }; // Utility class containing pre-processed conversions for a particular color struct Color { // RGB color in linear space vec3 linearRGB; // CIE 1931 XYZ representation of the color vec3 xyz; }; class ToneMapper { public: virtual ~ToneMapper() {} Loading Loading @@ -108,10 +116,11 @@ public: // described by destinationDataspace. To compute the gain, the input colors are provided by // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also // provided. Metadata is also provided for helping to compute the tonemapping curve. virtual double lookupTonemapGain( using Gain = double; virtual std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0; const std::vector<Color>& colors, const Metadata& metadata) = 0; }; // Retrieves a tonemapper instance. Loading libs/tonemap/tonemap.cpp +206 −197 Original line number Diff line number Diff line Loading @@ -236,12 +236,17 @@ public: return uniforms; } double lookupTonemapGain( std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override { const std::vector<Color>& colors, const Metadata& metadata) override { std::vector<Gain> gains; gains.reserve(colors.size()); for (const auto [_, xyz] : colors) { if (xyz.y <= 0.0) { return 1.0; gains.push_back(1.0); continue; } const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); Loading @@ -255,9 +260,9 @@ public: targetNits = xyz.y; break; case kTransferHLG: // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so // we'll clamp the luminance range in case we're mapping from PQ input to // HLG output. // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, // so we'll clamp the luminance range in case we're mapping from PQ // input to HLG output. targetNits = std::clamp(xyz.y, 0.0f, 1000.0f); break; default: Loading Loading @@ -299,7 +304,8 @@ public: (1.0 - t) + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; } else { // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite // interp double t = (targetNits - x2) / h23; targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + Loading @@ -317,8 +323,8 @@ public: case kTransferST2084: case kTransferHLG: { // Map from SDR onto an HDR output buffer // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto // [0, maxOutLumi] which is hard-coded to be 3000 nits. // Here we use a polynomial curve to map from [0, displayMaxLuminance] // onto [0, maxOutLumi] which is hard-coded to be 3000 nits. const double maxOutLumi = 3000.0; double x0 = 5.0; Loading Loading @@ -364,8 +370,9 @@ public: break; } } return targetNits / xyz.y; gains.push_back(targetNits / xyz.y); } return gains; } }; Loading Loading @@ -427,8 +434,6 @@ public: break; default: // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. program.append(R"( float libtonemap_OETFTone(float channel) { channel = channel / 10000.0; Loading Loading @@ -548,14 +553,39 @@ public: return uniforms; } double lookupTonemapGain( std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override { const std::vector<Color>& colors, const Metadata& metadata) override { std::vector<Gain> gains; gains.reserve(colors.size()); // Precompute constants for HDR->SDR tonemapping parameters constexpr double maxInLumi = 4000; const double maxOutLumi = metadata.displayMaxLuminance; const double x1 = maxOutLumi * 0.65; const double y1 = x1; const double x3 = maxInLumi; const double y3 = maxOutLumi; const double x2 = x1 + (x3 - x1) * 4.0 / 17.0; const double y2 = maxOutLumi * 0.9; const double greyNorm1 = OETF_ST2084(x1); const double greyNorm2 = OETF_ST2084(x2); const double greyNorm3 = OETF_ST2084(x3); const double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); const double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); for (const auto [linearRGB, _] : colors) { double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b}); if (maxRGB <= 0.0) { return 1.0; gains.push_back(1.0); continue; } const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); Loading @@ -569,36 +599,13 @@ public: targetNits = maxRGB; break; case kTransferHLG: // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so // we'll clamp the luminance range in case we're mapping from PQ input to // HLG output. // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, // so we'll clamp the luminance range in case we're mapping from PQ // input to HLG output. targetNits = std::clamp(maxRGB, 0.0, 1000.0); break; default: // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. double maxInLumi = 4000; double maxOutLumi = metadata.displayMaxLuminance; targetNits = maxRGB; double x1 = maxOutLumi * 0.65; double y1 = x1; double x3 = maxInLumi; double y3 = maxOutLumi; double x2 = x1 + (x3 - x1) * 4.0 / 17.0; double y2 = maxOutLumi * 0.9; const double greyNorm1 = OETF_ST2084(x1); const double greyNorm2 = OETF_ST2084(x2); const double greyNorm3 = OETF_ST2084(x3); double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); if (targetNits < x1) { break; } Loading Loading @@ -636,7 +643,9 @@ public: break; } return targetNits / maxRGB; gains.push_back(targetNits / maxRGB); } return gains; } }; Loading Loading
libs/renderengine/tests/RenderEngineTest.cpp +8 −2 Original line number Diff line number Diff line Loading @@ -1562,15 +1562,21 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB; const vec3 scaledXYZ = scaleOotf(xyz, kCurrentLuminanceNits); const double gain = const auto gains = tonemap::getToneMapper() ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: Dataspace>(sourceDataspace), static_cast<aidl::android::hardware::graphics::common:: Dataspace>( ui::Dataspace::DISPLAY_P3), scaleOotf(linearRGB, kCurrentLuminanceNits), scaledXYZ, {tonemap:: Color{.linearRGB = scaleOotf(linearRGB, kCurrentLuminanceNits), .xyz = scaledXYZ}}, metadata); EXPECT_EQ(1, gains.size()); const double gain = gains.front(); const vec3 normalizedXYZ = scaledXYZ * gain / metadata.displayMaxLuminance; const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * normalizedXYZ) * 255; Loading
libs/tonemap/include/tonemap/tonemap.h +12 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ struct Metadata { float contentMaxLuminance = 0.0; }; // Utility class containing pre-processed conversions for a particular color struct Color { // RGB color in linear space vec3 linearRGB; // CIE 1931 XYZ representation of the color vec3 xyz; }; class ToneMapper { public: virtual ~ToneMapper() {} Loading Loading @@ -108,10 +116,11 @@ public: // described by destinationDataspace. To compute the gain, the input colors are provided by // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also // provided. Metadata is also provided for helping to compute the tonemapping curve. virtual double lookupTonemapGain( using Gain = double; virtual std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0; const std::vector<Color>& colors, const Metadata& metadata) = 0; }; // Retrieves a tonemapper instance. Loading
libs/tonemap/tonemap.cpp +206 −197 Original line number Diff line number Diff line Loading @@ -236,12 +236,17 @@ public: return uniforms; } double lookupTonemapGain( std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override { const std::vector<Color>& colors, const Metadata& metadata) override { std::vector<Gain> gains; gains.reserve(colors.size()); for (const auto [_, xyz] : colors) { if (xyz.y <= 0.0) { return 1.0; gains.push_back(1.0); continue; } const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); Loading @@ -255,9 +260,9 @@ public: targetNits = xyz.y; break; case kTransferHLG: // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so // we'll clamp the luminance range in case we're mapping from PQ input to // HLG output. // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, // so we'll clamp the luminance range in case we're mapping from PQ // input to HLG output. targetNits = std::clamp(xyz.y, 0.0f, 1000.0f); break; default: Loading Loading @@ -299,7 +304,8 @@ public: (1.0 - t) + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; } else { // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite // interp double t = (targetNits - x2) / h23; targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + Loading @@ -317,8 +323,8 @@ public: case kTransferST2084: case kTransferHLG: { // Map from SDR onto an HDR output buffer // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto // [0, maxOutLumi] which is hard-coded to be 3000 nits. // Here we use a polynomial curve to map from [0, displayMaxLuminance] // onto [0, maxOutLumi] which is hard-coded to be 3000 nits. const double maxOutLumi = 3000.0; double x0 = 5.0; Loading Loading @@ -364,8 +370,9 @@ public: break; } } return targetNits / xyz.y; gains.push_back(targetNits / xyz.y); } return gains; } }; Loading Loading @@ -427,8 +434,6 @@ public: break; default: // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. program.append(R"( float libtonemap_OETFTone(float channel) { channel = channel / 10000.0; Loading Loading @@ -548,14 +553,39 @@ public: return uniforms; } double lookupTonemapGain( std::vector<Gain> lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override { const std::vector<Color>& colors, const Metadata& metadata) override { std::vector<Gain> gains; gains.reserve(colors.size()); // Precompute constants for HDR->SDR tonemapping parameters constexpr double maxInLumi = 4000; const double maxOutLumi = metadata.displayMaxLuminance; const double x1 = maxOutLumi * 0.65; const double y1 = x1; const double x3 = maxInLumi; const double y3 = maxOutLumi; const double x2 = x1 + (x3 - x1) * 4.0 / 17.0; const double y2 = maxOutLumi * 0.9; const double greyNorm1 = OETF_ST2084(x1); const double greyNorm2 = OETF_ST2084(x2); const double greyNorm3 = OETF_ST2084(x3); const double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); const double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); for (const auto [linearRGB, _] : colors) { double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b}); if (maxRGB <= 0.0) { return 1.0; gains.push_back(1.0); continue; } const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); Loading @@ -569,36 +599,13 @@ public: targetNits = maxRGB; break; case kTransferHLG: // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so // we'll clamp the luminance range in case we're mapping from PQ input to // HLG output. // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, // so we'll clamp the luminance range in case we're mapping from PQ // input to HLG output. targetNits = std::clamp(maxRGB, 0.0, 1000.0); break; default: // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. double maxInLumi = 4000; double maxOutLumi = metadata.displayMaxLuminance; targetNits = maxRGB; double x1 = maxOutLumi * 0.65; double y1 = x1; double x3 = maxInLumi; double y3 = maxOutLumi; double x2 = x1 + (x3 - x1) * 4.0 / 17.0; double y2 = maxOutLumi * 0.9; const double greyNorm1 = OETF_ST2084(x1); const double greyNorm2 = OETF_ST2084(x2); const double greyNorm3 = OETF_ST2084(x3); double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); if (targetNits < x1) { break; } Loading Loading @@ -636,7 +643,9 @@ public: break; } return targetNits / maxRGB; gains.push_back(targetNits / maxRGB); } return gains; } }; Loading