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