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