Skip to content

Commit

Permalink
Put more code behind AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP. (AOMediaCodec…
Browse files Browse the repository at this point in the history
  • Loading branch information
maryla-uc authored Sep 4, 2023
1 parent 034af20 commit 6ec0938
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 44 deletions.
11 changes: 7 additions & 4 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ typedef struct avifContentLightLevelInformationBox
uint16_t maxPALL;
} avifContentLightLevelInformationBox;

#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
// ---------------------------------------------------------------------------
// avifGainMap
// Gain Maps are a HIGHLY EXPERIMENTAL FEATURE. The format might still change and
Expand All @@ -525,7 +526,6 @@ typedef struct avifContentLightLevelInformationBox
// This is based on ISO/IEC JTC 1/SC 29/WG 3 m64379
// This product includes Gain Map technology under license by Adobe.

#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
struct avifImage;

// Gain map metadata, for tone mapping between SDR and HDR.
Expand Down Expand Up @@ -680,9 +680,11 @@ typedef struct avifImage
// avifImageCreate() and avifImageCreateEmpty() return NULL if arguments are invalid or if a memory allocation failed.
AVIF_API avifImage * avifImageCreate(uint32_t width, uint32_t height, uint32_t depth, avifPixelFormat yuvFormat);
AVIF_API avifImage * avifImageCreateEmpty(void); // helper for making an image to decode into
// Performs a deep copy of an image, including all metadata and planes, and the gain map metadata/planes if present.
// Performs a deep copy of an image, including all metadata and planes, and the gain map metadata/planes if present
// and if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined.
AVIF_API avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes);
// Performs a shallow copy of a rectangular area of an image. 'dstImage' does not own the planes. The gain map if any is ignored.
// Performs a shallow copy of a rectangular area of an image. 'dstImage' does not own the planes.
// Ignores the gainMap field (which exists only if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined).
AVIF_API avifResult avifImageSetViewRect(avifImage * dstImage, const avifImage * srcImage, const avifCropRect * rect);
AVIF_API void avifImageDestroy(avifImage * image);

Expand All @@ -695,7 +697,8 @@ AVIF_API avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t *
// Sets XMP metadata.
AVIF_API avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize);

// Allocate/free/steal planes. These functions do not affect the gain map image if present.
// Allocate/free/steal planes. These functions ignore the gainMap field (which exists only if
// AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined).
AVIF_API avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes); // Ignores any pre-existing planes
AVIF_API void avifImageFreePlanes(avifImage * image, avifPlanesFlags planes); // Ignores already-freed planes
AVIF_API void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, avifPlanesFlags planes);
Expand Down
2 changes: 1 addition & 1 deletion include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage);
// Copies the samples from srcImage to dstImage. dstImage must be allocated.
// srcImage and dstImage must have the same width, height, and depth.
// If the AVIF_PLANES_YUV bit is set in planes, then srcImage and dstImage must have the same yuvFormat and yuvRange.
// The gain map (if present) is not affected.
// Ignores the gainMap field (which exists only if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined).
void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes);

typedef struct avifAlphaParams
Expand Down
120 changes: 81 additions & 39 deletions src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ typedef struct avifTile
avifCodecDecodeInput * input;
avifCodecType codecType;
// This may point to a codec that it owns or point to a shared codec that it does not own. In the shared case, this will
// point to one of avifDecoderData.codec, avifDecoderData.codecAlpha or avifDecoderData.codecGainMap.
// point to one of the avifCodec instances in avifDecoderData.
struct avifCodec * codec;
avifImage * image;
uint32_t width; // Either avifTrack.width or avifDecoderItem.width
Expand Down Expand Up @@ -826,7 +826,9 @@ typedef struct avifDecoderData
avifTileArray tiles;
avifTileInfo color;
avifTileInfo alpha;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
avifTileInfo gainMap;
#endif
avifDecoderSource source;
// When decoding AVIF images with grid, use a single decoder instance for all the tiles instead of creating a decoder instance
// for each tile. If that is the case, |codec| will be used by all the tiles.
Expand All @@ -842,7 +844,9 @@ typedef struct avifDecoderData
// decoder instance (same as above).
avifCodec * codec;
avifCodec * codecAlpha;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
avifCodec * codecGainMap;
#endif
uint8_t majorBrand[4]; // From the file's ftyp, used by AVIF_DECODER_SOURCE_AUTO
avifDiagnostics * diag; // Shallow copy; owned by avifDecoder
const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
Expand Down Expand Up @@ -883,15 +887,18 @@ static void avifDecoderDataResetCodec(avifDecoderData * data)
}
if (tile->codec) {
// Check if tile->codec was created separately and destroy it in that case.
if (tile->codec != data->codec && tile->codec != data->codecAlpha && tile->codec != data->codecGainMap) {
avifBool tileOwnsCodec = tile->codec != data->codec && tile->codec != data->codecAlpha;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
tileOwnsCodec = tileOwnsCodec && tile->codec != data->codecGainMap;
#endif
if (tileOwnsCodec) {
avifCodecDestroy(tile->codec);
}
tile->codec = NULL;
}
}
data->color.decodedTileCount = 0;
data->alpha.decodedTileCount = 0;
data->gainMap.decodedTileCount = 0;
if (data->codec) {
avifCodecDestroy(data->codec);
data->codec = NULL;
Expand All @@ -900,10 +907,13 @@ static void avifDecoderDataResetCodec(avifDecoderData * data)
avifCodecDestroy(data->codecAlpha);
data->codecAlpha = NULL;
}
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
data->gainMap.decodedTileCount = 0;
if (data->codecGainMap) {
avifCodecDestroy(data->codecGainMap);
data->codecGainMap = NULL;
}
#endif
}

static avifTile * avifDecoderDataCreateTile(avifDecoderData * data, avifCodecType codecType, uint32_t width, uint32_t height, uint8_t operatingPoint)
Expand Down Expand Up @@ -951,7 +961,11 @@ static void avifDecoderDataClearTiles(avifDecoderData * data)
}
if (tile->codec) {
// Check if tile->codec was created separately and destroy it in that case.
if (tile->codec != data->codec && tile->codec != data->codecAlpha && tile->codec != data->codecGainMap) {
avifBool tileOwnsCodec = tile->codec != data->codec && tile->codec != data->codecAlpha;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
tileOwnsCodec = tileOwnsCodec && tile->codec != data->codecGainMap;
#endif
if (tileOwnsCodec) {
avifCodecDestroy(tile->codec);
}
tile->codec = NULL;
Expand All @@ -966,8 +980,6 @@ static void avifDecoderDataClearTiles(avifDecoderData * data)
data->color.decodedTileCount = 0;
data->alpha.tileCount = 0;
data->alpha.decodedTileCount = 0;
data->gainMap.tileCount = 0;
data->gainMap.decodedTileCount = 0;
if (data->codec) {
avifCodecDestroy(data->codec);
data->codec = NULL;
Expand All @@ -976,10 +988,14 @@ static void avifDecoderDataClearTiles(avifDecoderData * data)
avifCodecDestroy(data->codecAlpha);
data->codecAlpha = NULL;
}
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
data->gainMap.tileCount = 0;
data->gainMap.decodedTileCount = 0;
if (data->codecGainMap) {
avifCodecDestroy(data->codecGainMap);
data->codecGainMap = NULL;
}
#endif
}

static void avifDecoderDataDestroy(avifDecoderData * data)
Expand Down Expand Up @@ -4048,9 +4064,13 @@ static avifResult avifCodecCreateInternal(avifCodecChoice choice, const avifTile

static avifBool avifTilesCanBeDecodedWithSameCodecInstance(avifDecoderData * data)
{
if (data->color.tileCount == 1 && (data->alpha.tileCount == 1 || data->gainMap.tileCount == 1)) {
// Single tile image with single tile alpha or gain map plane. In this case each tile needs its own decoder since the planes will be
// "stolen". Stealing either the color or the alpha (or gain map) plane will invalidate the other ones when decode is called the second
avifBool hasSingleTileAlphaOrGainMap = data->alpha.tileCount == 1;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
hasSingleTileAlphaOrGainMap = hasSingleTileAlphaOrGainMap || data->gainMap.tileCount == 1;
#endif
if (data->color.tileCount == 1 && hasSingleTileAlphaOrGainMap) {
// Single tile image with single tile alpha plane or gain map. In this case each tile needs its own decoder since the planes will be
// "stolen". Stealing either the color or the alpha plane (or gain map) will invalidate the other ones when decode is called the second
// (or third) time.
return AVIF_FALSE;
}
Expand Down Expand Up @@ -4684,7 +4704,9 @@ avifResult avifDecoderReset(avifDecoder * decoder)

decoder->data->color.firstTileIndex = 0;
decoder->data->alpha.firstTileIndex = decoder->data->color.tileCount;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
decoder->data->gainMap.firstTileIndex = decoder->data->color.tileCount + decoder->data->alpha.tileCount;
#endif

// Sanity check tiles
for (uint32_t tileIndex = 0; tileIndex < data->tiles.count; ++tileIndex) {
Expand All @@ -4696,10 +4718,10 @@ avifResult avifDecoderReset(avifDecoder * decoder)
return AVIF_RESULT_BMFF_PARSE_FAILED;
}

if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
decoder->ioStats.alphaOBUSize += sample->size;
} else if (tile->input->itemCategory == AVIF_ITEM_COLOR) {
if (tile->input->itemCategory == AVIF_ITEM_COLOR) {
decoder->ioStats.colorOBUSize += sample->size;
} else if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
decoder->ioStats.alphaOBUSize += sample->size;
}
}
}
Expand Down Expand Up @@ -4952,26 +4974,32 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma
assert(info->tileCount == 1);
assert(tileIndex == 0);
avifImage * src = tile->image;

switch (tile->input->itemCategory) {
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
if (tile->input->itemCategory == AVIF_ITEM_GAIN_MAP) {
decoder->image->gainMap.image->width = src->width;
decoder->image->gainMap.image->height = src->height;
decoder->image->gainMap.image->depth = src->depth;
} else
case AVIF_ITEM_GAIN_MAP:
decoder->image->gainMap.image->width = src->width;
decoder->image->gainMap.image->height = src->height;
decoder->image->gainMap.image->depth = src->depth;
break;
#endif
if ((decoder->image->width != src->width) || (decoder->image->height != src->height) ||
(decoder->image->depth != src->depth)) {
if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
avifDiagnosticsPrintf(&decoder->diag,
"The color image item does not match the alpha image item in width, height, or bit depth");
return AVIF_RESULT_DECODE_ALPHA_FAILED;
}
avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);
default:
if ((decoder->image->width != src->width) || (decoder->image->height != src->height) ||
(decoder->image->depth != src->depth)) {
if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
avifDiagnosticsPrintf(&decoder->diag,
"The color image item does not match the alpha image item in width, height, or bit depth");
return AVIF_RESULT_DECODE_ALPHA_FAILED;
}
avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);

decoder->image->width = src->width;
decoder->image->height = src->height;
decoder->image->depth = src->depth;
decoder->image->width = src->width;
decoder->image->height = src->height;
decoder->image->depth = src->depth;
}
break;
}

if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
avifImageStealPlanes(decoder->image, src, AVIF_PLANES_A);
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
Expand All @@ -4987,6 +5015,17 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma
return AVIF_RESULT_OK;
}

// Returns AVIF_FALSE if there is currently a partially decoded frame.
static avifBool avifDecoderDataFrameFullyDecoded(const avifDecoderData * data)
{
avifBool fullyDecoded = (data->color.decodedTileCount == data->color.tileCount) &&
(data->alpha.decodedTileCount == data->alpha.tileCount);
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
fullyDecoded = fullyDecoded && data->gainMap.decodedTileCount == data->gainMap.tileCount;
#endif
return fullyDecoded;
}

avifResult avifDecoderNextImage(avifDecoder * decoder)
{
avifDiagnosticsClearError(&decoder->diag);
Expand All @@ -5000,17 +5039,22 @@ avifResult avifDecoderNextImage(avifDecoder * decoder)
return AVIF_RESULT_IO_NOT_SET;
}

if ((decoder->data->color.decodedTileCount == decoder->data->color.tileCount) &&
(decoder->data->alpha.decodedTileCount == decoder->data->alpha.tileCount) &&
(decoder->data->gainMap.decodedTileCount == decoder->data->gainMap.tileCount)) {
if (avifDecoderDataFrameFullyDecoded(decoder->data)) {
// A frame was decoded during the last avifDecoderNextImage() call.
decoder->data->color.decodedTileCount = 0;
decoder->data->alpha.decodedTileCount = 0;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
decoder->data->gainMap.decodedTileCount = 0;
#endif
}

#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
assert(decoder->data->tiles.count ==
(decoder->data->color.tileCount + decoder->data->alpha.tileCount + decoder->data->gainMap.tileCount));
#else
assert(decoder->data->tiles.count == (decoder->data->color.tileCount + decoder->data->alpha.tileCount));
#endif

const uint32_t nextImageIndex = (uint32_t)(decoder->imageIndex + 1);

// Ensure that we have created the codecs before proceeding with the decoding.
Expand All @@ -5034,22 +5078,22 @@ avifResult avifDecoderNextImage(avifDecoder * decoder)
if (!decoder->allowIncremental || (prepareAlphaTileResult != AVIF_RESULT_WAITING_ON_IO)) {
AVIF_CHECKRES(prepareAlphaTileResult);
}
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
// And same with the gain map if present.
const avifResult prepareGainMapTileResult = avifDecoderPrepareTiles(decoder, nextImageIndex, &decoder->data->gainMap);
if (!decoder->allowIncremental || (prepareGainMapTileResult != AVIF_RESULT_WAITING_ON_IO)) {
AVIF_CHECKRES(prepareGainMapTileResult);
}
#endif

// Decode all available color tiles now, then all available alpha and gain map tiles.
AVIF_CHECKRES(avifDecoderDecodeTiles(decoder, nextImageIndex, &decoder->data->color));
AVIF_CHECKRES(avifDecoderDecodeTiles(decoder, nextImageIndex, &decoder->data->alpha));
// Decode the gain map: noop if there is no gain map or AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is disabled
// since we never call avifDecoderGenerateImageTiles() for it.
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
AVIF_CHECKRES(avifDecoderDecodeTiles(decoder, nextImageIndex, &decoder->data->gainMap));
#endif

if ((decoder->data->color.decodedTileCount != decoder->data->color.tileCount) ||
(decoder->data->alpha.decodedTileCount != decoder->data->alpha.tileCount) ||
(decoder->data->gainMap.decodedTileCount != decoder->data->gainMap.tileCount)) {
if (!avifDecoderDataFrameFullyDecoded(decoder->data)) {
assert(decoder->allowIncremental);
// The image is not completely decoded. There should be no error unrelated to missing bytes,
// and at least some missing bytes.
Expand Down Expand Up @@ -5133,9 +5177,7 @@ avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex)
}

if (requestedIndex == decoder->imageIndex) {
if ((decoder->data->color.decodedTileCount == decoder->data->color.tileCount) &&
(decoder->data->alpha.decodedTileCount == decoder->data->alpha.tileCount) &&
(decoder->data->gainMap.decodedTileCount == decoder->data->gainMap.tileCount)) {
if (avifDecoderDataFrameFullyDecoded(decoder->data)) {
// The current fully decoded image (decoder->imageIndex) is requested, nothing to do
return AVIF_RESULT_OK;
}
Expand Down
2 changes: 2 additions & 0 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ typedef struct avifEncoderData
// Map the encoder settings quality and qualityAlpha to quantizer and quantizerAlpha
int quantizer;
int quantizerAlpha;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
int quantizerGainMap;
#endif
// tileRowsLog2 and tileColsLog2 are the actual tiling values after automatic tiling is handled
int tileRowsLog2;
int tileColsLog2;
Expand Down

0 comments on commit 6ec0938

Please sign in to comment.