Do not allow keeping the ICC when converting from RGB to gray (#2669)
diff --git a/apps/shared/avifjpeg.c b/apps/shared/avifjpeg.c index 1b974dc..09bfb64 100644 --- a/apps/shared/avifjpeg.c +++ b/apps/shared/avifjpeg.c
@@ -909,6 +909,12 @@ unsigned int iccDataLen; if (read_icc_profile(&cinfo, &iccDataTmp, &iccDataLen)) { iccData = iccDataTmp; + if (requestedFormat == AVIF_PIXEL_FORMAT_YUV400) { + fprintf(stderr, + "The image contains an RGB ICC profile which is incompatible with the requested output " + "format YUV400 (grayscale). Pass --ignore-icc to discard the ICC profile.\n"); + goto cleanup; + } if (avifImageSetProfileICC(avif, iccDataTmp, (size_t)iccDataLen) != AVIF_RESULT_OK) { fprintf(stderr, "Setting ICC profile failed: %s (out of memory)\n", inputFilename); goto cleanup;
diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c index b1685c8..9727ea9 100644 --- a/apps/shared/avifpng.c +++ b/apps/shared/avifpng.c
@@ -365,6 +365,13 @@ // 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) { + if (rawColorType != PNG_COLOR_TYPE_GRAY && rawColorType != PNG_COLOR_TYPE_GRAY_ALPHA && + avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + fprintf(stderr, + "The image contains an RGB ICC profile which is incompatible with the requested output " + "format YUV400 (grayscale). Pass --ignore-icc to discard the ICC profile.\n"); + goto cleanup; + } if (avifImageSetProfileICC(avif, iccpData, iccpDataLen) != AVIF_RESULT_OK) { fprintf(stderr, "Setting ICC profile failed: out of memory.\n"); goto cleanup;
diff --git a/tests/gtest/avifreadimagetest.cc b/tests/gtest/avifreadimagetest.cc index 1126128..68e1285 100644 --- a/tests/gtest/avifreadimagetest.cc +++ b/tests/gtest/avifreadimagetest.cc
@@ -289,6 +289,34 @@ 0); } +// Verify the invalidity of keeping the ICC profile for a gray image read from +// an RGB image. +TEST(ICCTest, RGB2Gray) { + for (const auto& file_name : + {"paris_icc_exif_xmp.png", "paris_exif_xmp_icc.jpg"}) { + const std::string file_path = std::string(data_path) + file_name; + for (bool ignore_icc : {false, true}) { + ImagePtr image(avifImageCreateEmpty()); + // Read the image. + const avifAppFileFormat file_format = avifReadImage( + file_path.c_str(), + /*requestedFormat=*/AVIF_PIXEL_FORMAT_YUV400, + /*requestedDepth=*/0, + /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, + /*ignoreColorProfile=*/ignore_icc, /*ignoreExif=*/false, + /*ignoreXMP=*/false, /*allowChangingCicp=*/true, + /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(), + /*outDepth=*/nullptr, /*sourceTiming=*/nullptr, + /*frameIter=*/nullptr); + if (ignore_icc) { + ASSERT_NE(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN); + } else { + ASSERT_EQ(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN); + } + } + } +} + //------------------------------------------------------------------------------ // Memory management tests
diff --git a/tests/test_cmd_grid.sh b/tests/test_cmd_grid.sh index 4418a09..f5e3f6e 100755 --- a/tests/test_cmd_grid.sh +++ b/tests/test_cmd_grid.sh
@@ -44,7 +44,7 @@ "${AVIFDEC}" "${ENCODED_FILE_2x2}" "${DECODED_FILE_2x2}" echo "Testing monochrome grid with odd width (403 px)" - "${AVIFENC}" -s 8 "${INPUT_PNG}" --grid 2x2 --yuv 400 -o "${ENCODED_FILE_2x2}" + "${AVIFENC}" -s 8 "${INPUT_PNG}" --grid 2x2 --yuv 400 --ignore-icc -o "${ENCODED_FILE_2x2}" "${AVIFDEC}" "${ENCODED_FILE_2x2}" "${DECODED_FILE_2x2}" echo "Testing max grid"
diff --git a/tests/test_cmd_icc_profile.sh b/tests/test_cmd_icc_profile.sh index 7e06acb..bb161ad 100755 --- a/tests/test_cmd_icc_profile.sh +++ b/tests/test_cmd_icc_profile.sh
@@ -24,6 +24,8 @@ INPUT_GRAY_PNG="${TESTDATA_DIR}/kodim03_grayscale_gamma1.6.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" +INPUT_ICC_JPG="${TESTDATA_DIR}/paris_exif_xmp_icc.jpg" # Output file names. ENCODED_FILE="avif_test_cmd_icc_profile_encoded.avif" DECODED_FILE="avif_test_cmd_icc_profile_decoded.png" @@ -44,6 +46,12 @@ "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Transfer Char.* 12$" "${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}" | grep "Matrix Coeffs.* 8$" + # 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 + "${AVIFENC}" -s 10 "${INPUT_ICC_JPG}" --yuv 400 --ignore-icc -o "${ENCODED_FILE}" + "${AVIFENC}" -s 10 "${INPUT_ICC_PNG}" --yuv 400 --ignore-icc -o "${ENCODED_FILE}" + # We use third-party tool (ImageMagick) to independently check # our generated ICC profile is valid and correct. if command -v magick &> /dev/null