diff --git a/include/avif/internal.h b/include/avif/internal.h index 3cf3aa016b..ceb6110a4f 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h @@ -156,6 +156,9 @@ void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage); // Ignores the gainMap field (which exists only if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined). void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes); +// --------------------------------------------------------------------------- +// Alpha + typedef struct avifAlphaParams { uint32_t width; @@ -321,6 +324,8 @@ typedef enum avifItemCategory AVIF_ITEM_CATEGORY_COUNT } avifItemCategory; +avifBool avifIsAlpha(avifItemCategory itemCategory); + // --------------------------------------------------------------------------- #if defined(AVIF_ENABLE_EXPERIMENTAL_AVIR) diff --git a/src/avif.c b/src/avif.c index 264d004572..aa7d8e6786 100644 --- a/src/avif.c +++ b/src/avif.c @@ -868,6 +868,16 @@ avifBool avifCleanApertureBoxConvertCropRect(avifCleanApertureBox * clap, // --------------------------------------------------------------------------- +avifBool avifIsAlpha(avifItemCategory itemCategory) +{ + if (itemCategory == AVIF_ITEM_ALPHA) { + return AVIF_TRUE; + } + return AVIF_FALSE; +} + +// --------------------------------------------------------------------------- + avifBool avifAreGridDimensionsValid(avifPixelFormat yuvFormat, uint32_t imageW, uint32_t imageH, uint32_t tileW, uint32_t tileH, avifDiagnostics * diag) { // ISO/IEC 23000-22:2019, Section 7.3.11.4.2: diff --git a/src/read.c b/src/read.c index e1a652bc9d..57b5d63f59 100644 --- a/src/read.c +++ b/src/read.c @@ -1523,7 +1523,7 @@ static avifResult avifDecoderDataAllocateGridImagePlanes(avifDecoderData * data, return AVIF_RESULT_INVALID_IMAGE_GRID; } - avifBool alpha = (tile->input->itemCategory == AVIF_ITEM_ALPHA); + const avifBool alpha = avifIsAlpha(tile->input->itemCategory); if (alpha) { // An alpha tile does not contain any YUV pixels. AVIF_ASSERT_OR_RETURN(tile->image->yuvFormat == AVIF_PIXEL_FORMAT_NONE); @@ -1618,8 +1618,7 @@ static avifResult avifDecoderDataCopyTileToImage(avifDecoderData * data, #endif AVIF_ASSERT_OR_RETURN(avifImageSetViewRect(&dstView, dst, &dstViewRect) == AVIF_RESULT_OK && avifImageSetViewRect(&srcView, tile->image, &srcViewRect) == AVIF_RESULT_OK); - avifImageCopySamples(&dstView, &srcView, (tile->input->itemCategory == AVIF_ITEM_ALPHA) ? AVIF_PLANES_A : AVIF_PLANES_YUV); - + avifImageCopySamples(&dstView, &srcView, avifIsAlpha(tile->input->itemCategory) ? AVIF_PLANES_A : AVIF_PLANES_YUV); return AVIF_RESULT_OK; } @@ -4968,7 +4967,7 @@ avifResult avifDecoderReset(avifDecoder * decoder) continue; } #endif - if (c == AVIF_ITEM_ALPHA && !mainItems[c]->width && !mainItems[c]->height) { + if (avifIsAlpha(c) && !mainItems[c]->width && !mainItems[c]->height) { // NON-STANDARD: Alpha subimage does not have an ispe property; adopt width/height from color item AVIF_ASSERT_OR_RETURN(!(decoder->strictFlags & AVIF_STRICT_ALPHA_ISPE_REQUIRED)); mainItems[c]->width = mainItems[AVIF_ITEM_COLOR]->width; @@ -4978,7 +4977,7 @@ avifResult avifDecoderReset(avifDecoder * decoder) AVIF_CHECKRES(avifDecoderGenerateImageTiles(decoder, &data->tileInfos[c], mainItems[c], c)); avifStrictFlags strictFlags = decoder->strictFlags; - if (c == AVIF_ITEM_ALPHA && !isAlphaItemInInput) { + if (avifIsAlpha(c) && !isAlphaItemInInput) { // In this case, the made up grid item will not have an associated pixi property. So validate everything else // but the pixi property. strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; @@ -4989,7 +4988,7 @@ avifResult avifDecoderReset(avifDecoder * decoder) if (mainItems[AVIF_ITEM_COLOR]->progressive) { decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE; - // data->color.firstTileIndex is not yet defined but will be set to 0 a few lines below. + // data->tileInfos[AVIF_ITEM_COLOR].firstTileIndex is not yet defined but will be set to 0 a few lines below. const avifTile * colorTile = &data->tiles.tile[0]; if (colorTile->input->samples.count > 1) { decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE; @@ -5184,7 +5183,7 @@ static avifResult avifGetErrorForItemCategory(avifItemCategory itemCategory) return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; } #endif - return (itemCategory == AVIF_ITEM_ALPHA) ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED; + return avifIsAlpha(itemCategory) ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED; } static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextImageIndex, avifTileInfo * info) @@ -5201,7 +5200,7 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma } avifBool isLimitedRangeAlpha = AVIF_FALSE; - if (!tile->codec->getNextImage(tile->codec, decoder, sample, tile->input->itemCategory == AVIF_ITEM_ALPHA, &isLimitedRangeAlpha, tile->image)) { + if (!tile->codec->getNextImage(tile->codec, decoder, sample, avifIsAlpha(tile->input->itemCategory), &isLimitedRangeAlpha, tile->image)) { avifDiagnosticsPrintf(&decoder->diag, "tile->codec->getNextImage() failed"); return avifGetErrorForItemCategory(tile->input->itemCategory); } @@ -5229,7 +5228,7 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma // of the specification. However, it was allowed in version 1.0.0 of the // specification. To allow such files, simply convert the alpha plane to // full range. - if ((tile->input->itemCategory == AVIF_ITEM_ALPHA) && isLimitedRangeAlpha) { + if (avifIsAlpha(tile->input->itemCategory) && isLimitedRangeAlpha) { avifResult result = avifImageLimitedToFullAlpha(tile->image); if (result != AVIF_RESULT_OK) { avifDiagnosticsPrintf(&decoder->diag, "avifImageLimitedToFullAlpha failed"); @@ -5281,7 +5280,7 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma default: if ((decoder->image->width != src->width) || (decoder->image->height != src->height) || (decoder->image->depth != src->depth)) { - if (tile->input->itemCategory == AVIF_ITEM_ALPHA) { + if (avifIsAlpha(tile->input->itemCategory)) { 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; @@ -5295,7 +5294,7 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma break; } - if (tile->input->itemCategory == AVIF_ITEM_ALPHA) { + if (avifIsAlpha(tile->input->itemCategory)) { avifImageStealPlanes(decoder->image, src, AVIF_PLANES_A); #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) } else if (tile->input->itemCategory == AVIF_ITEM_GAIN_MAP) { diff --git a/src/write.c b/src/write.c index 9cbe1f8d0b..3200d1b2ea 100644 --- a/src/write.c +++ b/src/write.c @@ -1115,7 +1115,7 @@ static avifResult avifEncoderAddImageItems(avifEncoder * encoder, uint16_t * topLevelItemID) { const uint32_t cellCount = gridCols * gridRows; - const char * infeName = (itemCategory == AVIF_ITEM_ALPHA) ? infeNameAlpha + const char * infeName = avifIsAlpha(itemCategory) ? infeNameAlpha #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) : (itemCategory == AVIF_ITEM_GAIN_MAP) ? infeNameGainMap #endif @@ -1192,7 +1192,12 @@ static avifBool avifEncoderDataShouldForceKeyframeForAlpha(const avifEncoderData static avifResult avifGetErrorForItemCategory(avifItemCategory itemCategory) { - return (itemCategory == AVIF_ITEM_ALPHA) ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; +#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) + if (itemCategory == AVIF_ITEM_GAIN_MAP) { + return AVIF_RESULT_ENCODE_GAIN_MAP_FAILED; + } +#endif + return avifIsAlpha(itemCategory) ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; } static avifResult avifValidateImageBasicProperties(const avifImage * avifImage) @@ -1662,18 +1667,20 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder, } cellImage = paddedCellImage; } - const int quantizer = (item->itemCategory == AVIF_ITEM_ALPHA) ? encoder->data->quantizerAlpha + + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + int quantizer = isAlpha ? encoder->data->quantizerAlpha #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) - : (item->itemCategory == AVIF_ITEM_GAIN_MAP) ? encoder->data->quantizerGainMap + : (item->itemCategory == AVIF_ITEM_GAIN_MAP) ? encoder->data->quantizerGainMap #endif - : encoder->data->quantizer; + : encoder->data->quantizer; // If alpha channel is present, set disableLaggedOutput to AVIF_TRUE. If the encoder supports it, this enables // avifEncoderDataShouldForceKeyframeForAlpha to force a keyframe in the alpha channel whenever a keyframe has been // encoded in the color channel for animated images. avifResult encodeResult = item->codec->encodeImage(item->codec, encoder, cellImage, - item->itemCategory == AVIF_ITEM_ALPHA, + isAlpha, encoder->data->tileRowsLog2, encoder->data->tileColsLog2, quantizer, @@ -1780,10 +1787,12 @@ static avifResult avifEncoderWriteMediaDataBox(avifEncoder * encoder, // only process metadata (XMP/Exif) payloads when metadataPass is true continue; } - avifBool isAlphaOrGainMap = item->itemCategory == AVIF_ITEM_ALPHA; + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + const avifBool isAlphaOrGainMap = isAlpha #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) - isAlphaOrGainMap = isAlphaOrGainMap || item->itemCategory == AVIF_ITEM_GAIN_MAP; + || item->itemCategory == AVIF_ITEM_GAIN_MAP #endif + ; if (alphaAndGainMapPass != isAlphaOrGainMap) { // only process alpha payloads when alphaPass is true continue; @@ -1794,8 +1803,7 @@ static avifResult avifEncoderWriteMediaDataBox(avifEncoder * encoder, // We always interleave all AV1 items for layered images. AVIF_ASSERT_OR_RETURN(item->encodeOutput->samples.count == item->mdatFixups.count); - avifEncoderItemReference * ref = (item->itemCategory == AVIF_ITEM_ALPHA) ? avifArrayPush(layeredAlphaItems) - : avifArrayPush(layeredColorItems); + avifEncoderItemReference * ref = avifArrayPush(isAlpha ? layeredAlphaItems : layeredColorItems); AVIF_CHECKERR(ref != NULL, AVIF_RESULT_OUT_OF_MEMORY); *ref = item; continue; @@ -1820,7 +1828,7 @@ static avifResult avifEncoderWriteMediaDataBox(avifEncoder * encoder, avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; AVIF_CHECKRES(avifRWStreamWrite(s, sample->data.data, sample->data.size)); - if (item->itemCategory == AVIF_ITEM_ALPHA) { + if (isAlpha) { encoder->ioStats.alphaOBUSize += sample->data.size; } else if (item->itemCategory == AVIF_ITEM_COLOR) { encoder->ioStats.colorOBUSize += sample->data.size; @@ -2254,7 +2262,7 @@ static avifResult avifRWStreamWriteProperties(avifItemPropertyDedup * const dedu imageHeight = item->gridHeight; } - // Properties all image items need + // Properties all image items need (coded and derived) // ispe = image spatial extent (width, height) avifItemPropertyDedupStart(dedup); avifBoxMarker ispe; @@ -2272,15 +2280,16 @@ static avifResult avifRWStreamWriteProperties(avifItemPropertyDedup * const dedu hasPixi = AVIF_FALSE; } #endif + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + uint8_t depth = (uint8_t)itemMetadata->depth; if (hasPixi) { avifItemPropertyDedupStart(dedup); - uint8_t channelCount = - (item->itemCategory == AVIF_ITEM_ALPHA || (itemMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3; + uint8_t channelCount = (isAlpha || (itemMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3; avifBoxMarker pixi; AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0, &pixi)); AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, channelCount)); // unsigned int (8) num_channels; for (uint8_t chan = 0; chan < channelCount; ++chan) { - AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, (uint8_t)itemMetadata->depth)); // unsigned int (8) bits_per_channel; + AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, depth)); // unsigned int (8) bits_per_channel; } avifRWStreamFinishBox(&dedup->s, pixi); AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->ipma, AVIF_FALSE)); @@ -2293,7 +2302,7 @@ static avifResult avifRWStreamWriteProperties(avifItemPropertyDedup * const dedu AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->ipma, AVIF_TRUE)); } - if (item->itemCategory == AVIF_ITEM_ALPHA) { + if (isAlpha) { // Alpha specific properties avifItemPropertyDedupStart(dedup);