Make avifImageAllocatePlanes() return avifResult

To catch memory allocation failures and invalid parameters, it is
safer to return a status from avifImageAllocatePlanes() and from
avifImageCopy().
Update CHANGELOG.md.
diff --git a/src/avif.c b/src/avif.c
index 947b42c..1856111 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -3,6 +3,8 @@
 
 #include "avif/internal.h"
 
+#include <limits.h>
+#include <stdint.h>
 #include <string.h>
 
 #define STR_HELPER(x) #x
@@ -163,7 +165,7 @@
     dstImage->imir = srcImage->imir;
 }
 
-void avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes)
+avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes)
 {
     avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
     avifImageCopyNoAlloc(dstImage, srcImage);
@@ -174,7 +176,10 @@
     avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size);
 
     if ((planes & AVIF_PLANES_YUV) && srcImage->yuvPlanes[AVIF_CHAN_Y]) {
-        avifImageAllocatePlanes(dstImage, AVIF_PLANES_YUV);
+        const avifResult allocationResult = avifImageAllocatePlanes(dstImage, AVIF_PLANES_YUV);
+        if (allocationResult != AVIF_RESULT_OK) {
+            return allocationResult;
+        }
 
         avifPixelFormatInfo formatInfo;
         avifGetPixelFormatInfo(srcImage->yuvFormat, &formatInfo);
@@ -200,13 +205,17 @@
     }
 
     if ((planes & AVIF_PLANES_A) && srcImage->alphaPlane) {
-        avifImageAllocatePlanes(dstImage, AVIF_PLANES_A);
+        const avifResult allocationResult = avifImageAllocatePlanes(dstImage, AVIF_PLANES_A);
+        if (allocationResult != AVIF_RESULT_OK) {
+            return allocationResult;
+        }
         for (uint32_t j = 0; j < dstImage->height; ++j) {
             uint8_t * srcAlphaRow = &srcImage->alphaPlane[j * srcImage->alphaRowBytes];
             uint8_t * dstAlphaRow = &dstImage->alphaPlane[j * dstImage->alphaRowBytes];
             memcpy(dstAlphaRow, srcAlphaRow, dstImage->alphaRowBytes);
         }
     }
+    return AVIF_RESULT_OK;
 }
 
 avifResult avifImageSetViewRect(avifImage * dstImage, const avifImage * srcImage, const avifCropRect * rect)
@@ -264,45 +273,70 @@
     avifRWDataSet(&image->xmp, xmp, xmpSize);
 }
 
-void avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes)
+avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes)
 {
-    int channelSize = avifImageUsesU16(image) ? 2 : 1;
-    int fullRowBytes = channelSize * image->width;
-    int fullSize = fullRowBytes * image->height;
+    if (image->width == 0 || image->height == 0) {
+        return AVIF_RESULT_INVALID_ARGUMENT;
+    }
+    const size_t channelSize = avifImageUsesU16(image) ? 2 : 1;
+    if (image->width > SIZE_MAX / channelSize) {
+        return AVIF_RESULT_INVALID_ARGUMENT;
+    }
+    const size_t fullRowBytes = channelSize * image->width;
+    if ((fullRowBytes > UINT32_MAX) || (image->height > SIZE_MAX / fullRowBytes)) {
+        return AVIF_RESULT_INVALID_ARGUMENT;
+    }
+    const size_t fullSize = fullRowBytes * image->height;
+
     if ((planes & AVIF_PLANES_YUV) && (image->yuvFormat != AVIF_PIXEL_FORMAT_NONE)) {
         avifPixelFormatInfo info;
         avifGetPixelFormatInfo(image->yuvFormat, &info);
 
-        int shiftedW = (image->width + info.chromaShiftX) >> info.chromaShiftX;
-        int shiftedH = (image->height + info.chromaShiftY) >> info.chromaShiftY;
+        // Intermediary computation as 64 bits in case width or height is exactly UINT32_MAX.
+        const uint32_t shiftedW = (uint32_t)(((uint64_t)image->width + info.chromaShiftX) >> info.chromaShiftX);
+        const uint32_t shiftedH = (uint32_t)(((uint64_t)image->height + info.chromaShiftY) >> info.chromaShiftY);
 
-        int uvRowBytes = channelSize * shiftedW;
-        int uvSize = uvRowBytes * shiftedH;
+        // These are less than or equal to fullRowBytes/fullSize. No need to check overflows.
+        const size_t uvRowBytes = channelSize * shiftedW;
+        const size_t uvSize = uvRowBytes * shiftedH;
 
+        image->imageOwnsYUVPlanes = AVIF_TRUE;
         if (!image->yuvPlanes[AVIF_CHAN_Y]) {
             image->yuvRowBytes[AVIF_CHAN_Y] = fullRowBytes;
             image->yuvPlanes[AVIF_CHAN_Y] = avifAlloc(fullSize);
+            if (!image->yuvPlanes[AVIF_CHAN_Y]) {
+                return AVIF_RESULT_OUT_OF_MEMORY;
+            }
         }
 
         if (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) {
             if (!image->yuvPlanes[AVIF_CHAN_U]) {
                 image->yuvRowBytes[AVIF_CHAN_U] = uvRowBytes;
                 image->yuvPlanes[AVIF_CHAN_U] = avifAlloc(uvSize);
+                if (!image->yuvPlanes[AVIF_CHAN_U]) {
+                    return AVIF_RESULT_OUT_OF_MEMORY;
+                }
             }
             if (!image->yuvPlanes[AVIF_CHAN_V]) {
                 image->yuvRowBytes[AVIF_CHAN_V] = uvRowBytes;
                 image->yuvPlanes[AVIF_CHAN_V] = avifAlloc(uvSize);
+                if (!image->yuvPlanes[AVIF_CHAN_V]) {
+                    return AVIF_RESULT_OUT_OF_MEMORY;
+                }
             }
         }
-        image->imageOwnsYUVPlanes = AVIF_TRUE;
     }
     if (planes & AVIF_PLANES_A) {
+        image->imageOwnsAlphaPlane = AVIF_TRUE;
         if (!image->alphaPlane) {
             image->alphaRowBytes = fullRowBytes;
             image->alphaPlane = avifAlloc(fullSize);
+            if (!image->alphaPlane) {
+                return AVIF_RESULT_OUT_OF_MEMORY;
+            }
         }
-        image->imageOwnsAlphaPlane = AVIF_TRUE;
     }
+    return AVIF_RESULT_OK;
 }
 
 void avifImageFreePlanes(avifImage * image, avifPlanesFlags planes)
diff --git a/src/read.c b/src/read.c
index bf5a7b5..64c3434 100644
--- a/src/read.c
+++ b/src/read.c
@@ -1363,7 +1363,10 @@
         }
     }
 
-    avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
+    if (avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV) != AVIF_RESULT_OK) {
+        avifDiagnosticsPrintf(data->diag, "Image allocation failure");
+        return AVIF_FALSE;
+    }
 
     avifPixelFormatInfo formatInfo;
     avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo);
@@ -3744,7 +3747,10 @@
     // codec's internal frame buffers. Allocate memory for the conversion.
     image->alphaPlane = NULL;
     image->alphaRowBytes = 0;
-    avifImageAllocatePlanes(image, AVIF_PLANES_A);
+    const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A);
+    if (allocationResult != AVIF_RESULT_OK) {
+        return allocationResult;
+    }
 
     if (image->depth > 8) {
         for (uint32_t j = 0; j < image->height; ++j) {
@@ -4145,8 +4151,7 @@
     if (result != AVIF_RESULT_OK) {
         return result;
     }
-    avifImageCopy(image, decoder->image, AVIF_PLANES_ALL);
-    return AVIF_RESULT_OK;
+    return avifImageCopy(image, decoder->image, AVIF_PLANES_ALL);
 }
 
 avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size)
diff --git a/src/reformat.c b/src/reformat.c
index 7d7c13f..1e2f89a 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -193,10 +193,14 @@
         return AVIF_RESULT_NOT_IMPLEMENTED;
     }
 
+    const avifBool hasAlpha = avifRGBFormatHasAlpha(rgb->format) && !rgb->ignoreAlpha;
+    avifResult allocationResult = avifImageAllocatePlanes(image, hasAlpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV);
+    if (allocationResult != AVIF_RESULT_OK) {
+        return allocationResult;
+    }
+
     avifAlphaMultiplyMode alphaMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP;
-    avifImageAllocatePlanes(image, AVIF_PLANES_YUV);
-    if (avifRGBFormatHasAlpha(rgb->format) && !rgb->ignoreAlpha) {
-        avifImageAllocatePlanes(image, AVIF_PLANES_A);
+    if (hasAlpha) {
         if (!rgb->alphaPremultiplied && image->alphaPremultiplied) {
             alphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY;
         } else if (rgb->alphaPremultiplied && !image->alphaPremultiplied) {
diff --git a/src/scale.c b/src/scale.c
index 17251b4..82d24d4 100644
--- a/src/scale.c
+++ b/src/scale.c
@@ -89,7 +89,11 @@
     }
 
     if (srcYUVPlanes[0]) {
-        avifImageAllocatePlanes(image, AVIF_PLANES_YUV);
+        const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_YUV);
+        if (allocationResult != AVIF_RESULT_OK) {
+            avifDiagnosticsPrintf(diag, "Allocation of YUV planes failed: %s", avifResultToString(allocationResult));
+            return AVIF_FALSE;
+        }
 
         avifPixelFormatInfo formatInfo;
         avifGetPixelFormatInfo(image->yuvFormat, &formatInfo);
@@ -132,7 +136,11 @@
     }
 
     if (srcAlphaPlane) {
-        avifImageAllocatePlanes(image, AVIF_PLANES_A);
+        const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A);
+        if (allocationResult != AVIF_RESULT_OK) {
+            avifDiagnosticsPrintf(diag, "Allocation of alpha plane failed: %s", avifResultToString(allocationResult));
+            return AVIF_FALSE;
+        }
 
         if (image->depth > 8) {
             uint16_t * const srcPlane = (uint16_t *)srcAlphaPlane;
diff --git a/src/write.c b/src/write.c
index ad2a956..2585678 100644
--- a/src/write.c
+++ b/src/write.c
@@ -683,7 +683,10 @@
 
     if (encoder->data->items.count == 0) {
         // Make a copy of the first image's metadata (sans pixels) for future writing/validation
-        avifImageCopy(encoder->data->imageMetadata, firstCell, 0);
+        const avifResult copyResult = avifImageCopy(encoder->data->imageMetadata, firstCell, 0);
+        if (copyResult != AVIF_RESULT_OK) {
+            return copyResult;
+        }
 
         // Prepare all AV1 items