Initial commit (basic functionality, not feature complete)
diff --git a/src/read.c b/src/read.c
new file mode 100644
index 0000000..c5ee4f6
--- /dev/null
+++ b/src/read.c
@@ -0,0 +1,331 @@
+// Copyright 2019 Joe Drago. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include "avif/internal.h"
+
+#include "aom/aom_decoder.h"
+#include "aom/aomdx.h"
+
+#include <string.h>
+
+#define MAX_ITEMS 8
+
+typedef struct avifItem
+{
+    int id;
+    uint8_t type[4];
+    uint32_t offset;
+    uint32_t size;
+} avifItem;
+
+typedef struct avifData
+{
+    avifBool ispe_seen;
+    uint32_t ispe_width;
+    uint32_t ispe_height;
+    avifItem items[MAX_ITEMS];
+} avifData;
+
+int findItemID(avifData * data, int itemID)
+{
+    if (itemID == 0) {
+        return -1;
+    }
+
+    for (int i = 0; i < MAX_ITEMS; ++i) {
+        if (data->items[i].id == itemID) {
+            return i;
+        }
+    }
+
+    for (int i = 0; i < MAX_ITEMS; ++i) {
+        if (data->items[i].id == 0) {
+            data->items[i].id = itemID;
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+static avifBool avifParse(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    avifStream s;
+    avifRawData rawData = { raw, rawLen };
+    avifStreamStart(&s, &rawData);
+
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        size_t contentSize;
+        uint8_t type[4];
+        CHECK(avifStreamReadBoxHeader(&s, type, &contentSize));
+
+        if (!memcmp(type, "meta", 4)) {
+            // Basic container FullBoxes, skip version and parse contents
+
+            CHECK(avifStreamReadAndEnforceVersion(&s, 0));
+            contentSize -= 4;
+
+            CHECK(avifParse(data, raw + s.offset, contentSize));
+
+        } else if (!memcmp(type, "iprp", 4) || !memcmp(type, "ipco", 4)) {
+            // Basic container Boxes, just parse contents
+
+            CHECK(avifParse(data, raw + s.offset, contentSize));
+
+        } else if (!memcmp(type, "ispe", 4)) {
+            // ImageSpatialExtentsProperty
+
+            CHECK(avifStreamReadAndEnforceVersion(&s, 0));
+            CHECK(avifStreamReadU32(&s, &data->ispe_width));
+            CHECK(avifStreamReadU32(&s, &data->ispe_height));
+            data->ispe_seen = AVIF_TRUE;
+
+        } else if (!memcmp(type, "iloc", 4)) {
+            // ItemLocationBox
+
+            CHECK(avifStreamReadAndEnforceVersion(&s, 0)); // TODO: support more
+
+            uint8_t offsetSizeAndLengthSize;
+            CHECK(avifStreamRead(&s, &offsetSizeAndLengthSize, 1));
+            uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
+            uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
+
+            uint8_t baseOffsetSizeAndReserved;
+            CHECK(avifStreamRead(&s, &baseOffsetSizeAndReserved, 1));
+            uint8_t baseOffsetSize = (baseOffsetSizeAndReserved >> 4) & 0xf; // unsigned int(4) base_offset_size;
+
+            uint16_t itemCount;
+            CHECK(avifStreamReadU16(&s, &itemCount)); // unsigned int(16) item_count;
+            for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
+                uint16_t itemID;                                           // unsigned int(16) item_ID;
+                CHECK(avifStreamReadU16(&s, &itemID));                     //
+                uint16_t dataReferenceIndex;                               // unsigned int(16) data_reference_index;
+                CHECK(avifStreamReadU16(&s, &dataReferenceIndex));         //
+                uint64_t baseOffset;                                       // unsigned int(base_offset_size*8) base_offset;
+                CHECK(avifStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
+                uint16_t extentCount;                                      // unsigned int(16) extent_count;
+                CHECK(avifStreamReadU16(&s, &extentCount));                //
+                if (extentCount == 1) {
+                    uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
+                    CHECK(avifStreamReadUX8(&s, &extentOffset, offsetSize));
+                    uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
+                    CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize));
+
+                    int itemIndex = findItemID(data, itemID);
+                    if (itemIndex == -1) {
+                        return AVIF_FALSE;
+                    }
+                    data->items[itemIndex].id = itemID;
+                    data->items[itemIndex].offset = (uint32_t)(baseOffset + extentOffset);
+                    data->items[itemIndex].size = (uint32_t)extentLength;
+                } else {
+                    // TODO: support more than one extent
+                    return AVIF_FALSE;
+                }
+            }
+
+        } else if (!memcmp(type, "iinf", 4)) {
+            // ItemInfoBox
+
+            uint8_t version;
+            CHECK(avifStreamReadVersionAndFlags(&s, &version));
+            contentSize -= 4;
+            if (version == 0) {
+                uint16_t entryCount;
+                CHECK(avifStreamReadU16(&s, &entryCount)); // unsigned int(16) entry_count;
+                contentSize -= sizeof(uint16_t);
+            } else if (version == 1) {
+                uint32_t entryCount;
+                CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(16) entry_count;
+                contentSize -= sizeof(uint32_t);
+            } else {
+                return AVIF_FALSE;
+            }
+
+            CHECK(avifParse(data, raw + s.offset, contentSize));
+
+        } else if (!memcmp(type, "infe", 4)) {
+            // ItemInfoEntry
+
+            size_t startOffset = s.offset;
+            CHECK(avifStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
+
+            uint16_t itemID;                                    // unsigned int(16) item_ID;
+            CHECK(avifStreamReadU16(&s, &itemID));              //
+            uint16_t itemProtectionIndex;                       // unsigned int(16) item_protection_index;
+            CHECK(avifStreamReadU16(&s, &itemProtectionIndex)); //
+            uint8_t itemType[4];                                // unsigned int(32) item_type;
+            CHECK(avifStreamRead(&s, itemType, 4));             //
+
+            // Skip the remainder of the box
+            CHECK(avifStreamSkip(&s, contentSize - (s.offset - startOffset)));
+
+            int itemIndex = findItemID(data, itemID);
+            if (itemIndex == -1) {
+                return AVIF_FALSE;
+            }
+            memcpy(data->items[itemIndex].type, itemType, sizeof(itemType));
+
+        } else {
+            // Unsupported box, move on
+            CHECK(avifStreamSkip(&s, contentSize));
+        }
+    }
+    return AVIF_TRUE;
+}
+
+static aom_image_t * decodeOBU(aom_codec_ctx_t * decoder, avifRawData * inputOBU)
+{
+    aom_codec_stream_info_t si;
+    aom_codec_iface_t * decoder_interface = aom_codec_av1_dx();
+    if (aom_codec_dec_init(decoder, decoder_interface, NULL, 0)) {
+        return NULL;
+    }
+
+    if (aom_codec_control(decoder, AV1D_SET_OUTPUT_ALL_LAYERS, 1)) {
+        return NULL;
+    }
+
+    si.is_annexb = 0;
+    if (aom_codec_peek_stream_info(decoder_interface, inputOBU->data, inputOBU->size, &si)) {
+        return NULL;
+    }
+
+    if (aom_codec_decode(decoder, inputOBU->data, inputOBU->size, NULL)) {
+        return NULL;
+    }
+
+    aom_codec_iter_t iter = NULL;
+    return aom_codec_get_frame(decoder, &iter); // It doesn't appear that I own this / need to free this
+}
+
+avifResult avifImageRead(avifImage * image, avifRawData * input)
+{
+    // -----------------------------------------------------------------------
+    // Parse BMFF boxes
+
+    avifData data;
+    memset(&data, 0, sizeof(data));
+    if (!avifParse(&data, input->data, input->size)) {
+        return AVIF_RESULT_BMFF_PARSE_FAILED;
+    }
+
+    // -----------------------------------------------------------------------
+
+    avifRawData colorOBU = AVIF_RAW_DATA_EMPTY;
+    avifRawData alphaOBU = AVIF_RAW_DATA_EMPTY;
+
+    for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
+        avifItem * item = &data.items[itemIndex];
+        if (!item->id || !item->size) {
+            break;
+        }
+        if (item->offset > input->size) {
+            break;
+        }
+        if ((item->offset + item->size) > input->size) {
+            break;
+        }
+
+        if (memcmp(item->type, "av01", 4)) {
+            // probably exif or some other data
+            continue;
+        }
+
+        if (colorOBU.size == 0) {
+            colorOBU.data = input->data + item->offset;
+            colorOBU.size = item->size;
+        } else {
+            alphaOBU.data = input->data + item->offset;
+            alphaOBU.size = item->size;
+            break;
+        }
+    }
+
+    if (colorOBU.size == 0) {
+        return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
+    }
+    avifBool hasAlpha = (alphaOBU.size > 0) ? AVIF_TRUE : AVIF_FALSE;
+
+    aom_codec_ctx_t colorDecoder;
+    aom_image_t * aomColorImage = decodeOBU(&colorDecoder, &colorOBU);
+    if (!aomColorImage) {
+        aom_codec_destroy(&colorDecoder);
+        return AVIF_RESULT_DECODE_COLOR_FAILED;
+    }
+
+    aom_codec_ctx_t alphaDecoder;
+    aom_image_t * aomAlphaImage = NULL;
+    if (hasAlpha) {
+        aomAlphaImage = decodeOBU(&alphaDecoder, &alphaOBU);
+        if (!aomAlphaImage) {
+            aom_codec_destroy(&colorDecoder);
+            aom_codec_destroy(&alphaDecoder);
+            return AVIF_RESULT_DECODE_ALPHA_FAILED;
+        }
+        if ((aomColorImage->d_w != aomAlphaImage->d_w) || (aomColorImage->d_h != aomAlphaImage->d_h)) {
+            aom_codec_destroy(&colorDecoder);
+            aom_codec_destroy(&alphaDecoder);
+            return AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH;
+        }
+    }
+
+    if (data.ispe_seen && ((data.ispe_width != aomColorImage->d_w) || (data.ispe_height != aomColorImage->d_h))) {
+        aom_codec_destroy(&colorDecoder);
+        if (hasAlpha) {
+            aom_codec_destroy(&alphaDecoder);
+        }
+        return AVIF_RESULT_ISPE_SIZE_MISMATCH;
+    }
+
+    avifPixelFormat pixelFormat;
+    int xShift = 0;
+    int yShift = 0;
+    switch (aomColorImage->fmt) {
+        case AOM_IMG_FMT_I42016:
+            pixelFormat = AVIF_PIXEL_FORMAT_YUV420;
+            xShift = 1;
+            yShift = 1;
+            break;
+        case AOM_IMG_FMT_I44416:
+            pixelFormat = AVIF_PIXEL_FORMAT_YUV444;
+            break;
+        default:
+            aom_codec_destroy(&colorDecoder);
+            if (hasAlpha) {
+                aom_codec_destroy(&alphaDecoder);
+            }
+            return AVIF_UNSUPPORTED_PIXEL_FORMAT;
+    }
+
+    avifImageCreatePixels(image, pixelFormat, aomColorImage->d_w, aomColorImage->d_h, aomColorImage->bit_depth);
+
+    uint16_t maxChannel = (1 << image->depth) - 1;
+    for (int j = 0; j < image->height; ++j) {
+        for (int i = 0; i < image->width; ++i) {
+            uint16_t * planeChannel;
+            int x = i >> xShift;
+            int y = j >> yShift;
+
+            planeChannel = (uint16_t *)&aomColorImage->planes[0][(j * aomColorImage->stride[0]) + (2 * i)];
+            image->planes[0][i + (j * image->strides[0])] = *planeChannel;
+            planeChannel = (uint16_t *)&aomColorImage->planes[1][(y * aomColorImage->stride[1]) + (2 * x)];
+            image->planes[1][x + (y * image->strides[1])] = *planeChannel;
+            planeChannel = (uint16_t *)&aomColorImage->planes[2][(y * aomColorImage->stride[2]) + (2 * x)];
+            image->planes[2][x + (y * image->strides[2])] = *planeChannel;
+
+            if (hasAlpha) {
+                uint16_t * planeChannel = (uint16_t *)&aomAlphaImage->planes[0][(j * aomColorImage->stride[0]) + (2 * i)];
+                image->planes[3][i + (j * image->strides[3])] = *planeChannel;
+            } else {
+                image->planes[3][i + (j * image->strides[3])] = maxChannel;
+            }
+        }
+    }
+
+    aom_codec_destroy(&colorDecoder);
+    if (hasAlpha) {
+        aom_codec_destroy(&alphaDecoder);
+    }
+    return AVIF_RESULT_OK;
+}