SDL: Use the expected plane size when capturing Android camera frames

From 02e85a153f6d56c36d671842faac42b5b83a6ba9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 1 Jan 2025 15:50:50 -0800
Subject: [PATCH] Use the expected plane size when capturing Android camera
 frames

On the Samsung Galaxy A52 the camera plane size is (pitch * (h - 1) + w) instead of (pitch * h). This led to us copying off the end of the plane when uploading the texture, so we pad out to our expected size.
---
 src/camera/android/SDL_camera_android.c | 35 ++++++++++++++++---------
 1 file changed, 22 insertions(+), 13 deletions(-)

diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c
index 03af812a76aa6..54b539a126628 100644
--- a/src/camera/android/SDL_camera_android.c
+++ b/src/camera/android/SDL_camera_android.c
@@ -327,31 +327,40 @@ static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_
         num_planes--;   // treat the interleaved planes as one.
     }
 
-    // !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet.
     size_t buflen = 0;
+    pAImage_getPlaneRowStride(image, 0, &frame->pitch);
     for (int i = 0; (i < num_planes) && (i < 3); i++) {
-        uint8_t *data = NULL;
-        int32_t datalen = 0;
-        pAImage_getPlaneData(image, i, &data, &datalen);
-        buflen += (int) datalen;
+        int32_t expected;
+        if (i == 0) {
+            expected = frame->pitch * frame->h;
+        } else {
+            expected = frame->pitch * (frame->h + 1) / 2;
+        }
+        buflen += expected;
     }
 
     frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
     if (frame->pixels == NULL) {
         result = SDL_CAMERA_FRAME_ERROR;
     } else {
-        int32_t row_stride = 0;
         Uint8 *dst = frame->pixels;
-        pAImage_getPlaneRowStride(image, 0, &row_stride);
-        frame->pitch = (int) row_stride;  // this is what SDL3 currently expects, probably incorrectly.
 
         for (int i = 0; (i < num_planes) && (i < 3); i++) {
             uint8_t *data = NULL;
             int32_t datalen = 0;
+            int32_t expected;
+            if (i == 0) {
+                expected = frame->pitch * frame->h;
+            } else {
+                expected = frame->pitch * (frame->h + 1) / 2;
+            }
             pAImage_getPlaneData(image, i, &data, &datalen);
-            const void *src = data;
-            SDL_memcpy(dst, src, datalen);
-            dst += datalen;
+
+            int32_t row_stride = 0;
+            pAImage_getPlaneRowStride(image, i, &row_stride);
+            SDL_assert(row_stride == frame->pitch);
+            SDL_memcpy(dst, data, SDL_min(expected, datalen));
+            dst += expected;
         }
     }
 
@@ -631,8 +640,8 @@ static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data,
     const int32_t *i32ptr = cfgentry.data.i32;
     for (int i = 0; i < cfgentry.count; i++, i32ptr += 4) {
         const int32_t fmt = i32ptr[0];
-        const int w = (int) i32ptr[1];
-        const int h = (int) i32ptr[2];
+        const int w = i32ptr[1];
+        const int h = i32ptr[2];
         const int32_t type = i32ptr[3];
         SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN;
         SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;