SDL: Document that the pitch value may be zero for surfaces that will be filled in by the application later.

From d496d187c5a4a5b535fa35b38eb4fa1b439801d0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 24 Jan 2023 22:49:33 -0800
Subject: [PATCH] Document that the pitch value may be zero for surfaces that
 will be filled in by the application later.

Also verify that the pitch isn't zero for surfaces with valid pixels

Fixes https://github.com/libsdl-org/SDL/issues/7143
---
 include/SDL3/SDL_surface.h    |  3 +++
 src/video/SDL_surface.c       | 29 +++++++++++++++++------------
 test/testautomation_surface.c |  8 ++++++++
 3 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h
index 13f1352ac1d5..0fdfdcfb83ac 100644
--- a/include/SDL3/SDL_surface.h
+++ b/include/SDL3/SDL_surface.h
@@ -137,6 +137,9 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateSurface
  * No copy is made of the pixel data. Pixel data is not managed automatically;
  * you must free the surface before you free the pixel data.
  *
+ * You may pass NULL for pixels and 0 for pitch to create a surface that you
+ * will fill in with valid values later.
+ *
  * \param pixels a pointer to existing pixel data
  * \param width the width of the surface
  * \param height the height of the surface
diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c
index 36d2fdde3a27..37157ae96673 100644
--- a/src/video/SDL_surface.c
+++ b/src/video/SDL_surface.c
@@ -203,7 +203,6 @@ SDL_CreateSurfaceFrom(void *pixels,
                       Uint32 format)
 {
     SDL_Surface *surface;
-    size_t minimalPitch;
 
     if (width < 0) {
         SDL_InvalidParamError("width");
@@ -215,20 +214,26 @@ SDL_CreateSurfaceFrom(void *pixels,
         return NULL;
     }
 
-    if (SDL_ISPIXELFORMAT_FOURCC(format)) {
-        int p;
-        if (SDL_CalculateYUVSize(format, width, height, NULL, &p) < 0) {
+    if (pitch == 0 && pixels == NULL) {
+        /* The application will fill these in later with valid values */
+    } else {
+        size_t minimalPitch;
+
+        if (SDL_ISPIXELFORMAT_FOURCC(format)) {
+            int p;
+            if (SDL_CalculateYUVSize(format, width, height, NULL, &p) < 0) {
+                SDL_InvalidParamError("pitch");
+                return NULL;
+            }
+            minimalPitch = p;
+        } else {
+            minimalPitch = SDL_CalculatePitch(format, width, SDL_TRUE);
+        }
+
+        if (pitch < 0 || (size_t)pitch < minimalPitch) {
             SDL_InvalidParamError("pitch");
             return NULL;
         }
-        minimalPitch = p;
-    } else {
-        minimalPitch = SDL_CalculatePitch(format, width, SDL_TRUE);
-    }
-
-    if (pitch < 0 || (pitch > 0 && ((size_t)pitch) < minimalPitch)) {
-        SDL_InvalidParamError("pitch");
-        return NULL;
     }
 
     surface = SDL_CreateSurface(0, 0, format);
diff --git a/test/testautomation_surface.c b/test/testautomation_surface.c
index ed9481b805cb..1c17c6dcad06 100644
--- a/test/testautomation_surface.c
+++ b/test/testautomation_surface.c
@@ -621,6 +621,14 @@ int surface_testOverflow(void *arg)
     SDLTest_AssertCheck(surface == NULL, "Should detect negative pitch");
     SDLTest_AssertCheck(SDL_strcmp(SDL_GetError(), expectedError) == 0,
                         "Expected \"%s\", got \"%s\"", expectedError, SDL_GetError());
+    surface = SDL_CreateSurfaceFrom(buf, 1, 1, 0, SDL_PIXELFORMAT_RGBA8888);
+    SDLTest_AssertCheck(surface == NULL, "Should detect zero pitch");
+    SDLTest_AssertCheck(SDL_strcmp(SDL_GetError(), expectedError) == 0,
+                        "Expected \"%s\", got \"%s\"", expectedError, SDL_GetError());
+    surface = SDL_CreateSurfaceFrom(NULL, 1, 1, 0, SDL_PIXELFORMAT_RGBA8888);
+    SDLTest_AssertCheck(surface != NULL, "Allow zero pitch for partially set up surfaces: %s",
+                        surface != NULL ? "(success)" : SDL_GetError());
+    SDL_DestroySurface(surface);
 
     /* Less than 1 byte per pixel: the pitch can legitimately be less than
      * the width, but it must be enough to hold the appropriate number of