Set Unspecified CICP in Seq Header OBU (#2687)
diff --git a/CHANGELOG.md b/CHANGELOG.md index e7f786a..0b390f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -30,6 +30,8 @@ * Update aom.cmd/LocalAom.cmake: v3.12.0-4-g65ddc22823. This fixes an encode & decode issue on PowerPC (ppc64le). * Change avifenc to start in automatic tiling mode. +* Always forward Unspecified (2) CICP color primaries, transfer characteristics, + and matrix coefficients to the AV1 encoder. Rely on the 'colr' box instead. ## [1.2.1] - 2025-03-17
diff --git a/src/codec_aom.c b/src/codec_aom.c index 2dc5f97..e472736 100644 --- a/src/codec_aom.c +++ b/src/codec_aom.c
@@ -838,26 +838,41 @@ // Set color_config() in the sequence header OBU. if (alpha) { + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE); + + // Keep the default AOM_CSP_UNKNOWN value. + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Keep default Unspecified (2) colour primaries, transfer characteristics, + // and matrix coefficients. } else { - // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, - // AOM_CICP_MC_UNSPECIFIED, AOM_CSP_UNKNOWN, and 0 (studio/limited range). Call - // aom_codec_control() only if the values are not the defaults. - if (image->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_PRIMARIES, (int)image->colorPrimaries); - } - if (image->transferCharacteristics != AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_TRANSFER_CHARACTERISTICS, (int)image->transferCharacteristics); - } - if (image->matrixCoefficients != AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_MATRIX_COEFFICIENTS, (int)image->matrixCoefficients); - } + // libaom's defaults are AOM_CSP_UNKNOWN and 0 (studio/limited range). + // Call aom_codec_control() only if the values are not the defaults. + + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. if (image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, (int)image->yuvChromaSamplePosition); } + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. if (image->yuvRange != AVIF_RANGE_LIMITED) { aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, (int)image->yuvRange); } + + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // It may also save 3 bytes since the AV1 encoder can set color_description_present_flag to 0 + // (see Section 5.5.2 "Color config syntax" of the AV1 specification). + // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, and + // AOM_CICP_MC_UNSPECIFIED. No need to call aom_codec_control(). + // aom_image_t::cp, aom_image_t::tc and aom_image_t::mc are ignored by aom_codec_encode(). } #if defined(AOM_CTRL_AV1E_SET_SKIP_POSTPROC_FILTERING) @@ -1032,7 +1047,13 @@ avifBool monochromeRequested = AVIF_FALSE; if (alpha) { + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. aomImage.range = AOM_CR_FULL_RANGE; + + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. monochromeRequested = AVIF_TRUE; if (aomImageAllocated) { const uint32_t bytesPerRow = ((image->depth > 8) ? 2 : 1) * image->width; @@ -1046,7 +1067,7 @@ aomImage.stride[0] = image->alphaRowBytes; } - // Ignore UV planes when monochrome + // Ignore UV planes when monochrome. Keep the default AOM_CSP_UNKNOWN value. } else { int yuvPlaneCount = 3; if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { @@ -1073,10 +1094,14 @@ } } - aomImage.cp = (aom_color_primaries_t)image->colorPrimaries; - aomImage.tc = (aom_transfer_characteristics_t)image->transferCharacteristics; - aomImage.mc = (aom_matrix_coefficients_t)image->matrixCoefficients; + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. aomImage.csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. aomImage.range = (aom_color_range_t)image->yuvRange; }
diff --git a/src/codec_avm.c b/src/codec_avm.c index 02cebe6..b8295e1 100644 --- a/src/codec_avm.c +++ b/src/codec_avm.c
@@ -625,26 +625,41 @@ // Set color_config() in the sequence header OBU. if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE); + + // Keep the default AOM_CSP_UNKNOWN value. + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Keep default Unspecified (2) colour primaries, transfer characteristics, + // and matrix coefficients. } else { - // libavm's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, - // AOM_CICP_MC_UNSPECIFIED, AOM_CSP_UNKNOWN, and 0 (studio/limited range). Call - // aom_codec_control() only if the values are not the defaults. - if (image->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_PRIMARIES, (int)image->colorPrimaries); - } - if (image->transferCharacteristics != AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_TRANSFER_CHARACTERISTICS, (int)image->transferCharacteristics); - } - if (image->matrixCoefficients != AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) { - aom_codec_control(&codec->internal->encoder, AV1E_SET_MATRIX_COEFFICIENTS, (int)image->matrixCoefficients); - } + // libaom's defaults are AOM_CSP_UNKNOWN and 0 (studio/limited range). + // Call aom_codec_control() only if the values are not the defaults. + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. if (image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, (int)image->yuvChromaSamplePosition); } + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. if (image->yuvRange != AVIF_RANGE_LIMITED) { aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, (int)image->yuvRange); } + + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // It may also save 3 bytes since the AV1 encoder can set color_description_present_flag to 0 + // (see Section 5.5.2 "Color config syntax" of the AV1 specification). + // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, and + // AOM_CICP_MC_UNSPECIFIED. No need to call aom_codec_control(). + // aom_image_t::cp, aom_image_t::tc and aom_image_t::mc are ignored by aom_codec_encode(). } if (!avifProcessAOMOptionsPostInit(codec, alpha)) { @@ -810,7 +825,13 @@ avifBool monochromeRequested = AVIF_FALSE; if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. aomImage.range = AOM_CR_FULL_RANGE; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. monochromeRequested = AVIF_TRUE; if (aomImageAllocated) { const uint32_t bytesPerRow = ((image->depth > 8) ? 2 : 1) * image->width; @@ -824,7 +845,7 @@ aomImage.stride[0] = image->alphaRowBytes; } - // Ignore UV planes when monochrome + // Ignore UV planes when monochrome. Keep the default AOM_CSP_UNKNOWN value. } else { int yuvPlaneCount = 3; if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { @@ -851,10 +872,14 @@ } } - aomImage.cp = (aom_color_primaries_t)image->colorPrimaries; - aomImage.tc = (aom_transfer_characteristics_t)image->transferCharacteristics; - aomImage.mc = (aom_matrix_coefficients_t)image->matrixCoefficients; + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. aomImage.csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. aomImage.range = (aom_color_range_t)image->yuvRange; }
diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c index c39c9a6..293d100 100644 --- a/src/codec_rav1e.c +++ b/src/codec_rav1e.c
@@ -91,11 +91,27 @@ const avifBool supports400 = rav1eSupports400(); RaPixelRange rav1eRange; if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. rav1eRange = RA_PIXEL_RANGE_FULL; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. codec->internal->chromaSampling = supports400 ? RA_CHROMA_SAMPLING_CS400 : RA_CHROMA_SAMPLING_CS420; codec->internal->yShift = 1; + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Use Unspecified (2) colour primaries, transfer characteristics, and matrix coefficients below. } else { + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. rav1eRange = (image->yuvRange == AVIF_RANGE_FULL) ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED; + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. codec->internal->yShift = 0; switch (image->yuvFormat) { case AVIF_PIXEL_FORMAT_YUV444: @@ -186,10 +202,15 @@ } } + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // It may also save 3 bytes since the AV1 encoder may set color_description_present_flag to 0 + // (see Section 5.5.2 "Color config syntax" of the AV1 specification). rav1e_config_set_color_description(rav1eConfig, - (RaMatrixCoefficients)image->matrixCoefficients, - (RaColorPrimaries)image->colorPrimaries, - (RaTransferCharacteristics)image->transferCharacteristics); + RA_MATRIX_COEFFICIENTS_UNSPECIFIED, + RA_COLOR_PRIMARIES_UNSPECIFIED, + RA_TRANSFER_CHARACTERISTICS_UNSPECIFIED); codec->internal->rav1eContext = rav1e_context_new(rav1eConfig); if (!codec->internal->rav1eContext) {
diff --git a/src/codec_svt.c b/src/codec_svt.c index 0a49955..e9a28c8 100644 --- a/src/codec_svt.c +++ b/src/codec_svt.c
@@ -85,10 +85,26 @@ int y_shift = 0; EbColorRange svt_range; if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. svt_range = EB_CR_FULL_RANGE; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. y_shift = 1; + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Use Unspecified (2) colour primaries, transfer characteristics, and matrix coefficients below. } else { + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. svt_range = (image->yuvRange == AVIF_RANGE_FULL) ? EB_CR_FULL_RANGE : EB_CR_STUDIO_RANGE; + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. switch (image->yuvFormat) { case AVIF_PIXEL_FORMAT_YUV444: color_format = EB_YUV444; @@ -125,6 +141,16 @@ } svt_config->encoder_color_format = color_format; svt_config->encoder_bit_depth = (uint8_t)image->depth; + + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // It may also save 3 bytes since the AV1 encoder may set color_description_present_flag to 0 + // (see Section 5.5.2 "Color config syntax" of the AV1 specification). + svt_config->color_primaries = EB_CICP_CP_UNSPECIFIED; + svt_config->transfer_characteristics = EB_CICP_TC_UNSPECIFIED; + svt_config->matrix_coefficients = EB_CICP_MC_UNSPECIFIED; + svt_config->color_range = svt_range; #if !SVT_AV1_CHECK_VERSION(0, 9, 0) svt_config->is_16bit_pipeline = image->depth > 8;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6cb09b7..6d4b40d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt
@@ -86,6 +86,7 @@ add_avif_gtest_with_data(avifanimationtest) add_avif_gtest(avifbasictest) add_avif_gtest(avifchangesettingtest) + add_avif_gtest(avifcicptest) add_avif_gtest(avifclaptest) add_avif_gtest(avifcllitest) add_avif_gtest(avifcodectest)
diff --git a/tests/data/goldens/kodim03_23_animation.avif.xml b/tests/data/goldens/kodim03_23_animation.avif.xml index 5fbdfc1..d506ae9 100644 --- a/tests/data/goldens/kodim03_23_animation.avif.xml +++ b/tests/data/goldens/kodim03_23_animation.avif.xml
@@ -124,7 +124,7 @@ </OBUConfig> <OBUSamples> <Sample number="1" DTS="0" CTS="0" size="REDACTED" RAP="1" > - <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="1" color_primaries="1" transfer_characteristics="13" matrix_coefficients="6" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> + <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="0" color_primaries="2" transfer_characteristics="2" matrix_coefficients="2" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> <OBU size="REDACTED" type="frame" header_size="4" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" uncompressed_header_bytes="12" frame_type="key" refresh_frame_flags="255" show_frame="1" width="768" height="512" nb_tiles="2" > <Tile number="0" start="19" size="REDACTED"/> <Tile number="1" start="12994" size="REDACTED"/>
diff --git a/tests/data/goldens/kodim03_23_animation_keyframes.avif.xml b/tests/data/goldens/kodim03_23_animation_keyframes.avif.xml index 882bc55..8823d73 100644 --- a/tests/data/goldens/kodim03_23_animation_keyframes.avif.xml +++ b/tests/data/goldens/kodim03_23_animation_keyframes.avif.xml
@@ -122,7 +122,7 @@ </OBUConfig> <OBUSamples> <Sample number="1" DTS="0" CTS="0" size="REDACTED" RAP="1" > - <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="1" color_primaries="1" transfer_characteristics="13" matrix_coefficients="6" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> + <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="0" color_primaries="2" transfer_characteristics="2" matrix_coefficients="2" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> <OBU size="REDACTED" type="frame" header_size="4" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" uncompressed_header_bytes="12" frame_type="key" refresh_frame_flags="255" show_frame="1" width="768" height="512" nb_tiles="2" > <Tile number="0" start="19" size="REDACTED"/> <Tile number="1" start="12994" size="REDACTED"/> @@ -130,7 +130,7 @@ </Sample> <Sample number="2" DTS="1" CTS="1" size="REDACTED" RAP="1" > - <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="1" color_primaries="1" transfer_characteristics="13" matrix_coefficients="6" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> + <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="0" color_primaries="2" transfer_characteristics="2" matrix_coefficients="2" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" /> <OBU size="REDACTED" type="frame" header_size="4" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" uncompressed_header_bytes="12" frame_type="key" refresh_frame_flags="255" show_frame="1" width="768" height="512" nb_tiles="2" > <Tile number="0" start="19" size="REDACTED"/> <Tile number="1" start="8808" size="REDACTED"/>
diff --git a/tests/gtest/avifcicptest.cc b/tests/gtest/avifcicptest.cc new file mode 100644 index 0000000..1e4495a --- /dev/null +++ b/tests/gtest/avifcicptest.cc
@@ -0,0 +1,108 @@ +// Copyright 2025 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include <tuple> + +#include "avif/avif.h" +#include "aviftest_helpers.h" +#include "gtest/gtest.h" + +namespace avif { +namespace { + +class CicpTest + : public testing::TestWithParam<std::tuple< + avifCodecChoice, avifColorPrimaries, avifTransferCharacteristics, + avifMatrixCoefficients, avifPixelFormat, avifPlanesFlag, avifRange>> { +}; + +TEST_P(CicpTest, EncodeDecode) { + const avifCodecChoice codec_choice = std::get<0>(GetParam()); + if (avifCodecName(codec_choice, AVIF_CODEC_FLAG_CAN_ENCODE) == nullptr) { + GTEST_SKIP() << "Codec unavailable, skip test."; + } + const avifColorPrimaries cp = std::get<1>(GetParam()); + const avifTransferCharacteristics tc = std::get<2>(GetParam()); + const avifMatrixCoefficients mc = std::get<3>(GetParam()); + const avifPixelFormat subsampling = std::get<4>(GetParam()); + const avifPlanesFlag planes = std::get<5>(GetParam()); + const avifRange range = std::get<6>(GetParam()); + + ImagePtr image = + testutil::CreateImage(32, 32, /*depth=*/8, subsampling, planes, range); + ASSERT_NE(image, nullptr); + testutil::FillImageGradient(image.get()); + image->colorPrimaries = cp; + image->transferCharacteristics = tc; + image->matrixCoefficients = mc; + + EncoderPtr encoder(avifEncoderCreate()); + ASSERT_NE(encoder, nullptr); + encoder->codecChoice = codec_choice; + encoder->speed = AVIF_SPEED_FASTEST; + testutil::AvifRwData encoded; + ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded), + AVIF_RESULT_OK); + + if (testutil::Av1DecoderAvailable()) { + ImagePtr decoded(avifImageCreateEmpty()); + ASSERT_NE(decoded, nullptr); + DecoderPtr decoder(avifDecoderCreate()); + ASSERT_NE(decoder, nullptr); + ASSERT_EQ(avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data, + encoded.size), + AVIF_RESULT_OK); + EXPECT_EQ(decoded->colorPrimaries, cp); + EXPECT_EQ(decoded->transferCharacteristics, tc); + EXPECT_EQ(decoded->matrixCoefficients, mc); + EXPECT_EQ(decoded->yuvRange, range); + } +} + +INSTANTIATE_TEST_SUITE_P( + Reserved0Identity, CicpTest, + // Identity MC require 4:4:4 and AVIF_CODEC_CHOICE_SVT only supports 4:2:0. + testing::Combine( + testing::Values(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_CHOICE_RAV1E), + testing::Values(static_cast<avifColorPrimaries>( + AVIF_COLOR_PRIMARIES_UNKNOWN)), // Reserved CICP value. + testing::Values(static_cast<avifTransferCharacteristics>( + AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN)), // Reserved CICP value. + testing::Values(static_cast<avifMatrixCoefficients>( + AVIF_MATRIX_COEFFICIENTS_IDENTITY)), + testing::Values(AVIF_PIXEL_FORMAT_YUV444), + testing::Values(AVIF_PLANES_YUV, AVIF_PLANES_ALL), + testing::Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL))); + +INSTANTIATE_TEST_SUITE_P( + Unspecified, CicpTest, + testing::Combine(testing::Values(AVIF_CODEC_CHOICE_AOM, + AVIF_CODEC_CHOICE_RAV1E, + AVIF_CODEC_CHOICE_SVT), + testing::Values(static_cast<avifColorPrimaries>( + AVIF_COLOR_PRIMARIES_UNSPECIFIED)), + testing::Values(static_cast<avifTransferCharacteristics>( + AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED)), + testing::Values(static_cast<avifMatrixCoefficients>( + AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED)), + testing::Values(AVIF_PIXEL_FORMAT_YUV420), + testing::Values(AVIF_PLANES_YUV, AVIF_PLANES_ALL), + testing::Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL))); + +INSTANTIATE_TEST_SUITE_P( + SrgbBt601, CicpTest, + testing::Combine(testing::Values(AVIF_CODEC_CHOICE_AOM, + AVIF_CODEC_CHOICE_RAV1E, + AVIF_CODEC_CHOICE_SVT), + testing::Values(static_cast<avifColorPrimaries>( + AVIF_COLOR_PRIMARIES_SRGB)), + testing::Values(static_cast<avifTransferCharacteristics>( + AVIF_TRANSFER_CHARACTERISTICS_SRGB)), + testing::Values(static_cast<avifMatrixCoefficients>( + AVIF_MATRIX_COEFFICIENTS_BT601)), + testing::Values(AVIF_PIXEL_FORMAT_YUV420), + testing::Values(AVIF_PLANES_YUV, AVIF_PLANES_ALL), + testing::Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL))); + +} // namespace +} // namespace avif