Fix y4m read/write for images of non-standard dimensions (#802)

Read/write y4m files row-by-row

The stride may not match the dimensions of the image.
diff --git a/apps/shared/y4m.c b/apps/shared/y4m.c
index 8c80772..a69bc64 100644
--- a/apps/shared/y4m.c
+++ b/apps/shared/y4m.c
@@ -363,27 +363,38 @@
     avifPixelFormatInfo info;
     avifGetPixelFormatInfo(avif->yuvFormat, &info);
 
-    uint32_t planeBytes[4];
-    planeBytes[0] = avif->yuvRowBytes[0] * avif->height;
-    planeBytes[1] = avif->yuvRowBytes[1] * ((avif->height + info.chromaShiftY) >> info.chromaShiftY);
-    planeBytes[2] = avif->yuvRowBytes[2] * ((avif->height + info.chromaShiftY) >> info.chromaShiftY);
-    if (frame.hasAlpha) {
-        planeBytes[3] = avif->alphaRowBytes * avif->height;
-    } else {
-        planeBytes[3] = 0;
-    }
-
-    for (int i = 0; i < 3; ++i) {
-        uint32_t bytesRead = (uint32_t)fread(avif->yuvPlanes[i], 1, planeBytes[i], frame.inputFile);
-        if (bytesRead != planeBytes[i]) {
-            fprintf(stderr, "Failed to read y4m plane (not enough data, wanted %d, got %d): %s\n", planeBytes[i], bytesRead, frame.displayFilename);
-            goto cleanup;
+    for (int plane = 0; plane < 3; ++plane) {
+        uint32_t planeWidth = (plane > 0) ? ((avif->width + info.chromaShiftX) >> info.chromaShiftX) : avif->width;
+        uint32_t planeHeight = (plane > 0) ? ((avif->height + info.chromaShiftY) >> info.chromaShiftY) : avif->height;
+        uint32_t rowBytes = planeWidth << (avif->depth > 8);
+        uint8_t * row = avif->yuvPlanes[plane];
+        for (uint32_t y = 0; y < planeHeight; ++y) {
+            uint32_t bytesRead = (uint32_t)fread(row, 1, rowBytes, frame.inputFile);
+            if (bytesRead != rowBytes) {
+                fprintf(stderr,
+                        "Failed to read y4m row (not enough data, wanted %" PRIu32 ", got %" PRIu32 "): %s\n",
+                        rowBytes,
+                        bytesRead,
+                        frame.displayFilename);
+                goto cleanup;
+            }
+            row += avif->yuvRowBytes[plane];
         }
     }
     if (frame.hasAlpha) {
-        if (fread(avif->alphaPlane, 1, planeBytes[3], frame.inputFile) != planeBytes[3]) {
-            fprintf(stderr, "Failed to read y4m plane (not enough data): %s\n", frame.displayFilename);
-            goto cleanup;
+        uint32_t rowBytes = avif->width << (avif->depth > 8);
+        uint8_t * row = avif->alphaPlane;
+        for (uint32_t y = 0; y < avif->height; ++y) {
+            uint32_t bytesRead = (uint32_t)fread(row, 1, rowBytes, frame.inputFile);
+            if (bytesRead != rowBytes) {
+                fprintf(stderr,
+                        "Failed to read y4m row (not enough data, wanted %" PRIu32 ", got %" PRIu32 "): %s\n",
+                        rowBytes,
+                        bytesRead,
+                        frame.displayFilename);
+                goto cleanup;
+            }
+            row += avif->alphaRowBytes;
         }
     }
 
@@ -517,28 +528,30 @@
         goto cleanup;
     }
 
-    uint8_t * planes[3];
-    uint32_t planeBytes[3];
-    planes[0] = avif->yuvPlanes[0];
-    planes[1] = avif->yuvPlanes[1];
-    planes[2] = avif->yuvPlanes[2];
-    planeBytes[0] = avif->yuvRowBytes[0] * avif->height;
-    planeBytes[1] = avif->yuvRowBytes[1] * (avif->height >> info.chromaShiftY);
-    planeBytes[2] = avif->yuvRowBytes[2] * (avif->height >> info.chromaShiftY);
-
-    for (int i = 0; i < 3; ++i) {
-        if (fwrite(planes[i], 1, planeBytes[i], f) != planeBytes[i]) {
-            fprintf(stderr, "Failed to write %" PRIu32 " bytes: %s\n", planeBytes[i], outputFilename);
-            success = AVIF_FALSE;
-            goto cleanup;
+    for (int plane = 0; plane < 3; ++plane) {
+        uint32_t planeWidth = (plane > 0) ? ((avif->width + info.chromaShiftX) >> info.chromaShiftX) : avif->width;
+        uint32_t planeHeight = (plane > 0) ? ((avif->height + info.chromaShiftY) >> info.chromaShiftY) : avif->height;
+        uint32_t rowBytes = planeWidth << (avif->depth > 8);
+        const uint8_t * row = avif->yuvPlanes[plane];
+        for (uint32_t y = 0; y < planeHeight; ++y) {
+            if (fwrite(row, 1, rowBytes, f) != rowBytes) {
+                fprintf(stderr, "Failed to write %" PRIu32 " bytes: %s\n", rowBytes, outputFilename);
+                success = AVIF_FALSE;
+                goto cleanup;
+            }
+            row += avif->yuvRowBytes[plane];
         }
     }
     if (writeAlpha) {
-        uint32_t alphaPlaneBytes = avif->alphaRowBytes * avif->height;
-        if (fwrite(avif->alphaPlane, 1, alphaPlaneBytes, f) != alphaPlaneBytes) {
-            fprintf(stderr, "Failed to write %" PRIu32 " bytes: %s\n", alphaPlaneBytes, outputFilename);
-            success = AVIF_FALSE;
-            goto cleanup;
+        uint32_t rowBytes = avif->width << (avif->depth > 8);
+        const uint8_t * row = avif->alphaPlane;
+        for (uint32_t y = 0; y < avif->height; ++y) {
+            if (fwrite(row, 1, rowBytes, f) != rowBytes) {
+                fprintf(stderr, "Failed to write %" PRIu32 " bytes: %s\n", rowBytes, outputFilename);
+                success = AVIF_FALSE;
+                goto cleanup;
+            }
+            row += avif->alphaRowBytes;
         }
     }