SDL: SDL_Rect: Added floating point versions of all the rectangle APIs.

From d81fee76235a1f13123818815ecebcb27764595d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 19 Mar 2022 10:27:31 -0400
Subject: [PATCH] SDL_Rect: Added floating point versions of all the rectangle
 APIs.

Fixes #5110.
---
 include/SDL_rect.h                | 122 +++++++-
 src/dynapi/SDL_dynapi_overrides.h |   5 +
 src/dynapi/SDL_dynapi_procs.h     |   5 +
 src/render/SDL_render.c           |  54 ----
 src/video/SDL_rect.c              | 448 +++---------------------------
 src/video/SDL_rect_impl.h         | 444 +++++++++++++++++++++++++++++
 6 files changed, 608 insertions(+), 470 deletions(-)
 create mode 100644 src/video/SDL_rect_impl.h

diff --git a/include/SDL_rect.h b/include/SDL_rect.h
index 6616ba6a281..5ff50f90316 100644
--- a/include/SDL_rect.h
+++ b/include/SDL_rect.h
@@ -54,8 +54,13 @@ typedef struct SDL_Point
 /**
  * The structure that defines a point (floating point)
  *
- * \sa SDL_EnclosePoints
- * \sa SDL_PointInRect
+ * \sa SDL_FRectEmpty
+ * \sa SDL_FRectEquals
+ * \sa SDL_HasIntersectionF
+ * \sa SDL_IntersectFRect
+ * \sa SDL_UnionFRect
+ * \sa SDL_EncloseFPoints
+ * \sa SDL_PointInFRect
  */
 typedef struct SDL_FPoint
 {
@@ -213,6 +218,119 @@ extern DECLSPEC SDL_bool SDLCALL SDL_IntersectRectAndLine(const SDL_Rect *
                                                           int *Y1, int *X2,
                                                           int *Y2);
 
+
+/* SDL_FRect versions... */
+
+/**
+ * Returns true if point resides inside a rectangle.
+ */
+SDL_FORCE_INLINE SDL_bool SDL_PointInFRect(const SDL_FPoint *p, const SDL_FRect *r)
+{
+    return ( (p->x >= r->x) && (p->x < (r->x + r->w)) &&
+             (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? SDL_TRUE : SDL_FALSE;
+}
+
+/**
+ * Returns true if the rectangle has no area.
+ */
+SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r)
+{
+    return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
+}
+
+/**
+ * Returns true if the two rectangles are equal.
+ */
+SDL_FORCE_INLINE SDL_bool SDL_FRectEquals(const SDL_FRect *a, const SDL_FRect *b)
+{
+    return (a && b && (a->x == b->x) && (a->y == b->y) &&
+            (a->w == b->w) && (a->h == b->h)) ? SDL_TRUE : SDL_FALSE;
+}
+
+/**
+ * Determine whether two rectangles intersect with float precision.
+ *
+ * If either pointer is NULL the function will return SDL_FALSE.
+ *
+ * \param A an SDL_FRect structure representing the first rectangle
+ * \param B an SDL_FRect structure representing the second rectangle
+ * \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise.
+ *
+ * \sa SDL_IntersectRect
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasIntersectionF(const SDL_FRect * A,
+                                                      const SDL_FRect * B);
+
+/**
+ * Calculate the intersection of two rectangles with float precision.
+ *
+ * If `result` is NULL then this function will return SDL_FALSE.
+ *
+ * \param A an SDL_FRect structure representing the first rectangle
+ * \param B an SDL_FRect structure representing the second rectangle
+ * \param result an SDL_FRect structure filled in with the intersection of
+ *               rectangles `A` and `B`
+ * \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise.
+ *
+ * \sa SDL_HasIntersectionF
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IntersectFRect(const SDL_FRect * A,
+                                                    const SDL_FRect * B,
+                                                    SDL_FRect * result);
+
+/**
+ * Calculate the union of two rectangles with float precision.
+ *
+ * \param A an SDL_FRect structure representing the first rectangle
+ * \param B an SDL_FRect structure representing the second rectangle
+ * \param result an SDL_FRect structure filled in with the union of rectangles
+ *               `A` and `B`
+ */
+extern DECLSPEC void SDLCALL SDL_UnionFRect(const SDL_FRect * A,
+                                            const SDL_FRect * B,
+                                            SDL_FRect * result);
+
+/**
+ * Calculate a minimal rectangle enclosing a set of points with float precision.
+ *
+ * If `clip` is not NULL then only points inside of the clipping rectangle are
+ * considered.
+ *
+ * \param points an array of SDL_FPoint structures representing points to be
+ *               enclosed
+ * \param count the number of structures in the `points` array
+ * \param clip an SDL_FRect used for clipping or NULL to enclose all points
+ * \param result an SDL_FRect structure filled in with the minimal enclosing
+ *               rectangle
+ * \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the
+ *          points were outside of the clipping rectangle.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_EncloseFPoints(const SDL_FPoint * points,
+                                                    int count,
+                                                    const SDL_FRect * clip,
+                                                    SDL_FRect * result);
+
+/**
+ * Calculate the intersection of a rectangle and line segment with float precision.
+ *
+ * This function is used to clip a line segment to a rectangle. A line segment
+ * contained entirely within the rectangle or that does not intersect will
+ * remain unchanged. A line segment that crosses the rectangle at either or
+ * both ends will be clipped to the boundary of the rectangle and the new
+ * coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary.
+ *
+ * \param rect an SDL_FRect structure representing the rectangle to intersect
+ * \param X1 a pointer to the starting X-coordinate of the line
+ * \param Y1 a pointer to the starting Y-coordinate of the line
+ * \param X2 a pointer to the ending X-coordinate of the line
+ * \param Y2 a pointer to the ending Y-coordinate of the line
+ * \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IntersectFRectAndLine(const SDL_FRect *
+                                                           rect, float *X1,
+                                                           float *Y1, float *X2,
+                                                           float *Y2);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 6e652e45a56..299dde30315 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -859,3 +859,8 @@
 #define SDL_GetTouchName SDL_GetTouchName_REAL
 #define SDL_ClearComposition SDL_ClearComposition_REAL
 #define SDL_IsTextInputShown SDL_IsTextInputShown_REAL
+#define SDL_HasIntersectionF SDL_HasIntersectionF_REAL
+#define SDL_IntersectFRect SDL_IntersectFRect_REAL
+#define SDL_UnionFRect SDL_UnionFRect_REAL
+#define SDL_EncloseFPoints SDL_EncloseFPoints_REAL
+#define SDL_IntersectFRectAndLine SDL_IntersectFRectAndLine_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 6323185767b..2c0d5b03e88 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -930,3 +930,8 @@ SDL_DYNAPI_PROC(int,SDL_AndroidSendMessage,(Uint32 a, int b),(a,b),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetTouchName,(int a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_IsTextInputShown,(void),(),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasIntersectionF,(const SDL_FRect *a, const SDL_FRect *b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_IntersectFRect,(const SDL_FRect *a, const SDL_FRect *b, SDL_FRect *c),(a,b,c),return)
+SDL_DYNAPI_PROC(void,SDL_UnionFRect,(const SDL_FRect *a, const SDL_FRect *b, SDL_FRect *c),(a,b,c),)
+SDL_DYNAPI_PROC(SDL_bool,SDL_EncloseFPoints,(const SDL_FPoint *a, int b, const SDL_FRect *c, SDL_FRect *d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_IntersectFRectAndLine,(const SDL_FRect *a, float *b, float *c, float *d, float *e),(a,b,c,d,e),return)
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 09e216dbefb..af38a8a939d 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -3382,60 +3382,6 @@ SDL_RenderFillRectsF(SDL_Renderer * renderer,
     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
 }
 
-/* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
-SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r)
-{
-    return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
-}
-
-/* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
-static SDL_bool
-SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B)
-{
-    float Amin, Amax, Bmin, Bmax;
-
-    if (!A) {
-        SDL_InvalidParamError("A");
-        return SDL_FALSE;
-    }
-
-    if (!B) {
-        SDL_InvalidParamError("B");
-        return SDL_FALSE;
-    }
-
-    /* Special cases for empty rects */
-    if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) {
-        return SDL_FALSE;
-    }
-
-    /* Horizontal intersection */
-    Amin = A->x;
-    Amax = Amin + A->w;
-    Bmin = B->x;
-    Bmax = Bmin + B->w;
-    if (Bmin > Amin)
-        Amin = Bmin;
-    if (Bmax < Amax)
-        Amax = Bmax;
-    if (Amax <= Amin)
-        return SDL_FALSE;
-
-    /* Vertical intersection */
-    Amin = A->y;
-    Amax = Amin + A->h;
-    Bmin = B->y;
-    Bmax = Bmin + B->h;
-    if (Bmin > Amin)
-        Amin = Bmin;
-    if (Bmax < Amax)
-        Amax = Bmax;
-    if (Amax <= Amin)
-        return SDL_FALSE;
-
-    return SDL_TRUE;
-}
-
 int
 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
diff --git a/src/video/SDL_rect.c b/src/video/SDL_rect.c
index 29e1af8f1ea..dfa939032a4 100644
--- a/src/video/SDL_rect.c
+++ b/src/video/SDL_rect.c
@@ -23,420 +23,8 @@
 #include "SDL_rect.h"
 #include "SDL_rect_c.h"
 
-SDL_bool
-SDL_HasIntersection(const SDL_Rect * A, const SDL_Rect * B)
-{
-    int Amin, Amax, Bmin, Bmax;
-
-    if (!A) {
-        SDL_InvalidParamError("A");
-        return SDL_FALSE;
-    } else if (!B) {
-        SDL_InvalidParamError("B");
-        return SDL_FALSE;
-    } else if (SDL_RectEmpty(A) || SDL_RectEmpty(B)) {
-        return SDL_FALSE;  /* Special cases for empty rects */
-    }
-
-    /* Horizontal intersection */
-    Amin = A->x;
-    Amax = Amin + A->w;
-    Bmin = B->x;
-    Bmax = Bmin + B->w;
-    if (Bmin > Amin) {
-        Amin = Bmin;
-    }
-    if (Bmax < Amax) {
-        Amax = Bmax;
-    }
-    if (Amax <= Amin) {
-        return SDL_FALSE;
-    }
-    /* Vertical intersection */
-    Amin = A->y;
-    Amax = Amin + A->h;
-    Bmin = B->y;
-    Bmax = Bmin + B->h;
-    if (Bmin > Amin) {
-        Amin = Bmin;
-    }
-    if (Bmax < Amax) {
-        Amax = Bmax;
-    }
-    if (Amax <= Amin) {
-        return SDL_FALSE;
-    }
-    return SDL_TRUE;
-}
-
-SDL_bool
-SDL_IntersectRect(const SDL_Rect * A, const SDL_Rect * B, SDL_Rect * result)
-{
-    int Amin, Amax, Bmin, Bmax;
-
-    if (!A) {
-        SDL_InvalidParamError("A");
-        return SDL_FALSE;
-    } else if (!B) {
-        SDL_InvalidParamError("B");
-        return SDL_FALSE;
-    } else if (!result) {
-        SDL_InvalidParamError("result");
-        return SDL_FALSE;
-    } else if (SDL_RectEmpty(A) || SDL_RectEmpty(B)) {  /* Special cases for empty rects */
-        result->w = 0;
-        result->h = 0;
-        return SDL_FALSE;
-    }
-
-    /* Horizontal intersection */
-    Amin = A->x;
-    Amax = Amin + A->w;
-    Bmin = B->x;
-    Bmax = Bmin + B->w;
-    if (Bmin > Amin) {
-        Amin = Bmin;
-    }
-    result->x = Amin;
-    if (Bmax < Amax) {
-        Amax = Bmax;
-    }
-    result->w = Amax - Amin;
-
-    /* Vertical intersection */
-    Amin = A->y;
-    Amax = Amin + A->h;
-    Bmin = B->y;
-    Bmax = Bmin + B->h;
-    if (Bmin > Amin) {
-        Amin = Bmin;
-    }
-    result->y = Amin;
-    if (Bmax < Amax) {
-        Amax = Bmax;
-    }
-    result->h = Amax - Amin;
-
-    return !SDL_RectEmpty(result);
-}
-
-void
-SDL_UnionRect(const SDL_Rect * A, const SDL_Rect * B, SDL_Rect * result)
-{
-    int Amin, Amax, Bmin, Bmax;
-
-    if (!A) {
-        SDL_InvalidParamError("A");
-        return;
-    } else if (!B) {
-        SDL_InvalidParamError("B");
-        return;
-    } else if (!result) {
-        SDL_InvalidParamError("result");
-        return;
-    } else if (SDL_RectEmpty(A)) {  /* Special cases for empty Rects */
-        if (SDL_RectEmpty(B)) {  /* A and B empty */
-            SDL_zerop(result);
-        } else {  /* A empty, B not empty */
-            *result = *B;
-        }
-        return;
-    } else if (SDL_RectEmpty(B)) {  /* A not empty, B empty */
-       *result = *A;
-       return;
-    }
-
-    /* Horizontal union */
-    Amin = A->x;
-    Amax = Amin + A->w;
-    Bmin = B->x;
-    Bmax = Bmin + B->w;
-    if (Bmin < Amin) {
-        Amin = Bmin;
-    }
-    result->x = Amin;
-    if (Bmax > Amax) {
-        Amax = Bmax;
-    }
-    result->w = Amax - Amin;
-
-    /* Vertical union */
-    Amin = A->y;
-    Amax = Amin + A->h;
-    Bmin = B->y;
-    Bmax = Bmin + B->h;
-    if (Bmin < Amin) {
-        Amin = Bmin;
-    }
-    result->y = Amin;
-    if (Bmax > Amax) {
-        Amax = Bmax;
-    }
-    result->h = Amax - Amin;
-}
-
-SDL_bool
-SDL_EnclosePoints(const SDL_Point * points, int count, const SDL_Rect * clip,
-                  SDL_Rect * result)
-{
-    int minx = 0;
-    int miny = 0;
-    int maxx = 0;
-    int maxy = 0;
-    int x, y, i;
-
-    if (!points) {
-        SDL_InvalidParamError("points");
-        return SDL_FALSE;
-    } else if (count < 1) {
-        SDL_InvalidParamError("count");
-        return SDL_FALSE;
-    }
-
-    if (clip) {
-        SDL_bool added = SDL_FALSE;
-        const int clip_minx = clip->x;
-        const int clip_miny = clip->y;
-        const int clip_maxx = clip->x+clip->w-1;
-        const int clip_maxy = clip->y+clip->h-1;
-
-        /* Special case for empty rectangle */
-        if (SDL_RectEmpty(clip)) {
-            return SDL_FALSE;
-        }
-
-        for (i = 0; i < count; ++i) {
-            x = points[i].x;
-            y = points[i].y;
-
-            if (x < clip_minx || x > clip_maxx ||
-                y < clip_miny || y > clip_maxy) {
-                continue;
-            }
-            if (!added) {
-                /* Special case: if no result was requested, we are done */
-                if (result == NULL) {
-                    return SDL_TRUE;
-                }
-
-                /* First point added */
-                minx = maxx = x;
-                miny = maxy = y;
-                added = SDL_TRUE;
-                continue;
-            }
-            if (x < minx) {
-                minx = x;
-            } else if (x > maxx) {
-                maxx = x;
-            }
-            if (y < miny) {
-                miny = y;
-            } else if (y > maxy) {
-                maxy = y;
-            }
-        }
-        if (!added) {
-            return SDL_FALSE;
-        }
-    } else {
-        /* Special case: if no result was requested, we are done */
-        if (result == NULL) {
-            return SDL_TRUE;
-        }
-
-        /* No clipping, always add the first point */
-        minx = maxx = points[0].x;
-        miny = maxy = points[0].y;
-
-        for (i = 1; i < count; ++i) {
-            x = points[i].x;
-            y = points[i].y;
-
-            if (x < minx) {
-                minx = x;
-            } else if (x > maxx) {
-                maxx = x;
-            }
-            if (y < miny) {
-                miny = y;
-            } else if (y > maxy) {
-                maxy = y;
-            }
-        }
-    }
-
-    if (result) {
-        result->x = minx;
-        result->y = miny;
-        result->w = (maxx-minx)+1;
-        result->h = (maxy-miny)+1;
-    }
-    return SDL_TRUE;
-}
-
-/* Use the Cohen-Sutherland algorithm for line clipping */
-#define CODE_BOTTOM 1
-#define CODE_TOP    2
-#define CODE_LEFT   4
-#define CODE_RIGHT  8
-
-static int
-ComputeOutCode(const SDL_Rect * rect, int x, int y)
-{
-    int code = 0;
-    if (y < rect->y) {
-        code |= CODE_TOP;
-    } else if (y >= rect->y + rect->h) {
-        code |= CODE_BOTTOM;
-    }
-    if (x < rect->x) {
-        code |= CODE_LEFT;
-    } else if (x >= rect->x + rect->w) {
-        code |= CODE_RIGHT;
-    }
-    return code;
-}
-
-SDL_bool
-SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2,
-                         int *Y2)
-{
-    int x = 0;
-    int y = 0;
-    int x1, y1;
-    int x2, y2;
-    int rectx1;
-    int recty1;
-    int rectx2;
-    int recty2;
-    int outcode1, outcode2;
-
-    if (!rect) {
-        SDL_InvalidParamError("rect");
-        return SDL_FALSE;
-    } else if (!X1) {
-        SDL_InvalidParamError("X1");
-        return SDL_FALSE;
-    } else if (!Y1) {
-        SDL_InvalidParamError("Y1");
-        return SDL_FALSE;
-    } else if (!X2) {
-        SDL_InvalidParamError("X2");
-        return SDL_FALSE;
-    } else if (!Y2) {
-        SDL_InvalidParamError("Y2");
-        return SDL_FALSE;
-    } else if (SDL_RectEmpty(rect)) {
-        return SDL_FALSE;  /* Special case for empty rect */
-    }
-
-    x1 = *X1;
-    y1 = *Y1;
-    x2 = *X2;
-    y2 = *Y2;
-    rectx1 = rect->x;
-    recty1 = rect->y;
-    rectx2 = rect->x + rect->w - 1;
-    recty2 = rect->y + rect->h - 1;
-
-    /* Check to see if entire line is inside rect */
-    if (x1 >= rectx1 && x1 <= rectx2 && x2 >= rectx1 && x2 <= rectx2 &&
-        y1 >= recty1 && y1 <= recty2 && y2 >= recty1 && y2 <= recty2) {
-        return SDL_TRUE;
-    }
-
-    /* Check to see if entire line is to one side of rect */
-    if ((x1 < rectx1 && x2 < rectx1) || (x1 > rectx2 && x2 > rectx2) ||
-        (y1 < recty1 && y2 < recty1) || (y1 > recty2 && y2 > recty2)) {
-        return SDL_FALSE;
-    }
-
-    if (y1 == y2) {  /* Horizontal line, easy to clip */
-        if (x1 < rectx1) {
-            *X1 = rectx1;
-        } else if (x1 > rectx2) {
-            *X1 = rectx2;
-        }
-        if (x2 < rectx1) {
-            *X2 = rectx1;
-        } else if (x2 > rectx2) {
-            *X2 = rectx2;
-        }
-        return SDL_TRUE;
-    }
-
-    if (x1 == x2) {  /* Vertical line, easy to clip */
-        if (y1 < recty1) {
-            *Y1 = recty1;
-        } else if (y1 > recty2) {
-            *Y1 = recty2;
-        }
-        if (y2 < recty1) {
-            *Y2 = recty1;
-        } else if (y2 > recty2) {
-            *Y2 = recty2;
-        }
-        return SDL_TRUE;
-    }
-
-    /* More complicated Cohen-Sutherland algorithm */
-    outcode1 = ComputeOutCode(rect, x1, y1);
-    outcode2 = ComputeOutCode(rect, x2, y2);
-    while (outcode1 || outcode2) {
-        if (outcode1 & outcode2) {
-            return SDL_FALSE;
-        }
-
-        if (outcode1) {
-            if (outcode1 & CODE_TOP) {
-                y = recty1;
-                x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
-            } else if (outcode1 & CODE_BOTTOM) {
-                y = recty2;
-                x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
-            } else if (outcode1 & CODE_LEFT) {
-                x = rectx1;
-                y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
-            } else if (outcode1 & CODE_RIGHT) {
-                x = rectx2;
-                y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
-            }
-            x1 = x;
-            y1 = y;
-            outcode1 = ComputeOutCode(rect, x, y);
-        } else {
-            if (outcode2 & CODE_TOP) {
-                y = recty1;
-                x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
-            } else if (outcode2 & CODE_BOTTOM) {
-                y = recty2;
-                x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
-            } else if (outcode2 & CODE_LEFT) {
-                /* If this assertion ever fires, here's the static analysis that warned about it:
-                   http://buildbot.libsdl.org/sdl-static-analysis/sdl-macosx-static-analysis/sdl-macosx-static-analysis-1101/report-b0d01a.html#EndPath */
-                SDL_assert(x2 != x1);  /* if equal: division by zero. */
-                x = rectx1;
-                y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
-            } else if (outcode2 & CODE_RIGHT) {
-                /* If this assertion ever fires, here's the static analysis that warned about it:
-                   http://buildbot.libsdl.org/sdl-static-analysis/sdl-macosx-static-analysis/sdl-macosx-static-analysis-1101/report-39b114.html#EndPath */
-                SDL_assert(x2 != x1);  /* if equal: division by zero. */
-                x = rectx2;
-                y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
-            }
-            x2 = x;
-            y2 = y;
-            outcode2 = ComputeOutCode(rect, x, y);
-        }
-    }
-    *X1 = x1;
-    *Y1 = y1;
-    *X2 = x2;
-    *Y2 = y2;
-    return SDL_TRUE;
-}
-
+/* There's no float version of this at the moment, because it's not a public API
+   and internally we only need the int version. */
 SDL_bool
 SDL_GetSpanEnclosingRect(int width, int height,
                          int numrects, const SDL_Rect * rects, SDL_Rect *span)
@@ -492,4 +80,36 @@ SDL_GetSpanEnclosingRect(int width, int height,
     return SDL_FALSE;
 }
 
+
+/* For use with the Cohen-Sutherland algorithm for line clipping, in SDL_rect_impl.h */
+#define CODE_BOTTOM 1
+#define CODE_TOP    2
+#define CODE_LEFT   4
+#define CODE_RIGHT  8
+
+/* Same code twice, for float and int versions... */
+#define RECTTYPE SDL_Rect
+#define POINTTYPE SDL_Point
+#define SCALARTYPE int
+#define COMPUTEOUTCODE ComputeOutCode
+#define SDL_HASINTERSECTION SDL_HasIntersection
+#define SDL_INTERSECTRECT SDL_IntersectRect
+#define SDL_RECTEMPTY SDL_RectEmpty
+#define SDL_UNIONRECT SDL_UnionRect
+#define SDL_ENCLOSEPOINTS SDL_EnclosePoints
+#define SDL_INTERSECTRECTANDLINE SDL_IntersectRectAndLine
+#include "SDL_rect_impl.h"
+
+#define RECTTYPE SDL_FRect
+#define POINTTYPE SDL_FPoint
+#define SCALARTYPE float
+#define COMPUTEOUTCODE ComputeOutCodeF
+#define SDL_HASINTERSECTION SDL_HasIntersectionF
+#define SDL_INTERSECTRECT SDL_IntersectFRect
+#define SDL_RECTEMPTY SDL_FRectEmpty
+#define SDL_UNIONRECT SDL_UnionFRect
+#define SDL_ENCLOSEPOINTS SDL_EncloseFPoints
+#define SDL_INTERSECTRECTANDLINE SDL_IntersectFRectAndLine
+#include "SDL_rect_impl.h"
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/SDL_rect_impl.h b/src/video/SDL_rect_impl.h
new file mode 100644
index 00000000000..993bb8eb0ab
--- /dev/null
+++ b/src/video/SDL_rect_impl.h
@@ -0,0 +1,444 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* This file is #included twice to support int and float versions with the same code. */
+
+SDL_bool
+SDL_HASINTERSECTION(const RECTTYPE * A, const RECTTYPE * B)
+{
+    SCALARTYPE Amin, Amax, Bmin, Bmax;
+
+    if (!A) {
+        SDL_InvalidParamError("A");
+        return SDL_FALSE;
+    } else if (!B) {
+        SDL_InvalidParamError("B");
+        return SDL_FALSE;
+    } else if (SDL_RECTEMPTY(A) || SDL_RECTEMPTY(B)) {
+        return SDL_FALSE;  /* Special cases for empty rects */
+    }
+
+    /* Horizontal intersection */
+    Amin = A->x;
+    Amax = Amin + A->w;
+    Bmin = B->x;
+    Bmax = Bmin + B->w;
+    if (Bmin > Amin) {
+        Amin = Bmin;
+    }
+    if (Bmax < Amax) {
+        Amax = Bmax;
+    }
+    if (Amax <= Amin) {
+        return SDL_FALSE;
+    }
+    /* Vertical intersection */
+    Amin = A->y;
+    Amax = Amin + A->h;
+    Bmin = B->y;
+    Bmax = Bmin + B->h;
+    if (Bmin > Amin) {
+        Amin = Bmin;
+    }
+    if (Bmax < Amax) {
+        Amax = Bmax;
+    }
+    if (Amax <= Amin) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+SDL_bool
+SDL_INTERSECTRECT(const RECTTYPE * A, const RECTTYPE * B, RECTTYPE * result)
+{
+    SCALARTYPE Amin, Amax, Bmin, Bmax;
+
+    if (!A) {
+        SDL_InvalidParamError("A");
+        return SDL_FALSE;
+    } else if (!B) {
+        SDL_InvalidParamError("B");
+        return SDL_FALSE;
+    } else if (!result) {
+        SDL_InvalidParamError("result");
+        return SDL_FALSE;
+    } else if (SDL_RECTEMPTY(A) || SDL_RECTEMPTY(B)) {  /* Special cases for empty rects */
+        result->w = 0;
+        result->h = 0;
+        return SDL_FALSE;
+    }
+
+    /* Horizontal intersection */
+    Amin = A->x;
+    Amax = Amin + A->w;
+    Bmin = B->x;
+    Bmax = Bmin + B->w;
+    if (Bmin > Amin) {
+        Amin = Bmin;
+    }
+    result->x = Amin;
+    if (Bmax < Amax) {
+        Amax = Bmax;
+    }
+    result->w = Amax - Amin;
+
+    /* Vertical intersection */
+    Amin = A->y;
+    Amax = Amin + A->h;
+    Bmin = B->y;
+    Bmax = Bmin + B->h;
+    if (Bmin > Amin) {
+        Amin = Bmin;
+    }
+    result->y = Amin;
+    if (Bmax < Amax) {
+        Amax = Bmax;
+    }
+    result->h = Amax - Amin;
+
+    return !SDL_RECTEMPTY(result);
+}
+
+void
+SDL_UNIONRECT(const RECTTYPE * A, const RECTTYPE * B, RECTTYPE * result)
+{
+    SCALARTYPE Amin, Amax, Bmin, Bmax;
+
+    if (!A) {
+        SDL_InvalidParamError("A");
+        return;
+    } else if (!B) {
+        SDL_InvalidParamError("B");
+        return;
+    } else if (!result) {
+        SDL_InvalidParamError("result");
+        return;
+    } else if (SDL_RECTEMPTY(A)) {  /* Special cases for empty Rects */
+        if (SDL_RECTEMPTY(B)) {  /* A and B empty */
+            SDL_zerop(result);
+        } else {  /* A empty, B not empty */
+            *result = *B;
+        }
+        return;
+    } else if (SDL_RECTEMPTY(B)) {  /* A not empty, B empty */
+       *result = *A;
+       return;
+    }
+
+    /* Horizontal union */
+    Amin = A->x;
+    Amax = Amin + A->w;
+    Bmin = B->x;
+    Bmax = Bmin + B->w;
+    if (Bmin < Amin) {
+        Amin = Bmin;
+    }
+    result->x = Amin;
+    if (Bmax > Amax) {
+        Amax = Bmax;
+    }
+    result->w = Amax - Amin;
+
+    /* Vertical union */
+    Amin = A->y;
+    Amax = Amin + A->h;
+    Bmin = B->y;
+    Bmax = Bmin + B->h;
+    if (Bmin < Amin) {
+        Amin = Bmin;
+    }
+    result->y = Amin;
+    if (Bmax > Amax) {
+        Amax = Bmax;
+    }
+    result->h = Amax - Amin;
+}
+
+SDL_bool SDL_ENCLOSEPOINTS(const POINTTYPE * points, int count, const RECTTYPE * clip,
+                  RECTTYPE * result)
+{
+    SCALARTYPE minx = 0;
+    SCALARTYPE miny = 0;
+    SCALARTYPE maxx = 0;
+    SCALARTYPE maxy = 0;
+    SCALARTYPE x, y;
+    int i;
+
+    if (!points) {
+        SDL_InvalidParamError("points");
+        return SDL_FALSE;
+    } else if (count < 1) {
+        SDL_InvalidParamError("count");
+        return SDL_FALSE;
+    }
+
+    if (clip) {
+        SDL_bool added = SDL_FALSE;
+        const SCALARTYPE clip_minx = clip->x;
+        const SCALARTYPE clip_miny = clip->y;
+        const SCALARTYPE clip_maxx = clip->x+clip->w-1;
+        const SCALARTYPE clip_maxy = clip->y+clip->h-1;
+
+        /* Special case for empty rectangle */
+        if (SDL_RECTEMPTY(clip)) {
+            return SDL_FALSE;
+        }
+
+        for (i = 0; i < count; ++i) {
+            x = points[i].x;
+            y = points[i].y;
+
+            if (x < clip_minx || x > clip_maxx ||
+                y < clip_miny || y > clip_maxy) {
+                continue;
+            }
+            if (!added) {
+                /* Special case: if no result was requested, we are done */
+                if (result == NULL) {
+                    return SDL_TRUE;
+                }
+
+                /* First point added */
+                minx = maxx = x;
+                miny = maxy = y;
+                added = SDL_TRUE;
+                continue;
+            }
+            if (x < minx) {
+                minx = x;
+            } else if (x > maxx) {
+                maxx = x;
+            }
+            if (y < miny) {
+                miny = y;
+            } else if (y > maxy) {
+                maxy = y;
+            }
+        }
+        if (!added) {
+            return SDL_FALSE;
+        }
+    } else {
+        /* Special case: if no result was requested, we are done */
+        if (result == NULL) {
+            return SDL_TRUE;
+        }
+
+        /* No clipping, always add the first point */
+        minx = maxx = points[0].x;
+        miny = maxy = points[0].y;
+
+        for (i = 1; i < count; ++i) {
+            x = points[i].x;
+            y = points[i].y;
+
+            if (x < minx) {
+                minx = x;
+            } else if (x > maxx) {
+                maxx = x;
+            }
+            if (y < miny) {
+                miny = y;
+            } else if (y > maxy) {
+                maxy = y;
+            }
+        }
+    }
+
+    if (result) {
+        result->x = minx;
+        result->y = miny;
+        result->w = (maxx-minx)+1;
+        result->h = (maxy-miny)+1;
+    }
+    return SDL_TRUE;
+}
+
+/* Use the Cohen-Sutherland algorithm for line clipping */
+static int
+COMPUTEOUTCODE(const RECTTYPE * rect, SCALARTYPE x, SCALARTYPE y)
+{
+    int code = 0;
+    if (y < rect->y) {
+        code |= CODE_TOP;
+    } else if (y >= rect->y + rect->h) {
+        code |= CODE_BOTTOM;
+    }
+    if (x < rect->x) {
+        code |= CODE_LEFT;
+    } else if (x >= rect->x + rect->w) {
+        code |= CODE_RIGHT;
+    }
+    return code;
+}
+
+SDL_bool
+SDL_INTERSECTRECTANDLINE(const RECTTYPE * rect, SCALARTYPE *X1, SCALARTYPE *Y1, SCALARTYPE *X2,
+                         SCALARTYPE *Y2)
+{
+    SCALARTYPE x = 0;
+    SCALARTYPE y = 0;
+    SCALARTYPE x1, y1;
+    SCALARTYPE x2, y2;
+    SCALARTYPE rectx1;
+    SCALARTYPE recty1;
+    SCALARTYPE rectx2;
+    SCALARTYPE recty2;
+    int outcode1, outcode2;
+
+    if (!rect) {
+        SDL_InvalidParamError("rect");
+        return SDL_FALSE;
+    } else if (!X1) {
+        SDL_InvalidParamError("X1");
+        return SDL_FALSE;
+    } else if (!Y1) {
+        SDL_InvalidParamError("Y1");
+        return SDL_FALSE;
+    } else if (!X2) {
+        SDL_InvalidParamError("X2");
+        return SDL_FALSE;
+    } else if (!Y2) {
+        SDL_InvalidParamError("Y2");
+        return SDL_FALSE;
+    } else if (SDL_RECTEMPTY(rect)) {
+        return SDL_FALSE;  /* Special case for empty rect */
+    }
+
+    x1 = *X1;
+    y1 = *Y1;
+    x2 = *X2;
+    y2 = *Y2;
+    rectx1 = rect->x;
+    recty1 = rect->y;
+    rectx2 = rect->x + rect->w - 1;
+    recty2 = rect->y + rect->h - 1;
+
+    /* Check to see if entire line is inside rect */
+    if (x1 >= rectx1 && x1 <= rectx2 && x2 >= rectx1 && x2 <= rectx2 &&
+        y1 >= recty1 && y1

(Patch may be truncated, please check the link at the top of this post.)