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);
}
//------------------------------------------------------------------------------