Add support for png iCCP chunk (#2952)

Handled using the API if available (libpng version >= 1.6.45), or with an unknown chunk callback otherwise.
Simplify the image reading code by removing the allowChangingCicp parameter.
This is handled at the call site in avifenc.c instead.
diff --git a/apps/avifenc.c b/apps/avifenc.c
index 35cc471..c68eea2 100644
--- a/apps/avifenc.c
+++ b/apps/avifenc.c
@@ -607,6 +607,8 @@
         }
     }
 
+    const avifColorPrimaries colorPrimariesBefore = dstImage->colorPrimaries;
+    const avifTransferCharacteristics transferCharacteristicsBefore = dstImage->transferCharacteristics;
     if (avifReadImage(currentFile->filename,
                       inputFormat,
                       input->requestedFormat,
@@ -615,7 +617,6 @@
                       ignoreColorProfile,
                       ignoreExif,
                       ignoreXMP,
-                      allowChangingCicp,
                       ignoreGainMap,
                       UINT32_MAX,
                       dstImage,
@@ -631,6 +632,11 @@
         }
         return AVIF_FALSE;
     }
+    if (!allowChangingCicp) {
+        // Restore the previous primaries/transfer in case avifReadImage changed them.
+        dstImage->colorPrimaries = colorPrimariesBefore;
+        dstImage->transferCharacteristics = transferCharacteristicsBefore;
+    }
 
     if (!input->frameIter) {
         ++input->fileIndex;
diff --git a/apps/avifgainmaputil/convert_command.cc b/apps/avifgainmaputil/convert_command.cc
index 706dbb4..b67f512 100644
--- a/apps/avifgainmaputil/convert_command.cc
+++ b/apps/avifgainmaputil/convert_command.cc
@@ -59,7 +59,6 @@
       arg_image_read_.ignore_profile,
       /*ignoreExif=*/false,
       /*ignoreXMP=*/false,
-      /*allowChangingCicp=*/true,
       /*ignoreGainMap=*/false, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
       /*outDepth=*/nullptr,
       /*sourceTiming=*/nullptr,
diff --git a/apps/avifgainmaputil/imageio.cc b/apps/avifgainmaputil/imageio.cc
index 6a7a8cc..4da4742 100644
--- a/apps/avifgainmaputil/imageio.cc
+++ b/apps/avifgainmaputil/imageio.cc
@@ -121,8 +121,8 @@
         requested_format, static_cast<int>(requested_depth),
         AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, ignore_profile,
         /*ignoreExif=*/false, /*ignoreXMP=*/false,
-        /*allowChangingCicp=*/true, /*ignoreGainMap=*/true,
-        AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image, /*outDepth=*/nullptr,
+        /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image,
+        /*outDepth=*/nullptr,
         /*sourceTiming=*/nullptr, /*frameIter=*/nullptr);
     if (file_format == AVIF_APP_FILE_FORMAT_UNKNOWN) {
       std::cout << "Failed to decode image: " << input_filename;
diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c
index 08e18c5..a0e8a12 100644
--- a/apps/shared/avifpng.c
+++ b/apps/shared/avifpng.c
@@ -210,6 +210,28 @@
     return AVIF_TRUE;
 }
 
+typedef struct avifUnknownCicpChunkData
+{
+    avifImage * avif;
+    avifBool cicpChunkRead;
+} avifUnknownCicpChunkData;
+
+// Callback for unknown chunks. Used to detect/handle chunks that we are missing support for.
+static int avifPNGReadUnknownChunk(png_structp png_ptr, png_unknown_chunkp chunk)
+{
+    avifUnknownCicpChunkData * data = (avifUnknownCicpChunkData *)png_get_user_chunk_ptr(png_ptr);
+    avifImage * avif = data->avif;
+    if (!memcmp(chunk->name, "cICP\0", 5)) {
+        if (chunk->size > 2) {
+            data->cicpChunkRead = AVIF_TRUE;
+            avif->colorPrimaries = chunk->data[0];
+            avif->transferCharacteristics = chunk->data[1];
+            // The matrix coefficient and full range flag are ignored.
+        }
+    }
+    return 1;
+}
+
 // Note on setjmp() and volatile variables:
 //
 // K & R, The C Programming Language 2nd Ed, p. 254 says:
@@ -233,7 +255,6 @@
                                 avifBool ignoreColorProfile,
                                 avifBool ignoreExif,
                                 avifBool ignoreXMP,
-                                avifBool allowChangingCicp,
                                 uint32_t imageSizeLimit,
                                 uint32_t * outPNGDepth)
 {
@@ -272,6 +293,9 @@
         goto cleanup;
     }
 
+    avifUnknownCicpChunkData unknownChunkData = { avif, AVIF_FALSE };
+    png_set_read_user_chunk_fn(png, &unknownChunkData, &avifPNGReadUnknownChunk);
+
     png_init_io(png, f);
     png_set_sig_bytes(png, 8);
     png_read_info(png, info);
@@ -351,13 +375,28 @@
         unsigned char * iccpData = NULL;
         png_uint_32 iccpDataLen = 0;
         int srgbIntent;
-
-        // PNG specification 1.2 Section 4.2.2:
-        // The sRGB and iCCP chunks should not both appear.
-        //
-        // When the sRGB / iCCP chunk is present, applications that recognize it and are capable of color management
-        // must ignore the gAMA and cHRM chunks and use the sRGB / iCCP chunk instead.
-        if (png_get_iCCP(png, info, &iccpProfileName, &iccpCompression, &iccpData, &iccpDataLen) == PNG_INFO_iCCP) {
+        // PNG specification Third Edition Section 4.3 lists color space information chunk types by priority:
+        //   Chunk Type	    Priority
+        //   cICP           1
+        //   iCCP           2
+        //   sRGB           3
+        //   cHRM and gAMA  4
+        //   If a single image contains more than one of these chunk types, the chunk with the lowest
+        //   Priority number should take precedence and any higher-numbered chunk types should be ignored.
+#if defined(PNG_cICP_SUPPORTED)
+        png_byte cicpColorPrimaries;
+        png_byte cicpTransferFunction;
+        png_byte cicpMatrixCoefficients;
+        png_byte cicpVideoFullRangeFlag;
+        if (png_get_cICP(png, info, &cicpColorPrimaries, &cicpTransferFunction, &cicpMatrixCoefficients, &cicpVideoFullRangeFlag)) {
+            avif->colorPrimaries = cicpColorPrimaries;
+            avif->transferCharacteristics = cicpTransferFunction;
+            // The matrix coefficient and full range flag are ignored: they are about YUV encoding and the PNG values are irrelevant.
+        } else
+#endif
+            if (unknownChunkData.cicpChunkRead) {
+            // Already handled in avifPNGReadUnknownChunk()
+        } else if (png_get_iCCP(png, info, &iccpProfileName, &iccpCompression, &iccpData, &iccpDataLen) == PNG_INFO_iCCP) {
             if (!rawColorTypeIsGray && avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
                 fprintf(stderr,
                         "The image contains a color ICC profile which is incompatible with the requested output "
@@ -374,78 +413,75 @@
                 fprintf(stderr, "Setting ICC profile failed: out of memory.\n");
                 goto cleanup;
             }
-        } else if (allowChangingCicp) {
-            if (png_get_sRGB(png, info, &srgbIntent) == PNG_INFO_sRGB) {
-                // srgbIntent ignored
-                avif->colorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
-                avif->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+        } else if (png_get_sRGB(png, info, &srgbIntent) == PNG_INFO_sRGB) {
+            // srgbIntent ignored
+            avif->colorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
+            avif->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+        } else {
+            avifBool needToGenerateICC = AVIF_FALSE;
+            double gamma;
+            double wX, wY, rX, rY, gX, gY, bX, bY;
+            float primaries[8];
+            if (png_get_gAMA(png, info, &gamma) == PNG_INFO_gAMA) {
+                gamma = 1.0 / gamma;
+                avif->transferCharacteristics = avifTransferCharacteristicsFindByGamma((float)gamma);
+                if (avif->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN) {
+                    needToGenerateICC = AVIF_TRUE;
+                }
             } else {
-                avifBool needToGenerateICC = AVIF_FALSE;
-                double gamma;
-                double wX, wY, rX, rY, gX, gY, bX, bY;
-                float primaries[8];
-                if (png_get_gAMA(png, info, &gamma) == PNG_INFO_gAMA) {
-                    gamma = 1.0 / gamma;
-                    avif->transferCharacteristics = avifTransferCharacteristicsFindByGamma((float)gamma);
-                    if (avif->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN) {
-                        needToGenerateICC = AVIF_TRUE;
-                    }
+                // No gamma information in file. Assume the default value.
+                // PNG specification 1.2 Section 10.5:
+                // Assume a CRT exponent of 2.2 unless detailed calibration measurements
+                // of this particular CRT are available.
+                gamma = 2.2;
+            }
+
+            if (png_get_cHRM(png, info, &wX, &wY, &rX, &rY, &gX, &gY, &bX, &bY) == PNG_INFO_cHRM) {
+                primaries[0] = (float)rX;
+                primaries[1] = (float)rY;
+                primaries[2] = (float)gX;
+                primaries[3] = (float)gY;
+                primaries[4] = (float)bX;
+                primaries[5] = (float)bY;
+                primaries[6] = (float)wX;
+                primaries[7] = (float)wY;
+                avif->colorPrimaries = avifColorPrimariesFind(primaries, NULL);
+                if (avif->colorPrimaries == AVIF_COLOR_PRIMARIES_UNKNOWN) {
+                    needToGenerateICC = AVIF_TRUE;
+                }
+            } else {
+                // No chromaticity information in file. Assume the default value.
+                // PNG specification 1.2 Section 10.6:
+                // Decoders may wish to do this for PNG files with no cHRM chunk.
+                // In that case, a reasonable default would be the CCIR 709 primaries [ITU-R-BT709].
+                avifColorPrimariesGetValues(AVIF_COLOR_PRIMARIES_BT709, primaries);
+            }
+
+            if (needToGenerateICC) {
+                avif->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
+                avif->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
+                fprintf(stderr,
+                        "INFO: legacy PNG color space information found in file %s not matching any CICP value. libavif is generating an ICC profile for it."
+                        " Use --ignore-profile to ignore color space information instead (may affect the colors of the encoded AVIF image).\n",
+                        inputFilename);
+
+                avifBool generateICCResult = AVIF_FALSE;
+                if (avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
+                    generateICCResult = avifGenerateGrayICC(&avif->icc, (float)gamma, &primaries[6]);
                 } else {
-                    // No gamma information in file. Assume the default value.
-                    // PNG specification 1.2 Section 10.5:
-                    // Assume a CRT exponent of 2.2 unless detailed calibration measurements
-                    // of this particular CRT are available.
-                    gamma = 2.2;
+                    generateICCResult = avifGenerateRGBICC(&avif->icc, (float)gamma, primaries);
                 }
 
-                if (png_get_cHRM(png, info, &wX, &wY, &rX, &rY, &gX, &gY, &bX, &bY) == PNG_INFO_cHRM) {
-                    primaries[0] = (float)rX;
-                    primaries[1] = (float)rY;
-                    primaries[2] = (float)gX;
-                    primaries[3] = (float)gY;
-                    primaries[4] = (float)bX;
-                    primaries[5] = (float)bY;
-                    primaries[6] = (float)wX;
-                    primaries[7] = (float)wY;
-                    avif->colorPrimaries = avifColorPrimariesFind(primaries, NULL);
-                    if (avif->colorPrimaries == AVIF_COLOR_PRIMARIES_UNKNOWN) {
-                        needToGenerateICC = AVIF_TRUE;
-                    }
-                } else {
-                    // No chromaticity information in file. Assume the default value.
-                    // PNG specification 1.2 Section 10.6:
-                    // Decoders may wish to do this for PNG files with no cHRM chunk.
-                    // In that case, a reasonable default would be the CCIR 709 primaries [ITU-R-BT709].
-                    avifColorPrimariesGetValues(AVIF_COLOR_PRIMARIES_BT709, primaries);
-                }
-
-                if (needToGenerateICC) {
-                    avif->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
-                    avif->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
+                if (!generateICCResult) {
                     fprintf(stderr,
-                            "INFO: legacy PNG color space information found in file %s not matching any CICP value. libavif is generating an ICC profile for it."
-                            " Use --ignore-profile to ignore color space information instead (may affect the colors of the encoded AVIF image).\n",
+                            "WARNING: libavif could not generate an ICC profile for file %s. "
+                            "It may be caused by invalid values in the color space information. "
+                            "The encoded AVIF image's colors may be affected.\n",
                             inputFilename);
-
-                    avifBool generateICCResult = AVIF_FALSE;
-                    if (avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
-                        generateICCResult = avifGenerateGrayICC(&avif->icc, (float)gamma, &primaries[6]);
-                    } else {
-                        generateICCResult = avifGenerateRGBICC(&avif->icc, (float)gamma, primaries);
-                    }
-
-                    if (!generateICCResult) {
-                        fprintf(stderr,
-                                "WARNING: libavif could not generate an ICC profile for file %s. "
-                                "It may be caused by invalid values in the color space information. "
-                                "The encoded AVIF image's colors may be affected.\n",
-                                inputFilename);
-                    }
                 }
             }
         }
         // Note: There is no support for the rare "Raw profile type icc" or "Raw profile type icm" text chunks.
-        // TODO(yguyon): Also check if there is a cICp chunk (https://github.com/AOMediaCodec/libavif/pull/1065#discussion_r958534232)
     }
 
     const int numChannels = png_get_channels(png, info);
@@ -528,7 +564,6 @@
                      avifBool ignoreColorProfile,
                      avifBool ignoreExif,
                      avifBool ignoreXMP,
-                     avifBool allowChangingCicp,
                      uint32_t imageSizeLimit,
                      uint32_t * outPNGDepth)
 {
@@ -553,7 +588,6 @@
                                          ignoreColorProfile,
                                          ignoreExif,
                                          ignoreXMP,
-                                         allowChangingCicp,
                                          imageSizeLimit,
                                          outPNGDepth);
 
diff --git a/apps/shared/avifpng.h b/apps/shared/avifpng.h
index 16d5a2f..5292a27 100644
--- a/apps/shared/avifpng.h
+++ b/apps/shared/avifpng.h
@@ -19,7 +19,6 @@
                      avifBool ignoreColorProfile,
                      avifBool ignoreExif,
                      avifBool ignoreXMP,
-                     avifBool allowChangingCicp,
                      uint32_t imageSizeLimit,
                      uint32_t * outPNGDepth);
 avifBool avifPNGWrite(const char * outputFilename,
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c
index ad5ad2c..111435f 100644
--- a/apps/shared/avifutil.c
+++ b/apps/shared/avifutil.c
@@ -315,7 +315,6 @@
                                 avifBool ignoreColorProfile,
                                 avifBool ignoreExif,
                                 avifBool ignoreXMP,
-                                avifBool allowChangingCicp,
                                 avifBool ignoreGainMap,
                                 uint32_t imageSizeLimit,
                                 avifImage * image,
@@ -343,17 +342,7 @@
             *outDepth = 8;
         }
     } else if (inputFormat == AVIF_APP_FILE_FORMAT_PNG) {
-        if (!avifPNGRead(filename,
-                         image,
-                         requestedFormat,
-                         requestedDepth,
-                         chromaDownsampling,
-                         ignoreColorProfile,
-                         ignoreExif,
-                         ignoreXMP,
-                         allowChangingCicp,
-                         imageSizeLimit,
-                         outDepth)) {
+        if (!avifPNGRead(filename, image, requestedFormat, requestedDepth, chromaDownsampling, ignoreColorProfile, ignoreExif, ignoreXMP, imageSizeLimit, outDepth)) {
             return AVIF_APP_FILE_FORMAT_UNKNOWN;
         }
     } else if (inputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) {
diff --git a/apps/shared/avifutil.h b/apps/shared/avifutil.h
index b61c283..1c664e6 100644
--- a/apps/shared/avifutil.h
+++ b/apps/shared/avifutil.h
@@ -73,6 +73,8 @@
 // 'ignoreGainMap' is only relevant for jpeg files that have a gain map
 // and only if AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION is ON
 // (requires libxml2). Otherwise it has no effect.
+// May set the image's colorPrimaries, transferCharacteristics and ICC fields
+// based on color information found in the image (unless ignoreColorProfile is true).
 avifAppFileFormat avifReadImage(const char * filename,
                                 avifAppFileFormat inputFormat,
                                 avifPixelFormat requestedFormat,
@@ -81,7 +83,6 @@
                                 avifBool ignoreColorProfile,
                                 avifBool ignoreExif,
                                 avifBool ignoreXMP,
-                                avifBool allowChangingCicp,
                                 avifBool ignoreGainMap,
                                 uint32_t imageSizeLimit,
                                 avifImage * image,
diff --git a/doc/avifenc.1.md b/doc/avifenc.1.md
index 3899cd9..4d733e0 100644
--- a/doc/avifenc.1.md
+++ b/doc/avifenc.1.md
@@ -120,7 +120,7 @@
 :   Provide an XMP metadata payload to be associated with the primary item (implies \--ignore-xmp).
 
 **\--icc** _FILENAME_
-:   Provide an ICC profile payload to be associated with the primary item (implies \--ignore-icc).
+:   Provide an ICC profile payload to be associated with the primary item (implies \--ignore-profile).
 
 **\--timescale**, **\--fps** _V_
 :   Timescale for image sequences. If all frames are 1 timescale in length, this is equivalent to frames per second. (Default: 30)
diff --git a/tests/data/README.md b/tests/data/README.md
index a17f24e..48c62ce 100644
--- a/tests/data/README.md
+++ b/tests/data/README.md
@@ -133,6 +133,15 @@
 It is [kodim03_yuv420_8bpc.avif](io/kodim03_yuv420_8bpc.avif) converted to
 grayscale and tagged as Gamma 1.6 gAMA PNG chunk.
 
+#### File [kodim03_grayscale_cicp.png](kodim03_grayscale_cicp.png)
+
+![](kodim03_grayscale_cicp.png)
+
+License: released by the Eastman Kodak Company for unrestricted usage
+
+It is [kodim03_yuv420_8bpc.avif](io/kodim03_yuv420_8bpc.avif) converted to
+grayscale and tagged with a cICP chunk.
+
 #### File [kodim03_grayscale_gamma1.6-reference.png](kodim03_grayscale_gamma1.6-reference.png)
 
 ![](kodim03_grayscale_gamma1.6-reference.png)
diff --git a/tests/data/kodim03_grayscale_cicp.png b/tests/data/kodim03_grayscale_cicp.png
new file mode 100644
index 0000000..433c286
--- /dev/null
+++ b/tests/data/kodim03_grayscale_cicp.png
Binary files differ
diff --git a/tests/data/kodim03_grayscale_gamma1.6.png b/tests/data/kodim03_grayscale_gamma1.6.png
index 433c286..0b9ea27 100644
--- a/tests/data/kodim03_grayscale_gamma1.6.png
+++ b/tests/data/kodim03_grayscale_gamma1.6.png
Binary files differ
diff --git a/tests/gtest/are_images_equal.cc b/tests/gtest/are_images_equal.cc
index 64b7c17..11231a9 100644
--- a/tests/gtest/are_images_equal.cc
+++ b/tests/gtest/are_images_equal.cc
@@ -37,7 +37,7 @@
                       AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
                       /*ignoreColorProfile==*/AVIF_FALSE,
                       /*ignoreExif=*/AVIF_FALSE,
-                      /*ignoreXMP=*/AVIF_FALSE, /*allowChangingCicp=*/AVIF_TRUE,
+                      /*ignoreXMP=*/AVIF_FALSE,
                       // TODO(maryla): also compare gain maps.
                       /*ignoreGainMap=*/AVIF_TRUE,
                       /*imageSizeLimit=*/std::numeric_limits<uint32_t>::max(),
diff --git a/tests/gtest/avif_fuzztest_read_image.cc b/tests/gtest/avif_fuzztest_read_image.cc
index 0a20be8..f04cd18 100644
--- a/tests/gtest/avif_fuzztest_read_image.cc
+++ b/tests/gtest/avif_fuzztest_read_image.cc
@@ -46,7 +46,7 @@
                    avifPixelFormat requested_format, int requested_depth,
                    avifChromaDownsampling chroma_downsampling,
                    bool ignore_color_profile, bool ignore_exif, bool ignore_xmp,
-                   bool allow_changing_cicp, bool ignore_gain_map,
+                   bool ignore_gain_map,
                    avifMatrixCoefficients matrix_coefficients) {
   ASSERT_FALSE(GetSeedDataDirs().empty());  // Make sure seeds are available.
 
@@ -80,8 +80,8 @@
   const avifAppFileFormat file_format = avifReadImage(
       file_path.c_str(), AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
       requested_format, requested_depth, chroma_downsampling,
-      ignore_color_profile, ignore_exif, ignore_xmp, allow_changing_cicp,
-      ignore_gain_map, imageSizeLimit, avif_image.get(), &out_depth, &timing,
+      ignore_color_profile, ignore_exif, ignore_xmp, ignore_gain_map,
+      imageSizeLimit, avif_image.get(), &out_depth, &timing,
       /*frameIter=*/nullptr);
 
   if (file_format != AVIF_APP_FILE_FORMAT_UNKNOWN) {
@@ -122,7 +122,6 @@
                  /*ignore_color_profile=*/Arbitrary<bool>(),
                  /*ignore_exif=*/Arbitrary<bool>(),
                  /*ignore_xmp=*/Arbitrary<bool>(),
-                 /*allow_changing_cicp=*/Arbitrary<bool>(),
                  /*ignore_gain_map=*/Arbitrary<bool>(),
                  ElementOf({AVIF_MATRIX_COEFFICIENTS_IDENTITY,
                             AVIF_MATRIX_COEFFICIENTS_BT709,
diff --git a/tests/gtest/avifjpeggainmaptest.cc b/tests/gtest/avifjpeggainmaptest.cc
index a86e0f5..456dd5f 100644
--- a/tests/gtest/avifjpeggainmaptest.cc
+++ b/tests/gtest/avifjpeggainmaptest.cc
@@ -77,7 +77,7 @@
         testutil::ReadImage(data_path, filename, AVIF_PIXEL_FORMAT_YUV444, 8,
                             AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
                             /*ignore_icc=*/false, /*ignore_exif=*/false,
-                            /*ignore_xmp=*/true, /*allow_changing_cicp=*/true,
+                            /*ignore_xmp=*/true,
                             /*ignore_gain_map=*/false);
     ASSERT_NE(image, nullptr);
     ASSERT_NE(image->gainMap, nullptr);
@@ -104,7 +104,7 @@
       data_path, "paris_exif_xmp_gainmap_littleendian.jpg",
       AVIF_PIXEL_FORMAT_YUV444, 8, AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
       /*ignore_icc=*/false, /*ignore_exif=*/false,
-      /*ignore_xmp=*/false, /*allow_changing_cicp=*/true,
+      /*ignore_xmp=*/false,
       /*ignore_gain_map=*/true);
   ASSERT_NE(image, nullptr);
   ASSERT_EQ(image->gainMap, nullptr);
diff --git a/tests/gtest/aviflosslesstest.cc b/tests/gtest/aviflosslesstest.cc
index 9832ed0..c74788d 100644
--- a/tests/gtest/aviflosslesstest.cc
+++ b/tests/gtest/aviflosslesstest.cc
@@ -28,8 +28,8 @@
       /*requestedDepth=*/0,
       /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
       /*ignoreColorProfile=*/false, /*ignoreExif=*/false, /*ignoreXMP=*/false,
-      /*allowChangingCicp=*/true, /*ignoreGainMap=*/true,
-      AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(), /*outDepth=*/nullptr,
+      /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
+      /*outDepth=*/nullptr,
       /*sourceTiming=*/nullptr, /*frameIter=*/nullptr);
   ASSERT_EQ(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN);
 }
@@ -51,7 +51,7 @@
       /*requestedFormat=*/pixel_format, /*requestedDepth=*/0,
       /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
       /*ignoreColorProfile=*/ignore_icc, /*ignoreExif=*/false,
-      /*ignoreXMP=*/false, /*allowChangingCicp=*/true, /*ignoreGainMap=*/true,
+      /*ignoreXMP=*/false, /*ignoreGainMap=*/true,
       AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(), /*outDepth=*/nullptr,
       /*sourceTiming=*/nullptr, /*frameIter=*/nullptr);
   if (matrix_coefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY &&
@@ -72,17 +72,17 @@
   ImagePtr image(avifImageCreateEmpty());
   ASSERT_NE(image, nullptr);
   image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED;
-  ASSERT_NE(AVIF_APP_FILE_FORMAT_UNKNOWN,
-            avifReadImage(
-                path.c_str(), AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
-                AVIF_PIXEL_FORMAT_NONE, /*requestedDepth=*/0,
-                /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
-                /*ignoreColorProfile=*/true, /*ignoreExif=*/true,
-                /*ignoreXMP=*/true,
-                /*allowChangingCicp=*/true, /*ignoreGainMap=*/true,
-                AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
-                /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
-                /*frameIter=*/nullptr));
+  ASSERT_NE(
+      AVIF_APP_FILE_FORMAT_UNKNOWN,
+      avifReadImage(
+          path.c_str(), AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
+          AVIF_PIXEL_FORMAT_NONE, /*requestedDepth=*/0,
+          /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
+          /*ignoreColorProfile=*/true, /*ignoreExif=*/true,
+          /*ignoreXMP=*/true,
+          /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
+          /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
+          /*frameIter=*/nullptr));
   is_gray = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400;
 }
 
diff --git a/tests/gtest/avifpng16bittest.cc b/tests/gtest/avifpng16bittest.cc
index ebb8cf3..5472033 100644
--- a/tests/gtest/avifpng16bittest.cc
+++ b/tests/gtest/avifpng16bittest.cc
@@ -38,8 +38,8 @@
                     AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
                     /*ignoreColorProfile=*/true,
                     /*ignoreExif=*/true, /*ignoreXMP=*/true,
-                    /*allowChangingCicp=*/true, /*ignoreGainMap=*/true,
-                    AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(), &output_depth,
+                    /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT,
+                    image.get(), &output_depth,
                     /*sourceTiming=*/nullptr,
                     /*frameIter=*/nullptr) == AVIF_APP_FILE_FORMAT_UNKNOWN) {
     return nullptr;
diff --git a/tests/gtest/avifreadimagetest.cc b/tests/gtest/avifreadimagetest.cc
index 15708c8..c1423fe 100644
--- a/tests/gtest/avifreadimagetest.cc
+++ b/tests/gtest/avifreadimagetest.cc
@@ -114,26 +114,6 @@
   EXPECT_EQ(image->icc.size, 0u);
 }
 
-// Verify that color info does not get overwritten if allow_changing_cicp is
-// false.
-TEST(PngTest, ColorGamma22ForbitChangingCicp) {
-  const ImagePtr image = testutil::ReadImage(
-      data_path, "ffffcc-gamma2.2.png",
-      /*requested_format=*/AVIF_PIXEL_FORMAT_NONE,
-      /*requested_depth=*/0, AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
-      /*ignore_icc=*/false, /*ignore_exif*/ false,
-      /*ignore_xmp=*/false, /*allow_changing_cicp=*/false);
-  ASSERT_NE(image, nullptr);
-
-  // Color info should still be unspecified even if file gamma is 2.2
-  EXPECT_EQ(image->colorPrimaries, AVIF_COLOR_PRIMARIES_UNSPECIFIED);
-  EXPECT_EQ(image->transferCharacteristics,
-            AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED);
-
-  // should not generate ICC profile
-  EXPECT_EQ(image->icc.size, 0u);
-}
-
 // Verify we can read a color PNG file tagged as gamma 1.6 through gAMA chunk,
 // and generate a color profile for it.
 TEST(PngTest, ColorGamma16) {
@@ -301,7 +281,7 @@
       /*requestedDepth=*/0,
       /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
       /*ignoreColorProfile=*/ignore_icc, /*ignoreExif=*/false,
-      /*ignoreXMP=*/false, /*allowChangingCicp=*/true,
+      /*ignoreXMP=*/false,
       /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
       /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
       /*frameIter=*/nullptr);
@@ -470,7 +450,7 @@
           filepath.c_str(), AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
           AVIF_PIXEL_FORMAT_NONE, /*requestedDepth=*/0,
           AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, /*ignoreColorProfile=*/true,
-          /*ignoreExif=*/true, /*ignoreXMP=*/true, /*allowChangingCicp=*/true,
+          /*ignoreExif=*/true, /*ignoreXMP=*/true,
           /*ignoreGainMap=*/true, image_size_limit, image.get(),
           /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
           /*frameIter=*/nullptr);
diff --git a/tests/gtest/aviftest_helpers.cc b/tests/gtest/aviftest_helpers.cc
index 34357bf..4fd63f3 100644
--- a/tests/gtest/aviftest_helpers.cc
+++ b/tests/gtest/aviftest_helpers.cc
@@ -594,15 +594,14 @@
                    avifPixelFormat requested_format, int requested_depth,
                    avifChromaDownsampling chroma_downsampling,
                    avifBool ignore_icc, avifBool ignore_exif,
-                   avifBool ignore_xmp, avifBool allow_changing_cicp,
-                   avifBool ignore_gain_map) {
+                   avifBool ignore_xmp, avifBool ignore_gain_map) {
   ImagePtr image(avifImageCreateEmpty());
   if (!image ||
       avifReadImage((std::string(folder_path) + file_name).c_str(),
                     AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
                     requested_format, requested_depth, chroma_downsampling,
-                    ignore_icc, ignore_exif, ignore_xmp, allow_changing_cicp,
-                    ignore_gain_map, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
+                    ignore_icc, ignore_exif, ignore_xmp, ignore_gain_map,
+                    AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
                     /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
                     /*frameIter=*/nullptr) == AVIF_APP_FILE_FORMAT_UNKNOWN) {
     return nullptr;
diff --git a/tests/gtest/aviftest_helpers.h b/tests/gtest/aviftest_helpers.h
index 1caf4da..866dd56 100644
--- a/tests/gtest/aviftest_helpers.h
+++ b/tests/gtest/aviftest_helpers.h
@@ -171,7 +171,6 @@
                        AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
                    avifBool ignore_icc = false, avifBool ignore_exif = false,
                    avifBool ignore_xmp = false,
-                   avifBool allow_changing_cicp = true,
                    avifBool ignore_gain_map = false);
 // Convenient wrapper around avifPNGWrite() for debugging purposes.
 // Do not remove.
diff --git a/tests/test_cmd_icc_profile.sh b/tests/test_cmd_icc_profile.sh
index cb2f254..ad8d1a2 100755
--- a/tests/test_cmd_icc_profile.sh
+++ b/tests/test_cmd_icc_profile.sh
@@ -22,6 +22,7 @@
 INPUT_COLOR_PNG="${TESTDATA_DIR}/ArcTriomphe-cHRM-red-green-swap.png"
 REFERENCE_COLOR_PNG="${TESTDATA_DIR}/ArcTriomphe-cHRM-red-green-swap-reference.png"
 INPUT_GRAY_PNG="${TESTDATA_DIR}/kodim03_grayscale_gamma1.6.png"
+INPUT_CICP_PNG="${TESTDATA_DIR}/kodim03_grayscale_cicp.png"
 REFERENCE_GRAY_PNG="${TESTDATA_DIR}/kodim03_grayscale_gamma1.6-reference.png"
 SRGB_ICC="${TESTDATA_DIR}/sRGB2014.icc"
 INPUT_ICC_PNG="${TESTDATA_DIR}/paris_icc_exif_xmp.png"
@@ -46,6 +47,12 @@
   "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Transfer Char.* 12$"
   "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Matrix Coeffs.* 8$"
 
+  # Check the cICP chunk gets read
+  "${AVIFENC}" -s 8 "${INPUT_CICP_PNG}" -o "${ENCODED_FILE}"
+  "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Color Primaries.* 1$"
+  "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Transfer Char.* 4$"
+  "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Matrix Coeffs.* 6$"
+
   # RGB ICC cannot be kept when asking for gray.
   "${AVIFENC}" -s 10 "${INPUT_ICC_JPG}" --yuv 400 -o "${ENCODED_FILE}" && exit 1
   "${AVIFENC}" -s 10 "${INPUT_ICC_PNG}" --yuv 400 -o "${ENCODED_FILE}" && exit 1