CICP Refactor (breaking change!)
* Remove most references to "NCLX", as it is mostly an implementation detail, and the values are really from MPEG-CICP
* Eliminate avifProfileFormat: having an ICC profile is not mutually exclusive with signaling CICP
* CICP is now always available in an avifImage, set to unspecified by default
* Added --cicp as an alias for --nclx (semi-deprecated)
* Setting CICP via avifenc no longer overrides ICC profiles, they co-exist
* Simplified avifenc argument parsing / warnings logic
* avifenc/avifdec/avifdump now all display CICP when dumping AVIF information
* nclx colr box contents are guaranteed to override AV1 bitstream CICP (as MIAF standard specifies)
* Added comments explaining various decisions and citing standards
* Removed ICC inspection code regarding chroma-derived mtxCoeffs; this was overdesigned. Now just honor the assoc. colorPrimaries enum
* Reworked all examples in the README to reflect the new state of things, and clean out some cruft
diff --git a/src/avif.c b/src/avif.c
index 6a1a403..e88d407 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -103,6 +103,9 @@
memset(image, 0, sizeof(avifImage));
image->yuvRange = AVIF_RANGE_FULL;
image->alphaRange = AVIF_RANGE_FULL;
+ image->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
+ image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
+ image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED;
}
avifImage * avifImageCreate(int width, int height, int depth, avifPixelFormat yuvFormat)
@@ -132,19 +135,17 @@
dstImage->yuvRange = srcImage->yuvRange;
dstImage->alphaRange = srcImage->alphaRange;
+ dstImage->colorPrimaries = srcImage->colorPrimaries;
+ dstImage->transferCharacteristics = srcImage->transferCharacteristics;
+ dstImage->matrixCoefficients = srcImage->matrixCoefficients;
+
dstImage->transformFlags = srcImage->transformFlags;
memcpy(&dstImage->pasp, &srcImage->pasp, sizeof(dstImage->pasp));
memcpy(&dstImage->clap, &srcImage->clap, sizeof(dstImage->clap));
memcpy(&dstImage->irot, &srcImage->irot, sizeof(dstImage->irot));
memcpy(&dstImage->imir, &srcImage->imir, sizeof(dstImage->pasp));
- if (srcImage->profileFormat == AVIF_PROFILE_FORMAT_ICC) {
- avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size);
- } else if (srcImage->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- avifImageSetProfileNCLX(dstImage, &srcImage->nclx);
- } else {
- avifImageSetProfileNone(dstImage);
- }
+ avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size);
avifImageSetMetadataExif(dstImage, srcImage->exif.data, srcImage->exif.size);
avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size);
@@ -202,26 +203,9 @@
avifFree(image);
}
-void avifImageSetProfileNone(avifImage * image)
-{
- image->profileFormat = AVIF_PROFILE_FORMAT_NONE;
- avifRWDataFree(&image->icc);
-}
-
void avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize)
{
- avifImageSetProfileNone(image);
- if (iccSize) {
- image->profileFormat = AVIF_PROFILE_FORMAT_ICC;
- avifRWDataSet(&image->icc, icc, iccSize);
- }
-}
-
-void avifImageSetProfileNCLX(avifImage * image, avifNclxColorProfile * nclx)
-{
- avifImageSetProfileNone(image);
- image->profileFormat = AVIF_PROFILE_FORMAT_NCLX;
- memcpy(&image->nclx, nclx, sizeof(avifNclxColorProfile));
+ avifRWDataSet(&image->icc, icc, iccSize);
}
void avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize)
diff --git a/src/codec_aom.c b/src/codec_aom.c
index 93afce7..8e74374 100644
--- a/src/codec_aom.c
+++ b/src/codec_aom.c
@@ -136,15 +136,9 @@
image->yuvFormat = yuvFormat;
image->yuvRange = (codec->internal->image->range == AOM_CR_STUDIO_RANGE) ? AVIF_RANGE_LIMITED : AVIF_RANGE_FULL;
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NONE) {
- // If the AVIF container doesn't provide a color profile, allow the AV1 OBU to provide one as a fallback
- avifNclxColorProfile nclx;
- nclx.colourPrimaries = (avifNclxColourPrimaries)codec->internal->image->cp;
- nclx.transferCharacteristics = (avifNclxTransferCharacteristics)codec->internal->image->tc;
- nclx.matrixCoefficients = (avifNclxMatrixCoefficients)codec->internal->image->mc;
- nclx.range = image->yuvRange;
- avifImageSetProfileNCLX(image, &nclx);
- }
+ image->colorPrimaries = (avifColorPrimaries)codec->internal->image->cp;
+ image->transferCharacteristics = (avifTransferCharacteristics)codec->internal->image->tc;
+ image->matrixCoefficients = (avifMatrixCoefficients)codec->internal->image->mc;
avifPixelFormatInfo formatInfo;
avifGetPixelFormatInfo(yuvFormat, &formatInfo);
@@ -366,14 +360,12 @@
}
}
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- aomImage->cp = (aom_color_primaries_t)image->nclx.colourPrimaries;
- aomImage->tc = (aom_transfer_characteristics_t)image->nclx.transferCharacteristics;
- aomImage->mc = (aom_matrix_coefficients_t)image->nclx.matrixCoefficients;
- aom_codec_control(&aomEncoder, AV1E_SET_COLOR_PRIMARIES, aomImage->cp);
- aom_codec_control(&aomEncoder, AV1E_SET_TRANSFER_CHARACTERISTICS, aomImage->tc);
- aom_codec_control(&aomEncoder, AV1E_SET_MATRIX_COEFFICIENTS, aomImage->mc);
- }
+ aomImage->cp = (aom_color_primaries_t)image->colorPrimaries;
+ aomImage->tc = (aom_transfer_characteristics_t)image->transferCharacteristics;
+ aomImage->mc = (aom_matrix_coefficients_t)image->matrixCoefficients;
+ aom_codec_control(&aomEncoder, AV1E_SET_COLOR_PRIMARIES, aomImage->cp);
+ aom_codec_control(&aomEncoder, AV1E_SET_TRANSFER_CHARACTERISTICS, aomImage->tc);
+ aom_codec_control(&aomEncoder, AV1E_SET_MATRIX_COEFFICIENTS, aomImage->mc);
}
aom_codec_encode(&aomEncoder, aomImage, 0, 1, 0);
diff --git a/src/codec_dav1d.c b/src/codec_dav1d.c
index b864c8a..a20a377 100644
--- a/src/codec_dav1d.c
+++ b/src/codec_dav1d.c
@@ -151,15 +151,9 @@
image->yuvFormat = yuvFormat;
image->yuvRange = codec->internal->colorRange;
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NONE) {
- // If the AVIF container doesn't provide a color profile, allow the AV1 OBU to provide one as a fallback
- avifNclxColorProfile nclx;
- nclx.colourPrimaries = (avifNclxColourPrimaries)dav1dImage->seq_hdr->pri;
- nclx.transferCharacteristics = (avifNclxTransferCharacteristics)dav1dImage->seq_hdr->trc;
- nclx.matrixCoefficients = (avifNclxMatrixCoefficients)dav1dImage->seq_hdr->mtrx;
- nclx.range = image->yuvRange;
- avifImageSetProfileNCLX(image, &nclx);
- }
+ image->colorPrimaries = (avifColorPrimaries)dav1dImage->seq_hdr->pri;
+ image->transferCharacteristics = (avifTransferCharacteristics)dav1dImage->seq_hdr->trc;
+ image->matrixCoefficients = (avifMatrixCoefficients)dav1dImage->seq_hdr->mtrx;
avifPixelFormatInfo formatInfo;
avifGetPixelFormatInfo(yuvFormat, &formatInfo);
diff --git a/src/codec_libgav1.c b/src/codec_libgav1.c
index a3b02da..d7260c3 100644
--- a/src/codec_libgav1.c
+++ b/src/codec_libgav1.c
@@ -106,15 +106,9 @@
image->yuvFormat = yuvFormat;
image->yuvRange = codec->internal->colorRange;
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NONE) {
- // If the AVIF container doesn't provide a color profile, allow the AV1 OBU to provide one as a fallback
- avifNclxColorProfile nclx;
- nclx.colourPrimaries = (avifNclxColourPrimaries)gav1Image->color_primary;
- nclx.transferCharacteristics = (avifNclxTransferCharacteristics)gav1Image->transfer_characteristics;
- nclx.matrixCoefficients = (avifNclxMatrixCoefficients)gav1Image->matrix_coefficients;
- nclx.range = image->yuvRange;
- avifImageSetProfileNCLX(image, &nclx);
- }
+ image->colorPrimaries = (avifColorPrimaries)gav1Image->color_primary;
+ image->transferCharacteristics = (avifTransferCharacteristics)gav1Image->transfer_characteristics;
+ image->matrixCoefficients = (avifMatrixCoefficients)gav1Image->matrix_coefficients;
avifPixelFormatInfo formatInfo;
avifGetPixelFormatInfo(yuvFormat, &formatInfo);
diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c
index 96cbb80..5c8b332 100644
--- a/src/codec_rav1e.c
+++ b/src/codec_rav1e.c
@@ -113,12 +113,10 @@
}
}
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- rav1e_config_set_color_description(rav1eConfig,
- (RaMatrixCoefficients)image->nclx.matrixCoefficients,
- (RaColorPrimaries)image->nclx.colourPrimaries,
- (RaTransferCharacteristics)image->nclx.transferCharacteristics);
- }
+ rav1e_config_set_color_description(rav1eConfig,
+ (RaMatrixCoefficients)image->matrixCoefficients,
+ (RaColorPrimaries)image->colorPrimaries,
+ (RaTransferCharacteristics)image->transferCharacteristics);
rav1eContext = rav1e_context_new(rav1eConfig);
if (!rav1eContext) {
diff --git a/src/colr.c b/src/colr.c
index 120990f..32862cd 100644
--- a/src/colr.c
+++ b/src/colr.c
@@ -6,146 +6,38 @@
#include <math.h>
#include <string.h>
-// ------------------------------------------------------------------------------------------------
-// Adapted from gb_math:
-//
-// gb_math.h - v0.07c - public domain C math library - no warranty implied; use at your own risk
-
-typedef float gbFloat3[3];
-
-typedef union gbVec3
+struct avifColorPrimariesTable
{
- struct
- {
- float x, y, z;
- } xyz;
- float e[3];
-} gbVec3;
-
-typedef union gbMat3
-{
- gbVec3 col[3];
- float e[9];
-} gbMat3;
-
-static gbFloat3 * gb_float33_m(gbMat3 * m)
-{
- return (gbFloat3 *)m;
-}
-
-static void gb_float33_mul_vec3(gbVec3 * out, float m[3][3], gbVec3 v)
-{
- out->xyz.x = m[0][0] * v.xyz.x + m[0][1] * v.xyz.y + m[0][2] * v.xyz.z;
- out->xyz.y = m[1][0] * v.xyz.x + m[1][1] * v.xyz.y + m[1][2] * v.xyz.z;
- out->xyz.z = m[2][0] * v.xyz.x + m[2][1] * v.xyz.y + m[2][2] * v.xyz.z;
-}
-
-static void gb_mat3_mul_vec3(gbVec3 * out, gbMat3 * m, gbVec3 in)
-{
- gb_float33_mul_vec3(out, gb_float33_m(m), in);
-}
-
-static void gb_float33_transpose(float (*vec)[3])
-{
- int i, j;
- for (j = 0; j < 3; j++) {
- for (i = j + 1; i < 3; i++) {
- float t = vec[i][j];
- vec[i][j] = vec[j][i];
- vec[j][i] = t;
- }
- }
-}
-
-static void gb_mat3_transpose(gbMat3 * m)
-{
- gb_float33_transpose(gb_float33_m(m));
-}
-
-static float gb_mat3_determinant(gbMat3 * m)
-{
- gbFloat3 * e = gb_float33_m(m);
- float d = +e[0][0] * (e[1][1] * e[2][2] - e[1][2] * e[2][1]) - e[0][1] * (e[1][0] * e[2][2] - e[1][2] * e[2][0]) +
- e[0][2] * (e[1][0] * e[2][1] - e[1][1] * e[2][0]);
- return d;
-}
-
-static void gb_float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3])
-{
- int i, j;
- float temp1[3][3], temp2[3][3];
- if (mat1 == out) {
- memcpy(temp1, mat1, sizeof(temp1));
- mat1 = temp1;
- }
- if (mat2 == out) {
- memcpy(temp2, mat2, sizeof(temp2));
- mat2 = temp2;
- }
- for (j = 0; j < 3; j++) {
- for (i = 0; i < 3; i++) {
- out[j][i] = mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1] + mat1[2][i] * mat2[j][2];
- }
- }
-}
-
-static void gb_mat3_mul(gbMat3 * out, gbMat3 * m1, gbMat3 * m2)
-{
- gb_float33_mul(gb_float33_m(out), gb_float33_m(m1), gb_float33_m(m2));
-}
-
-static void gb_mat3_inverse(gbMat3 * out, gbMat3 * in)
-{
- gbFloat3 * o = gb_float33_m(out);
- gbFloat3 * i = gb_float33_m(in);
-
- float ood = 1.0f / gb_mat3_determinant(in);
-
- o[0][0] = +(i[1][1] * i[2][2] - i[2][1] * i[1][2]) * ood;
- o[0][1] = -(i[1][0] * i[2][2] - i[2][0] * i[1][2]) * ood;
- o[0][2] = +(i[1][0] * i[2][1] - i[2][0] * i[1][1]) * ood;
- o[1][0] = -(i[0][1] * i[2][2] - i[2][1] * i[0][2]) * ood;
- o[1][1] = +(i[0][0] * i[2][2] - i[2][0] * i[0][2]) * ood;
- o[1][2] = -(i[0][0] * i[2][1] - i[2][0] * i[0][1]) * ood;
- o[2][0] = +(i[0][1] * i[1][2] - i[1][1] * i[0][2]) * ood;
- o[2][1] = -(i[0][0] * i[1][2] - i[1][0] * i[0][2]) * ood;
- o[2][2] = +(i[0][0] * i[1][1] - i[1][0] * i[0][1]) * ood;
-}
-
-// ------------------------------------------------------------------------------------------------
-
-struct avifColourPrimariesTable
-{
- avifNclxColourPrimaries colourPrimariesEnum;
+ avifColorPrimaries colorPrimariesEnum;
const char * name;
float primaries[8]; // rX, rY, gX, gY, bX, bY, wX, wY
};
-static const struct avifColourPrimariesTable avifColourPrimariesTables[] = {
- { AVIF_NCLX_COLOUR_PRIMARIES_BT709, "BT.709", { 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_BT470M, "BT.470-6 System M", { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.310f, 0.316f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_BT470BG, "BT.470-6 System BG", { 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_BT601, "BT.601", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_SMPTE240, "SMPTE 240M", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_GENERIC_FILM, "Generic film", { 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_BT2020, "BT.2020", { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_XYZ, "XYZ", { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.3333f, 0.3333f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_SMPTE431, "SMPTE RP 431-2", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_SMPTE432, "SMPTE EG 432-1 (DCI P3)", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f } },
- { AVIF_NCLX_COLOUR_PRIMARIES_EBU3213, "EBU Tech. 3213-E", { 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f } }
+static const struct avifColorPrimariesTable avifColorPrimariesTables[] = {
+ { AVIF_COLOR_PRIMARIES_BT709, "BT.709", { 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f } },
+ { AVIF_COLOR_PRIMARIES_BT470M, "BT.470-6 System M", { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.310f, 0.316f } },
+ { AVIF_COLOR_PRIMARIES_BT470BG, "BT.470-6 System BG", { 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f } },
+ { AVIF_COLOR_PRIMARIES_BT601, "BT.601", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
+ { AVIF_COLOR_PRIMARIES_SMPTE240, "SMPTE 240M", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
+ { AVIF_COLOR_PRIMARIES_GENERIC_FILM, "Generic film", { 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f } },
+ { AVIF_COLOR_PRIMARIES_BT2020, "BT.2020", { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f } },
+ { AVIF_COLOR_PRIMARIES_XYZ, "XYZ", { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.3333f, 0.3333f } },
+ { AVIF_COLOR_PRIMARIES_SMPTE431, "SMPTE RP 431-2", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f } },
+ { AVIF_COLOR_PRIMARIES_SMPTE432, "SMPTE EG 432-1 (DCI P3)", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f } },
+ { AVIF_COLOR_PRIMARIES_EBU3213, "EBU Tech. 3213-E", { 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f } }
};
-static const int avifColourPrimariesTableSize = sizeof(avifColourPrimariesTables) / sizeof(avifColourPrimariesTables[0]);
+static const int avifColorPrimariesTableSize = sizeof(avifColorPrimariesTables) / sizeof(avifColorPrimariesTables[0]);
-void avifNclxColourPrimariesGetValues(avifNclxColourPrimaries ancp, float outPrimaries[8])
+void avifColorPrimariesGetValues(avifColorPrimaries acp, float outPrimaries[8])
{
- for (int i = 0; i < avifColourPrimariesTableSize; ++i) {
- if (avifColourPrimariesTables[i].colourPrimariesEnum == ancp) {
- memcpy(outPrimaries, avifColourPrimariesTables[i].primaries, sizeof(avifColourPrimariesTables[i].primaries));
+ for (int i = 0; i < avifColorPrimariesTableSize; ++i) {
+ if (avifColorPrimariesTables[i].colorPrimariesEnum == acp) {
+ memcpy(outPrimaries, avifColorPrimariesTables[i].primaries, sizeof(avifColorPrimariesTables[i].primaries));
return;
}
}
// if we get here, the color primaries are unknown. Just return a reasonable default.
- memcpy(outPrimaries, avifColourPrimariesTables[0].primaries, sizeof(avifColourPrimariesTables[0].primaries));
+ memcpy(outPrimaries, avifColorPrimariesTables[0].primaries, sizeof(avifColorPrimariesTables[0].primaries));
}
static avifBool matchesTo3RoundedPlaces(float a, float b)
@@ -160,243 +52,26 @@
matchesTo3RoundedPlaces(p1[5], p2[5]) && matchesTo3RoundedPlaces(p1[6], p2[6]) && matchesTo3RoundedPlaces(p1[7], p2[7]);
}
-avifNclxColourPrimaries avifNclxColourPrimariesFind(float inPrimaries[8], const char ** outName)
+avifColorPrimaries avifColorPrimariesFind(float inPrimaries[8], const char ** outName)
{
if (outName) {
*outName = NULL;
}
- for (int i = 0; i < avifColourPrimariesTableSize; ++i) {
- if (primariesMatch(inPrimaries, avifColourPrimariesTables[i].primaries)) {
+ for (int i = 0; i < avifColorPrimariesTableSize; ++i) {
+ if (primariesMatch(inPrimaries, avifColorPrimariesTables[i].primaries)) {
if (outName) {
- *outName = avifColourPrimariesTables[i].name;
+ *outName = avifColorPrimariesTables[i].name;
}
- return avifColourPrimariesTables[i].colourPrimariesEnum;
+ return avifColorPrimariesTables[i].colorPrimariesEnum;
}
}
- return AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN;
-}
-
-static float fixedToFloat(int32_t fixed)
-{
- float sign = 1.0f;
- if (fixed < 0) {
- sign = -1.0f;
- fixed *= -1;
- }
- return sign * ((float)((fixed >> 16) & 0xffff) + ((float)(fixed & 0xffff) / 65536.0f));
-}
-
-#if 0
-static void convertXYZToXYY(float XYZ[3], float xyY[3], float whitePointX, float whitePointY)
-{
- float sum = XYZ[0] + XYZ[1] + XYZ[2];
- if (sum <= 0.0f) {
- xyY[0] = whitePointX;
- xyY[1] = whitePointY;
- xyY[2] = 0.0f;
- return;
- }
- xyY[0] = XYZ[0] / sum;
- xyY[1] = XYZ[1] / sum;
- xyY[2] = XYZ[1];
-}
-
-static void convertXYYToXYZ(float * xyY, float * XYZ)
-{
- if (xyY[2] <= 0.0f) {
- XYZ[0] = 0.0f;
- XYZ[1] = 0.0f;
- XYZ[2] = 0.0f;
- return;
- }
- XYZ[0] = (xyY[0] * xyY[2]) / xyY[1];
- XYZ[1] = xyY[2];
- XYZ[2] = ((1 - xyY[0] - xyY[1]) * xyY[2]) / xyY[1];
-}
-
-static void convertMaxXYToXYZ(float x, float y, float * XYZ)
-{
- float xyY[3];
- xyY[0] = x;
- xyY[1] = y;
- xyY[2] = 1.0f;
- convertXYYToXYZ(xyY, XYZ);
-}
-
-static void convertXYZToXY(float XYZ[3], float xy[2], float whitePointX, float whitePointY)
-{
- float xyY[3];
- convertXYZToXYY(XYZ, xyY, whitePointX, whitePointY);
- xy[0] = xyY[0];
- xy[1] = xyY[1];
-}
-#endif /* if 0 */
-
-static float calcMaxY(float r, float g, float b, gbMat3 * colorants)
-{
- gbVec3 rgb, XYZ;
- rgb.e[0] = r;
- rgb.e[1] = g;
- rgb.e[2] = b;
- gb_mat3_mul_vec3(&XYZ, colorants, rgb);
- return XYZ.xyz.y;
-}
-
-static avifBool readXYZ(const uint8_t * data, size_t size, float xyz[3])
-{
- avifROData xyzData;
- xyzData.data = data;
- xyzData.size = size;
- avifROStream s;
- avifROStreamStart(&s, &xyzData);
- CHECK(avifROStreamSkip(&s, 8));
-
- int32_t fixedXYZ[3];
- CHECK(avifROStreamReadU32(&s, (uint32_t *)&fixedXYZ[0]));
- CHECK(avifROStreamReadU32(&s, (uint32_t *)&fixedXYZ[1]));
- CHECK(avifROStreamReadU32(&s, (uint32_t *)&fixedXYZ[2]));
-
- xyz[0] = fixedToFloat(fixedXYZ[0]);
- xyz[1] = fixedToFloat(fixedXYZ[1]);
- xyz[2] = fixedToFloat(fixedXYZ[2]);
- return AVIF_TRUE;
-}
-
-static avifBool readMat3(const uint8_t * data, size_t size, gbMat3 * m)
-{
- avifROData xyzData;
- xyzData.data = data;
- xyzData.size = size;
- avifROStream s;
- avifROStreamStart(&s, &xyzData);
- CHECK(avifROStreamSkip(&s, 8));
-
- for (int i = 0; i < 9; ++i) {
- int32_t fixedXYZ;
- CHECK(avifROStreamReadU32(&s, (uint32_t *)&fixedXYZ));
- m->e[i] = fixedToFloat(fixedXYZ);
- }
- return AVIF_TRUE;
-}
-
-static avifBool calcYUVInfoFromICC(const uint8_t * iccData, size_t iccSize, float coeffs[3])
-{
- avifROData icc;
- icc.data = iccData;
- icc.size = iccSize;
-
- avifROStream s;
- avifROStreamStart(&s, &icc);
-
- uint8_t iccMajorVersion;
- CHECK(avifROStreamSkip(&s, 8)); // skip to version
- CHECK(avifROStreamRead(&s, &iccMajorVersion, 1));
-
- avifROStreamStart(&s, &icc); // start stream over
- CHECK(avifROStreamSkip(&s, 128)); // skip past the ICC header
-
- uint32_t tagCount;
- CHECK(avifROStreamReadU32(&s, &tagCount));
-
- avifBool rXYZPresent = AVIF_FALSE;
- avifBool gXYZPresent = AVIF_FALSE;
- avifBool bXYZPresent = AVIF_FALSE;
- avifBool wtptPresent = AVIF_FALSE;
- avifBool chadPresent = AVIF_FALSE;
- gbMat3 colorants;
- gbMat3 chad, invChad;
- gbVec3 wtpt;
- for (int i = 0; i < 9; ++i) {
- colorants.e[i] = 0.0f;
- chad.e[i] = 0.0f;
- }
- wtpt.e[0] = 0.0f;
- wtpt.e[1] = 0.0f;
- wtpt.e[2] = 0.0f;
-
- for (uint32_t tagIndex = 0; tagIndex < tagCount; ++tagIndex) {
- uint8_t tagSignature[4];
- uint32_t tagOffset;
- uint32_t tagSize;
- CHECK(avifROStreamRead(&s, tagSignature, 4));
- CHECK(avifROStreamReadU32(&s, &tagOffset));
- CHECK(avifROStreamReadU32(&s, &tagSize));
- if ((tagOffset + tagSize) > icc.size) {
- return AVIF_FALSE;
- }
- if (!memcmp(tagSignature, "rXYZ", 4)) {
- CHECK(readXYZ(icc.data + tagOffset, tagSize, &colorants.e[0]));
- rXYZPresent = AVIF_TRUE;
- } else if (!memcmp(tagSignature, "gXYZ", 4)) {
- CHECK(readXYZ(icc.data + tagOffset, tagSize, &colorants.e[3]));
- gXYZPresent = AVIF_TRUE;
- } else if (!memcmp(tagSignature, "bXYZ", 4)) {
- CHECK(readXYZ(icc.data + tagOffset, tagSize, &colorants.e[6]));
- bXYZPresent = AVIF_TRUE;
- } else if (!memcmp(tagSignature, "wtpt", 4)) {
- CHECK(readXYZ(icc.data + tagOffset, tagSize, &wtpt.e[0]));
- wtptPresent = AVIF_TRUE;
- } else if (!memcmp(tagSignature, "chad", 4)) {
- CHECK(readMat3(icc.data + tagOffset, tagSize, &chad));
- chadPresent = AVIF_TRUE;
- }
- }
-
- if (!rXYZPresent || !gXYZPresent || !bXYZPresent || !wtptPresent) {
- return AVIF_FALSE;
- }
-
- // These are read in column order, transpose to fix
- gb_mat3_transpose(&colorants);
- gb_mat3_transpose(&chad);
-
- gb_mat3_inverse(&invChad, &chad);
-
- if (chadPresent) {
- // TODO: make sure ICC profiles with no chad still behave?
-
- gbMat3 tmpColorants;
- memcpy(&tmpColorants, &colorants, sizeof(tmpColorants));
- gb_mat3_mul(&colorants, &tmpColorants, &invChad);
-
- // TODO: make sure older versions work well?
- if (iccMajorVersion >= 4) {
- gbVec3 tmp;
- memcpy(&tmp, &wtpt, sizeof(tmp));
- gb_mat3_mul_vec3(&wtpt, &invChad, tmp);
- }
- }
-
- // white point and color primaries harvesting (unnecessary for YUV coefficients)
-#if 0
- float whitePoint[2];
- convertXYZToXY(&wtpt.e[0], &whitePoint, 0.0f, 0.0f);
-
- float primaries[6];
- {
- // transpose to get sets of 3-tuples for R, G, B
- gb_mat3_transpose(&colorants);
-
- convertXYZToXY(&colorants.e[0], &primaries[0], whitePoint[0], whitePoint[1]);
- convertXYZToXY(&colorants.e[3], &primaries[2], whitePoint[0], whitePoint[1]);
- convertXYZToXY(&colorants.e[6], &primaries[4], whitePoint[0], whitePoint[1]);
-
- // put it back
- gb_mat3_transpose(&colorants);
- }
-#endif
-
- // YUV coefficients are simply the brightest Y that a primary can be (where the white point's Y is 1.0)
- coeffs[0] = calcMaxY(1.0f, 0.0f, 0.0f, &colorants);
- coeffs[2] = calcMaxY(0.0f, 0.0f, 1.0f, &colorants);
- coeffs[1] = 1.0f - coeffs[0] - coeffs[2];
- return AVIF_TRUE;
+ return AVIF_COLOR_PRIMARIES_UNKNOWN;
}
struct avifMatrixCoefficientsTable
{
- avifNclxMatrixCoefficients matrixCoefficientsEnum;
+ avifMatrixCoefficients matrixCoefficientsEnum;
const char * name;
const float kr;
const float kb;
@@ -404,26 +79,26 @@
// https://www.itu.int/rec/T-REC-H.273-201612-I/en
static const struct avifMatrixCoefficientsTable matrixCoefficientsTables[] = {
- //{ AVIF_NCLX_MATRIX_COEFFICIENTS_IDENTITY, "Identity", 0.0f, 0.0f, }, // Handled elsewhere
- { AVIF_NCLX_MATRIX_COEFFICIENTS_BT709, "BT.709", 0.2126f, 0.0722f },
- { AVIF_NCLX_MATRIX_COEFFICIENTS_FCC, "FCC USFC 73.682", 0.30f, 0.11f },
- { AVIF_NCLX_MATRIX_COEFFICIENTS_BT470BG, "BT.470-6 System BG", 0.299f, 0.114f },
- { AVIF_NCLX_MATRIX_COEFFICIENTS_BT601, "BT.601", 0.299f, 0.144f },
- { AVIF_NCLX_MATRIX_COEFFICIENTS_SMPTE240, "SMPTE ST 240", 0.212f, 0.087f },
- { AVIF_NCLX_MATRIX_COEFFICIENTS_BT2020_NCL, "BT.2020 (non-constant luminance)", 0.2627f, 0.0593f },
- //{ AVIF_NCLX_MATRIX_COEFFICIENTS_BT2020_CL, "BT.2020 (constant luminance)", 0.2627f, 0.0593f }, // FIXME: It is not an linear transformation.
- //{ AVIF_NCLX_MATRIX_COEFFICIENTS_ST2085, "ST 2085", 0.0f, 0.0f }, // FIXME: ST2085 can't represent using Kr and Kb.
- //{ AVIF_NCLX_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL, "Chromaticity-derived constant luminance system", 0.0f, 0.0f } // FIXME: It is not an linear transformation.
- //{ AVIF_NCLX_MATRIX_COEFFICIENTS_ICTCP, "BT.2100-0 ICtCp", 0.0f, 0.0f }, // FIXME: This can't represent using Kr and Kb.
+ //{ AVIF_MATRIX_COEFFICIENTS_IDENTITY, "Identity", 0.0f, 0.0f, }, // Handled elsewhere
+ { AVIF_MATRIX_COEFFICIENTS_BT709, "BT.709", 0.2126f, 0.0722f },
+ { AVIF_MATRIX_COEFFICIENTS_FCC, "FCC USFC 73.682", 0.30f, 0.11f },
+ { AVIF_MATRIX_COEFFICIENTS_BT470BG, "BT.470-6 System BG", 0.299f, 0.114f },
+ { AVIF_MATRIX_COEFFICIENTS_BT601, "BT.601", 0.299f, 0.144f },
+ { AVIF_MATRIX_COEFFICIENTS_SMPTE240, "SMPTE ST 240", 0.212f, 0.087f },
+ { AVIF_MATRIX_COEFFICIENTS_BT2020_NCL, "BT.2020 (non-constant luminance)", 0.2627f, 0.0593f },
+ //{ AVIF_MATRIX_COEFFICIENTS_BT2020_CL, "BT.2020 (constant luminance)", 0.2627f, 0.0593f }, // FIXME: It is not an linear transformation.
+ //{ AVIF_MATRIX_COEFFICIENTS_ST2085, "ST 2085", 0.0f, 0.0f }, // FIXME: ST2085 can't represent using Kr and Kb.
+ //{ AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL, "Chromaticity-derived constant luminance system", 0.0f, 0.0f } // FIXME: It is not an linear transformation.
+ //{ AVIF_MATRIX_COEFFICIENTS_ICTCP, "BT.2100-0 ICtCp", 0.0f, 0.0f }, // FIXME: This can't represent using Kr and Kb.
};
static const int avifMatrixCoefficientsTableSize = sizeof(matrixCoefficientsTables) / sizeof(matrixCoefficientsTables[0]);
-static avifBool calcYUVInfoFromNCLX(avifNclxColorProfile * nclx, float coeffs[3])
+static avifBool calcYUVInfoFromCICP(avifImage * image, float coeffs[3])
{
- if (nclx->matrixCoefficients == AVIF_NCLX_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL) {
+ if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL) {
float primaries[8];
- avifNclxColourPrimariesGetValues(nclx->colourPrimaries, primaries);
+ avifColorPrimariesGetValues(image->colorPrimaries, primaries);
float const rX = primaries[0];
float const rY = primaries[1];
float const gX = primaries[2];
@@ -449,7 +124,7 @@
} else {
for (int i = 0; i < avifMatrixCoefficientsTableSize; ++i) {
const struct avifMatrixCoefficientsTable * const table = &matrixCoefficientsTables[i];
- if (table->matrixCoefficientsEnum == nclx->matrixCoefficients) {
+ if (table->matrixCoefficientsEnum == image->matrixCoefficients) {
coeffs[0] = table->kr;
coeffs[2] = table->kb;
coeffs[1] = 1.0f - coeffs[0] - coeffs[2];
@@ -462,24 +137,21 @@
void avifCalcYUVCoefficients(avifImage * image, float * outR, float * outG, float * outB)
{
- // sRGB (BT.709) defaults
+ // sRGB (BT.709) defaults, as explained here:
+ //
+ // https://github.com/AOMediaCodec/av1-avif/issues/83
+ //
+ // MIAF (ISO/IEC FDIS 23000-22) Section 7.3.6.4 states that matrix_coefficients should be assumed
+ // to be 1 (BT.709) if there is no associated colour property.
float kr = 0.2126f;
float kb = 0.0722f;
float kg = 1.0f - kr - kb;
float coeffs[3];
- if ((image->profileFormat == AVIF_PROFILE_FORMAT_ICC) && image->icc.data && image->icc.size) {
- if (calcYUVInfoFromICC(image->icc.data, image->icc.size, coeffs)) {
- kr = coeffs[0];
- kg = coeffs[1];
- kb = coeffs[2];
- }
- } else if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- if (calcYUVInfoFromNCLX(&image->nclx, coeffs)) {
- kr = coeffs[0];
- kg = coeffs[1];
- kb = coeffs[2];
- }
+ if (calcYUVInfoFromCICP(image, coeffs)) {
+ kr = coeffs[0];
+ kg = coeffs[1];
+ kb = coeffs[2];
}
*outR = kr;
diff --git a/src/read.c b/src/read.c
index e314407..51daf43 100644
--- a/src/read.c
+++ b/src/read.c
@@ -65,10 +65,15 @@
// colr
typedef struct avifColourInformationBox
{
- avifProfileFormat format;
+ avifBool hasICC;
const uint8_t * icc;
size_t iccSize;
- avifNclxColorProfile nclx;
+
+ avifBool hasNCLX;
+ avifColorPrimaries colorPrimaries;
+ avifTransferCharacteristics transferCharacteristics;
+ avifMatrixCoefficients matrixCoefficients;
+ avifRange range;
} avifColourInformationBox;
#define MAX_PIXI_PLANE_DEPTHS 4
@@ -392,7 +397,13 @@
avifDecoderSource source;
const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
uint32_t primaryItemID;
- uint32_t metaBoxID; // Ever-incrementing ID for tracking which 'meta' box contains an idat, and which idat an iloc might refer to
+ uint32_t metaBoxID; // Ever-incrementing ID for uniquely identifying which 'meta' box contains an idat
+ avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet.
+ // This allows nclx colr boxes to override AV1 CICP, as specified in the MIAF
+ // standard (ISO/IEC 23000-22:2019), section 7.3.6.4:
+ //
+ // "The colour information property takes precedence over any colour information in the image
+ // bitstream, i.e. if the property is present, colour information in the bitstream shall be ignored."
} avifDecoderData;
static avifDecoderData * avifDecoderDataCreate()
@@ -577,31 +588,26 @@
}
avifTile * firstTile = &data->tiles.tile[firstTileIndex];
- unsigned int tileWidth = firstTile->image->width;
- unsigned int tileHeight = firstTile->image->height;
- unsigned int tileDepth = firstTile->image->depth;
- avifPixelFormat tileFormat = firstTile->image->yuvFormat;
+ avifBool firstTileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
- avifProfileFormat tileProfile = firstTile->image->profileFormat;
- avifNclxColorProfile * tileNCLX = &firstTile->image->nclx;
- avifRange tileRange = firstTile->image->yuvRange;
- avifBool tileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
-
+ // Check for tile consistency: All tiles in a grid image should match in the properties checked below.
for (unsigned int i = 1; i < tileCount; ++i) {
avifTile * tile = &data->tiles.tile[firstTileIndex + i];
avifBool uvPresent = (tile->image->yuvPlanes[AVIF_CHAN_U] && tile->image->yuvPlanes[AVIF_CHAN_V]);
- if ((tile->image->width != tileWidth) || (tile->image->height != tileHeight) || (tile->image->depth != tileDepth) ||
- (tile->image->yuvFormat != tileFormat) || (tile->image->yuvRange != tileRange) || (uvPresent != tileUVPresent) ||
- ((tileProfile == AVIF_PROFILE_FORMAT_NCLX) &&
- ((tile->image->profileFormat != tileProfile) || (tile->image->nclx.colourPrimaries != tileNCLX->colourPrimaries) ||
- (tile->image->nclx.transferCharacteristics != tileNCLX->transferCharacteristics) ||
- (tile->image->nclx.matrixCoefficients != tileNCLX->matrixCoefficients) || (tile->image->nclx.range != tileNCLX->range)))) {
+ if ((tile->image->width != firstTile->image->width) || (tile->image->height != firstTile->image->height) ||
+ (tile->image->depth != firstTile->image->depth) || (tile->image->yuvFormat != firstTile->image->yuvFormat) ||
+ (tile->image->yuvRange != firstTile->image->yuvRange) || (uvPresent != firstTileUVPresent) ||
+ ((tile->image->colorPrimaries != firstTile->image->colorPrimaries) ||
+ (tile->image->transferCharacteristics != firstTile->image->transferCharacteristics) ||
+ (tile->image->matrixCoefficients != firstTile->image->matrixCoefficients))) {
return AVIF_FALSE;
}
}
- if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) || (dstImage->depth != tileDepth) ||
- (dstImage->yuvFormat != tileFormat)) {
+ // Lazily populate dstImage with the new frame's properties. If we're decoding alpha,
+ // these values must already match.
+ if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) ||
+ (dstImage->depth != firstTile->image->depth) || (dstImage->yuvFormat != firstTile->image->yuvFormat)) {
if (alpha) {
// Alpha doesn't match size, just bail out
return AVIF_FALSE;
@@ -610,18 +616,21 @@
avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
dstImage->width = grid->outputWidth;
dstImage->height = grid->outputHeight;
- dstImage->depth = tileDepth;
- dstImage->yuvFormat = tileFormat;
- dstImage->yuvRange = tileRange;
- if ((dstImage->profileFormat == AVIF_PROFILE_FORMAT_NONE) && (tileProfile == AVIF_PROFILE_FORMAT_NCLX)) {
- avifImageSetProfileNCLX(dstImage, tileNCLX);
+ dstImage->depth = firstTile->image->depth;
+ dstImage->yuvFormat = firstTile->image->yuvFormat;
+ dstImage->yuvRange = firstTile->image->yuvRange;
+ if (!data->cicpSet) {
+ data->cicpSet = AVIF_TRUE;
+ dstImage->colorPrimaries = firstTile->image->colorPrimaries;
+ dstImage->transferCharacteristics = firstTile->image->transferCharacteristics;
+ dstImage->matrixCoefficients = firstTile->image->matrixCoefficients;
}
}
avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
avifPixelFormatInfo formatInfo;
- avifGetPixelFormatInfo(tileFormat, &formatInfo);
+ avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo);
unsigned int tileIndex = firstTileIndex;
size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1;
@@ -629,21 +638,21 @@
for (unsigned int colIndex = 0; colIndex < grid->columns; ++colIndex, ++tileIndex) {
avifTile * tile = &data->tiles.tile[tileIndex];
- unsigned int widthToCopy = tileWidth;
- unsigned int maxX = tileWidth * (colIndex + 1);
+ unsigned int widthToCopy = firstTile->image->width;
+ unsigned int maxX = firstTile->image->width * (colIndex + 1);
if (maxX > grid->outputWidth) {
widthToCopy -= maxX - grid->outputWidth;
}
- unsigned int heightToCopy = tileHeight;
- unsigned int maxY = tileHeight * (rowIndex + 1);
+ unsigned int heightToCopy = firstTile->image->height;
+ unsigned int maxY = firstTile->image->height * (rowIndex + 1);
if (maxY > grid->outputHeight) {
heightToCopy -= maxY - grid->outputHeight;
}
// Y and A channels
- size_t yaColOffset = colIndex * tileWidth;
- size_t yaRowOffset = rowIndex * tileHeight;
+ size_t yaColOffset = colIndex * firstTile->image->width;
+ size_t yaRowOffset = rowIndex * firstTile->image->height;
size_t yaRowBytes = widthToCopy * pixelBytes;
if (alpha) {
@@ -662,7 +671,7 @@
memcpy(dst, src, yaRowBytes);
}
- if (!tileUVPresent) {
+ if (!firstTileUVPresent) {
continue;
}
@@ -857,31 +866,32 @@
{
BEGIN_STREAM(s, raw, rawLen);
- data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
+ data->properties.prop[propertyIndex].colr.hasICC = AVIF_FALSE;
+ data->properties.prop[propertyIndex].colr.hasNCLX = AVIF_FALSE;
- uint8_t colourType[4]; // unsigned int(32) colour_type;
- CHECK(avifROStreamRead(&s, colourType, 4));
- if (!memcmp(colourType, "rICC", 4) || !memcmp(colourType, "prof", 4)) {
- data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
+ uint8_t colorType[4]; // unsigned int(32) colour_type;
+ CHECK(avifROStreamRead(&s, colorType, 4));
+ if (!memcmp(colorType, "rICC", 4) || !memcmp(colorType, "prof", 4)) {
+ data->properties.prop[propertyIndex].colr.hasICC = AVIF_TRUE;
data->properties.prop[propertyIndex].colr.icc = avifROStreamCurrent(&s);
data->properties.prop[propertyIndex].colr.iccSize = avifROStreamRemainingBytes(&s);
- } else if (!memcmp(colourType, "nclx", 4)) {
+ } else if (!memcmp(colorType, "nclx", 4)) {
uint16_t tmp16;
// unsigned int(16) colour_primaries;
CHECK(avifROStreamReadU16(&s, &tmp16));
- data->properties.prop[propertyIndex].colr.nclx.colourPrimaries = (avifNclxColourPrimaries)tmp16;
+ data->properties.prop[propertyIndex].colr.colorPrimaries = (avifColorPrimaries)tmp16;
// unsigned int(16) transfer_characteristics;
CHECK(avifROStreamReadU16(&s, &tmp16));
- data->properties.prop[propertyIndex].colr.nclx.transferCharacteristics = (avifNclxTransferCharacteristics)tmp16;
+ data->properties.prop[propertyIndex].colr.transferCharacteristics = (avifTransferCharacteristics)tmp16;
// unsigned int(16) matrix_coefficients;
CHECK(avifROStreamReadU16(&s, &tmp16));
- data->properties.prop[propertyIndex].colr.nclx.matrixCoefficients = (avifNclxMatrixCoefficients)tmp16;
+ data->properties.prop[propertyIndex].colr.matrixCoefficients = (avifMatrixCoefficients)tmp16;
// unsigned int(1) full_range_flag;
// unsigned int(7) reserved = 0;
uint8_t tmp8;
CHECK(avifROStreamRead(&s, &tmp8, 1));
- data->properties.prop[propertyIndex].colr.nclx.range = (avifRange)(tmp8 & 0x80);
- data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NCLX;
+ data->properties.prop[propertyIndex].colr.range = (avifRange)(tmp8 & 0x80);
+ data->properties.prop[propertyIndex].colr.hasNCLX = AVIF_TRUE;
}
return AVIF_TRUE;
}
@@ -1932,10 +1942,11 @@
avifDecoderDataClearTiles(data);
// Prepare / cleanup decoded image state
- if (!decoder->image) {
- decoder->image = avifImageCreateEmpty();
+ if (decoder->image) {
+ avifImageDestroy(decoder->image);
}
- decoder->image->transformFlags = AVIF_TRANSFORM_NONE;
+ decoder->image = avifImageCreateEmpty();
+ data->cicpSet = AVIF_FALSE;
memset(&decoder->ioStats, 0, sizeof(decoder->ioStats));
@@ -2200,10 +2211,14 @@
}
if (colorOBUItem->colrPresent) {
- if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_ICC) {
+ if (colorOBUItem->colr.hasICC) {
avifImageSetProfileICC(decoder->image, colorOBUItem->colr.icc, colorOBUItem->colr.iccSize);
- } else if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_NCLX) {
- avifImageSetProfileNCLX(decoder->image, &colorOBUItem->colr.nclx);
+ } else if (colorOBUItem->colr.hasNCLX) {
+ data->cicpSet = AVIF_TRUE;
+ decoder->image->colorPrimaries = colorOBUItem->colr.colorPrimaries;
+ decoder->image->transferCharacteristics = colorOBUItem->colr.transferCharacteristics;
+ decoder->image->matrixCoefficients = colorOBUItem->colr.matrixCoefficients;
+ decoder->image->yuvRange = colorOBUItem->colr.range;
}
}
@@ -2309,8 +2324,11 @@
decoder->image->height = srcColor->height;
decoder->image->depth = srcColor->depth;
- if (decoder->image->profileFormat == AVIF_PROFILE_FORMAT_NONE && srcColor->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- avifImageSetProfileNCLX(decoder->image, &srcColor->nclx);
+ if (!decoder->data->cicpSet) {
+ decoder->data->cicpSet = AVIF_TRUE;
+ decoder->image->colorPrimaries = srcColor->colorPrimaries;
+ decoder->image->transferCharacteristics = srcColor->transferCharacteristics;
+ decoder->image->matrixCoefficients = srcColor->matrixCoefficients;
}
}
diff --git a/src/reformat.c b/src/reformat.c
index ff6bbcb..616f6a1 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -29,16 +29,14 @@
avifCalcYUVCoefficients(image, &state->kr, &state->kg, &state->kb);
state->mode = AVIF_REFORMAT_MODE_YUV_COEFFICIENTS;
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- if (image->nclx.matrixCoefficients == AVIF_NCLX_MATRIX_COEFFICIENTS_IDENTITY) {
- state->mode = AVIF_REFORMAT_MODE_IDENTITY;
- }
+ if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) {
+ state->mode = AVIF_REFORMAT_MODE_IDENTITY;
+ }
- if (state->mode != AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) {
- state->kr = 0.0f;
- state->kg = 0.0f;
- state->kb = 0.0f;
- }
+ if (state->mode != AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) {
+ state->kr = 0.0f;
+ state->kg = 0.0f;
+ state->kb = 0.0f;
}
state->yuvChannelBytes = (image->depth > 8) ? 2 : 1;
diff --git a/src/write.c b/src/write.c
index 205df62..37f9a9d 100644
--- a/src/write.c
+++ b/src/write.c
@@ -397,22 +397,22 @@
} else {
// Color specific properties
- if (item->image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
- avifBoxMarker colr = avifRWStreamWriteBox(&s, "colr", -1, 0);
- avifRWStreamWriteChars(&s, "nclx", 4); // unsigned int(32) colour_type;
- avifRWStreamWriteU16(&s, (uint16_t)item->image->nclx.colourPrimaries); // unsigned int(16) colour_primaries;
- avifRWStreamWriteU16(&s, (uint16_t)item->image->nclx.transferCharacteristics); // unsigned int(16) transfer_characteristics;
- avifRWStreamWriteU16(&s, (uint16_t)item->image->nclx.matrixCoefficients); // unsigned int(16) matrix_coefficients;
- avifRWStreamWriteU8(&s, item->image->nclx.range & 0x80); // unsigned int(1) full_range_flag;
- // unsigned int(7) reserved = 0;
- avifRWStreamFinishBox(&s, colr);
- ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
- } else if ((item->image->profileFormat == AVIF_PROFILE_FORMAT_ICC) && item->image->icc.data && (item->image->icc.size > 0)) {
+ if (item->image->icc.data && (item->image->icc.size > 0)) {
avifBoxMarker colr = avifRWStreamWriteBox(&s, "colr", -1, 0);
avifRWStreamWriteChars(&s, "prof", 4); // unsigned int(32) colour_type;
avifRWStreamWrite(&s, item->image->icc.data, item->image->icc.size);
avifRWStreamFinishBox(&s, colr);
ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
+ } else {
+ avifBoxMarker colr = avifRWStreamWriteBox(&s, "colr", -1, 0);
+ avifRWStreamWriteChars(&s, "nclx", 4); // unsigned int(32) colour_type;
+ avifRWStreamWriteU16(&s, (uint16_t)item->image->colorPrimaries); // unsigned int(16) colour_primaries;
+ avifRWStreamWriteU16(&s, (uint16_t)item->image->transferCharacteristics); // unsigned int(16) transfer_characteristics;
+ avifRWStreamWriteU16(&s, (uint16_t)item->image->matrixCoefficients); // unsigned int(16) matrix_coefficients;
+ avifRWStreamWriteU8(&s, item->image->yuvRange & 0x80); // unsigned int(1) full_range_flag;
+ // unsigned int(7) reserved = 0;
+ avifRWStreamFinishBox(&s, colr);
+ ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
}
// Write (Optional) Transformations