Refactor write.c to use a similar Data/Item design as read.c
* Rename read.c's avifData -> avifDecoderData and all associated funcs (disambiguity)
* Rename read.c's struct avifItem -> struct avifDecoderItem (disambiguity)
* Rename read.c's avifDataNewTile() -> avifDataCreateTile() (consistency)
diff --git a/src/write.c b/src/write.c
index c5a7d33..d50436d 100644
--- a/src/write.c
+++ b/src/write.c
@@ -27,6 +27,76 @@
static void fillConfigBox(avifCodec * codec, avifImage * image, avifBool alpha);
static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg);
+// ---------------------------------------------------------------------------
+// avifEncoderItem
+
+// one "item" worth for encoder
+typedef struct avifEncoderItem
+{
+ uint16_t id;
+ uint8_t type[4];
+ avifImage * image; // avifImage* to use when encoding or populating ipma for this item (unowned)
+ avifCodec * codec; // only present on type==av01
+ avifRWData content; // OBU data on av01, metadata payload for Exif/XMP
+ avifBool alpha;
+
+ const char * infeName;
+ size_t infeNameSize;
+ const char * infeContentType;
+ size_t infeContentTypeSize;
+ size_t infeOffsetOffset; // Stream offset where infe offset was written, so it can be properly set after mdat is written
+
+ uint16_t irefToID; // if non-zero, make an iref from this id -> irefToID
+ const char * irefType;
+
+ struct ipmaArray ipma;
+} avifEncoderItem;
+AVIF_ARRAY_DECLARE(avifEncoderItemArray, avifEncoderItem, item);
+
+// ---------------------------------------------------------------------------
+// avifEncoderData
+
+typedef struct avifEncoderData
+{
+ avifEncoderItemArray items;
+ uint16_t lastItemID;
+ uint16_t primaryItemID;
+} avifEncoderData;
+
+static avifEncoderData * avifEncoderDataCreate()
+{
+ avifEncoderData * data = (avifEncoderData *)avifAlloc(sizeof(avifEncoderData));
+ memset(data, 0, sizeof(avifEncoderData));
+ avifArrayCreate(&data->items, sizeof(avifEncoderItem), 8);
+ return data;
+}
+
+static avifEncoderItem * avifEncoderDataCreateItem(avifEncoderData * data, const char * type, const char * infeName, size_t infeNameSize)
+{
+ avifEncoderItem * item = (avifEncoderItem *)avifArrayPushPtr(&data->items);
+ ++data->lastItemID;
+ item->id = data->lastItemID;
+ memcpy(item->type, type, sizeof(item->type));
+ item->infeName = infeName;
+ item->infeNameSize = infeNameSize;
+ return item;
+}
+
+static void avifEncoderDataDestroy(avifEncoderData * data)
+{
+ for (uint32_t i = 0; i < data->items.count; ++i) {
+ avifEncoderItem * item = &data->items.item[i];
+ if (item->codec) {
+ avifCodecDestroy(item->codec);
+ }
+ avifRWDataFree(&item->content);
+ }
+ avifArrayDestroy(&data->items);
+ avifFree(data);
+}
+
+// ---------------------------------------------------------------------------
+
avifEncoder * avifEncoderCreate(void)
{
avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder));
@@ -39,11 +109,13 @@
encoder->tileRowsLog2 = 0;
encoder->tileColsLog2 = 0;
encoder->speed = AVIF_SPEED_DEFAULT;
+ encoder->data = avifEncoderDataCreate();
return encoder;
}
void avifEncoderDestroy(avifEncoder * encoder)
{
+ avifEncoderDataDestroy(encoder->data);
avifFree(encoder);
}
@@ -54,59 +126,86 @@
}
avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
- avifRWData colorOBU = AVIF_DATA_EMPTY;
- avifRWData alphaOBU = AVIF_DATA_EMPTY;
- avifCodec * codec[AVIF_CODEC_PLANES_COUNT];
- codec[AVIF_CODEC_PLANES_COLOR] = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
- if (!codec[AVIF_CODEC_PLANES_COLOR]) {
+ avifEncoderItem * colorItem = avifEncoderDataCreateItem(encoder->data, "av01", "Color", 6);
+ colorItem->image = image;
+ colorItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
+ if (!colorItem->codec) {
// Just bail out early, we're not surviving this function without an encoder compiled in
return AVIF_RESULT_NO_CODEC_AVAILABLE;
}
+ encoder->data->primaryItemID = colorItem->id;
avifBool imageIsOpaque = avifImageIsOpaque(image);
- if (imageIsOpaque) {
- codec[AVIF_CODEC_PLANES_ALPHA] = NULL;
- } else {
- codec[AVIF_CODEC_PLANES_ALPHA] = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
- if (!codec[AVIF_CODEC_PLANES_ALPHA]) {
+ if (!imageIsOpaque) {
+ avifEncoderItem * alphaItem = avifEncoderDataCreateItem(encoder->data, "av01", "Alpha", 6);
+ alphaItem->image = image;
+ alphaItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
+ if (!alphaItem->codec) {
return AVIF_RESULT_NO_CODEC_AVAILABLE;
}
+ alphaItem->alpha = AVIF_TRUE;
+ alphaItem->irefToID = encoder->data->primaryItemID;
+ alphaItem->irefType = "auxl";
}
// -----------------------------------------------------------------------
- // Validate Exif payload (if any) and find TIFF header offset
+ // Create metadata items (Exif, XMP)
- uint32_t exifTiffHeaderOffset = 0;
if (image->exif.size > 0) {
- if (image->exif.size < 4) {
- // Can't even fit the TIFF header, something is wrong
- return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
- }
-
- const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
- const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
- for (; exifTiffHeaderOffset < (image->exif.size - 4); ++exifTiffHeaderOffset) {
- if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderBE, sizeof(tiffHeaderBE))) {
- break;
+ // Validate Exif payload (if any) and find TIFF header offset
+ uint32_t exifTiffHeaderOffset = 0;
+ if (image->exif.size > 0) {
+ if (image->exif.size < 4) {
+ // Can't even fit the TIFF header, something is wrong
+ return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
}
- if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderLE, sizeof(tiffHeaderLE))) {
- break;
+
+ const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
+ const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
+ for (; exifTiffHeaderOffset < (image->exif.size - 4); ++exifTiffHeaderOffset) {
+ if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderBE, sizeof(tiffHeaderBE))) {
+ break;
+ }
+ if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderLE, sizeof(tiffHeaderLE))) {
+ break;
+ }
+ }
+
+ if (exifTiffHeaderOffset >= image->exif.size - 4) {
+ // Couldn't find the TIFF header
+ return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
}
}
- if (exifTiffHeaderOffset >= image->exif.size - 4) {
- // Couldn't find the TIFF header
- return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
- }
+ avifEncoderItem * exifItem = avifEncoderDataCreateItem(encoder->data, "Exif", "Exif", 5);
+ exifItem->irefToID = encoder->data->primaryItemID;
+ exifItem->irefType = "cdsc";
+
+ avifRWDataRealloc(&exifItem->content, sizeof(uint32_t) + image->exif.size);
+ exifTiffHeaderOffset = avifHTONL(exifTiffHeaderOffset);
+ memcpy(exifItem->content.data, &exifTiffHeaderOffset, sizeof(uint32_t));
+ memcpy(exifItem->content.data + sizeof(uint32_t), image->exif.data, image->exif.size);
+ }
+
+ if (image->xmp.size > 0) {
+ avifEncoderItem * xmpItem = avifEncoderDataCreateItem(encoder->data, "mime", "XMP", 4);
+ xmpItem->irefToID = encoder->data->primaryItemID;
+ xmpItem->irefType = "cdsc";
+
+ xmpItem->infeContentType = xmpContentType;
+ xmpItem->infeContentTypeSize = xmpContentTypeSize;
+ avifRWDataSet(&xmpItem->content, image->xmp.data, image->xmp.size);
}
// -----------------------------------------------------------------------
// Pre-fill config boxes based on image (codec can query/update later)
- fillConfigBox(codec[AVIF_CODEC_PLANES_COLOR], image, AVIF_FALSE);
- if (codec[AVIF_CODEC_PLANES_ALPHA]) {
- fillConfigBox(codec[AVIF_CODEC_PLANES_ALPHA], image, AVIF_TRUE);
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->codec && item->image) {
+ fillConfigBox(item->codec, item->image, item->alpha);
+ }
}
// -----------------------------------------------------------------------
@@ -131,23 +230,23 @@
// -----------------------------------------------------------------------
// Encode AV1 OBUs
- if (!codec[AVIF_CODEC_PLANES_COLOR]->encodeImage(codec[AVIF_CODEC_PLANES_COLOR], image, encoder, &colorOBU, AVIF_FALSE)) {
- result = AVIF_RESULT_ENCODE_COLOR_FAILED;
- goto writeCleanup;
- }
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->codec && item->image) {
+ if (!item->codec->encodeImage(item->codec, item->image, encoder, &item->content, item->alpha)) {
+ result = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
+ goto writeCleanup;
+ }
- if (!imageIsOpaque) {
- if (!codec[AVIF_CODEC_PLANES_ALPHA]->encodeImage(codec[AVIF_CODEC_PLANES_ALPHA], image, encoder, &alphaOBU, AVIF_TRUE)) {
- result = AVIF_RESULT_ENCODE_ALPHA_FAILED;
- goto writeCleanup;
+ // TODO: rethink this if/when image grid encoding support is added
+ if (item->alpha) {
+ encoder->ioStats.alphaOBUSize = item->content.size;
+ } else {
+ encoder->ioStats.colorOBUSize = item->content.size;
+ }
}
}
- // TODO: consider collapsing all items into local structs for iteration / code sharing
- avifBool hasAlpha = (alphaOBU.size > 0);
- avifBool hasExif = (image->exif.size > 0);
- avifBool hasXMP = (image->xmp.size > 0);
-
// -----------------------------------------------------------------------
// Write ftyp
@@ -184,94 +283,31 @@
// -----------------------------------------------------------------------
// Write pitm
- avifRWStreamWriteBox(&s, "pitm", 0, sizeof(uint16_t));
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) item_ID;
-
- // -----------------------------------------------------------------------
- // Calculate item IDs and counts
-
- uint16_t itemCount = 1;
- uint16_t colorItemID = 1;
- uint16_t nextItemID = 2;
- uint16_t alphaItemID = 0;
- uint16_t exifItemID = 0;
- uint16_t xmpItemID = 0;
- if (hasAlpha) {
- ++itemCount;
- alphaItemID = nextItemID;
- ++nextItemID;
- }
- if (hasExif) {
- ++itemCount;
- exifItemID = nextItemID;
- ++nextItemID;
- }
- if (hasXMP) {
- ++itemCount;
- xmpItemID = nextItemID;
- ++nextItemID;
+ if (encoder->data->primaryItemID != 0) {
+ avifRWStreamWriteBox(&s, "pitm", 0, sizeof(uint16_t));
+ avifRWStreamWriteU16(&s, encoder->data->primaryItemID); // unsigned int(16) item_ID;
}
// -----------------------------------------------------------------------
// Write iloc
- // Remember where we want to store the offsets to the mdat OBU offsets to adjust them later.
- // These are named as such because they are remembering the stream offset where we will write an offset later.
- size_t colorOBUOffsetOffset = 0;
- size_t alphaOBUOffsetOffset = 0;
- size_t exifOffsetOffset = 0;
- size_t xmpOffsetOffset = 0;
-
avifBoxMarker iloc = avifRWStreamWriteBox(&s, "iloc", 0, 0);
- // iloc header
- uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size;
- // unsigned int(4) length_size;
- avifRWStreamWrite(&s, &offsetSizeAndLengthSize, 1); //
- avifRWStreamWriteZeros(&s, 1); // unsigned int(4) base_offset_size;
- // unsigned int(4) reserved;
- avifRWStreamWriteU16(&s, itemCount); // unsigned int(16) item_count;
+ uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size;
+ // unsigned int(4) length_size;
+ avifRWStreamWrite(&s, &offsetSizeAndLengthSize, 1); //
+ avifRWStreamWriteZeros(&s, 1); // unsigned int(4) base_offset_size;
+ // unsigned int(4) reserved;
+ avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) item_count;
- // Item ID #1 (Color OBU)
- avifRWStreamWriteU16(&s, colorItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
- colorOBUOffsetOffset = avifRWStreamOffset(&s); //
- avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
- avifRWStreamWriteU32(&s, (uint32_t)colorOBU.size); // unsigned int(length_size*8) extent_length;
-
- if (hasAlpha) {
- avifRWStreamWriteU16(&s, alphaItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
- alphaOBUOffsetOffset = avifRWStreamOffset(&s); //
- avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
- avifRWStreamWriteU32(&s, (uint32_t)alphaOBU.size); // unsigned int(length_size*8) extent_length;
- }
-
- if (hasExif) {
- // From ISO/IEC 23008-12:2017
- // :: aligned(8) class ExifDataBlock() {
- // :: unsigned int(32) exif_tiff_header_offset;
- // :: unsigned int(8) exif_payload[];
- // :: }
- uint32_t exifDataBlockSize = (uint32_t)(sizeof(uint32_t) + image->exif.size);
-
- avifRWStreamWriteU16(&s, exifItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
- exifOffsetOffset = avifRWStreamOffset(&s); //
- avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
- avifRWStreamWriteU32(&s, exifDataBlockSize); // unsigned int(length_size*8) extent_length;
- }
-
- if (hasXMP) {
- avifRWStreamWriteU16(&s, xmpItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
- xmpOffsetOffset = avifRWStreamOffset(&s); //
- avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
- avifRWStreamWriteU32(&s, (uint32_t)image->xmp.size); // unsigned int(length_size*8) extent_length;
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
+ avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
+ avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
+ item->infeOffsetOffset = avifRWStreamOffset(&s); //
+ avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
+ avifRWStreamWriteU32(&s, (uint32_t)item->content.size); // unsigned int(length_size*8) extent_length;
}
avifRWStreamFinishBox(&s, iloc);
@@ -280,216 +316,168 @@
// Write iinf
avifBoxMarker iinf = avifRWStreamWriteBox(&s, "iinf", 0, 0);
- avifRWStreamWriteU16(&s, itemCount); // unsigned int(16) entry_count;
+ avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) entry_count;
- avifBoxMarker infeImage = avifRWStreamWriteBox(&s, "infe", 2, 0);
- avifRWStreamWriteU16(&s, colorItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
- avifRWStreamWriteChars(&s, "av01", 4); // unsigned int(32) item_type;
- avifRWStreamWriteChars(&s, "Color", 6); // string item_name; (writing null terminator)
- avifRWStreamFinishBox(&s, infeImage);
- if (hasAlpha) {
- avifBoxMarker infeAlpha = avifRWStreamWriteBox(&s, "infe", 2, 0);
- avifRWStreamWriteU16(&s, alphaItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
- avifRWStreamWriteChars(&s, "av01", 4); // unsigned int(32) item_type;
- avifRWStreamWriteChars(&s, "Alpha", 6); // string item_name; (writing null terminator)
- avifRWStreamFinishBox(&s, infeAlpha);
- }
- if (hasExif) {
- avifBoxMarker infeExif = avifRWStreamWriteBox(&s, "infe", 2, 0);
- avifRWStreamWriteU16(&s, exifItemID); // unsigned int(16) item_ID;
- avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
- avifRWStreamWriteChars(&s, "Exif", 4); // unsigned int(32) item_type;
- avifRWStreamWriteChars(&s, "Exif", 5); // string item_name; (writing null terminator)
- avifRWStreamFinishBox(&s, infeExif);
- }
- if (hasXMP) {
- avifBoxMarker infeXMP = avifRWStreamWriteBox(&s, "infe", 2, 0);
- avifRWStreamWriteU16(&s, xmpItemID); // unsigned int(16) item_ID;
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+
+ avifBoxMarker infe = avifRWStreamWriteBox(&s, "infe", 2, 0);
+ avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
- avifRWStreamWriteChars(&s, "mime", 4); // unsigned int(32) item_type;
- avifRWStreamWriteChars(&s, "XMP", 4); // string item_name; (writing null terminator)
- avifRWStreamWriteChars(&s, xmpContentType, xmpContentTypeSize); // string content_type; (writing null terminator)
- avifRWStreamFinishBox(&s, infeXMP);
+ avifRWStreamWrite(&s, item->type, 4); // unsigned int(32) item_type;
+ avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator)
+ if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator)
+ avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize);
+ }
+ avifRWStreamFinishBox(&s, infe);
}
+
avifRWStreamFinishBox(&s, iinf);
// -----------------------------------------------------------------------
- // Write iref (auxl) for alpha, if any
+ // Write iref boxes
- if (hasAlpha) {
- avifBoxMarker iref = avifRWStreamWriteBox(&s, "iref", 0, 0);
- avifBoxMarker auxl = avifRWStreamWriteBox(&s, "auxl", -1, 0);
- avifRWStreamWriteU16(&s, alphaItemID); // unsigned int(16) from_item_ID;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
- avifRWStreamWriteU16(&s, colorItemID); // unsigned int(16) to_item_ID;
- avifRWStreamFinishBox(&s, auxl);
- avifRWStreamFinishBox(&s, iref);
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->irefToID != 0) {
+ avifBoxMarker iref = avifRWStreamWriteBox(&s, "iref", 0, 0);
+ avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, -1, 0);
+ avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID;
+ avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
+ avifRWStreamWriteU16(&s, item->irefToID); // unsigned int(16) to_item_ID;
+ avifRWStreamFinishBox(&s, refType);
+ avifRWStreamFinishBox(&s, iref);
+ }
}
// -----------------------------------------------------------------------
- // Write iref (cdsc) for Exif, if any
-
- if (hasExif) {
- avifBoxMarker iref = avifRWStreamWriteBox(&s, "iref", 0, 0);
- avifBoxMarker cdsc = avifRWStreamWriteBox(&s, "cdsc", -1, 0);
- avifRWStreamWriteU16(&s, exifItemID); // unsigned int(16) from_item_ID;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
- avifRWStreamWriteU16(&s, colorItemID); // unsigned int(16) to_item_ID;
- avifRWStreamFinishBox(&s, cdsc);
- avifRWStreamFinishBox(&s, iref);
- }
-
- // -----------------------------------------------------------------------
- // Write iref (cdsc) for XMP, if any
-
- if (hasXMP) {
- avifBoxMarker iref = avifRWStreamWriteBox(&s, "iref", 0, 0);
- avifBoxMarker cdsc = avifRWStreamWriteBox(&s, "cdsc", -1, 0);
- avifRWStreamWriteU16(&s, xmpItemID); // unsigned int(16) from_item_ID;
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
- avifRWStreamWriteU16(&s, colorItemID); // unsigned int(16) to_item_ID;
- avifRWStreamFinishBox(&s, cdsc);
- avifRWStreamFinishBox(&s, iref);
- }
-
- // -----------------------------------------------------------------------
- // Write iprp->ipco->ispe
+ // Write iprp -> ipco/ipma
avifBoxMarker iprp = avifRWStreamWriteBox(&s, "iprp", -1, 0);
- {
- uint8_t ipcoIndex = 0;
- struct ipmaArray ipmaColor;
- memset(&ipmaColor, 0, sizeof(ipmaColor));
- struct ipmaArray ipmaAlpha;
- memset(&ipmaAlpha, 0, sizeof(ipmaAlpha));
- avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", -1, 0);
- {
- avifBoxMarker ispe = avifRWStreamWriteBox(&s, "ispe", 0, 0);
- avifRWStreamWriteU32(&s, image->width); // unsigned int(32) image_width;
- avifRWStreamWriteU32(&s, image->height); // unsigned int(32) image_height;
- avifRWStreamFinishBox(&s, ispe);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex); // ipma is 1-indexed, doing this afterwards is correct
- ipmaPush(&ipmaAlpha, ipcoIndex); // Alpha shares the ispe prop
+ uint8_t itemPropertyIndex = 0;
+ avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", -1, 0);
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ memset(&item->ipma, 0, sizeof(item->ipma));
+ if (!item->image || !item->codec) {
+ // No ipma to write for this item
+ continue;
+ }
- if (image->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
+ // Properties all av01 items need
+
+ avifBoxMarker ispe = avifRWStreamWriteBox(&s, "ispe", 0, 0);
+ avifRWStreamWriteU32(&s, item->image->width); // unsigned int(32) image_width;
+ avifRWStreamWriteU32(&s, item->image->height); // unsigned int(32) image_height;
+ avifRWStreamFinishBox(&s, ispe);
+ ipmaPush(&item->ipma, ++itemPropertyIndex); // ipma is 1-indexed, doing this afterwards is correct
+
+ uint8_t channelCount = item->alpha ? 1 : 3; // TODO: write the correct value here when adding monochrome support
+ avifBoxMarker pixi = avifRWStreamWriteBox(&s, "pixi", 0, 0);
+ avifRWStreamWriteU8(&s, channelCount); // unsigned int (8) num_channels;
+ for (uint8_t chan = 0; chan < channelCount; ++chan) {
+ avifRWStreamWriteU8(&s, (uint8_t)item->image->depth); // unsigned int (8) bits_per_channel;
+ }
+ avifRWStreamFinishBox(&s, pixi);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
+
+ writeConfigBox(&s, &item->codec->configBox);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
+
+ if (item->alpha) {
+ // Alpha specific properties
+
+ avifBoxMarker auxC = avifRWStreamWriteBox(&s, "auxC", 0, 0);
+ avifRWStreamWriteChars(&s, alphaURN, alphaURNSize); // string aux_type;
+ avifRWStreamFinishBox(&s, auxC);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
+ } 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, image->nclx.colourPrimaries); // unsigned int(16) colour_primaries;
- avifRWStreamWriteU16(&s, image->nclx.transferCharacteristics); // unsigned int(16) transfer_characteristics;
- avifRWStreamWriteU16(&s, image->nclx.matrixCoefficients); // unsigned int(16) matrix_coefficients;
- avifRWStreamWriteU8(&s, image->nclx.fullRangeFlag & 0x80); // unsigned int(1) full_range_flag;
- // unsigned int(7) reserved = 0;
+ avifRWStreamWriteChars(&s, "nclx", 4); // unsigned int(32) colour_type;
+ avifRWStreamWriteU16(&s, item->image->nclx.colourPrimaries); // unsigned int(16) colour_primaries;
+ avifRWStreamWriteU16(&s, item->image->nclx.transferCharacteristics); // unsigned int(16) transfer_characteristics;
+ avifRWStreamWriteU16(&s, item->image->nclx.matrixCoefficients); // unsigned int(16) matrix_coefficients;
+ avifRWStreamWriteU8(&s, item->image->nclx.fullRangeFlag & 0x80); // unsigned int(1) full_range_flag;
+ // unsigned int(7) reserved = 0;
avifRWStreamFinishBox(&s, colr);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
- } else if ((image->profileFormat == AVIF_PROFILE_FORMAT_ICC) && image->icc.data && (image->icc.size > 0)) {
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
+ } else if ((item->image->profileFormat == AVIF_PROFILE_FORMAT_ICC) && 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, image->icc.data, image->icc.size);
+ avifRWStreamWrite(&s, item->image->icc.data, item->image->icc.size);
avifRWStreamFinishBox(&s, colr);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
}
- avifBoxMarker pixiC = avifRWStreamWriteBox(&s, "pixi", 0, 0);
- avifRWStreamWriteU8(&s, 3); // unsigned int (8) num_channels;
- avifRWStreamWriteU8(&s, (uint8_t)image->depth); // unsigned int (8) bits_per_channel;
- avifRWStreamWriteU8(&s, (uint8_t)image->depth); // unsigned int (8) bits_per_channel;
- avifRWStreamWriteU8(&s, (uint8_t)image->depth); // unsigned int (8) bits_per_channel;
- avifRWStreamFinishBox(&s, pixiC);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
-
- writeConfigBox(&s, &codec[AVIF_CODEC_PLANES_COLOR]->configBox);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
-
// Write (Optional) Transformations
- if (image->transformFlags & AVIF_TRANSFORM_PASP) {
+ if (item->image->transformFlags & AVIF_TRANSFORM_PASP) {
avifBoxMarker pasp = avifRWStreamWriteBox(&s, "pasp", -1, 0);
- avifRWStreamWriteU32(&s, image->pasp.hSpacing); // unsigned int(32) hSpacing;
- avifRWStreamWriteU32(&s, image->pasp.vSpacing); // unsigned int(32) vSpacing;
+ avifRWStreamWriteU32(&s, item->image->pasp.hSpacing); // unsigned int(32) hSpacing;
+ avifRWStreamWriteU32(&s, item->image->pasp.vSpacing); // unsigned int(32) vSpacing;
avifRWStreamFinishBox(&s, pasp);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
}
- if (image->transformFlags & AVIF_TRANSFORM_CLAP) {
+ if (item->image->transformFlags & AVIF_TRANSFORM_CLAP) {
avifBoxMarker clap = avifRWStreamWriteBox(&s, "clap", -1, 0);
- avifRWStreamWriteU32(&s, image->clap.widthN); // unsigned int(32) cleanApertureWidthN;
- avifRWStreamWriteU32(&s, image->clap.widthD); // unsigned int(32) cleanApertureWidthD;
- avifRWStreamWriteU32(&s, image->clap.heightN); // unsigned int(32) cleanApertureHeightN;
- avifRWStreamWriteU32(&s, image->clap.heightD); // unsigned int(32) cleanApertureHeightD;
- avifRWStreamWriteU32(&s, image->clap.horizOffN); // unsigned int(32) horizOffN;
- avifRWStreamWriteU32(&s, image->clap.horizOffD); // unsigned int(32) horizOffD;
- avifRWStreamWriteU32(&s, image->clap.vertOffN); // unsigned int(32) vertOffN;
- avifRWStreamWriteU32(&s, image->clap.vertOffD); // unsigned int(32) vertOffD;
+ avifRWStreamWriteU32(&s, item->image->clap.widthN); // unsigned int(32) cleanApertureWidthN;
+ avifRWStreamWriteU32(&s, item->image->clap.widthD); // unsigned int(32) cleanApertureWidthD;
+ avifRWStreamWriteU32(&s, item->image->clap.heightN); // unsigned int(32) cleanApertureHeightN;
+ avifRWStreamWriteU32(&s, item->image->clap.heightD); // unsigned int(32) cleanApertureHeightD;
+ avifRWStreamWriteU32(&s, item->image->clap.horizOffN); // unsigned int(32) horizOffN;
+ avifRWStreamWriteU32(&s, item->image->clap.horizOffD); // unsigned int(32) horizOffD;
+ avifRWStreamWriteU32(&s, item->image->clap.vertOffN); // unsigned int(32) vertOffN;
+ avifRWStreamWriteU32(&s, item->image->clap.vertOffD); // unsigned int(32) vertOffD;
avifRWStreamFinishBox(&s, clap);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
}
- if (image->transformFlags & AVIF_TRANSFORM_IROT) {
+ if (item->image->transformFlags & AVIF_TRANSFORM_IROT) {
avifBoxMarker irot = avifRWStreamWriteBox(&s, "irot", -1, 0);
- uint8_t angle = image->irot.angle & 0x3;
+ uint8_t angle = item->image->irot.angle & 0x3;
avifRWStreamWrite(&s, &angle, 1); // unsigned int (6) reserved = 0; unsigned int (2) angle;
avifRWStreamFinishBox(&s, irot);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
}
- if (image->transformFlags & AVIF_TRANSFORM_IMIR) {
+ if (item->image->transformFlags & AVIF_TRANSFORM_IMIR) {
avifBoxMarker imir = avifRWStreamWriteBox(&s, "imir", -1, 0);
- uint8_t axis = image->imir.axis & 0x1;
+ uint8_t axis = item->image->imir.axis & 0x1;
avifRWStreamWrite(&s, &axis, 1); // unsigned int (7) reserved = 0; unsigned int (1) axis;
avifRWStreamFinishBox(&s, imir);
- ++ipcoIndex;
- ipmaPush(&ipmaColor, ipcoIndex);
- }
-
- if (hasAlpha) {
- avifBoxMarker pixiA = avifRWStreamWriteBox(&s, "pixi", 0, 0);
- avifRWStreamWriteU8(&s, 1); // unsigned int (8) num_channels;
- avifRWStreamWriteU8(&s, (uint8_t)image->depth); // unsigned int (8) bits_per_channel;
- avifRWStreamFinishBox(&s, pixiA);
- ++ipcoIndex;
- ipmaPush(&ipmaAlpha, ipcoIndex);
-
- writeConfigBox(&s, &codec[AVIF_CODEC_PLANES_ALPHA]->configBox);
- ++ipcoIndex;
- ipmaPush(&ipmaAlpha, ipcoIndex);
-
- avifBoxMarker auxC = avifRWStreamWriteBox(&s, "auxC", 0, 0);
- avifRWStreamWriteChars(&s, alphaURN, alphaURNSize); // string aux_type;
- avifRWStreamFinishBox(&s, auxC);
- ++ipcoIndex;
- ipmaPush(&ipmaAlpha, ipcoIndex);
+ ipmaPush(&item->ipma, ++itemPropertyIndex);
}
}
- avifRWStreamFinishBox(&s, ipco);
-
- avifBoxMarker ipma = avifRWStreamWriteBox(&s, "ipma", 0, 0);
- {
- int ipmaCount = hasAlpha ? 2 : 1;
- avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count;
-
- avifRWStreamWriteU16(&s, 1); // unsigned int(16) item_ID;
- avifRWStreamWriteU8(&s, ipmaColor.count); // unsigned int(8) association_count;
- for (int i = 0; i < ipmaColor.count; ++i) { //
- avifRWStreamWriteU8(&s, ipmaColor.associations[i]); // bit(1) essential; unsigned int(7) property_index;
- }
-
- if (hasAlpha) {
- avifRWStreamWriteU16(&s, 2); // unsigned int(16) item_ID;
- avifRWStreamWriteU8(&s, ipmaAlpha.count); // unsigned int(8) association_count;
- for (int i = 0; i < ipmaAlpha.count; ++i) { //
- avifRWStreamWriteU8(&s, ipmaAlpha.associations[i]); // bit(1) essential; unsigned int(7) property_index;
- }
- }
- }
- avifRWStreamFinishBox(&s, ipma);
}
+ avifRWStreamFinishBox(&s, ipco);
+
+ avifBoxMarker ipma = avifRWStreamWriteBox(&s, "ipma", 0, 0);
+ {
+ int ipmaCount = 0;
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->ipma.count > 0) {
+ ++ipmaCount;
+ }
+ }
+ avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count;
+
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->ipma.count == 0) {
+ continue;
+ }
+
+ avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
+ avifRWStreamWriteU8(&s, item->ipma.count); // unsigned int(8) association_count;
+ for (int i = 0; i < item->ipma.count; ++i) { //
+ avifRWStreamWriteU8(&s, item->ipma.associations[i]); // bit(1) essential; unsigned int(7) property_index;
+ }
+ }
+ }
+ avifRWStreamFinishBox(&s, ipma);
+
avifRWStreamFinishBox(&s, iprp);
// -----------------------------------------------------------------------
@@ -501,65 +489,35 @@
// Write mdat
avifBoxMarker mdat = avifRWStreamWriteBox(&s, "mdat", -1, 0);
- uint32_t colorOBUOffset = (uint32_t)s.offset;
- avifRWStreamWrite(&s, colorOBU.data, colorOBU.size);
- uint32_t alphaOBUOffset = (uint32_t)s.offset;
- avifRWStreamWrite(&s, alphaOBU.data, alphaOBU.size);
- uint32_t exifOffset = (uint32_t)s.offset;
- if (image->exif.size > 0) {
- avifRWStreamWriteU32(&s, (uint32_t)exifTiffHeaderOffset); // unsigned int(32) exif_tiff_header_offset; (Annex A.2.1)
- avifRWStreamWrite(&s, image->exif.data, image->exif.size);
+ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
+ avifEncoderItem * item = &encoder->data->items.item[itemIndex];
+ if (item->content.size == 0) {
+ continue;
+ }
+
+ uint32_t infeOffset = (uint32_t)s.offset;
+ avifRWStreamWrite(&s, item->content.data, item->content.size);
+
+ if (item->infeOffsetOffset != 0) {
+ size_t prevOffset = avifRWStreamOffset(&s);
+ avifRWStreamSetOffset(&s, item->infeOffsetOffset);
+ avifRWStreamWriteU32(&s, infeOffset);
+ avifRWStreamSetOffset(&s, prevOffset);
+ }
}
- uint32_t xmpOffset = (uint32_t)s.offset;
- avifRWStreamWrite(&s, image->xmp.data, image->xmp.size);
avifRWStreamFinishBox(&s, mdat);
// -----------------------------------------------------------------------
// Finish up stream
- // Set offsets needed in meta box based on where we eventually wrote mdat
- size_t prevOffset = avifRWStreamOffset(&s);
- if (colorOBUOffsetOffset != 0) {
- avifRWStreamSetOffset(&s, colorOBUOffsetOffset);
- avifRWStreamWriteU32(&s, colorOBUOffset);
- }
- if (alphaOBUOffsetOffset != 0) {
- avifRWStreamSetOffset(&s, alphaOBUOffsetOffset);
- avifRWStreamWriteU32(&s, alphaOBUOffset);
- }
- if (exifOffsetOffset != 0) {
- avifRWStreamSetOffset(&s, exifOffsetOffset);
- avifRWStreamWriteU32(&s, exifOffset);
- }
- if (xmpOffsetOffset != 0) {
- avifRWStreamSetOffset(&s, xmpOffsetOffset);
- avifRWStreamWriteU32(&s, xmpOffset);
- }
- avifRWStreamSetOffset(&s, prevOffset);
-
- // Close write stream
avifRWStreamFinishWrite(&s);
// -----------------------------------------------------------------------
- // IO stats
-
- encoder->ioStats.colorOBUSize = colorOBU.size;
- encoder->ioStats.alphaOBUSize = alphaOBU.size;
-
- // -----------------------------------------------------------------------
// Set result and cleanup
result = AVIF_RESULT_OK;
writeCleanup:
- if (codec[AVIF_CODEC_PLANES_COLOR]) {
- avifCodecDestroy(codec[AVIF_CODEC_PLANES_COLOR]);
- }
- if (codec[AVIF_CODEC_PLANES_ALPHA]) {
- avifCodecDestroy(codec[AVIF_CODEC_PLANES_ALPHA]);
- }
- avifRWDataFree(&colorOBU);
- avifRWDataFree(&alphaOBU);
return result;
}