Skip to content

Commit

Permalink
Remove backwardDirection from avifGainMapMetadata
Browse files Browse the repository at this point in the history
This did not end up in the ISO 21496-1 specification.
  • Loading branch information
ccameron-chromium authored Jun 27, 2024
1 parent 22e47a3 commit 4654e63
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 51 deletions.
2 changes: 0 additions & 2 deletions apps/avifgainmaputil/printmetadata_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ avifResult PrintMetadataCommand::Run() {
std::cout << " * " << std::left << std::setw(w) << "Gain Map Gamma: "
<< FormatFractions(metadata.gainMapGammaN, metadata.gainMapGammaD)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Backward Direction: "
<< (metadata.backwardDirection ? "True" : "False") << "\n";
std::cout << " * " << std::left << std::setw(w) << "Use Base Color Space: "
<< (metadata.useBaseColorSpace ? "True" : "False") << "\n";

Expand Down
1 change: 0 additions & 1 deletion apps/avifgainmaputil/swapbase_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ avifResult ChangeBase(const avifImage& image, int depth,

// Swap base and alternate in the gain map metadata.
avifGainMapMetadata& metadata = swapped->gainMap->metadata;
metadata.backwardDirection = !metadata.backwardDirection;
metadata.useBaseColorSpace = !metadata.useBaseColorSpace;
std::swap(metadata.baseHdrHeadroomN, metadata.alternateHdrHeadroomN);
std::swap(metadata.baseHdrHeadroomD, metadata.alternateHdrHeadroomD);
Expand Down
3 changes: 0 additions & 3 deletions apps/shared/avifjpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,6 @@ static avifBool avifJPEGParseGainMapXMPProperties(const xmlNode * rootNode, avif

avifGainMapMetadataDouble metadataDouble;
// Set default values from Adobe's spec.
metadataDouble.backwardDirection = AVIF_FALSE;
metadataDouble.baseHdrHeadroom = 0.0;
metadataDouble.alternateHdrHeadroom = 1.0;
for (int i = 0; i < 3; ++i) {
Expand Down Expand Up @@ -637,13 +636,11 @@ static avifBool avifJPEGParseGainMapXMPProperties(const xmlNode * rootNode, avif
const char * baseRenditionIsHDR;
if (avifJPEGFindGainMapProperty(descNode, "BaseRenditionIsHDR", /*maxValues=*/1, &baseRenditionIsHDR, &numValues)) {
if (!strcmp(baseRenditionIsHDR, "True")) {
metadataDouble.backwardDirection = AVIF_TRUE;
SwapDoubles(&metadataDouble.baseHdrHeadroom, &metadataDouble.alternateHdrHeadroom);
for (int c = 0; c < 3; ++c) {
SwapDoubles(&metadataDouble.baseOffset[c], &metadataDouble.alternateOffset[c]);
}
} else if (!strcmp(baseRenditionIsHDR, "False")) {
metadataDouble.backwardDirection = AVIF_FALSE;
} else {
return AVIF_FALSE; // Unexpected value.
}
Expand Down
10 changes: 3 additions & 7 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -630,17 +630,14 @@ typedef struct avifGainMapMetadata
//
// If 'H' is the display's current log2-encoded HDR capacity (HDR to SDR ratio),
// then the weight 'w' to apply the gain map is computed as follows:
// f = clamp((H - hdrCapacityMin) /
// (hdrCapacityMax - hdrCapacityMin), 0, 1);
// w = backwardDirection ? f * -1 : f;
// f = clamp((H - baseHdrHeadroom) /
// (alternateHdrHeadroom - baseHdrHeadroom), 0, 1);
// w = sign(alternateHdrHeadroom - baseHdrHeadroom) * f
uint32_t baseHdrHeadroomN;
uint32_t baseHdrHeadroomD;
uint32_t alternateHdrHeadroomN;
uint32_t alternateHdrHeadroomD;

// True if the gain map should be applied in reverse, see weight formula above.
avifBool backwardDirection;

// True if tone mapping should be performed in the color space of the
// base image. If false, the color space of the alternate image should
// be used.
Expand Down Expand Up @@ -702,7 +699,6 @@ typedef struct avifGainMapMetadataDouble
double alternateOffset[3];
double baseHdrHeadroom;
double alternateHdrHeadroom;
avifBool backwardDirection;
avifBool useBaseColorSpace;
} avifGainMapMetadataDouble;

Expand Down
50 changes: 30 additions & 20 deletions src/gainmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ avifBool avifGainMapMetadataDoubleToFractions(avifGainMapMetadata * dst, const a
}
AVIF_CHECK(avifDoubleToUnsignedFraction(src->baseHdrHeadroom, &dst->baseHdrHeadroomN, &dst->baseHdrHeadroomD));
AVIF_CHECK(avifDoubleToUnsignedFraction(src->alternateHdrHeadroom, &dst->alternateHdrHeadroomN, &dst->alternateHdrHeadroomD));
dst->backwardDirection = src->backwardDirection;
dst->useBaseColorSpace = src->useBaseColorSpace;
return AVIF_TRUE;
}
Expand Down Expand Up @@ -50,7 +49,6 @@ avifBool avifGainMapMetadataFractionsToDouble(avifGainMapMetadataDouble * dst, c
}
dst->baseHdrHeadroom = (double)src->baseHdrHeadroomN / src->baseHdrHeadroomD;
dst->alternateHdrHeadroom = (double)src->alternateHdrHeadroomN / src->alternateHdrHeadroomD;
dst->backwardDirection = src->backwardDirection;
dst->useBaseColorSpace = src->useBaseColorSpace;
return AVIF_TRUE;
}
Expand Down Expand Up @@ -81,11 +79,8 @@ static float avifGetGainMapWeight(float hdrHeadroom, const avifGainMapMetadataDo
// 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;
}
return w;
const float w = AVIF_CLAMP((hdrHeadroom - baseHdrHeadroom) / (alternateHdrHeadroom - baseHdrHeadroom), 0.0f, 1.0f);
return (alternateHdrHeadroom < baseHdrHeadroom) ? -w : w;
}

// Linear interpolation between 'a' and 'b' (returns 'a' if w == 0.0f, returns 'b' if w == 1.0f).
Expand Down Expand Up @@ -517,8 +512,6 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
const avifBool colorSpacesDiffer = (baseColorPrimaries != altColorPrimaries);
avifColorPrimaries gainMapMathPrimaries;
AVIF_CHECKRES(avifChooseColorSpaceForGainMapMath(baseColorPrimaries, altColorPrimaries, &gainMapMathPrimaries));
const avifBool useBaseColorSpace = (gainMapMathPrimaries == baseColorPrimaries);

const int width = baseRgbImage->width;
const int height = baseRgbImage->height;

Expand Down Expand Up @@ -549,6 +542,7 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,

avifGainMapMetadataDouble gainMapMetadata;
avifGainMapMetadataSetDefaults(&gainMapMetadata);
gainMapMetadata.useBaseColorSpace = (gainMapMathPrimaries == baseColorPrimaries);

float (*baseGammaToLinear)(float) = avifTransferCharacteristicsGetGammaToLinearFunction(baseTransferCharacteristics);
float (*altGammaToLinear)(float) = avifTransferCharacteristicsGetGammaToLinearFunction(altTransferCharacteristics);
Expand All @@ -557,7 +551,7 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,

double rgbConversionCoeffs[3][3];
if (colorSpacesDiffer) {
if (useBaseColorSpace) {
if (gainMapMetadata.useBaseColorSpace) {
if (!avifColorPrimariesComputeRGBToRGBMatrix(altColorPrimaries, baseColorPrimaries, rgbConversionCoeffs)) {
avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion");
res = AVIF_RESULT_NOT_IMPLEMENTED;
Expand All @@ -581,11 +575,12 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
float channelMin[3] = { 0 };
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
avifGetRGBAPixel(useBaseColorSpace ? altRgbImage : baseRgbImage, i, j, useBaseColorSpace ? &altRGBInfo : &baseRGBInfo, rgba);
avifGetRGBAPixel(gainMapMetadata.useBaseColorSpace ? altRgbImage : baseRgbImage, i, j,
gainMapMetadata.useBaseColorSpace ? &altRGBInfo : &baseRGBInfo, rgba);

// Convert to linear.
for (int c = 0; c < 3; ++c) {
if (useBaseColorSpace) {
if (gainMapMetadata.useBaseColorSpace) {
rgba[c] = altGammaToLinear(rgba[c]);
} else {
rgba[c] = baseGammaToLinear(rgba[c]);
Expand All @@ -604,7 +599,7 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
const float maxOffset = 0.1f;
if (channelMin[c] < -kEpsilon) {
// Increase the offset to avoid negative values.
if (useBaseColorSpace) {
if (gainMapMetadata.useBaseColorSpace) {
gainMapMetadata.alternateOffset[c] = AVIF_MIN(gainMapMetadata.alternateOffset[c] - channelMin[c], maxOffset);
} else {
gainMapMetadata.baseOffset[c] = AVIF_MIN(gainMapMetadata.baseOffset[c] - channelMin[c], maxOffset);
Expand All @@ -630,7 +625,7 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
}

if (colorSpacesDiffer) {
if (useBaseColorSpace) {
if (gainMapMetadata.useBaseColorSpace) {
// convert altRGBA to baseRGBA's color space
avifLinearRGBConvertColorSpace(altRGBA, rgbConversionCoeffs);
} else {
Expand Down Expand Up @@ -660,6 +655,23 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
}
}

// Populate the gain map metadata's headrooms.
gainMapMetadata.baseHdrHeadroom = log2f(AVIF_MAX(baseMax, kEpsilon));
gainMapMetadata.alternateHdrHeadroom = log2f(AVIF_MAX(altMax, kEpsilon));

// Multiply the gainmap by sign(alternateHdrHeadroom - baseHdrHeadroom), to
// ensure that it stores the log-ratio of the HDR representation to the SDR
// representation.
if (gainMapMetadata.alternateHdrHeadroom < gainMapMetadata.baseHdrHeadroom) {
for (int c = 0; c < numGainMapChannels; ++c) {
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
gainMapF[c][j * width + i] *= -1.f;
}
}
}
}

// Find approximate min/max for each channel, discarding outliers.
float gainMapMinLog2[3] = { 0.0f, 0.0f, 0.0f };
float gainMapMaxLog2[3] = { 0.0f, 0.0f, 0.0f };
Expand All @@ -670,16 +682,14 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,
}
}

// Fill in the gain map's metadata.
// Populate the gain map metadata's min and max values.
for (int c = 0; c < 3; ++c) {
gainMapMetadata.gainMapMin[c] = gainMapMinLog2[singleChannel ? 0 : c];
gainMapMetadata.gainMapMax[c] = gainMapMaxLog2[singleChannel ? 0 : c];
gainMapMetadata.baseHdrHeadroom = log2f(AVIF_MAX(baseMax, kEpsilon));
gainMapMetadata.alternateHdrHeadroom = log2f(AVIF_MAX(altMax, kEpsilon));
// baseOffset, alternateOffset and gainMapGamma are all left to their default values.
// They could be tweaked based on the images to optimize quality/compression.
}
gainMapMetadata.useBaseColorSpace = useBaseColorSpace;

// All of gainMapMetadata has been populated now (except for gamma which is left to the default
// value), so convert to the fraction form in which it will be stored.
if (!avifGainMapMetadataDoubleToFractions(&gainMap->metadata, &gainMapMetadata)) {
res = AVIF_RESULT_UNKNOWN_ERROR;
goto cleanup;
Expand Down
1 change: 0 additions & 1 deletion src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -1973,7 +1973,6 @@ static avifBool avifParseToneMappedImageBox(avifGainMapMetadata * metadata, cons
uint8_t channelCount = (flags & 1) * 2 + 1;
AVIF_ASSERT_OR_RETURN(channelCount == 1 || channelCount == 3);
metadata->useBaseColorSpace = (flags & 2) != 0;
metadata->backwardDirection = (flags & 4) != 0;
const avifBool useCommonDenominator = (flags & 8) != 0;

if (useCommonDenominator) {
Expand Down
6 changes: 1 addition & 5 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -919,9 +919,6 @@ static avifBool avifWriteToneMappedImagePayload(avifRWData * data, const avifGai
if (metadata->useBaseColorSpace) {
flags |= 2;
}
if (metadata->backwardDirection) {
flags |= 4;
}
const uint32_t denom = metadata->baseHdrHeadroomD;
avifBool useCommonDenominator = metadata->baseHdrHeadroomD == denom && metadata->alternateHdrHeadroomD == denom;
for (int c = 0; c < channelCount; ++c) {
Expand Down Expand Up @@ -1609,8 +1606,7 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
}
const avifGainMapMetadata * firstMetadata = &firstGainMap->metadata;
const avifGainMapMetadata * cellMetadata = &cellGainMap->metadata;
if (cellMetadata->backwardDirection != firstMetadata->backwardDirection ||
cellMetadata->baseHdrHeadroomN != firstMetadata->baseHdrHeadroomN ||
if (cellMetadata->baseHdrHeadroomN != firstMetadata->baseHdrHeadroomN ||
cellMetadata->baseHdrHeadroomD != firstMetadata->baseHdrHeadroomD ||
cellMetadata->alternateHdrHeadroomN != firstMetadata->alternateHdrHeadroomN ||
cellMetadata->alternateHdrHeadroomD != firstMetadata->alternateHdrHeadroomD) {
Expand Down
4 changes: 0 additions & 4 deletions tests/gtest/avif_fuzztest_enc_dec_experimental.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ ::testing::Environment* const kStackLimitEnv = SetStackLimitTo512x1024Bytes();

void CheckGainMapMetadataMatches(const avifGainMapMetadata& actual,
const avifGainMapMetadata& expected) {
// 'expecteed' is the source struct which has arbitrary data and booleans
// values can contain any value, but the decoded ('actual') struct should
// be 0 or 1.
EXPECT_EQ(actual.backwardDirection, expected.backwardDirection ? 1 : 0);
EXPECT_EQ(actual.baseHdrHeadroomN, expected.baseHdrHeadroomN);
EXPECT_EQ(actual.baseHdrHeadroomD, expected.baseHdrHeadroomD);
EXPECT_EQ(actual.alternateHdrHeadroomN, expected.alternateHdrHeadroomN);
Expand Down
11 changes: 4 additions & 7 deletions tests/gtest/avifgainmaptest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const char* data_path = nullptr;

void CheckGainMapMetadataMatches(const avifGainMapMetadata& lhs,
const avifGainMapMetadata& rhs) {
EXPECT_EQ(lhs.backwardDirection, rhs.backwardDirection);
EXPECT_EQ(lhs.baseHdrHeadroomN, rhs.baseHdrHeadroomN);
EXPECT_EQ(lhs.baseHdrHeadroomD, rhs.baseHdrHeadroomD);
EXPECT_EQ(lhs.alternateHdrHeadroomN, rhs.alternateHdrHeadroomN);
Expand All @@ -47,12 +46,15 @@ void CheckGainMapMetadataMatches(const avifGainMapMetadata& lhs,

avifGainMapMetadata GetTestGainMapMetadata(bool base_rendition_is_hdr) {
avifGainMapMetadata metadata = {};
metadata.backwardDirection = base_rendition_is_hdr;
metadata.useBaseColorSpace = true;
metadata.baseHdrHeadroomN = 0;
metadata.baseHdrHeadroomD = 1;
metadata.alternateHdrHeadroomN = 6;
metadata.alternateHdrHeadroomD = 2;
if (base_rendition_is_hdr) {
std::swap(metadata.baseHdrHeadroomN, metadata.alternateHdrHeadroomN);
std::swap(metadata.baseHdrHeadroomD, metadata.alternateHdrHeadroomD);
}
for (int c = 0; c < 3; ++c) {
metadata.baseOffsetN[c] = 10 * c;
metadata.baseOffsetD[c] = 1000;
Expand Down Expand Up @@ -884,7 +886,6 @@ TEST(GainMapTest, ConvertMetadata) {
metadata_double.alternateOffset[1] = 0.0;
metadata_double.baseHdrHeadroom = 1.0;
metadata_double.alternateHdrHeadroom = 10.0;
metadata_double.backwardDirection = AVIF_TRUE;

// Convert to avifGainMapMetadata.
avifGainMapMetadata metadata = {};
Expand All @@ -909,7 +910,6 @@ TEST(GainMapTest, ConvertMetadata) {
EXPECT_FRACTION_NEAR(metadata.alternateHdrHeadroomN,
metadata.alternateHdrHeadroomD,
metadata_double.alternateHdrHeadroom);
EXPECT_EQ(metadata.backwardDirection, metadata_double.backwardDirection);

// Convert back to avifGainMapMetadataDouble.
avifGainMapMetadataDouble metadata_double2 = {};
Expand All @@ -933,8 +933,6 @@ TEST(GainMapTest, ConvertMetadata) {
kEpsilon);
EXPECT_NEAR(metadata_double2.alternateHdrHeadroom,
metadata_double.alternateHdrHeadroom, kEpsilon);
EXPECT_EQ(metadata_double2.backwardDirection,
metadata_double.backwardDirection);
}

TEST(GainMapTest, ConvertMetadataToFractionInvalid) {
Expand All @@ -955,7 +953,6 @@ TEST(GainMapTest, ConvertMetadataToDoubleInvalid) {
static void SwapBaseAndAlternate(const avifImage& new_alternate,
avifGainMap& gain_map) {
avifGainMapMetadata& metadata = gain_map.metadata;
metadata.backwardDirection = !metadata.backwardDirection;
metadata.useBaseColorSpace = !metadata.useBaseColorSpace;
std::swap(metadata.baseHdrHeadroomN, metadata.alternateHdrHeadroomN);
std::swap(metadata.baseHdrHeadroomD, metadata.alternateHdrHeadroomD);
Expand Down
1 change: 0 additions & 1 deletion tests/gtest/avifjpeggainmaptest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ void CheckGainMapMetadata(
EXPECT_NEAR(
static_cast<double>(m.alternateHdrHeadroomN) / m.alternateHdrHeadroomD,
alternate_hdr_headroom, kEpsilon);
EXPECT_EQ(m.backwardDirection, backward_direction);
}

TEST(JpegTest, ReadJpegWithGainMap) {
Expand Down

0 comments on commit 4654e63

Please sign in to comment.