Free grid cell images if avifImageSplitGrid fails

Destroy the images in gridCells before avifImageSplitGrid() returns on
failure. Previously the caller was responsible for destroying the images
in gridCells after avifImageSplitGrid() returned on failure.

Co-authored-by: Wan-Teh Chang <wtc@google.com>
diff --git a/apps/avifgainmaputil/imageio.cc b/apps/avifgainmaputil/imageio.cc
index d50a862..4c221a9 100644
--- a/apps/avifgainmaputil/imageio.cc
+++ b/apps/avifgainmaputil/imageio.cc
@@ -146,11 +146,6 @@
   std::vector<avifImage*> grid_cells_ptrs(grid_cell_count);
   if (!avifImageSplitGrid(image, grid_cols, grid_rows,
                           grid_cells_ptrs.data())) {
-    for (avifImage* img : grid_cells_ptrs) {
-      if (img) {
-        avifImageDestroy(img);
-      }
-    }
     return AVIF_RESULT_UNKNOWN_ERROR;
   }
   // Take ownership of the pointers returned by avifImageSplitGrid.
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c
index 39a4b29..8578b29 100644
--- a/apps/shared/avifutil.c
+++ b/apps/shared/avifutil.c
@@ -538,6 +538,7 @@
 
 avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gridCols, uint32_t gridRows, avifImage ** gridCells)
 {
+    avifBool success = AVIF_FALSE;
     uint32_t cellWidth, cellHeight;
     avifPixelFormatInfo formatInfo;
     avifGetPixelFormatInfo(gridSplitImage->yuvFormat, &formatInfo);
@@ -549,15 +550,18 @@
     }
     const avifBool hasGainMap = gridSplitImage->gainMap && gridSplitImage->gainMap->image;
 
+    uint32_t createdCells = 0;
     for (uint32_t gridY = 0; gridY < gridRows; ++gridY) {
         for (uint32_t gridX = 0; gridX < gridCols; ++gridX) {
             uint32_t gridIndex = gridX + (gridY * gridCols);
             avifImage * cellImage = avifImageCreateEmpty();
             if (!cellImage) {
                 fprintf(stderr, "ERROR: Cell creation failed: out of memory\n");
-                return AVIF_FALSE;
+                goto cleanup;
             }
             gridCells[gridIndex] = cellImage;
+            assert(gridIndex == createdCells);
+            createdCells++;
 
             avifCropRect cellRect = { gridX * cellWidth, gridY * cellHeight, cellWidth, cellHeight };
             if (cellRect.x + cellRect.width > gridSplitImage->width) {
@@ -569,14 +573,14 @@
             const avifResult copyResult = avifImageSetViewRect(cellImage, gridSplitImage, &cellRect);
             if (copyResult != AVIF_RESULT_OK) {
                 fprintf(stderr, "ERROR: Cell creation failed: %s\n", avifResultToString(copyResult));
-                return AVIF_FALSE;
+                goto cleanup;
             }
 
             if (hasGainMap) {
                 cellImage->gainMap = avifGainMapCreate();
                 if (!cellImage->gainMap) {
                     fprintf(stderr, "ERROR: Gain map creation failed: out of memory\n");
-                    return AVIF_FALSE;
+                    goto cleanup;
                 }
                 // Copy gain map metadata.
                 memcpy(cellImage->gainMap, gridSplitImage->gainMap, sizeof(avifGainMap));
@@ -592,16 +596,11 @@
         gainMapGridCells = (avifImage **)calloc(gridCols * gridRows, sizeof(avifImage *));
         if (!gainMapGridCells) {
             fprintf(stderr, "ERROR: Memory allocation failed for gain map grid cells\n");
-            return AVIF_FALSE;
+            goto cleanup;
         }
         if (!avifImageSplitGrid(gridSplitImage->gainMap->image, gridCols, gridRows, gainMapGridCells)) {
-            for (uint32_t i = 0; i < gridCols * gridRows; ++i) {
-                if (gainMapGridCells[i]) {
-                    avifImageDestroy(gainMapGridCells[i]);
-                }
-            }
             free(gainMapGridCells);
-            return AVIF_FALSE;
+            goto cleanup;
         }
 
         for (uint32_t gridIndex = 0; gridIndex < gridCols * gridRows; ++gridIndex) {
@@ -618,21 +617,21 @@
         const avifResult result = avifImageSetProfileICC(firstCell, gridSplitImage->icc.data, gridSplitImage->icc.size);
         if (result != AVIF_RESULT_OK) {
             fprintf(stderr, "ERROR: Failed to set ICC profile on grid cell: %s\n", avifResultToString(result));
-            return AVIF_FALSE;
+            goto cleanup;
         }
     }
     if (gridSplitImage->exif.size > 0) {
         const avifResult result = avifRWDataSet(&firstCell->exif, gridSplitImage->exif.data, gridSplitImage->exif.size);
         if (result != AVIF_RESULT_OK) {
             fprintf(stderr, "ERROR: Failed to set Exif metadata on grid cell: %s\n", avifResultToString(result));
-            return AVIF_FALSE;
+            goto cleanup;
         }
     }
     if (gridSplitImage->xmp.size > 0) {
         const avifResult result = avifImageSetMetadataXMP(firstCell, gridSplitImage->xmp.data, gridSplitImage->xmp.size);
         if (result != AVIF_RESULT_OK) {
             fprintf(stderr, "ERROR: Failed to set XMP metadata on grid cell: %s\n", avifResultToString(result));
-            return AVIF_FALSE;
+            goto cleanup;
         }
     }
     if (gridSplitImage->gainMap && gridSplitImage->gainMap->image && gridSplitImage->gainMap->altICC.size > 0) {
@@ -642,12 +641,21 @@
                 avifRWDataSet(&cellImage->gainMap->altICC, gridSplitImage->gainMap->altICC.data, gridSplitImage->gainMap->altICC.size);
             if (result != AVIF_RESULT_OK) {
                 fprintf(stderr, "ERROR: Failed to set ICC profile on gain map grid cell: %s\n", avifResultToString(result));
-                return AVIF_FALSE;
+                goto cleanup;
             }
         }
     }
 
-    return AVIF_TRUE;
+    success = AVIF_TRUE;
+
+cleanup:
+    if (!success) {
+        for (uint32_t i = 0; i < createdCells; ++i) {
+            avifImageDestroy(gridCells[i]);
+            gridCells[i] = NULL;
+        }
+    }
+    return success;
 }
 
 void avifRGBImageSetViewRect(avifRGBImage * dstImage, const avifRGBImage * srcImage, const avifCropRect * cropRect)