diff --git a/include/avif/avif.h b/include/avif/avif.h index 08555cb767..6a1ea87609 100644 --- a/include/avif/avif.h +++ b/include/avif/avif.h @@ -151,7 +151,7 @@ typedef enum avifResult AVIF_RESULT_NOT_IMPLEMENTED, // a requested code path is not (yet) implemented AVIF_RESULT_OUT_OF_MEMORY, AVIF_RESULT_CANNOT_CHANGE_SETTING, // a setting that can't change is changed during encoding - AVIF_RESULT_INCOMPATIBLE_IMAGE // given image is not compatible with already encoded image + AVIF_RESULT_INCOMPATIBLE_IMAGE // the image is incompatible with already encoded images } avifResult; AVIF_API const char * avifResultToString(avifResult result); @@ -1054,11 +1054,11 @@ typedef struct avifEncoder avifCodecChoice codecChoice; // settings (see Notes above) - int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default). - uint64_t timescale; // timescale of the media (Hz) int maxThreads; int speed; - // changeable encoder settings. + int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default). + uint64_t timescale; // timescale of the media (Hz) + // changeable encoder settings int minQuantizer; int maxQuantizer; int minQuantizerAlpha; diff --git a/include/avif/internal.h b/include/avif/internal.h index c3a2749135..b854698a0f 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h @@ -262,14 +262,14 @@ struct avifCodecInternal; typedef enum avifEncoderChange { - AVIF_ENCODER_CHANGE_MIN_QUANTIZER = (1 << 0), - AVIF_ENCODER_CHANGE_MAX_QUANTIZER = (1 << 1), - AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA = (1 << 2), - AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA = (1 << 3), - AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2 = (1 << 4), - AVIF_ENCODER_CHANGE_TILE_COLS_LOG2 = (1 << 5), - - AVIF_ENCODER_CHANGE_CODEC_SPECIFIC = (1 << 31) + AVIF_ENCODER_CHANGE_MIN_QUANTIZER = (1u << 0), + AVIF_ENCODER_CHANGE_MAX_QUANTIZER = (1u << 1), + AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA = (1u << 2), + AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA = (1u << 3), + AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2 = (1u << 4), + AVIF_ENCODER_CHANGE_TILE_COLS_LOG2 = (1u << 5), + + AVIF_ENCODER_CHANGE_CODEC_SPECIFIC = (1u << 31) } avifEncoderChange; typedef uint32_t avifEncoderChanges; diff --git a/src/avif.c b/src/avif.c index 5381b44dd2..d6e4afb08c 100644 --- a/src/avif.c +++ b/src/avif.c @@ -98,8 +98,8 @@ const char * avifResultToString(avifResult result) case AVIF_RESULT_INVALID_ARGUMENT: return "Invalid argument"; case AVIF_RESULT_NOT_IMPLEMENTED: return "Not implemented"; case AVIF_RESULT_OUT_OF_MEMORY: return "Out of memory"; - case AVIF_RESULT_CANNOT_CHANGE_SETTING: return "Can not change some settings during encoding"; - case AVIF_RESULT_INCOMPATIBLE_IMAGE: return "This image is incompatible with already encoded image"; + case AVIF_RESULT_CANNOT_CHANGE_SETTING: return "Cannot change some setting during encoding"; + case AVIF_RESULT_INCOMPATIBLE_IMAGE: return "The image is incompatible with already encoded images"; case AVIF_RESULT_UNKNOWN_ERROR: default: break; diff --git a/src/codec_aom.c b/src/codec_aom.c index 4d9c801183..f91b38ff6e 100644 --- a/src/codec_aom.c +++ b/src/codec_aom.c @@ -532,10 +532,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, avifCodecEncodeOutput * output) { struct aom_codec_enc_cfg * cfg = &codec->internal->cfg; - aom_codec_iface_t * encoderInterface = NULL; - unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; - int aomCpuUsed = -1; - avifBool lossless = AVIF_FALSE; + avifBool quantizerUpdated = AVIF_FALSE; if (!codec->internal->encoderInitialized) { // Map encoder speed to AOM usage + CpuUsed: @@ -550,6 +547,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, // Speed 8: RealTime CpuUsed 8 // Speed 9: RealTime CpuUsed 9 // Speed 10: RealTime CpuUsed 9 + unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; // Use the new AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) for still // image encoding if it is available. #if defined(AOM_USAGE_ALL_INTRA) @@ -557,6 +555,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, aomUsage = AOM_USAGE_ALL_INTRA; } #endif + int aomCpuUsed = -1; if (encoder->speed != AVIF_SPEED_DEFAULT) { aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9); if (aomCpuUsed >= 7) { @@ -600,7 +599,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); - encoderInterface = aom_codec_av1_cx(); + aom_codec_iface_t * encoderInterface = aom_codec_av1_cx(); aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage); if (err != AOM_CODEC_OK) { avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err)); @@ -665,7 +664,8 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, cfg->g_profile = seqProfile; cfg->g_bit_depth = image->depth; cfg->g_input_bit_depth = image->depth; - + cfg->g_w = image->width; + cfg->g_h = image->height; if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { // Set the maximum number of frames to encode to 1. This instructs // libaom to set still_picture and reduced_still_picture_header to @@ -688,6 +688,15 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, cfg->g_threads = encoder->maxThreads; } + if (alpha) { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); + } else { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); + } + quantizerUpdated = AVIF_TRUE; + codec->internal->monochromeEnabled = AVIF_FALSE; if (aomVersion > aomVersion_2_0_0) { // There exists a bug in libaom's chroma_check() function where it will attempt to @@ -703,39 +712,15 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, cfg->monochrome = 1; } } - } - - avifBool dimensionsChanged = AVIF_FALSE; - if (!codec->internal->encoderInitialized) { - cfg->g_w = image->width; - cfg->g_h = image->height; - } else if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { - // We are not ready for dimension change for now. - return AVIF_RESULT_NOT_IMPLEMENTED; - } - if (!codec->internal->encoderInitialized || encoderChanges) { - int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); - int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); - if (alpha) { - minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); - maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); + if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; } - lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS)); - cfg->rc_min_quantizer = minQuantizer; - cfg->rc_max_quantizer = maxQuantizer; - } - if (!codec->internal->encoderInitialized) { aom_codec_flags_t encoderFlags = 0; if (image->depth > 8) { encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH; } - - if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { - return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; - } - if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) { avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_init() failed: %s: %s", @@ -743,63 +728,68 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, aom_codec_error_detail(&codec->internal->encoder)); return AVIF_RESULT_UNKNOWN_ERROR; } + codec->internal->encoderInitialized = AVIF_TRUE; + avifBool lossless = ((cfg->rc_min_quantizer == AVIF_QUANTIZER_LOSSLESS) && (cfg->rc_max_quantizer == AVIF_QUANTIZER_LOSSLESS)); + if (lossless) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1); + } if (encoder->maxThreads > 1) { aom_codec_control(&codec->internal->encoder, AV1E_SET_ROW_MT, 1); } + if (encoder->tileRowsLog2 != 0) { + int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); + } + if (encoder->tileColsLog2 != 0) { + int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); + } if (aomCpuUsed != -1) { if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) { return AVIF_RESULT_UNKNOWN_ERROR; } } - } else if ((encoderChanges & ~AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) || dimensionsChanged) { - // Codec specific options does not change cfg, so no need to update it. - aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg); - if (err != AOM_CODEC_OK) { - avifDiagnosticsPrintf(codec->diag, - "aom_codec_enc_config_set() failed: %s: %s", - aom_codec_error(&codec->internal->encoder), - aom_codec_error_detail(&codec->internal->encoder)); - return AVIF_RESULT_UNKNOWN_ERROR; - } - } - - if (!codec->internal->encoderInitialized || (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC)) { if (!avifProcessAOMOptionsPostInit(codec, alpha)) { return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; } - } - - avifBool quantizerUpdated = AVIF_FALSE; - if (!codec->internal->encoderInitialized) { - quantizerUpdated = AVIF_TRUE; - if (lossless) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); - } - int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); - if (tileRowsLog2 > 0) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); - } - int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); - if (tileColsLog2 > 0) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); - } if (!codec->internal->tuningSet) { if (aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, AOM_TUNE_SSIM) != AOM_CODEC_OK) { return AVIF_RESULT_UNKNOWN_ERROR; } } - codec->internal->encoderInitialized = AVIF_TRUE; - } else if (encoderChanges) { + } else { + avifBool dimensionsChanged = AVIF_FALSE; + if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { + // We are not ready for dimension change for now. + return AVIF_RESULT_NOT_IMPLEMENTED; + } if (alpha) { if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); quantizerUpdated = AVIF_TRUE; - aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); } } else { if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); quantizerUpdated = AVIF_TRUE; - aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); + } + } + if (quantizerUpdated) { + avifBool lossless = + ((cfg->rc_min_quantizer == AVIF_QUANTIZER_LOSSLESS) && (cfg->rc_max_quantizer == AVIF_QUANTIZER_LOSSLESS)); + aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); + } + if (quantizerUpdated || dimensionsChanged) { + aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg); + if (err != AOM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "aom_codec_enc_config_set() failed: %s: %s", + aom_codec_error(&codec->internal->encoder), + aom_codec_error_detail(&codec->internal->encoder)); + return AVIF_RESULT_UNKNOWN_ERROR; } } if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) { @@ -808,10 +798,15 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) { aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, AVIF_CLAMP(encoder->tileColsLog2, 0, 6)); } + if (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) { + if (!avifProcessAOMOptionsPostInit(codec, alpha)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + } } #if defined(AOM_USAGE_ALL_INTRA) - if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet && quantizerUpdated) { + if (quantizerUpdated && cfg->g_usage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet) { // The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to // function. A libavif user may not know this internal detail and therefore may only // set the min and max quantizers in the avifEncoder struct. If this is the case, set diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c index ee682688e5..8e6dbffb2a 100644 --- a/src/codec_rav1e.c +++ b/src/codec_rav1e.c @@ -53,16 +53,16 @@ static avifResult rav1eCodecEncodeImage(avifCodec * codec, avifEncoder * encoder, const avifImage * image, avifBool alpha, - avifEncoderChanges updatedConfig, + avifEncoderChanges encoderChanges, uint32_t addImageFlags, avifCodecEncodeOutput * output) { - // rav1e does not support changing config. - if (updatedConfig) { + // rav1e does not support changing encoder settings. + if (encoderChanges) { return AVIF_RESULT_NOT_IMPLEMENTED; } - // rav1e does not support changing encoding dimension. + // rav1e does not support changing image dimensions. if (!codec->internal->rav1eContext) { codec->internal->encodeWidth = image->width; codec->internal->encodeHeight = image->height; diff --git a/src/codec_svt.c b/src/codec_svt.c index f6a4c185da..3b49881dd2 100644 --- a/src/codec_svt.c +++ b/src/codec_svt.c @@ -46,16 +46,16 @@ static avifResult svtCodecEncodeImage(avifCodec * codec, avifEncoder * encoder, const avifImage * image, avifBool alpha, - avifEncoderChanges updatedConfig, + avifEncoderChanges encoderChanges, uint32_t addImageFlags, avifCodecEncodeOutput * output) { - // svt does not support changing config. - if (updatedConfig) { + // SVT-AV1 does not support changing encoder settings. + if (encoderChanges) { return AVIF_RESULT_NOT_IMPLEMENTED; } - // svt does not support changing encoding dimension. + // SVT-AV1 does not support changing image dimensions. if (codec->internal->svt_encoder != NULL) { if ((codec->internal->svt_config.source_width != image->width) || (codec->internal->svt_config.source_height != image->height)) { return AVIF_RESULT_NOT_IMPLEMENTED; diff --git a/src/write.c b/src/write.c index 5cfcff9795..45f002f785 100644 --- a/src/write.c +++ b/src/write.c @@ -294,15 +294,15 @@ avifEncoder * avifEncoderCreate(void) avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder)); memset(encoder, 0, sizeof(avifEncoder)); encoder->maxThreads = 1; + encoder->speed = AVIF_SPEED_DEFAULT; + encoder->keyframeInterval = 0; + encoder->timescale = 1; encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS; encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS; encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; encoder->tileRowsLog2 = 0; encoder->tileColsLog2 = 0; - encoder->speed = AVIF_SPEED_DEFAULT; - encoder->keyframeInterval = 0; - encoder->timescale = 1; encoder->data = avifEncoderDataCreate(); encoder->csOptions = avifCodecSpecificOptionsCreate(); return encoder; @@ -320,59 +320,61 @@ void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, avifCodecSpecificOptionsSet(encoder->csOptions, key, value); } -static void avifBackupSettings(avifEncoder * encoder) +static void avifEncoderBackupSettings(avifEncoder * encoder) { avifEncoder * lastEncoder = &encoder->data->lastEncoder; - // lastEncoder->data is used to mark that lastEncoder is initialized. + // lastEncoder->data is only used to mark that lastEncoder is initialized. lastEncoder->data + // must not be dereferenced. lastEncoder->data = encoder->data; lastEncoder->codecChoice = encoder->codecChoice; + lastEncoder->maxThreads = encoder->maxThreads; + lastEncoder->speed = encoder->speed; lastEncoder->keyframeInterval = encoder->keyframeInterval; lastEncoder->timescale = encoder->timescale; - lastEncoder->maxThreads = encoder->maxThreads; lastEncoder->minQuantizer = encoder->minQuantizer; lastEncoder->maxQuantizer = encoder->maxQuantizer; lastEncoder->minQuantizerAlpha = encoder->minQuantizerAlpha; lastEncoder->maxQuantizerAlpha = encoder->maxQuantizerAlpha; lastEncoder->tileRowsLog2 = encoder->tileRowsLog2; lastEncoder->tileColsLog2 = encoder->tileColsLog2; - lastEncoder->speed = encoder->speed; } -// This function detect changes made on avifEncoder. -// It reports if the change is valid, i.e. if any setting that can't change was changed. -// It also reports detected changes in updatedConfig. -static avifBool avifEncoderSettingsChanged(const avifEncoder * encoder, avifEncoderChanges * encoderChanges) +// This function detects changes made on avifEncoder. It returns true on success (i.e., if every +// change is valid), or false on failure (i.e., if any setting that can't change was changed). It +// reports detected changes in encoderChanges. +static avifBool avifEncoderDetectChanges(const avifEncoder * encoder, avifEncoderChanges * encoderChanges) { const avifEncoder * lastEncoder = &encoder->data->lastEncoder; + *encoderChanges = 0; if (!lastEncoder->data) { + // lastEncoder is not initialized. return AVIF_TRUE; } - if ((lastEncoder->codecChoice != encoder->codecChoice) || (lastEncoder->keyframeInterval != encoder->keyframeInterval) || - (lastEncoder->timescale != encoder->timescale) || (lastEncoder->maxThreads != encoder->maxThreads) || - (lastEncoder->speed != encoder->speed)) { + if ((lastEncoder->codecChoice != encoder->codecChoice) || (lastEncoder->maxThreads != encoder->maxThreads) || + (lastEncoder->speed != encoder->speed) || (lastEncoder->keyframeInterval != encoder->keyframeInterval) || + (lastEncoder->timescale != encoder->timescale)) { return AVIF_FALSE; } - *encoderChanges = 0; - if ((lastEncoder->minQuantizer != encoder->minQuantizer)) { + if (lastEncoder->minQuantizer != encoder->minQuantizer) { *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER; } - if ((lastEncoder->maxQuantizer != encoder->maxQuantizer)) { + if (lastEncoder->maxQuantizer != encoder->maxQuantizer) { *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER; } - if ((lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha)) { + if (lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha) { *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA; } - if ((lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha)) { + if (lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha) { *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA; } - if ((lastEncoder->tileRowsLog2 != encoder->tileRowsLog2)) { + if (lastEncoder->tileRowsLog2 != encoder->tileRowsLog2) { *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2; } - if ((lastEncoder->tileColsLog2 != encoder->tileColsLog2)) { + if (lastEncoder->tileColsLog2 != encoder->tileColsLog2) { *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_COLS_LOG2; } if (encoder->csOptions->count > 0) { @@ -669,11 +671,11 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder, return AVIF_RESULT_NO_CODEC_AVAILABLE; } - avifEncoderChanges encoderChanges = 0; - if (!avifEncoderSettingsChanged(encoder, &encoderChanges)) { + avifEncoderChanges encoderChanges; + if (!avifEncoderDetectChanges(encoder, &encoderChanges)) { return AVIF_RESULT_CANNOT_CHANGE_SETTING; } - avifBackupSettings(encoder); + avifEncoderBackupSettings(encoder); // ----------------------------------------------------------------------- // Validate images @@ -876,7 +878,8 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder, // Another frame in an image sequence const avifImage * imageMetadata = encoder->data->imageMetadata; - // If the first image had an alpha plane (even if fully opaque), all subsequent images must have alpha as well. + // If the first image in the sequence had an alpha plane (even if fully opaque), all + // subsequent images must have alpha as well. if ((imageMetadata->depth != firstCell->depth) || (imageMetadata->yuvFormat != firstCell->yuvFormat) || (imageMetadata->yuvRange != firstCell->yuvRange) || (imageMetadata->colorPrimaries != firstCell->colorPrimaries) || (imageMetadata->transferCharacteristics != firstCell->transferCharacteristics) || diff --git a/tests/gtest/avifchangesettingtest.cc b/tests/gtest/avifchangesettingtest.cc index 13d3b36317..871a06cbd5 100644 --- a/tests/gtest/avifchangesettingtest.cc +++ b/tests/gtest/avifchangesettingtest.cc @@ -88,7 +88,7 @@ void TestEncodeDecode(avifCodecChoice codec, ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK); ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_WAITING_ON_IO); - ((testutil::AvifIOLimitedReader*)io)->clamp = + reinterpret_cast(io)->clamp = testutil::AvifIOLimitedReader::kNoClamp; ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); ASSERT_EQ(avifDecoderNextImage(decoder.get()), diff --git a/tests/gtest/aviftest_helpers.cc b/tests/gtest/aviftest_helpers.cc index d981a5027a..e108bc6b6f 100644 --- a/tests/gtest/aviftest_helpers.cc +++ b/tests/gtest/aviftest_helpers.cc @@ -5,6 +5,7 @@ #include #include +#include #include "avif/avif.h" @@ -214,36 +215,39 @@ bool AreImagesEqual(const avifImage& image1, const avifImage& image2, return true; } -static avifResult avifIOLimitedReaderRead(struct avifIO* io, uint32_t readFlags, +static avifResult avifIOLimitedReaderRead(avifIO* io, uint32_t readFlags, uint64_t offset, size_t size, avifROData* out) { auto reader = reinterpret_cast(io); + if (offset > UINT64_MAX - size) { + return AVIF_RESULT_IO_ERROR; + } if (offset + size > reader->clamp) { return AVIF_RESULT_WAITING_ON_IO; } - return reader->underlayIO->read(reader->underlayIO, readFlags, offset, size, - out); + return reader->underlyingIO->read(reader->underlyingIO, readFlags, offset, + size, out); } -static void avifIOLimitedReaderDestroy(struct avifIO* io) { +static void avifIOLimitedReaderDestroy(avifIO* io) { auto reader = reinterpret_cast(io); - reader->underlayIO->destroy(reader->underlayIO); + reader->underlyingIO->destroy(reader->underlyingIO); delete reader; } -avifIO* AvifIOCreateLimitedReader(avifIO* underlayIO, uint64_t clamp) { +avifIO* AvifIOCreateLimitedReader(avifIO* underlyingIO, uint64_t clamp) { return reinterpret_cast( new AvifIOLimitedReader{{ avifIOLimitedReaderDestroy, avifIOLimitedReaderRead, nullptr, - underlayIO->sizeHint, - underlayIO->persistent, + underlyingIO->sizeHint, + underlyingIO->persistent, nullptr, }, - underlayIO, + underlyingIO, clamp}); } diff --git a/tests/gtest/aviftest_helpers.h b/tests/gtest/aviftest_helpers.h index ccca8a99c7..510e6b3050 100644 --- a/tests/gtest/aviftest_helpers.h +++ b/tests/gtest/aviftest_helpers.h @@ -66,13 +66,15 @@ struct AvifIOLimitedReader { static constexpr uint64_t kNoClamp = std::numeric_limits::max(); avifIO io; - avifIO* underlayIO; + avifIO* underlyingIO; uint64_t clamp; }; -avifIO* AvifIOCreateLimitedReader(avifIO* underlayIO, uint64_t clamp); +avifIO* AvifIOCreateLimitedReader(avifIO* underlyingIO, uint64_t clamp); } // namespace testutil } // namespace libavif +//------------------------------------------------------------------------------ + #endif // LIBAVIF_TESTS_AVIFTEST_HELPERS_H_