From a131f09b995281f1cd0a872c54cbd8f20df73d83 Mon Sep 17 00:00:00 2001 From: Ram Mohan M Date: Thu, 18 Jul 2024 15:22:25 +0530 Subject: [PATCH] Allow for larger excursions of min, max content boost Current implementation clips min/max content boost to a smaller range. Allow for larger excursions for better hdr intent representation. Also, fixed round factor before encoding gainmap coefficient Test: ./ultrahdr_unit_test --- lib/include/ultrahdr/gainmapmath.h | 21 +--- lib/src/gainmapmath.cpp | 36 +++---- lib/src/jpegr.cpp | 166 +++++++++++++++++++---------- tests/gainmapmath_test.cpp | 129 ++++++++++++---------- 4 files changed, 203 insertions(+), 149 deletions(-) diff --git a/lib/include/ultrahdr/gainmapmath.h b/lib/include/ultrahdr/gainmapmath.h index 9ffef38e..a74ce21c 100644 --- a/lib/include/ultrahdr/gainmapmath.h +++ b/lib/include/ultrahdr/gainmapmath.h @@ -194,7 +194,7 @@ struct GainLUT { GainLUT(uhdr_gainmap_metadata_ext_t* metadata, float displayBoost) { this->mGammaInv = 1.0f / metadata->gamma; - float boostFactor = displayBoost > 0 ? displayBoost / metadata->max_content_boost : 1.0f; + float boostFactor = displayBoost > 0 ? displayBoost / metadata->hdr_capacity_max : 1.0f; for (int32_t idx = 0; idx < kGainFactorNumEntries; idx++) { float value = static_cast(idx) / static_cast(kGainFactorNumEntries - 1); float logBoost = log2(metadata->min_content_boost) * (1.0f - value) + @@ -540,23 +540,14 @@ void transformYuv444(uhdr_raw_image_t* image, const std::array& coeffs /* * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR - * luminances in linear space, and the hdr ratio to encode against. - * - * Note: since this library always uses gamma of 1.0, offsetSdr of 0.0, and - * offsetHdr of 0.0, this function doesn't handle different metadata values for - * these fields. + * luminances in linear space and gainmap metadata fields. */ -uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata); -uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata, - float log2MinContentBoost, float log2MaxContentBoost); +float computeGain(float sdr, float hdr); +uint8_t affineMapGain(float gainlog2, float mingainlog2, float maxgainlog2, float gamma); /* * Calculates the linear luminance in nits after applying the given gain * value, with the given hdr ratio, to the given sdr input in the range [0, 1]. - * - * Note: similar to encodeGain(), this function only supports gamma 1.0, - * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to - * gainMapMax, as this library encodes. */ Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata); Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata, float displayBoost); @@ -565,10 +556,6 @@ Color applyGainLUT(Color e, float gain, GainLUT& gainLUT); /* * Apply gain in R, G and B channels, with the given hdr ratio, to the given sdr input * in the range [0, 1]. - * - * Note: similar to encodeGain(), this function only supports gamma 1.0, - * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to - * gainMapMax, as this library encodes. */ Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata); Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata, float displayBoost); diff --git a/lib/src/gainmapmath.cpp b/lib/src/gainmapmath.cpp index 90a5c03e..bb8b7d81 100644 --- a/lib/src/gainmapmath.cpp +++ b/lib/src/gainmapmath.cpp @@ -636,24 +636,20 @@ void transformYuv444(uhdr_raw_image_t* image, const std::array& coeffs //////////////////////////////////////////////////////////////////////////////// // Gain map calculations -uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata) { - return encodeGain(y_sdr, y_hdr, metadata, log2(metadata->min_content_boost), - log2(metadata->max_content_boost)); -} - -uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metadata, - float log2MinContentBoost, float log2MaxContentBoost) { - float gain = 1.0f; - if (y_sdr > 0.0f) { - gain = y_hdr / y_sdr; +float computeGain(float sdr, float hdr) { + if (sdr == 0.0f) return 0.0f; // for sdr black return no gain + if (hdr == 0.0f) { // for hdr black, return a gain large enough to attenuate the sdr pel + float offset = (1.0f / 64); + return log2(offset / (offset + sdr)); } + return log2(hdr / sdr); +} - if (gain < metadata->min_content_boost) gain = metadata->min_content_boost; - if (gain > metadata->max_content_boost) gain = metadata->max_content_boost; - float gain_normalized = - (log2(gain) - log2MinContentBoost) / (log2MaxContentBoost - log2MinContentBoost); - float gain_normalized_gamma = powf(gain_normalized, metadata->gamma); - return static_cast(gain_normalized_gamma * 255.0f); +uint8_t affineMapGain(float gainlog2, float mingainlog2, float maxgainlog2, float gamma) { + float mappedVal = (gainlog2 - mingainlog2) / (maxgainlog2 - mingainlog2); + if (gamma != 1.0f) mappedVal = pow(mappedVal, gamma); + mappedVal *= 255; + return CLIP3(mappedVal + 0.5f, 0, 255); } Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata) { @@ -668,7 +664,7 @@ Color applyGain(Color e, float gain, uhdr_gainmap_metadata_ext_t* metadata, floa gain = pow(gain, 1.0f / metadata->gamma); float logBoost = log2(metadata->min_content_boost) * (1.0f - gain) + log2(metadata->max_content_boost) * gain; - float gainFactor = exp2(logBoost * displayBoost / metadata->max_content_boost); + float gainFactor = exp2(logBoost * displayBoost / metadata->hdr_capacity_max); return e * gainFactor; } @@ -697,9 +693,9 @@ Color applyGain(Color e, Color gain, uhdr_gainmap_metadata_ext_t* metadata, floa log2(metadata->max_content_boost) * gain.g; float logBoostB = log2(metadata->min_content_boost) * (1.0f - gain.b) + log2(metadata->max_content_boost) * gain.b; - float gainFactorR = exp2(logBoostR * displayBoost / metadata->max_content_boost); - float gainFactorG = exp2(logBoostG * displayBoost / metadata->max_content_boost); - float gainFactorB = exp2(logBoostB * displayBoost / metadata->max_content_boost); + float gainFactorR = exp2(logBoostR * displayBoost / metadata->hdr_capacity_max); + float gainFactorG = exp2(logBoostG * displayBoost / metadata->hdr_capacity_max); + float gainFactorB = exp2(logBoostB * displayBoost / metadata->hdr_capacity_max); return {{{e.r * gainFactorR, e.g * gainFactorG, e.b * gainFactorB}}}; } diff --git a/lib/src/jpegr.cpp b/lib/src/jpegr.cpp index 4fc22dd4..8707bebf 100644 --- a/lib/src/jpegr.cpp +++ b/lib/src/jpegr.cpp @@ -514,17 +514,6 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ return status; } - gainmap_metadata->max_content_boost = hdr_white_nits / kSdrWhiteNits; - gainmap_metadata->min_content_boost = 1.0f; - gainmap_metadata->gamma = mGamma; - gainmap_metadata->offset_sdr = 0.0f; - gainmap_metadata->offset_hdr = 0.0f; - gainmap_metadata->hdr_capacity_min = 1.0f; - gainmap_metadata->hdr_capacity_max = gainmap_metadata->max_content_boost; - - float log2MinBoost = log2(gainmap_metadata->min_content_boost); - float log2MaxBoost = log2(gainmap_metadata->max_content_boost); - ColorTransformFn hdrGamutConversionFn = getGamutConversionFn(sdr_intent->cg, hdr_intent->cg); if (hdrGamutConversionFn == nullptr) { status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; @@ -608,18 +597,29 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, map_width, map_height, 64); uhdr_raw_image_ext_t* dest = gainmap_img.get(); + uhdr_memory_block_t gainmap_mem(map_width * map_height * sizeof(float) * + (mUseMultiChannelGainMap ? 3 : 1)); + float* gainmap_data = reinterpret_cast(gainmap_mem.m_buffer.get()); + float gainmap_min[3] = {127.0f, 127.0f, 127.0f}; + float gainmap_max[3] = {-128.0f, -128.0f, -128.0f}; + std::mutex gainmap_minmax; + const int threads = (std::min)(GetCPUCoreCount(), 4); const int jobSizeInRows = 1; size_t rowStep = threads == 1 ? map_height : jobSizeInRows; JobQueue jobQueue; - std::function generateMap = - [this, sdr_intent, hdr_intent, gainmap_metadata, dest, hdrInvOetf, hdrGamutConversionFn, - luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn, hdr_sample_pixel_fn, - hdr_white_nits, log2MinBoost, log2MaxBoost, use_luminance, &jobQueue]() -> void { + std::function generateMap = [this, sdr_intent, hdr_intent, gainmap_data, map_width, + hdrInvOetf, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, + hdrYuvToRgbFn, sdr_sample_pixel_fn, hdr_sample_pixel_fn, + hdr_white_nits, use_luminance, &gainmap_min, &gainmap_max, + &gainmap_minmax, &jobQueue]() -> void { size_t rowStart, rowEnd; + float gainmap_min_th[3] = {127.0f, 127.0f, 127.0f}; + float gainmap_max_th[3] = {-128.0f, -128.0f, -128.0f}; + while (jobQueue.dequeueJob(rowStart, rowEnd)) { for (size_t y = rowStart; y < rowEnd; ++y) { - for (size_t x = 0; x < dest->w; ++x) { + for (size_t x = 0; x < map_width; ++x) { Color sdr_yuv_gamma = sdr_sample_pixel_fn(sdr_intent, mMapDimensionScaleFactor, x, y); Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma); // We are assuming the SDR input is always sRGB transfer. @@ -637,17 +637,19 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ if (mUseMultiChannelGainMap) { Color sdr_rgb_nits = sdr_rgb * kSdrWhiteNits; Color hdr_rgb_nits = hdr_rgb * hdr_white_nits; - size_t pixel_idx = (x + y * dest->stride[UHDR_PLANE_PACKED]) * 3; - - reinterpret_cast(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] = encodeGain( - sdr_rgb_nits.r, hdr_rgb_nits.r, gainmap_metadata, log2MinBoost, log2MaxBoost); - reinterpret_cast(dest->planes[UHDR_PLANE_PACKED])[pixel_idx + 1] = encodeGain( - sdr_rgb_nits.g, hdr_rgb_nits.g, gainmap_metadata, log2MinBoost, log2MaxBoost); - reinterpret_cast(dest->planes[UHDR_PLANE_PACKED])[pixel_idx + 2] = encodeGain( - sdr_rgb_nits.b, hdr_rgb_nits.b, gainmap_metadata, log2MinBoost, log2MaxBoost); + size_t pixel_idx = (x + y * map_width) * 3; + + gainmap_data[pixel_idx] = computeGain(sdr_rgb_nits.r, hdr_rgb_nits.r); + gainmap_data[pixel_idx + 1] = computeGain(sdr_rgb_nits.g, hdr_rgb_nits.g); + gainmap_data[pixel_idx + 2] = computeGain(sdr_rgb_nits.b, hdr_rgb_nits.b); + for (int i = 0; i < 3; i++) { + gainmap_min_th[i] = (std::min)(gainmap_data[pixel_idx + i], gainmap_min_th[i]); + gainmap_max_th[i] = (std::max)(gainmap_data[pixel_idx + i], gainmap_max_th[i]); + } } else { float sdr_y_nits; float hdr_y_nits; + if (use_luminance) { sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits; hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits; @@ -656,14 +658,21 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ hdr_y_nits = fmax(hdr_rgb.r, fmax(hdr_rgb.g, hdr_rgb.b)) * hdr_white_nits; } - size_t pixel_idx = x + y * dest->stride[UHDR_PLANE_Y]; - - reinterpret_cast(dest->planes[UHDR_PLANE_Y])[pixel_idx] = - encodeGain(sdr_y_nits, hdr_y_nits, gainmap_metadata, log2MinBoost, log2MaxBoost); + size_t pixel_idx = x + y * map_width; + gainmap_data[pixel_idx] = computeGain(sdr_y_nits, hdr_y_nits); + gainmap_min_th[0] = (std::min)(gainmap_data[pixel_idx], gainmap_min_th[0]); + gainmap_max_th[0] = (std::max)(gainmap_data[pixel_idx], gainmap_max_th[0]); } } } } + { + std::unique_lock lock{gainmap_minmax}; + for (int index = 0; index < (mUseMultiChannelGainMap ? 3 : 1); index++) { + gainmap_min[index] = (std::min)(gainmap_min[index], gainmap_min_th[index]); + gainmap_max[index] = (std::max)(gainmap_max[index], gainmap_max_th[index]); + } + } }; // generate map @@ -681,6 +690,71 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ generateMap(); std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); }); + float min_content_boost_log2 = gainmap_min[0]; + float max_content_boost_log2 = gainmap_max[0]; + for (int index = 1; index < (mUseMultiChannelGainMap ? 3 : 1); index++) { + min_content_boost_log2 = (std::min)(gainmap_min[index], min_content_boost_log2); + max_content_boost_log2 = (std::max)(gainmap_max[index], max_content_boost_log2); + } + // -13.0 emphirically is a small enough gain factor that is capable of representing hdr + // black from any sdr luminance. Allowing further excursion might not offer any benefit and on the + // downside can cause bigger error during affine map and inverse map. + min_content_boost_log2 = (std::max)(-13.0f, min_content_boost_log2); + if (fabs(max_content_boost_log2 - min_content_boost_log2) < FLT_EPSILON) { + max_content_boost_log2 += 0.1; // to avoid div by zero during affine transform + } + + std::function encodeMap = [this, gainmap_data, map_width, dest, min_content_boost_log2, + max_content_boost_log2, &jobQueue]() -> void { + size_t rowStart, rowEnd; + + while (jobQueue.dequeueJob(rowStart, rowEnd)) { + if (mUseMultiChannelGainMap) { + for (size_t j = rowStart; j < rowEnd; j++) { + size_t dst_pixel_idx = j * dest->stride[UHDR_PLANE_PACKED] * 3; + size_t src_pixel_idx = j * map_width * 3; + for (size_t i = 0; i < map_width * 3; i++) { + reinterpret_cast(dest->planes[UHDR_PLANE_PACKED])[dst_pixel_idx + i] = + affineMapGain(gainmap_data[src_pixel_idx + i], min_content_boost_log2, + max_content_boost_log2, this->mGamma); + } + } + } else { + for (size_t j = rowStart; j < rowEnd; j++) { + size_t dst_pixel_idx = j * dest->stride[UHDR_PLANE_Y]; + size_t src_pixel_idx = j * map_width; + for (size_t i = 0; i < map_width; i++) { + reinterpret_cast(dest->planes[UHDR_PLANE_Y])[dst_pixel_idx + i] = + affineMapGain(gainmap_data[src_pixel_idx + i], min_content_boost_log2, + max_content_boost_log2, this->mGamma); + } + } + } + } + }; + workers.clear(); + jobQueue.reset(); + rowStep = threads == 1 ? map_height : 1; + for (int th = 0; th < threads - 1; th++) { + workers.push_back(std::thread(encodeMap)); + } + for (size_t rowStart = 0; rowStart < map_height;) { + size_t rowEnd = (std::min)(rowStart + rowStep, map_height); + jobQueue.enqueueJob(rowStart, rowEnd); + rowStart = rowEnd; + } + jobQueue.markQueueForEnd(); + encodeMap(); + std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); }); + + gainmap_metadata->max_content_boost = exp2(max_content_boost_log2); + gainmap_metadata->min_content_boost = exp2(min_content_boost_log2); + gainmap_metadata->gamma = this->mGamma; + gainmap_metadata->offset_sdr = 0.0f; + gainmap_metadata->offset_hdr = 0.0f; + gainmap_metadata->hdr_capacity_min = 1.0f; + gainmap_metadata->hdr_capacity_max = hdr_white_nits / kSdrWhiteNits; + return status; } @@ -1076,26 +1150,6 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima gainmap_metadata->offset_hdr); return status; } - if (gainmap_metadata->hdr_capacity_min != gainmap_metadata->min_content_boost) { - uhdr_error_info_t status; - status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; - status.has_detail = 1; - snprintf(status.detail, sizeof status.detail, - "Unsupported gainmap metadata, min_content_boost. Min content boost is expected to be " - "same as hdr capacity min. Min content boost %f, Hdr Capacity min %f", - gainmap_metadata->min_content_boost, gainmap_metadata->hdr_capacity_min); - return status; - } - if (gainmap_metadata->hdr_capacity_max != gainmap_metadata->max_content_boost) { - uhdr_error_info_t status; - status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; - status.has_detail = 1; - snprintf(status.detail, sizeof status.detail, - "Unsupported gainmap metadata, max_content_boost. Max content boost is expected to be " - "same as hdr capacity max. Max content boost %f, Hdr Capacity max %f", - gainmap_metadata->max_content_boost, gainmap_metadata->hdr_capacity_max); - return status; - } if (sdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCr444 && sdr_intent->fmt != UHDR_IMG_FMT_16bppYCbCr422 && sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420) { @@ -1147,7 +1201,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima dest->cg = sdr_intent->cg; // Table will only be used when map scale factor is integer. ShepardsIDW idwTable(static_cast(map_scale_factor)); - float display_boost = (std::min)(max_display_boost, gainmap_metadata->max_content_boost); + float display_boost = (std::min)(max_display_boost, gainmap_metadata->hdr_capacity_max); GainLUT gainLUT(gainmap_metadata, display_boost); GetPixelFn get_pixel_fn = getPixelFn(sdr_intent->fmt); @@ -1162,11 +1216,14 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima JobQueue jobQueue; std::function applyRecMap = [sdr_intent, gainmap_img, dest, &jobQueue, &idwTable, - output_ct, &gainLUT, display_boost, map_scale_factor, - get_pixel_fn]() -> void { + output_ct, &gainLUT, display_boost, +#if !USE_APPLY_GAIN_LUT + gainmap_metadata, +#endif + map_scale_factor, get_pixel_fn]() -> void { size_t width = sdr_intent->w; - size_t rowStart, rowEnd; + while (jobQueue.dequeueJob(rowStart, rowEnd)) { for (size_t y = rowStart; y < rowEnd; ++y) { for (size_t x = 0; x < width; ++x) { @@ -1192,7 +1249,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima #if USE_APPLY_GAIN_LUT rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT); #else - rgb_hdr = applyGain(rgb_sdr, gain, metadata, display_boost); + rgb_hdr = applyGain(rgb_sdr, gain, gainmap_metadata, display_boost); #endif } else { Color gain; @@ -1208,7 +1265,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima #if USE_APPLY_GAIN_LUT rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT); #else - rgb_hdr = applyGain(rgb_sdr, gain, metadata, display_boost); + rgb_hdr = applyGain(rgb_sdr, gain, gainmap_metadata, display_boost); #endif } @@ -1268,6 +1325,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima jobQueue.markQueueForEnd(); applyRecMap(); std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); }); + return g_no_error; } diff --git a/tests/gainmapmath_test.cpp b/tests/gainmapmath_test.cpp index 16e85185..9bf9eabf 100644 --- a/tests/gainmapmath_test.cpp +++ b/tests/gainmapmath_test.cpp @@ -1132,6 +1132,8 @@ TEST_F(GainMapMathTest, applyGainLUT) { metadata.min_content_boost = 1.0f / static_cast(boost); metadata.max_content_boost = static_cast(boost); metadata.gamma = 1.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; GainLUT gainLUT(&metadata); GainLUT gainLUTWithBoost(&metadata, metadata.max_content_boost); for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { @@ -1165,6 +1167,8 @@ TEST_F(GainMapMathTest, applyGainLUT) { metadata.min_content_boost = 1.0f; metadata.max_content_boost = static_cast(boost); metadata.gamma = 1.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; GainLUT gainLUT(&metadata); GainLUT gainLUTWithBoost(&metadata, metadata.max_content_boost); for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { @@ -1198,6 +1202,8 @@ TEST_F(GainMapMathTest, applyGainLUT) { metadata.min_content_boost = 1.0f / powf(static_cast(boost), 1.0f / 3.0f); metadata.max_content_boost = static_cast(boost); metadata.gamma = 1.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; GainLUT gainLUT(&metadata); GainLUT gainLUTWithBoost(&metadata, metadata.max_content_boost); for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { @@ -1257,64 +1263,59 @@ TEST_F(GainMapMathTest, ColorConversionLookup) { } TEST_F(GainMapMathTest, EncodeGain) { - uhdr_gainmap_metadata_ext_t metadata; - - metadata.min_content_boost = 1.0f / 4.0f; - metadata.max_content_boost = 4.0f; - metadata.gamma = 1.0f; - - EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127); - EXPECT_EQ(encodeGain(0.0f, 1.0f, &metadata), 127); - EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0); - EXPECT_EQ(encodeGain(0.5f, 0.0f, &metadata), 0); - - EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 127); - EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 255); - EXPECT_EQ(encodeGain(1.0f, 5.0f, &metadata), 255); - EXPECT_EQ(encodeGain(4.0f, 1.0f, &metadata), 0); - EXPECT_EQ(encodeGain(4.0f, 0.5f, &metadata), 0); - EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 191); - EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 63); - - metadata.max_content_boost = 2.0f; - metadata.min_content_boost = 1.0f / 2.0f; - - EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 255); - EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 0); - EXPECT_EQ(encodeGain(1.0f, 1.41421f, &metadata), 191); - EXPECT_EQ(encodeGain(1.41421f, 1.0f, &metadata), 63); - - metadata.max_content_boost = 8.0f; - metadata.min_content_boost = 1.0f / 8.0f; - - EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255); - EXPECT_EQ(encodeGain(8.0f, 1.0f, &metadata), 0); - EXPECT_EQ(encodeGain(1.0f, 2.82843f, &metadata), 191); - EXPECT_EQ(encodeGain(2.82843f, 1.0f, &metadata), 63); - - metadata.max_content_boost = 8.0f; - metadata.min_content_boost = 1.0f; - - EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 0); - EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0); - - EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 0); - EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255); - EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 170); - EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 85); - - metadata.max_content_boost = 8.0f; - metadata.min_content_boost = 0.5f; - - EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 63); - EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0); - - EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 63); - EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255); - EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 191); - EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 127); - EXPECT_EQ(encodeGain(1.0f, 0.7071f, &metadata), 31); - EXPECT_EQ(encodeGain(1.0f, 0.5f, &metadata), 0); + float min_boost = log2(1.0f / 4.0f); + float max_boost = log2(4.0f); + float gamma = 1.0f; + + EXPECT_EQ(affineMapGain(computeGain(0.0f, 1.0f), min_boost, max_boost, 1.0f), 128); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(0.5f, 0.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0), min_boost, max_boost, 1.0f), 128); + + EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 5.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(4.0f, 1.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(4.0f, 0.5f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 191); + EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 64); + + min_boost = log2(1.0f / 2.0f); + max_boost = log2(2.0f); + + EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.41421f), min_boost, max_boost, 1.0f), 191); + EXPECT_EQ(affineMapGain(computeGain(1.41421f, 1.0f), min_boost, max_boost, 1.0f), 64); + + min_boost = log2(1.0f / 8.0f); + max_boost = log2(8.0f); + + EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(8.0f, 1.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.82843f), min_boost, max_boost, 1.0f), 191); + EXPECT_EQ(affineMapGain(computeGain(2.82843f, 1.0f), min_boost, max_boost, 1.0f), 64); + + min_boost = log2(1.0f); + max_boost = log2(8.0f); + + EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 170); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 85); + + min_boost = log2(1.0f / 2.0f); + max_boost = log2(8.0f); + + EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 64); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 64); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 191); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 128); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.7071f), min_boost, max_boost, 1.0f), 32); + EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.5f), min_boost, max_boost, 1.0f), 0); } TEST_F(GainMapMathTest, ApplyGain) { @@ -1322,6 +1323,8 @@ TEST_F(GainMapMathTest, ApplyGain) { metadata.min_content_boost = 1.0f / 4.0f; metadata.max_content_boost = 4.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; metadata.gamma = 1.0f; float displayBoost = metadata.max_content_boost; @@ -1337,6 +1340,8 @@ TEST_F(GainMapMathTest, ApplyGain) { metadata.max_content_boost = 2.0f; metadata.min_content_boost = 1.0f / 2.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f); @@ -1346,6 +1351,8 @@ TEST_F(GainMapMathTest, ApplyGain) { metadata.max_content_boost = 8.0f; metadata.min_content_boost = 1.0f / 8.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f); @@ -1355,6 +1362,8 @@ TEST_F(GainMapMathTest, ApplyGain) { metadata.max_content_boost = 8.0f; metadata.min_content_boost = 1.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite()); EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f); @@ -1363,6 +1372,8 @@ TEST_F(GainMapMathTest, ApplyGain) { metadata.max_content_boost = 8.0f; metadata.min_content_boost = 0.5f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite()); @@ -1373,6 +1384,8 @@ TEST_F(GainMapMathTest, ApplyGain) { Color e = {{{0.0f, 0.5f, 1.0f}}}; metadata.max_content_boost = 4.0f; metadata.min_content_boost = 1.0f / 4.0f; + metadata.hdr_capacity_max = metadata.max_content_boost; + metadata.hdr_capacity_min = metadata.min_content_boost; EXPECT_RGB_NEAR(applyGain(e, 0.0f, &metadata), e / 4.0f); EXPECT_RGB_NEAR(applyGain(e, 0.25f, &metadata), e / 2.0f);