Skip to content

Commit

Permalink
Significantly simplify gain map API (AOMediaCodec#2481)
Browse files Browse the repository at this point in the history
Gain map metadata is now alwayas parsed (when gain map code is enabled
at compile time).

Ideally, gain map metadata should be stored towards the beginning of the file
(since it's in the 'mdat' part).
  • Loading branch information
maryla-uc authored Oct 24, 2024
1 parent 755f960 commit 094d841
Show file tree
Hide file tree
Showing 19 changed files with 175 additions and 275 deletions.
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ The changes are relative to the previous release, unless the baseline is specifi
draft.
* Ignore gain maps with unsupported metadata. Handle gain maps with
writer_version > 0 correctly.
The combination of settings enableParsingGainMapMetadata=false with
enableDecodingGainMap=true is no longer allowed and returns an invalid argument
error.
The `gainMapPresent` field is now only populated if enableParsingGainMapMetadata
is true.
Simplify gain map API: remove the enableParsingGainMapMetadata setting, now gain
map metadata is always parsed if present and if this feature is compiled in.
Replace enableDecodingGainMap and ignoreColorAndAlpha with a bit field to choose
image content to decode. Remove gainMapPresent: users can check if
decoder->image->gainMap != NULL instead.
* Write an empty HandlerBox name field instead of "libavif" (saves 7 bytes).
* Update aom.cmd/LocalAom.cmake: v3.10.0
* Update avm.cmd: research-v8.0.0
Expand Down
13 changes: 3 additions & 10 deletions apps/avifdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,8 @@ int main(int argc, char * argv[])
decoder->imageDimensionLimit = imageDimensionLimit;
decoder->strictFlags = strictFlags;
decoder->allowProgressive = allowProgressive;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
// Decode the gain map (if present) to allow showing its info.
decoder->enableParsingGainMapMetadata = AVIF_TRUE;
decoder->enableDecodingGainMap = AVIF_TRUE;
#endif
decoder->imageContentToDecode = AVIF_IMAGE_CONTENT_ALL;

avifResult result = avifDecoderSetIOFile(decoder, inputFilename);
if (result != AVIF_RESULT_OK) {
fprintf(stderr, "Cannot open file for read: %s\n", inputFilename);
Expand Down Expand Up @@ -348,11 +345,7 @@ int main(int argc, char * argv[])

printf("Image decoded: %s\n", inputFilename);
printf("Image details:\n");
avifBool gainMapPresent = AVIF_FALSE;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
gainMapPresent = decoder->gainMapPresent;
#endif
avifImageDump(decoder->image, 0, 0, gainMapPresent, decoder->progressiveState);
avifImageDump(decoder->image, 0, 0, decoder->progressiveState);

if (ignoreICC && (decoder->image->icc.size > 0)) {
printf("[--ignore-icc] Discarding ICC profile.\n");
Expand Down
5 changes: 0 additions & 5 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2479,14 +2479,9 @@ int main(int argc, char * argv[])
}
printf("AVIF to be written:%s\n", lossyHint);
const avifImage * avif = gridCells ? gridCells[0] : image;
avifBool gainMapPresent = AVIF_FALSE;
#if defined(AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION)
gainMapPresent = (avif->gainMap && avif->gainMap->image);
#endif
avifImageDump(avif,
settings.gridDims[0],
settings.gridDims[1],
gainMapPresent,
settings.layers > 1 ? AVIF_PROGRESSIVE_STATE_AVAILABLE : AVIF_PROGRESSIVE_STATE_UNAVAILABLE);

avifEncodedByteSizes byteSizes = { 0, 0, 0 };
Expand Down
4 changes: 1 addition & 3 deletions apps/avifgainmaputil/extractgainmap_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ avifResult ExtractGainMapCommand::Run() {
if (decoder == NULL) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
decoder->enableParsingGainMapMetadata = true;
decoder->enableDecodingGainMap = true;
decoder->ignoreColorAndAlpha = true;
decoder->imageContentToDecode = AVIF_IMAGE_CONTENT_GAIN_MAP;

avifResult result =
ReadAvif(decoder.get(), arg_input_filename_, /*ignore_profile=*/true);
Expand Down
5 changes: 1 addition & 4 deletions apps/avifgainmaputil/imageio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,9 @@ avifResult WriteAvif(const avifImage* image, avifEncoder* encoder,
const std::string& output_filename) {
avifRWData encoded = AVIF_DATA_EMPTY;
std::cout << "AVIF to be written:\n";
const bool gain_map_present =
(image->gainMap != nullptr && image->gainMap->image != nullptr);
avifImageDump(image,
/*gridCols=*/1,
/*gridRows=*/1, gain_map_present,
AVIF_PROGRESSIVE_STATE_UNAVAILABLE);
/*gridRows=*/1, AVIF_PROGRESSIVE_STATE_UNAVAILABLE);
std::cout << "Encoding AVIF at quality " << encoder->quality << " speed "
<< encoder->speed << ", please wait...\n";
avifResult result = avifEncoderWrite(encoder, image, &encoded);
Expand Down
3 changes: 1 addition & 2 deletions apps/avifgainmaputil/printmetadata_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ avifResult PrintMetadataCommand::Run() {
if (decoder == NULL) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
decoder->enableParsingGainMapMetadata = true;

avifResult result =
avifDecoderSetIOFile(decoder.get(), arg_input_filename_.value().c_str());
Expand All @@ -55,7 +54,7 @@ avifResult PrintMetadataCommand::Run() {
<< decoder->diag.error << ")\n";
return result;
}
if (!decoder->gainMapPresent) {
if (decoder->image->gainMap == nullptr) {
std::cerr << "Input image " << arg_input_filename_
<< " does not contain a gain map\n";
return AVIF_RESULT_INVALID_ARGUMENT;
Expand Down
3 changes: 1 addition & 2 deletions apps/avifgainmaputil/swapbase_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ avifResult SwapBaseCommand::Run() {
if (decoder == NULL) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
decoder->enableParsingGainMapMetadata = true;
decoder->enableDecodingGainMap = true;
decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP;
avifResult result = ReadAvif(decoder.get(), arg_input_filename_,
arg_image_read_.ignore_profile);
if (result != AVIF_RESULT_OK) {
Expand Down
3 changes: 1 addition & 2 deletions apps/avifgainmaputil/tonemap_command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ avifResult TonemapCommand::Run() {
if (decoder == NULL) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
decoder->enableDecodingGainMap = true;
decoder->enableParsingGainMapMetadata = true;
decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP;
avifResult result = ReadAvif(decoder.get(), arg_input_filename_,
arg_image_read_.ignore_profile);
if (result != AVIF_RESULT_OK) {
Expand Down
21 changes: 5 additions & 16 deletions apps/shared/avifutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,7 @@ static void printClapFraction(const char * name, int32_t n, int32_t d)
}
}

static void avifImageDumpInternal(const avifImage * avif,
uint32_t gridCols,
uint32_t gridRows,
avifBool alphaPresent,
avifBool gainMapPresent,
avifProgressiveState progressiveState)
static void avifImageDumpInternal(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool alphaPresent, avifProgressiveState progressiveState)
{
uint32_t width = avif->width;
uint32_t height = avif->height;
Expand Down Expand Up @@ -161,29 +156,23 @@ static void avifImageDumpInternal(const avifImage * avif,
printf(" * CLLI : %hu, %hu\n", gainMapImage->clli.maxCLL, gainMapImage->clli.maxPALL);
}
printf("\n");
} else if (gainMapPresent) {
} else if (avif->gainMap != NULL) {
printf("Present (but ignored)\n");
} else {
printf("Absent\n");
}
#else
(void)gainMapPresent;
#endif // AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP
}

void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool gainMapPresent, avifProgressiveState progressiveState)
void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifProgressiveState progressiveState)
{
const avifBool alphaPresent = avif->alphaPlane && (avif->alphaRowBytes > 0);
avifImageDumpInternal(avif, gridCols, gridRows, alphaPresent, gainMapPresent, progressiveState);
avifImageDumpInternal(avif, gridCols, gridRows, alphaPresent, progressiveState);
}

void avifContainerDump(const avifDecoder * decoder)
{
avifBool gainMapPresent = AVIF_FALSE;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
gainMapPresent = decoder->gainMapPresent;
#endif
avifImageDumpInternal(decoder->image, 0, 0, decoder->alphaPresent, gainMapPresent, decoder->progressiveState);
avifImageDumpInternal(decoder->image, 0, 0, decoder->alphaPresent, decoder->progressiveState);
if (decoder->imageSequenceTrackPresent) {
if (decoder->repetitionCount == AVIF_REPETITION_COUNT_INFINITE) {
printf(" * Repeat Count : Infinite\n");
Expand Down
2 changes: 1 addition & 1 deletion apps/shared/avifutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extern "C" {
#define AVIF_FMT_ZU "zu"
#endif

void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool gainMapPresent, avifProgressiveState progressiveState);
void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifProgressiveState progressiveState);
void avifContainerDump(const avifDecoder * decoder);
void avifPrintVersions(void);
void avifDumpDiagnostics(const avifDiagnostics * diag);
Expand Down
43 changes: 23 additions & 20 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,23 @@ typedef enum avifProgressiveState
} avifProgressiveState;
AVIF_API const char * avifProgressiveStateToString(avifProgressiveState progressiveState);

// Types of image content that can be decoded.
typedef enum avifImageContentTypeFlag
{
AVIF_IMAGE_CONTENT_NONE = 0,
// Color only or alpha only is not currently supported.
AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA = (1 << 0) | (1 << 1),
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
AVIF_IMAGE_CONTENT_GAIN_MAP = (1 << 2),
AVIF_IMAGE_CONTENT_ALL = AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA | AVIF_IMAGE_CONTENT_GAIN_MAP,
#else
AVIF_IMAGE_CONTENT_ALL = AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA,
#endif

AVIF_IMAGE_CONTENT_DECODE_DEFAULT = AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA,
} avifImageContentTypeFlag;
typedef uint32_t avifImageContentTypeFlags;

// NOTE: The avifDecoder struct may be extended in a future release. Code outside the libavif
// library must allocate avifDecoder by calling the avifDecoderCreate() function.
typedef struct avifDecoder
Expand Down Expand Up @@ -1295,23 +1312,8 @@ typedef struct avifDecoder

// Version 1.1.0 ends here. Add any new members after this line.

#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
// Enable parsing the gain map metadata if present (defaults to AVIF_FALSE).
// Gain map metadata is read during avifDecoderParse(). Like Exif and XMP, this data
// can be (unfortunately) packed at the end of the file, which will cause
// avifDecoderParse() to return AVIF_RESULT_WAITING_ON_IO until it finds it.
// If you don't actually use this data, it's best to leave this to AVIF_FALSE (default).
avifBool enableParsingGainMapMetadata;
// Enable decoding the gain map image if present (defaults to AVIF_FALSE).
// If set to true, enableParsingGainMapMetadata must also be true.
avifBool enableDecodingGainMap;
// Do not decode the color/alpha planes of the main image.
// Can be useful to decode the gain map image only.
avifBool ignoreColorAndAlpha;
// True when avifDecoderParse() detects a supported gain map.
// Requires enableParsingGainMapMetadata to be set to true.
avifBool gainMapPresent;
#endif
// Image content to decode (if present). Defaults to AVIF_IMAGE_CONTENT_DECODE_DEFAULT.
avifImageContentTypeFlags imageContentToDecode;
} avifDecoder;

// Returns NULL in case of memory allocation failure.
Expand Down Expand Up @@ -1373,9 +1375,10 @@ AVIF_API avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint3
// function can be called next to retrieve the number of top rows that can be immediately accessed
// from the luma plane of decoder->image, and alpha if any. The corresponding rows from the chroma planes,
// if any, can also be accessed (half rounded up if subsampled, same number of rows otherwise).
// If a gain map is present and AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is on and enableDecodingGainMap is also on,
// the gain map's planes can also be accessed in the same way. If the gain map's height is different from
// the main image, then the number of available gain map rows is at least:
// If a gain map is present and AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is on and
// (imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP) is nonzero, the gain map's planes can also be accessed
// in the same way. If the gain map's height is different from the main image, then the number of
// available gain map rows is at least:
// roundf((float)decoded_row_count / decoder->image->height * decoder->image->gainMap.image->height)
// When gain map scaling is needed, callers might choose to use a few less rows depending on how many rows
// are needed by the scaling algorithm, to avoid the last row(s) changing when more data becomes available.
Expand Down
Loading

0 comments on commit 094d841

Please sign in to comment.