Explicitly handle gainmaps where the base and alt headrooms are equal. (#1876)
diff --git a/src/gainmap.c b/src/gainmap.c
index 44ba129..e1c84ba 100644
--- a/src/gainmap.c
+++ b/src/gainmap.c
@@ -77,6 +77,11 @@
{
const float baseHdrHeadroom = (float)metadata->baseHdrHeadroom;
const float alternateHdrHeadroom = (float)metadata->alternateHdrHeadroom;
+ if (baseHdrHeadroom == alternateHdrHeadroom) {
+ // Do not apply the gain map if the HDR headroom is the same.
+ // This case is not handled in the specification and does not make practical sense.
+ return 0.0f;
+ }
float w = AVIF_CLAMP((hdrHeadroom - baseHdrHeadroom) / (alternateHdrHeadroom - baseHdrHeadroom), 0.0f, 1.0f);
if (metadata->backwardDirection) {
w *= -1.0f;
diff --git a/tests/gtest/avifgainmaptest.cc b/tests/gtest/avifgainmaptest.cc
index b938085..6d63aa6 100644
--- a/tests/gtest/avifgainmaptest.cc
+++ b/tests/gtest/avifgainmaptest.cc
@@ -1217,6 +1217,44 @@
/*out_rgb_format=*/AVIF_RGB_FORMAT_RGB,
/*reference=*/"", /*min_psnr=*/0.0f, /*max_psnr=*/0.0f)));
+TEST(ToneMapTest, ToneMapImageSameHeadroom) {
+ const std::string path =
+ std::string(data_path) + "seine_sdr_gainmap_srgb.avif";
+ ImagePtr image(avifImageCreateEmpty());
+ ASSERT_NE(image, nullptr);
+ DecoderPtr decoder(avifDecoderCreate());
+ ASSERT_NE(decoder, nullptr);
+ decoder->enableDecodingGainMap = true;
+ decoder->enableParsingGainMapMetadata = true;
+ avifResult result =
+ avifDecoderReadFile(decoder.get(), image.get(), path.c_str());
+ ASSERT_EQ(result, AVIF_RESULT_OK)
+ << avifResultToString(result) << " " << decoder->diag.error;
+
+ ASSERT_NE(image->gainMap, nullptr);
+ ASSERT_NE(image->gainMap->image, nullptr);
+
+ // Force the alternate and base HDR headroom to the same value.
+ image->gainMap->metadata.baseHdrHeadroomN =
+ image->gainMap->metadata.alternateHdrHeadroomN;
+ image->gainMap->metadata.baseHdrHeadroomD =
+ image->gainMap->metadata.alternateHdrHeadroomD;
+ const float headroom = static_cast<float>(
+ static_cast<float>(image->gainMap->metadata.baseHdrHeadroomN) /
+ image->gainMap->metadata.baseHdrHeadroomD);
+
+ // Check that when the two headrooms are the same, the gain map is not applied
+ // whatever the target headroom is.
+ for (const float tonemap_to : {headroom, headroom - 0.5f, headroom + 0.5f}) {
+ ToneMapImageAndCompareToReference(
+ image.get(), *image->gainMap, /*hdr_headroom=*/tonemap_to,
+ /*out_depth=*/image->depth,
+ /*out_transfer_characteristics=*/image->transferCharacteristics,
+ AVIF_RGB_FORMAT_RGB, /*reference_image=*/image.get(),
+ /*min_psnr=*/60, /*max_psnr=*/100);
+ }
+}
+
class CreateGainMapTest : public testing::TestWithParam<std::tuple<
/*downscaling=*/int, /*gain_map_depth=*/int,
/*gain_map_format=*/avifPixelFormat,