Skip to content

Commit

Permalink
Add signed/unsigned fraction structs.
Browse files Browse the repository at this point in the history
  • Loading branch information
maryla-uc committed Sep 26, 2024
1 parent dd6325e commit 527b80f
Show file tree
Hide file tree
Showing 19 changed files with 365 additions and 374 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ The changes are relative to the previous release, unless the baseline is specifi
* Update svt.cmd/svt.sh/LocalSvt.cmake: v2.2.1
* Change experimental gainmap API: remove avifGainMapMetadata and
avifGainMapMetadataDouble structs.
* Add avifDoubleToSignedFraction and avifDoubleToUnsignedFraction functions.
* Add avif(Un)SignedFraction structs and avifDoubleTo(Un)SignedFraction
utility functions.

## [1.1.1] - 2024-07-30

Expand Down
2 changes: 1 addition & 1 deletion apps/avifgainmaputil/convert_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ avifResult ConvertCommand::Run() {
if (arg_swap_base_) {
int depth = arg_image_read_.depth;
if (depth == 0) {
depth = image->gainMap->alternateHdrHeadroomN == 0 ? 8 : 10;
depth = image->gainMap->alternateHdrHeadroom.n == 0 ? 8 : 10;
}
ImagePtr new_base(avifImageCreateEmpty());
if (new_base == nullptr) {
Expand Down
41 changes: 18 additions & 23 deletions apps/avifgainmaputil/printmetadata_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ std::string FormatFraction(T numerator, uint32_t denominator) {
}

template <typename T>
std::string FormatFractions(const T numerator[3],
const uint32_t denominator[3]) {
std::string FormatFractions(const T fractions[3]) {
std::stringstream stream;
const int w = 40;
stream << "R " << std::left << std::setw(w)
<< FormatFraction(numerator[0], denominator[0]) << " G " << std::left
<< std::setw(w) << FormatFraction(numerator[1], denominator[1])
<< FormatFraction(fractions[0].n, fractions[0].d) << " G " << std::left
<< std::setw(w) << FormatFraction(fractions[1].n, fractions[1].d)
<< " B " << std::left << std::setw(w)
<< FormatFraction(numerator[2], denominator[2]);
<< FormatFraction(fractions[2].n, fractions[2].d);
return stream.str();
}
} // namespace
Expand Down Expand Up @@ -68,28 +67,24 @@ avifResult PrintMetadataCommand::Run() {
const avifGainMap& gainMap = *decoder->image->gainMap;
const int w = 20;
std::cout << " * " << std::left << std::setw(w) << "Base headroom: "
<< FormatFraction(gainMap.baseHdrHeadroomN,
gainMap.baseHdrHeadroomD)
<< FormatFraction(gainMap.baseHdrHeadroom.n,
gainMap.baseHdrHeadroom.d)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Alternate headroom: "
<< FormatFraction(gainMap.alternateHdrHeadroomN,
gainMap.alternateHdrHeadroomD)
<< FormatFraction(gainMap.alternateHdrHeadroom.n,
gainMap.alternateHdrHeadroom.d)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Gain Map Min: "
<< FormatFractions(gainMap.gainMapMinN, gainMap.gainMapMinD)
std::cout << " * " << std::left << std::setw(w)
<< "Gain Map Min: " << FormatFractions(gainMap.gainMapMin) << "\n";
std::cout << " * " << std::left << std::setw(w)
<< "Gain Map Max: " << FormatFractions(gainMap.gainMapMax) << "\n";
std::cout << " * " << std::left << std::setw(w)
<< "Base Offset: " << FormatFractions(gainMap.baseOffset) << "\n";
std::cout << " * " << std::left << std::setw(w)
<< "Alternate Offset: " << FormatFractions(gainMap.alternateOffset)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Gain Map Max: "
<< FormatFractions(gainMap.gainMapMaxN, gainMap.gainMapMaxD)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Base Offset: "
<< FormatFractions(gainMap.baseOffsetN, gainMap.baseOffsetD)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Alternate Offset: "
<< FormatFractions(gainMap.alternateOffsetN,
gainMap.alternateOffsetD)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Gain Map Gamma: "
<< FormatFractions(gainMap.gainMapGammaN, gainMap.gainMapGammaD)
std::cout << " * " << std::left << std::setw(w)
<< "Gain Map Gamma: " << FormatFractions(gainMap.gainMapGamma)
<< "\n";
std::cout << " * " << std::left << std::setw(w) << "Use Base Color Space: "
<< (gainMap.useBaseColorSpace ? "True" : "False") << "\n";
Expand Down
13 changes: 6 additions & 7 deletions apps/avifgainmaputil/swapbase_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ avifResult ChangeBase(const avifImage& image, int depth,
swapped->yuvFormat = yuvFormat;

const float headroom =
image.gainMap->alternateHdrHeadroomD == 0
image.gainMap->alternateHdrHeadroom.d == 0
? 0.0f
: static_cast<float>(image.gainMap->alternateHdrHeadroomN) /
image.gainMap->alternateHdrHeadroomD;
: static_cast<float>(image.gainMap->alternateHdrHeadroom.n) /
image.gainMap->alternateHdrHeadroom.d;
const bool tone_mapping_to_sdr = (headroom == 0.0f);

swapped->colorPrimaries = image.gainMap->altColorPrimaries;
Expand Down Expand Up @@ -102,11 +102,10 @@ avifResult ChangeBase(const avifImage& image, int depth,
// Swap base and alternate in the gain map
avifGainMap* gainMap = swapped->gainMap;
gainMap->useBaseColorSpace = !gainMap->useBaseColorSpace;
std::swap(gainMap->baseHdrHeadroomN, gainMap->alternateHdrHeadroomN);
std::swap(gainMap->baseHdrHeadroomD, gainMap->alternateHdrHeadroomD);
std::swap(gainMap->baseHdrHeadroom.n, gainMap->alternateHdrHeadroom.n);
std::swap(gainMap->baseHdrHeadroom.d, gainMap->alternateHdrHeadroom.d);
for (int c = 0; c < 3; ++c) {
std::swap(gainMap->baseOffsetN, gainMap->alternateOffsetN);
std::swap(gainMap->baseOffsetD, gainMap->alternateOffsetD);
std::swap(gainMap->baseOffset, gainMap->alternateOffset);
}

return AVIF_RESULT_OK;
Expand Down
12 changes: 6 additions & 6 deletions apps/avifgainmaputil/tonemap_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ avifResult TonemapCommand::Run() {
}

const float base_hdr_hreadroom =
image->gainMap->baseHdrHeadroomD == 0
image->gainMap->baseHdrHeadroom.d == 0
? 0.0f
: (float)image->gainMap->baseHdrHeadroomN /
image->gainMap->baseHdrHeadroomD;
: (float)image->gainMap->baseHdrHeadroom.n /
image->gainMap->baseHdrHeadroom.d;
const float alternate_hdr_hreadroom =
image->gainMap->alternateHdrHeadroomD == 0
image->gainMap->alternateHdrHeadroom.d == 0
? 0.0f
: (float)image->gainMap->alternateHdrHeadroomN /
image->gainMap->alternateHdrHeadroomD;
: (float)image->gainMap->alternateHdrHeadroom.n /
image->gainMap->alternateHdrHeadroom.d;
// We are either tone mapping to the base image (i.e. leaving it as is),
// or tone mapping to the alternate image (i.e. fully applying the gain map),
// or tone mapping in between (partially applying the gain map).
Expand Down
14 changes: 7 additions & 7 deletions apps/shared/avifjpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,14 +641,14 @@ static avifBool avifJPEGParseGainMapXMPProperties(const xmlNode * rootNode, avif
}

for (int i = 0; i < 3; ++i) {
AVIF_CHECK(avifDoubleToSignedFraction(gainMapMin[i], &gainMap->gainMapMinN[i], &gainMap->gainMapMinD[i]));
AVIF_CHECK(avifDoubleToSignedFraction(gainMapMax[i], &gainMap->gainMapMaxN[i], &gainMap->gainMapMaxD[i]));
AVIF_CHECK(avifDoubleToUnsignedFraction(gainMapGamma[i], &gainMap->gainMapGammaN[i], &gainMap->gainMapGammaD[i]));
AVIF_CHECK(avifDoubleToSignedFraction(baseOffset[i], &gainMap->baseOffsetN[i], &gainMap->baseOffsetD[i]));
AVIF_CHECK(avifDoubleToSignedFraction(alternateOffset[i], &gainMap->alternateOffsetN[i], &gainMap->alternateOffsetD[i]));
AVIF_CHECK(avifDoubleToSignedFraction(gainMapMin[i], &gainMap->gainMapMin[i]));
AVIF_CHECK(avifDoubleToSignedFraction(gainMapMax[i], &gainMap->gainMapMax[i]));
AVIF_CHECK(avifDoubleToUnsignedFraction(gainMapGamma[i], &gainMap->gainMapGamma[i]));
AVIF_CHECK(avifDoubleToSignedFraction(baseOffset[i], &gainMap->baseOffset[i]));
AVIF_CHECK(avifDoubleToSignedFraction(alternateOffset[i], &gainMap->alternateOffset[i]));
}
AVIF_CHECK(avifDoubleToUnsignedFraction(baseHdrHeadroom, &gainMap->baseHdrHeadroomN, &gainMap->baseHdrHeadroomD));
AVIF_CHECK(avifDoubleToUnsignedFraction(alternateHdrHeadroom, &gainMap->alternateHdrHeadroomN, &gainMap->alternateHdrHeadroomD));
AVIF_CHECK(avifDoubleToUnsignedFraction(baseHdrHeadroom, &gainMap->baseHdrHeadroom));
AVIF_CHECK(avifDoubleToUnsignedFraction(alternateHdrHeadroom, &gainMap->alternateHdrHeadroom));
// Not in Adobe's spec but both color spaces should be the same so this value doesn't matter.
gainMap->useBaseColorSpace = AVIF_TRUE;

Expand Down
2 changes: 1 addition & 1 deletion apps/shared/avifutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static void avifImageDumpInternal(const avifImage * avif,
avifPixelFormatToString(gainMapImage->yuvFormat),
(gainMapImage->yuvRange == AVIF_RANGE_FULL) ? "Full" : "Limited",
gainMapImage->matrixCoefficients,
(avif->gainMap->baseHdrHeadroomN == 0) ? "SDR" : "HDR");
(avif->gainMap->baseHdrHeadroom.n == 0) ? "SDR" : "HDR");
printf(" * Alternate image:\n");
printf(" * Color Primaries: %u\n", avif->gainMap->altColorPrimaries);
printf(" * Transfer Char. : %u\n", avif->gainMap->altTransferCharacteristics);
Expand Down
39 changes: 22 additions & 17 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,20 +426,32 @@ typedef struct avifDiagnostics
AVIF_API void avifDiagnosticsClearError(avifDiagnostics * diag);

// ---------------------------------------------------------------------------
// Fraction utility
// Fraction utilities

typedef struct avifFraction
{
int32_t n;
int32_t d;
} avifFraction;

typedef struct avifSignedFraction
{
int32_t n;
uint32_t d;
} avifSignedFraction;

typedef struct avifUnsignedFraction
{
uint32_t n;
uint32_t d;
} avifUnsignedFraction;

// Creates an int32/uint32 fraction that is approximately equal to 'v'.
// Returns AVIF_FALSE if 'v' is NaN or abs(v) is > INT32_MAX.
AVIF_API AVIF_NODISCARD avifBool avifDoubleToSignedFraction(double v, int32_t * numerator, uint32_t * denominator);
AVIF_API AVIF_NODISCARD avifBool avifDoubleToSignedFraction(double v, avifSignedFraction * fraction);
// Creates a uint32/uint32 fraction that is approximately equal to 'v'.
// Returns AVIF_FALSE if 'v' is < 0 or > UINT32_MAX or NaN.
AVIF_API AVIF_NODISCARD avifBool avifDoubleToUnsignedFraction(double v, uint32_t * numerator, uint32_t * denominator);
AVIF_API AVIF_NODISCARD avifBool avifDoubleToUnsignedFraction(double v, avifUnsignedFraction * fraction);

// ---------------------------------------------------------------------------
// Optional transformation structs
Expand Down Expand Up @@ -618,15 +630,12 @@ typedef struct avifGainMap
// gainMapLog2 = lerp(gainMapMin, gainMapMax, pow(gainMapEncoded, gainMapGamma));
// where 'lerp' is a linear interpolation function.
// Minimum value in the gain map, log2-encoded, per RGB channel.
int32_t gainMapMinN[3];
uint32_t gainMapMinD[3];
avifSignedFraction gainMapMin[3];
// Maximum value in the gain map, log2-encoded, per RGB channel.
int32_t gainMapMaxN[3];
uint32_t gainMapMaxD[3];
avifSignedFraction gainMapMax[3];
// Gain map gamma value with which the gain map was encoded, per RGB channel.
// For decoding, the inverse value (1/gamma) should be used.
uint32_t gainMapGammaN[3];
uint32_t gainMapGammaD[3];
avifUnsignedFraction gainMapGamma[3];

// Parameters used in gain map computation/tone mapping to avoid numerical
// instability.
Expand All @@ -635,11 +644,9 @@ typedef struct avifGainMap
// (see below).

// Offset constants for the base image, per RGB channel.
int32_t baseOffsetN[3];
uint32_t baseOffsetD[3];
avifSignedFraction baseOffset[3];
// Offset constants for the alternate image, per RGB channel.
int32_t alternateOffsetN[3];
uint32_t alternateOffsetD[3];
avifSignedFraction alternateOffset[3];

// Log2-encoded HDR headroom of the base and alternate images respectively.
// If baseHdrHeadroom is < alternateHdrHeadroom, the result of tone mapping
Expand All @@ -660,10 +667,8 @@ typedef struct avifGainMap
// 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;
avifUnsignedFraction baseHdrHeadroom;
avifUnsignedFraction alternateHdrHeadroom;

// 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
Expand Down
7 changes: 0 additions & 7 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,6 @@ AVIF_NODISCARD avifBool avifFractionCD(avifFraction * a, avifFraction * b);
AVIF_NODISCARD avifBool avifFractionAdd(avifFraction a, avifFraction b, avifFraction * result);
AVIF_NODISCARD avifBool avifFractionSub(avifFraction a, avifFraction b, avifFraction * result);

// Creates an int32 fraction that is approximately equal to 'v'.
// Returns AVIF_FALSE if 'v' is NaN or abs(v) is > INT32_MAX.
AVIF_NODISCARD avifBool avifDoubleToSignedFraction(double v, int32_t * numerator, uint32_t * denominator);
// Creates a uint32 fraction that is approximately equal to 'v'.
// Returns AVIF_FALSE if 'v' is < 0 or > UINT32_MAX or NaN.
AVIF_NODISCARD avifBool avifDoubleToUnsignedFraction(double v, uint32_t * numerator, uint32_t * denominator);

void avifImageSetDefaults(avifImage * image);
// Copies all fields that do not need to be freed/allocated from srcImage to dstImage.
void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage);
Expand Down
44 changes: 22 additions & 22 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,21 +263,21 @@ avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifP
AVIF_CHECKERR(dstImage->gainMap, AVIF_RESULT_OUT_OF_MEMORY);
}
for (int c = 0; c < 3; ++c) {
dstImage->gainMap->gainMapMinN[c] = srcImage->gainMap->gainMapMinN[c];
dstImage->gainMap->gainMapMinD[c] = srcImage->gainMap->gainMapMinD[c];
dstImage->gainMap->gainMapMaxN[c] = srcImage->gainMap->gainMapMaxN[c];
dstImage->gainMap->gainMapMaxD[c] = srcImage->gainMap->gainMapMaxD[c];
dstImage->gainMap->gainMapGammaN[c] = srcImage->gainMap->gainMapGammaN[c];
dstImage->gainMap->gainMapGammaD[c] = srcImage->gainMap->gainMapGammaD[c];
dstImage->gainMap->baseOffsetN[c] = srcImage->gainMap->baseOffsetN[c];
dstImage->gainMap->baseOffsetD[c] = srcImage->gainMap->baseOffsetD[c];
dstImage->gainMap->alternateOffsetN[c] = srcImage->gainMap->alternateOffsetN[c];
dstImage->gainMap->alternateOffsetD[c] = srcImage->gainMap->alternateOffsetD[c];
dstImage->gainMap->gainMapMin[c].n = srcImage->gainMap->gainMapMin[c].n;
dstImage->gainMap->gainMapMin[c].d = srcImage->gainMap->gainMapMin[c].d;
dstImage->gainMap->gainMapMax[c].n = srcImage->gainMap->gainMapMax[c].n;
dstImage->gainMap->gainMapMax[c].d = srcImage->gainMap->gainMapMax[c].d;
dstImage->gainMap->gainMapGamma[c].n = srcImage->gainMap->gainMapGamma[c].n;
dstImage->gainMap->gainMapGamma[c].d = srcImage->gainMap->gainMapGamma[c].d;
dstImage->gainMap->baseOffset[c].n = srcImage->gainMap->baseOffset[c].n;
dstImage->gainMap->baseOffset[c].d = srcImage->gainMap->baseOffset[c].d;
dstImage->gainMap->alternateOffset[c].n = srcImage->gainMap->alternateOffset[c].n;
dstImage->gainMap->alternateOffset[c].d = srcImage->gainMap->alternateOffset[c].d;
}
dstImage->gainMap->baseHdrHeadroomN = srcImage->gainMap->baseHdrHeadroomN;
dstImage->gainMap->baseHdrHeadroomD = srcImage->gainMap->baseHdrHeadroomD;
dstImage->gainMap->alternateHdrHeadroomN = srcImage->gainMap->alternateHdrHeadroomN;
dstImage->gainMap->alternateHdrHeadroomD = srcImage->gainMap->alternateHdrHeadroomD;
dstImage->gainMap->baseHdrHeadroom.n = srcImage->gainMap->baseHdrHeadroom.n;
dstImage->gainMap->baseHdrHeadroom.d = srcImage->gainMap->baseHdrHeadroom.d;
dstImage->gainMap->alternateHdrHeadroom.n = srcImage->gainMap->alternateHdrHeadroom.n;
dstImage->gainMap->alternateHdrHeadroom.d = srcImage->gainMap->alternateHdrHeadroom.d;
dstImage->gainMap->useBaseColorSpace = srcImage->gainMap->useBaseColorSpace;
AVIF_CHECKRES(avifRWDataSet(&dstImage->gainMap->altICC, srcImage->gainMap->altICC.data, srcImage->gainMap->altICC.size));
dstImage->gainMap->altColorPrimaries = srcImage->gainMap->altColorPrimaries;
Expand Down Expand Up @@ -1211,14 +1211,14 @@ avifGainMap * avifGainMapCreate(void)
gainMap->useBaseColorSpace = AVIF_TRUE;
// Set all denominators to valid values (1).
for (int i = 0; i < 3; ++i) {
gainMap->gainMapMinD[i] = 1;
gainMap->gainMapMaxD[i] = 1;
gainMap->gainMapGammaD[i] = 1;
gainMap->baseOffsetD[i] = 1;
gainMap->alternateOffsetD[i] = 1;
}
gainMap->baseHdrHeadroomD = 1;
gainMap->alternateHdrHeadroomD = 1;
gainMap->gainMapMin[i].d = 1;
gainMap->gainMapMax[i].d = 1;
gainMap->gainMapGamma[i].d = 1;
gainMap->baseOffset[i].d = 1;
gainMap->alternateOffset[i].d = 1;
}
gainMap->baseHdrHeadroom.d = 1;
gainMap->alternateHdrHeadroom.d = 1;
return gainMap;
}

Expand Down
Loading

0 comments on commit 527b80f

Please sign in to comment.