Add ICC profile plumbing (via the colr box). This still needs YUV coeff harvesting to be correct
diff --git a/src/avif.c b/src/avif.c
index 96bcf71..60dab89 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -58,6 +58,17 @@
avifFree(image);
}
+void avifImageSetProfileICC(avifImage * image, uint8_t * icc, size_t iccSize)
+{
+ if (iccSize) {
+ image->profileFormat = AVIF_PROFILE_FORMAT_ICC;
+ avifRawDataSet(&image->icc, icc, iccSize);
+ } else {
+ image->profileFormat = AVIF_PROFILE_FORMAT_NONE;
+ avifRawDataFree(&image->icc);
+ }
+}
+
void avifImageAllocatePlanes(avifImage * image, uint32_t planes)
{
int channelSize = avifImageUsesU16(image) ? 2 : 1;
diff --git a/src/read.c b/src/read.c
index bea1bf0..ca95083 100644
--- a/src/read.c
+++ b/src/read.c
@@ -37,16 +37,25 @@
char auxType[AUXTYPE_SIZE];
} avifAuxiliaryType;
+typedef struct avifColourInformationBox
+{
+ avifProfileFormat format;
+ uint8_t * icc;
+ size_t iccSize;
+} avifColourInformationBox;
+
typedef struct avifItem
{
int id;
uint8_t type[4];
uint32_t offset;
uint32_t size;
- avifBool ispe_seen;
+ avifBool ispePresent;
avifImageSpatialExtents ispe;
- avifBool auxC_seen;
+ avifBool auxCPresent;
avifAuxiliaryType auxC;
+ avifBool colrPresent;
+ avifColourInformationBox colr;
int thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
int auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
} avifItem;
@@ -56,6 +65,7 @@
uint8_t type[4];
avifImageSpatialExtents ispe;
avifAuxiliaryType auxC;
+ avifColourInformationBox colr;
} avifProperty;
typedef struct avifFileType
@@ -184,6 +194,21 @@
return AVIF_TRUE;
}
+static avifBool avifParseColourInformationBox(avifData * data, uint8_t * raw, size_t rawLen, int propertyIndex)
+{
+ BEGIN_STREAM(s, raw, rawLen);
+ uint8_t colourType[4]; // unsigned int(32) colour_type;
+ CHECK(avifStreamRead(&s, colourType, 4));
+ if (!memcmp(colourType, "rICC", 4) || !memcmp(colourType, "prof", 4)) {
+ data->properties[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
+ data->properties[propertyIndex].colr.icc = avifStreamCurrent(&s);
+ data->properties[propertyIndex].colr.iccSize = avifStreamRemainingBytes(&s);
+ } else {
+ data->properties[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
+ }
+ return AVIF_TRUE;
+}
+
static avifBool avifParseItemPropertyContainerBox(avifData * data, uint8_t * raw, size_t rawLen)
{
BEGIN_STREAM(s, raw, rawLen);
@@ -207,6 +232,9 @@
if (!memcmp(header.type, "auxC", 4)) {
CHECK(avifParseAuxiliaryTypeProperty(data, avifStreamCurrent(&s), header.size, propertyIndex));
}
+ if (!memcmp(header.type, "colr", 4)) {
+ CHECK(avifParseColourInformationBox(data, avifStreamCurrent(&s), header.size, propertyIndex));
+ }
CHECK(avifStreamSkip(&s, header.size));
}
@@ -267,11 +295,14 @@
// Associate property with item
avifProperty * prop = &data->properties[propertyIndex];
if (!memcmp(prop->type, "ispe", 4)) {
- data->items[itemIndex].ispe_seen = AVIF_TRUE;
+ data->items[itemIndex].ispePresent = AVIF_TRUE;
memcpy(&data->items[itemIndex].ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
} else if (!memcmp(prop->type, "auxC", 4)) {
- data->items[itemIndex].auxC_seen = AVIF_TRUE;
+ data->items[itemIndex].auxCPresent = AVIF_TRUE;
memcpy(&data->items[itemIndex].auxC, &prop->auxC, sizeof(avifAuxiliaryType));
+ } else if (!memcmp(prop->type, "colr", 4)) {
+ data->items[itemIndex].colrPresent = AVIF_TRUE;
+ memcpy(&data->items[itemIndex].colr, &prop->colr, sizeof(avifColourInformationBox));
}
}
}
@@ -638,8 +669,8 @@
}
if (
- (colorOBUItem && aomColorImage && colorOBUItem->ispe_seen && ((colorOBUItem->ispe.width != aomColorImage->d_w) || (colorOBUItem->ispe.height != aomColorImage->d_h))) ||
- (alphaOBUItem && aomAlphaImage && alphaOBUItem->ispe_seen && ((alphaOBUItem->ispe.width != aomAlphaImage->d_w) || (alphaOBUItem->ispe.height != aomAlphaImage->d_h)))
+ (colorOBUItem && aomColorImage && colorOBUItem->ispePresent && ((colorOBUItem->ispe.width != aomColorImage->d_w) || (colorOBUItem->ispe.height != aomColorImage->d_h))) ||
+ (alphaOBUItem && aomAlphaImage && alphaOBUItem->ispePresent && ((alphaOBUItem->ispe.width != aomAlphaImage->d_w) || (alphaOBUItem->ispe.height != aomAlphaImage->d_h)))
)
{
aom_codec_destroy(&colorDecoder);
@@ -649,6 +680,10 @@
return AVIF_RESULT_ISPE_SIZE_MISMATCH;
}
+ if (colorOBUItem->colrPresent && (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_ICC)) {
+ avifImageSetProfileICC(image, colorOBUItem->colr.icc, colorOBUItem->colr.iccSize);
+ }
+
avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE;
switch (aomColorImage->fmt) {
case AOM_IMG_FMT_I420:
diff --git a/src/write.c b/src/write.c
index 675b6f4..e741a6b 100644
--- a/src/write.c
+++ b/src/write.c
@@ -205,6 +205,15 @@
ipmaPush(&ipmaColor, ipcoIndex); // ipma is 1-indexed, doing this afterwards is correct
ipmaPush(&ipmaAlpha, ipcoIndex); // Alpha shares the ispe prop
+ if ((image->profileFormat == AVIF_PROFILE_FORMAT_ICC) && image->icc.data && (image->icc.data > 0)) {
+ avifBoxMarker colr = avifStreamWriteBox(&s, "colr", -1, 0);
+ avifStreamWrite(&s, "prof", 4); // unsigned int(32) colour_type;
+ avifStreamWrite(&s, image->icc.data, image->icc.size);
+ avifStreamFinishBox(&s, colr);
+ ++ipcoIndex;
+ ipmaPush(&ipmaColor, ipcoIndex);
+ }
+
avifBoxMarker pixiC = avifStreamWriteBox(&s, "pixi", 0, 0);
avifStreamWriteU8(&s, 3); // unsigned int (8) num_channels;
avifStreamWriteU8(&s, image->depth); // unsigned int (8) bits_per_channel;