Add the ignoreICC option to avifDecoder This allows us to remove the ignoreColorProfile parameter from the avifImageCreateView() function. There is a change of behavior to avifdec: The message "[--ignore-icc] Discarding ICC profile.\n" won't be printed if the --ignore-icc or --icc option is specified and the image has an ICC profile. Bug: https://github.com/AOMediaCodec/libavif/issues/3277
diff --git a/CHANGELOG.md b/CHANGELOG.md index 510cc2d..f567966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ ### Added since 1.4.2 +* Add the ignoreICC option to avifDecoder * avifenc: add --ignore-alpha flag to discard alpha channel on encode * avifgainmaputil: add --ignore-alpha flag to discard alpha channel @@ -25,6 +26,9 @@ * Fix printed gain map size in avifenc when converting a jpeg with a gain map and using --grid. * Allow setting avifDecoder::imageSizeLimit to any value. +* avifdec: The message "[--ignore-icc] Discarding ICC profile.\n" won't be + printed if the --ignore-icc or --icc option is specified and the image has an + ICC profile. ## [1.4.2] - 2026-05-26
diff --git a/apps/avifdec.c b/apps/avifdec.c index 25880ea..4176152 100644 --- a/apps/avifdec.c +++ b/apps/avifdec.c
@@ -321,6 +321,7 @@ } else if (enableSampleTransforms) { decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS; } + decoder->ignoreICC = ignoreICC; avifResult result = avifDecoderSetIOFile(decoder, inputFilename); if (result != AVIF_RESULT_OK) { @@ -405,30 +406,25 @@ } } - if (ignoreICC) { + assert(!ignoreICC || decoder->image->icc.size == 0); + if (iccOverrideFilename) { imageView = avifImageCreateEmpty(); if (!imageView) { fprintf(stderr, "ERROR: Out of memory\n"); goto cleanup; } - if (decoder->image->icc.size > 0) { - printf("[--ignore-icc] Discarding ICC profile.\n"); - } result = avifImageCreateView(imageView, decoder->image, - /*ignoreColorProfile=*/AVIF_TRUE, /*ignoreAlpha=*/AVIF_FALSE); if (result != AVIF_RESULT_OK) { fprintf(stderr, "ERROR: Failed to create image view\n"); goto cleanup; } - if (iccOverrideFilename) { - printf("[--icc] Setting ICC profile: %s\n", iccOverrideFilename); - result = avifImageSetProfileICC(imageView, iccOverride.data, iccOverride.size); - if (result != AVIF_RESULT_OK) { - fprintf(stderr, "ERROR: Failed to set ICC: %s\n", avifResultToString(result)); - goto cleanup; - } + printf("[--icc] Setting ICC profile: %s\n", iccOverrideFilename); + result = avifImageSetProfileICC(imageView, iccOverride.data, iccOverride.size); + if (result != AVIF_RESULT_OK) { + fprintf(stderr, "ERROR: Failed to set ICC: %s\n", avifResultToString(result)); + goto cleanup; } } else { imageView = decoder->image;
diff --git a/apps/avifgainmaputil/extractgainmap_command.cc b/apps/avifgainmaputil/extractgainmap_command.cc index ccb0da7..31b766d 100644 --- a/apps/avifgainmaputil/extractgainmap_command.cc +++ b/apps/avifgainmaputil/extractgainmap_command.cc
@@ -25,6 +25,7 @@ } decoder->maxThreads = arg_jobs_.jobs.value(); decoder->imageContentToDecode = AVIF_IMAGE_CONTENT_GAIN_MAP; + decoder->ignoreICC = true; avifResult result = ReadAvif(decoder.get(), arg_input_filename_); if (result != AVIF_RESULT_OK) { @@ -36,7 +37,6 @@ return AVIF_RESULT_OUT_OF_MEMORY; } result = avifImageCreateView(image.get(), decoder->image, - /*ignoreColorProfile=*/true, /*ignoreAlpha=*/false); if (result != AVIF_RESULT_OK) { return result;
diff --git a/apps/avifgainmaputil/imageio.cc b/apps/avifgainmaputil/imageio.cc index 329831e..5e2fc53 100644 --- a/apps/avifgainmaputil/imageio.cc +++ b/apps/avifgainmaputil/imageio.cc
@@ -3,6 +3,7 @@ #include "imageio.h" +#include <cassert> #include <cstring> #include <fstream> #include <iostream> @@ -201,10 +202,11 @@ if (decoder == nullptr) { return AVIF_RESULT_OUT_OF_MEMORY; } + decoder->maxThreads = jobs; if (!ignore_gain_map) { decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP; } - decoder->maxThreads = jobs; + decoder->ignoreICC = ignore_profile; avifResult result = ReadAvif(decoder.get(), input_filename); if (result != AVIF_RESULT_OK) { return result; @@ -214,8 +216,7 @@ if (!view) { return AVIF_RESULT_OUT_OF_MEMORY; } - result = avifImageCreateView(view.get(), decoder->image, ignore_profile, - ignore_alpha); + result = avifImageCreateView(view.get(), decoder->image, ignore_alpha); if (result != AVIF_RESULT_OK) { return result; } @@ -284,6 +285,12 @@ << " (" << decoder->diag.error << ")\n"; return result; } + if (decoder->ignoreICC) { + assert(decoder->image->icc.size == 0); + if (decoder->image->gainMap) { + assert(decoder->image->gainMap->altICC.size == 0); + } + } return AVIF_RESULT_OK; }
diff --git a/apps/avifgainmaputil/swapbase_command.cc b/apps/avifgainmaputil/swapbase_command.cc index 3dd6c43..14432a9 100644 --- a/apps/avifgainmaputil/swapbase_command.cc +++ b/apps/avifgainmaputil/swapbase_command.cc
@@ -165,6 +165,7 @@ } decoder->maxThreads = arg_jobs_.jobs.value(); decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP; + decoder->ignoreICC = arg_image_read_.ignore_profile; avifResult result = ReadAvif(decoder.get(), arg_input_filename_); if (result != AVIF_RESULT_OK) { return result; @@ -175,7 +176,6 @@ return AVIF_RESULT_OUT_OF_MEMORY; } result = avifImageCreateView(image.get(), decoder->image, - arg_image_read_.ignore_profile, arg_image_read_.ignore_alpha); if (result != AVIF_RESULT_OK) { return result;
diff --git a/apps/avifgainmaputil/tonemap_command.cc b/apps/avifgainmaputil/tonemap_command.cc index 8b36c58..fa40592 100644 --- a/apps/avifgainmaputil/tonemap_command.cc +++ b/apps/avifgainmaputil/tonemap_command.cc
@@ -67,6 +67,7 @@ } decoder->maxThreads = arg_jobs_.jobs.value(); decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP; + decoder->ignoreICC = arg_image_read_.ignore_profile; avifResult result = ReadAvif(decoder.get(), arg_input_filename_); if (result != AVIF_RESULT_OK) { return result; @@ -77,7 +78,6 @@ return AVIF_RESULT_OUT_OF_MEMORY; } result = avifImageCreateView(image.get(), decoder->image, - arg_image_read_.ignore_profile, arg_image_read_.ignore_alpha); if (result != AVIF_RESULT_OK) { return result;
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c index 61a6f12..20e47b7 100644 --- a/apps/shared/avifutil.c +++ b/apps/shared/avifutil.c
@@ -824,7 +824,7 @@ return AVIF_RESULT_OK; } -avifResult avifImageCreateView(avifImage * dstImage, const avifImage * srcImage, avifBool ignoreColorProfile, avifBool ignoreAlpha) +avifResult avifImageCreateView(avifImage * dstImage, const avifImage * srcImage, avifBool ignoreAlpha) { avifResult res = AVIF_RESULT_OK; if (!dstImage || !srcImage) { @@ -841,7 +841,7 @@ avifImageFreePlanes(dstImage, AVIF_PLANES_A); } - if (!ignoreColorProfile) { + if (srcImage->icc.size > 0) { res = avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size); if (res != AVIF_RESULT_OK) { return res; @@ -889,7 +889,7 @@ dstImage->gainMap->altICC.data = NULL; dstImage->gainMap->altICC.size = 0; - if (!ignoreColorProfile && srcImage->gainMap->altICC.size > 0) { + if (srcImage->gainMap->altICC.size > 0) { res = avifRWDataSet(&dstImage->gainMap->altICC, srcImage->gainMap->altICC.data, srcImage->gainMap->altICC.size); if (res != AVIF_RESULT_OK) { return res; @@ -903,7 +903,6 @@ } res = avifImageCreateView(dstImage->gainMap->image, srcImage->gainMap->image, - ignoreColorProfile, /*ignoreAlpha=*/AVIF_TRUE); if (res != AVIF_RESULT_OK) { return res;
diff --git a/apps/shared/avifutil.h b/apps/shared/avifutil.h index e99f9d5..cfa1da5 100644 --- a/apps/shared/avifutil.h +++ b/apps/shared/avifutil.h
@@ -132,7 +132,7 @@ // If a gain map is present in 'srcImage', the gain map of 'dstImage' is also set to // a view of the original gain map. // 'dstImage' should be an empty image. It will not own the pixel data. -avifResult avifImageCreateView(avifImage * dstImage, const avifImage * srcImage, avifBool ignoreColorProfile, avifBool ignoreAlpha); +avifResult avifImageCreateView(avifImage * dstImage, const avifImage * srcImage, avifBool ignoreAlpha); #ifdef __cplusplus } // extern "C"
diff --git a/include/avif/avif.h b/include/avif/avif.h index 27baf2f..faab302 100644 --- a/include/avif/avif.h +++ b/include/avif/avif.h
@@ -1384,6 +1384,10 @@ // Version 1.2.0 ends here. Add any new members after this line. // -------------------------------------------------------------------------------------------- + + // Enable this to avoid reading and surfacing ICC profile to the decoded avifImage and gain map + // metadata. + avifBool ignoreICC; } avifDecoder; // Creates a decoder initialized with default settings values.
diff --git a/src/read.c b/src/read.c index 0d823e2..fb8226f 100644 --- a/src/read.c +++ b/src/read.c
@@ -5648,7 +5648,7 @@ // On success, this function returns AVIF_RESULT_OK and does the following: // * If a colr property was found in |properties|: -// - Read the icc data into |icc| from |io|. +// - If |icc| is not null, reads the icc data into |icc| from |io|. // - Sets the CICP values as documented in avifReadColorNclxProperty(). // This function fails if more than one icc or nclx property is found in // |properties|. The output parameters may be populated even in case of failure @@ -5671,10 +5671,12 @@ if (colrICCSeen) { return AVIF_RESULT_BMFF_PARSE_FAILED; } - avifROData iccRead; - AVIF_CHECKRES(io->read(io, 0, prop->u.colr.iccOffset, prop->u.colr.iccSize, &iccRead)); + if (icc) { + avifROData iccRead; + AVIF_CHECKRES(io->read(io, 0, prop->u.colr.iccOffset, prop->u.colr.iccSize, &iccRead)); + AVIF_CHECKRES(avifRWDataSet(icc, iccRead.data, iccRead.size)); + } colrICCSeen = AVIF_TRUE; - AVIF_CHECKRES(avifRWDataSet(icc, iccRead.data, iccRead.size)); } } return avifReadColorNclxProperty(properties, colorPrimaries, transferCharacteristics, matrixCoefficients, yuvRange, cicpSet); @@ -5818,7 +5820,7 @@ // This may allocate gainMapTmp.altICC which must be freed in case of error. result = avifReadColorProperties(decoder->io, &toneMappedImageItemTmp->properties, - &gainMapTmp.altICC, + decoder->ignoreICC ? NULL : &gainMapTmp.altICC, &gainMapTmp.altColorPrimaries, &gainMapTmp.altTransferCharacteristics, &gainMapTmp.altMatrixCoefficients, @@ -6548,7 +6550,7 @@ AVIF_CHECKRES(avifReadColorProperties(decoder->io, colorProperties, - &decoder->image->icc, + decoder->ignoreICC ? NULL : &decoder->image->icc, &decoder->image->colorPrimaries, &decoder->image->transferCharacteristics, &decoder->image->matrixCoefficients,
diff --git a/tests/gtest/avifmetadatatest.cc b/tests/gtest/avifmetadatatest.cc index e54ed7a..28dfb26 100644 --- a/tests/gtest/avifmetadatatest.cc +++ b/tests/gtest/avifmetadatatest.cc
@@ -215,6 +215,14 @@ EXPECT_EQ(decoder->image->icc.data[2], 2); EXPECT_EQ(decoder->image->icc.data[3], 84); avifDecoderDestroy(decoder); + + decoder = avifDecoderCreate(); + ASSERT_NE(decoder, nullptr); + decoder->ignoreICC = true; + EXPECT_EQ(avifDecoderSetIOFile(decoder, file_path.c_str()), AVIF_RESULT_OK); + EXPECT_EQ(avifDecoderParse(decoder), AVIF_RESULT_OK); + ASSERT_EQ(decoder->image->icc.size, 0u); + avifDecoderDestroy(decoder); } //------------------------------------------------------------------------------