SDL: Fixed clamp texture address mode in software renderer

From 86200d1203f3739d10deb6b72f1ce643b7c1bfb2 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 21 Jul 2025 10:03:49 -0700
Subject: [PATCH] Fixed clamp texture address mode in software renderer

---
 src/render/SDL_render.c            | 16 ++++++++
 src/render/software/SDL_triangle.c | 64 +++++++-----------------------
 2 files changed, 30 insertions(+), 50 deletions(-)

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index d66a8987c62a6..a3351ce1fc440 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -5001,6 +5001,22 @@ static bool SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer,
             }
         }
 
+        // Check if UVs within range
+        if (is_quad) {
+            const float *uv0_ = (const float *)((const char *)uv + A * color_stride);
+            const float *uv1_ = (const float *)((const char *)uv + B * color_stride);
+            const float *uv2_ = (const float *)((const char *)uv + C * color_stride);
+            const float *uv3_ = (const float *)((const char *)uv + C2 * color_stride);
+            if (uv0_[0] >= 0.0f && uv0_[0] <= 1.0f &&
+                uv1_[0] >= 0.0f && uv1_[0] <= 1.0f &&
+                uv2_[0] >= 0.0f && uv2_[0] <= 1.0f &&
+                uv3_[0] >= 0.0f && uv3_[0] <= 1.0f) {
+                // ok
+            } else {
+                is_quad = 0;
+            }
+        }
+
         // Start rendering rect
         if (is_quad) {
             SDL_FRect s;
diff --git a/src/render/software/SDL_triangle.c b/src/render/software/SDL_triangle.c
index 2a4d150b884e2..265dc46c81d0f 100644
--- a/src/render/software/SDL_triangle.c
+++ b/src/render/software/SDL_triangle.c
@@ -149,19 +149,6 @@ static void bounding_rect_fixedpoint(const SDL_Point *a, const SDL_Point *b, con
     r->h = (max_y - min_y) >> FP_BITS;
 }
 
-// bounding rect of three points
-static void bounding_rect(const SDL_Point *a, const SDL_Point *b, const SDL_Point *c, SDL_Rect *r)
-{
-    int min_x = SDL_min(a->x, SDL_min(b->x, c->x));
-    int max_x = SDL_max(a->x, SDL_max(b->x, c->x));
-    int min_y = SDL_min(a->y, SDL_min(b->y, c->y));
-    int max_y = SDL_max(a->y, SDL_max(b->y, c->y));
-    r->x = min_x;
-    r->y = min_y;
-    r->w = (max_x - min_x);
-    r->h = (max_y - min_y);
-}
-
 /* Triangle rendering, using Barycentric coordinates (w0, w1, w2)
  *
  * The cross product isn't computed from scratch at each iteration,
@@ -186,13 +173,25 @@ static void bounding_rect(const SDL_Point *a, const SDL_Point *b, const SDL_Poin
 #define TRIANGLE_GET_TEXTCOORD                                                          \
     int srcx = (int)(((Sint64)w0 * s2s0_x + (Sint64)w1 * s2s1_x + s2_x_area.x) / area); \
     int srcy = (int)(((Sint64)w0 * s2s0_y + (Sint64)w1 * s2s1_y + s2_x_area.y) / area); \
-    if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_WRAP) {                           \
+    if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_CLAMP) {                          \
+        if (srcx < 0) {                                                                 \
+            srcx = 0;                                                                   \
+        } else if (srcx >= src_surface->w) {                                            \
+            srcx = src_surface->w - 1;                                                  \
+        }                                                                               \
+    } else if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_WRAP) {                    \
         srcx %= src_surface->w;                                                         \
         if (srcx < 0) {                                                                 \
             srcx += (src_surface->w - 1);                                               \
         }                                                                               \
     }                                                                                   \
-    if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_WRAP) {                           \
+    if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_CLAMP) {                          \
+        if (srcy < 0) {                                                                 \
+            srcy = 0;                                                                   \
+        } else if (srcy >= src_surface->h) {                                            \
+            srcy = src_surface->h - 1;                                                  \
+        }                                                                               \
+    } else if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_WRAP) {                    \
         srcy %= src_surface->h;                                                         \
         if (srcy < 0) {                                                                 \
             srcy += (src_surface->h - 1);                                               \
@@ -543,41 +542,6 @@ bool SDL_SW_BlitTriangle(
 
     SDL_GetSurfaceBlendMode(src, &blend);
 
-    // TRIANGLE_GET_TEXTCOORD interpolates up to the max values included, so reduce by 1
-    if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_CLAMP ||
-        texture_address_mode_v == SDL_TEXTURE_ADDRESS_CLAMP) {
-        SDL_Rect srcrect;
-        bounding_rect(s0, s1, s2, &srcrect);
-        if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_CLAMP) {
-            int maxx = srcrect.x + srcrect.w;
-            if (srcrect.w > 0) {
-                if (s0->x == maxx) {
-                    s0->x--;
-                }
-                if (s1->x == maxx) {
-                    s1->x--;
-                }
-                if (s2->x == maxx) {
-                    s2->x--;
-                }
-            }
-        }
-        if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_CLAMP) {
-            int maxy = srcrect.y + srcrect.h;
-            if (srcrect.h > 0) {
-                if (s0->y == maxy) {
-                    s0->y--;
-                }
-                if (s1->y == maxy) {
-                    s1->y--;
-                }
-                if (s2->y == maxy) {
-                    s2->y--;
-                }
-            }
-        }
-    }
-
     if (is_uniform) {
         // SDL_GetSurfaceColorMod(src, &r, &g, &b);
         has_modulation = c0.r != 255 || c0.g != 255 || c0.b != 255 || c0.a != 255;