SDL: A zero source rect is valid for blitting

From 345d9bfe192493cbaf7cc547a00c0ad1d1e3bf1b Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 25 Sep 2024 22:15:20 -0700
Subject: [PATCH] A zero source rect is valid for blitting

A zero sized source rect draws pixels sourced from a 0x0 region on the source surface.

Fixes https://github.com/libsdl-org/SDL/issues/8580
---
 src/video/SDL_blit.c          |  2 +-
 src/video/SDL_surface.c       |  2 +-
 test/testautomation_surface.c | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c
index c853accf91c03..2e782bdb4c00b 100644
--- a/src/video/SDL_blit.c
+++ b/src/video/SDL_blit.c
@@ -59,7 +59,7 @@ static bool SDLCALL SDL_SoftBlit(SDL_Surface *src, const SDL_Rect *srcrect,
     }
 
     // Set up source and destination buffer pointers, and BLIT!
-    if (okay && !SDL_RectEmpty(srcrect)) {
+    if (okay) {
         SDL_BlitFunc RunBlit;
         SDL_BlitInfo *info = &src->internal->map.info;
 
diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c
index 26df19a12e700..46c1626fc5834 100644
--- a/src/video/SDL_surface.c
+++ b/src/video/SDL_surface.c
@@ -1226,7 +1226,7 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
     SDL_GetRectIntersection(clip_rect, &final_dst, &final_dst);
 
     if (final_dst.w == 0 || final_dst.h == 0 ||
-        final_src.w <= 0 || final_src.h <= 0) {
+        final_src.w < 0 || final_src.h < 0) {
         // No-op.
         return true;
     }
diff --git a/test/testautomation_surface.c b/test/testautomation_surface.c
index 838fe8f291a15..7ba11caac49b7 100644
--- a/test/testautomation_surface.c
+++ b/test/testautomation_surface.c
@@ -821,6 +821,27 @@ static int SDLCALL surface_testLoadFailure(void *arg)
     return TEST_COMPLETED;
 }
 
+/**
+ * Tests blitting from a zero sized source rectangle
+ */
+static int SDLCALL surface_testBlitZeroSource(void *arg)
+{
+    SDL_Surface *src = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA8888);
+    SDL_Surface *dst = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA8888);
+    SDL_Rect srcrect = { 0, 0, 0, 0 };
+    int ret;
+
+    SDLTest_AssertPass("Call to SDL_BlitSurfaceScaled() with zero sized source rectangle");
+    SDL_FillSurfaceRect(src, NULL, SDL_MapSurfaceRGB(src, 255, 255, 255));
+    SDL_BlitSurfaceScaled(src, &srcrect, dst, NULL, SDL_SCALEMODE_NEAREST);
+    ret = SDLTest_CompareSurfaces(dst, src, 0);
+    SDLTest_AssertCheck(ret == 0, "Validate result from SDLTest_CompareSurfaces, expected: 0, got: %i", ret);
+    SDL_DestroySurface(src);
+    SDL_DestroySurface(dst);
+
+    return TEST_COMPLETED;
+}
+
 /**
  * Tests some blitting routines.
  */
@@ -1465,6 +1486,10 @@ static const SDLTest_TestCaseReference surfaceTestSaveLoadBitmap = {
     surface_testSaveLoadBitmap, "surface_testSaveLoadBitmap", "Tests sprite saving and loading.", TEST_ENABLED
 };
 
+static const SDLTest_TestCaseReference surfaceTestBlitZeroSource = {
+    surface_testBlitZeroSource, "surface_testBlitZeroSource", "Tests blitting from a zero sized source rectangle", TEST_ENABLED
+};
+
 static const SDLTest_TestCaseReference surfaceTestBlit = {
     surface_testBlit, "surface_testBlit", "Tests basic blitting.", TEST_ENABLED
 };
@@ -1552,6 +1577,7 @@ static const SDLTest_TestCaseReference surfaceTestPremultiplyAlpha = {
 /* Sequence of Surface test cases */
 static const SDLTest_TestCaseReference *surfaceTests[] = {
     &surfaceTestSaveLoadBitmap,
+    &surfaceTestBlitZeroSource,
     &surfaceTestBlit,
     &surfaceTestBlitTiled,
     &surfaceTestBlit9Grid,