SDL: Handle DPI scaling in SDL_GetWindowSurface

From 67c91353e01f6f2c0cc80c17eeddbad6def7cb01 Mon Sep 17 00:00:00 2001
From: Eric Wasylishen <[EMAIL REDACTED]>
Date: Wed, 11 Jan 2023 19:45:01 -0700
Subject: [PATCH] Handle DPI scaling in SDL_GetWindowSurface

Fixes DPI awareness of testdrawchessboard (previously, the surface was
being created in points instead of pixels, resulting in the demo app
only drawing in a corner of the screen on High-DPI displays)

*_CreateWindowFramebuffer()/*_UpdateWindowFramebuffer(): are updated
to use SDL_GetWindowSizeInPixels instead of SDL_GetWindowSize() or
window->w/window->h.

Most of the _CreateWindowFramebuffer backends are untested except
for Windows.

Fixes #7047
---
 include/SDL3/SDL_video.h                      |  2 +-
 src/render/software/SDL_render_sw.c           |  2 +-
 src/video/SDL_video.c                         | 23 +++++++++----
 src/video/dummy/SDL_nullframebuffer.c         |  2 +-
 .../emscripten/SDL_emscriptenframebuffer.c    |  2 +-
 src/video/n3ds/SDL_n3dsframebuffer.c          |  2 +-
 src/video/ngage/SDL_ngageframebuffer.cpp      |  2 +-
 .../offscreen/SDL_offscreenframebuffer.c      |  2 +-
 src/video/riscos/SDL_riscosframebuffer.c      | 11 ++++---
 src/video/windows/SDL_windowsframebuffer.c    | 11 ++++---
 src/video/x11/SDL_x11framebuffer.c            | 33 +++++++++++--------
 11 files changed, 57 insertions(+), 35 deletions(-)

diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index 07a978c6946f..f9f6ef1a1fb4 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -1261,7 +1261,7 @@ extern DECLSPEC int SDLCALL SDL_UpdateWindowSurface(SDL_Window * window);
  *
  * \param window the window to update
  * \param rects an array of SDL_Rect structures representing areas of the
- *              surface to copy
+ *              surface to copy, in pixels
  * \param numrects the number of rectangles
  * \returns 0 on success or a negative error code on failure; call
  *          SDL_GetError() for more information.
diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c
index 814fdd947b73..4c4b0391a173 100644
--- a/src/render/software/SDL_render_sw.c
+++ b/src/render/software/SDL_render_sw.c
@@ -90,7 +90,7 @@ static int SW_GetOutputSize(SDL_Renderer *renderer, int *w, int *h)
     }
 
     if (renderer->window) {
-        SDL_GetWindowSize(renderer->window, w, h);
+        SDL_GetWindowSizeInPixels(renderer->window, w, h);
         return 0;
     }
 
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 6d9644eca025..569c845fb43d 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -203,6 +203,9 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U
     SDL_RendererInfo info;
     SDL_WindowTextureData *data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
     int i;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     if (data == NULL) {
         SDL_Renderer *renderer = NULL;
@@ -280,7 +283,7 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U
 
     data->texture = SDL_CreateTexture(data->renderer, *format,
                                       SDL_TEXTUREACCESS_STREAMING,
-                                      window->w, window->h);
+                                      w, h);
     if (!data->texture) {
         /* codechecker_false_positive [Malloc] Static analyzer doesn't realize allocated `data` is saved to SDL_WINDOWTEXTUREDATA and not leaked here. */
         return -1; /* NOLINT(clang-analyzer-unix.Malloc) */
@@ -288,11 +291,11 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U
 
     /* Create framebuffer data */
     data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format);
-    data->pitch = (((window->w * data->bytes_per_pixel) + 3) & ~3);
+    data->pitch = (((w * data->bytes_per_pixel) + 3) & ~3);
 
     {
         /* Make static analysis happy about potential SDL_malloc(0) calls. */
-        const size_t allocsize = (size_t)window->h * data->pitch;
+        const size_t allocsize = (size_t)h * data->pitch;
         data->pixels = SDL_malloc((allocsize > 0) ? allocsize : 1);
         if (!data->pixels) {
             return SDL_OutOfMemory();
@@ -316,6 +319,9 @@ static int SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window *window,
     SDL_WindowTextureData *data;
     SDL_Rect rect;
     void *src;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
     if (data == NULL || !data->texture) {
@@ -323,7 +329,7 @@ static int SDL_UpdateWindowTexture(SDL_VideoDevice *unused, SDL_Window *window,
     }
 
     /* Update a single rect that contains subrects for best DMA performance */
-    if (SDL_GetSpanEnclosingRect(window->w, window->h, numrects, rects, &rect)) {
+    if (SDL_GetSpanEnclosingRect(w, h, numrects, rects, &rect)) {
         src = (void *)((Uint8 *)data->pixels +
                        rect.y * data->pitch +
                        rect.x * data->bytes_per_pixel);
@@ -2569,6 +2575,9 @@ static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
     void *pixels = NULL;
     int pitch = 0;
     SDL_bool created_framebuffer = SDL_FALSE;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     /* This will switch the video backend from using a software surface to
        using a GPU texture through the 2D render API, if we think this would
@@ -2643,7 +2652,7 @@ static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
         return window->surface;
     }
 
-    return SDL_CreateSurfaceFrom(pixels, window->w, window->h, pitch, format);
+    return SDL_CreateSurfaceFrom(pixels, w, h, pitch, format);
 }
 
 SDL_Surface *SDL_GetWindowSurface(SDL_Window *window)
@@ -2673,8 +2682,8 @@ int SDL_UpdateWindowSurface(SDL_Window *window)
 
     full_rect.x = 0;
     full_rect.y = 0;
-    full_rect.w = window->w;
-    full_rect.h = window->h;
+    SDL_GetWindowSizeInPixels(window, &full_rect.w, &full_rect.h);
+
     return SDL_UpdateWindowSurfaceRects(window, &full_rect, 1);
 }
 
diff --git a/src/video/dummy/SDL_nullframebuffer.c b/src/video/dummy/SDL_nullframebuffer.c
index 74b67d18ec5a..96a2cad6e5ec 100644
--- a/src/video/dummy/SDL_nullframebuffer.c
+++ b/src/video/dummy/SDL_nullframebuffer.c
@@ -37,7 +37,7 @@ int SDL_DUMMY_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
     SDL_DUMMY_DestroyWindowFramebuffer(_this, window);
 
     /* Create a new one */
-    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowSizeInPixels(window, &w, &h);
     surface = SDL_CreateSurface(w, h, surface_format);
     if (surface == NULL) {
         return -1;
diff --git a/src/video/emscripten/SDL_emscriptenframebuffer.c b/src/video/emscripten/SDL_emscriptenframebuffer.c
index 848c9d10cf04..5c0f663f773e 100644
--- a/src/video/emscripten/SDL_emscriptenframebuffer.c
+++ b/src/video/emscripten/SDL_emscriptenframebuffer.c
@@ -39,7 +39,7 @@ int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format
     SDL_DestroySurface(surface);
 
     /* Create a new one */
-    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     surface = SDL_CreateSurface(w, h, surface_format);
     if (surface == NULL) {
diff --git a/src/video/n3ds/SDL_n3dsframebuffer.c b/src/video/n3ds/SDL_n3dsframebuffer.c
index 89bb7bc46583..2bdad707775e 100644
--- a/src/video/n3ds/SDL_n3dsframebuffer.c
+++ b/src/video/n3ds/SDL_n3dsframebuffer.c
@@ -69,7 +69,7 @@ SDL_FORCE_INLINE SDL_Surface *
 CreateNewWindowFramebuffer(SDL_Window *window)
 {
     int w, h;
-    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowSizeInPixels(window, &w, &h);
     return SDL_CreateSurface(w, h, FRAMEBUFFER_FORMAT);
 }
 
diff --git a/src/video/ngage/SDL_ngageframebuffer.cpp b/src/video/ngage/SDL_ngageframebuffer.cpp
index 23289725a4d1..0d98ea82e984 100644
--- a/src/video/ngage/SDL_ngageframebuffer.cpp
+++ b/src/video/ngage/SDL_ngageframebuffer.cpp
@@ -55,7 +55,7 @@ int SDL_NGAGE_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
     SDL_NGAGE_DestroyWindowFramebuffer(_this, window);
 
     /* Create a new one */
-    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowSizeInPixels(window, &w, &h);
     surface = SDL_CreateSurface(w, h, surface_format);
     if (surface == NULL) {
         return -1;
diff --git a/src/video/offscreen/SDL_offscreenframebuffer.c b/src/video/offscreen/SDL_offscreenframebuffer.c
index d7fac9d00a1c..5eff510fdbb8 100644
--- a/src/video/offscreen/SDL_offscreenframebuffer.c
+++ b/src/video/offscreen/SDL_offscreenframebuffer.c
@@ -37,7 +37,7 @@ int SDL_OFFSCREEN_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *for
     SDL_OFFSCREEN_DestroyWindowFramebuffer(_this, window);
 
     /* Create a new one */
-    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowSizeInPixels(window, &w, &h);
     surface = SDL_CreateSurface(w, h, surface_format);
     if (surface == NULL) {
         return -1;
diff --git a/src/video/riscos/SDL_riscosframebuffer.c b/src/video/riscos/SDL_riscosframebuffer.c
index 917134e57f6a..1620f4dbb7c5 100644
--- a/src/video/riscos/SDL_riscosframebuffer.c
+++ b/src/video/riscos/SDL_riscosframebuffer.c
@@ -39,6 +39,9 @@ int RISCOS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, vo
     _kernel_swi_regs regs;
     SDL_DisplayMode mode;
     int size;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     /* Free the old framebuffer surface */
     RISCOS_DestroyWindowFramebuffer(_this, window);
@@ -54,10 +57,10 @@ int RISCOS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, vo
     }
 
     /* Calculate pitch */
-    *pitch = (((window->w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
+    *pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
 
     /* Allocate the sprite area */
-    size = sizeof(sprite_area) + sizeof(sprite_header) + ((*pitch) * window->h);
+    size = sizeof(sprite_area) + sizeof(sprite_header) + ((*pitch) * h);
     driverdata->fb_area = SDL_malloc(size);
     if (!driverdata->fb_area) {
         return SDL_OutOfMemory();
@@ -73,8 +76,8 @@ int RISCOS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, vo
     regs.r[1] = (int)driverdata->fb_area;
     regs.r[2] = (int)sprite_name;
     regs.r[3] = 0;
-    regs.r[4] = window->w;
-    regs.r[5] = window->h;
+    regs.r[4] = w;
+    regs.r[5] = h;
     regs.r[6] = sprite_mode;
     error = _kernel_swi(OS_SpriteOp, &regs, &regs);
     if (error != NULL) {
diff --git a/src/video/windows/SDL_windowsframebuffer.c b/src/video/windows/SDL_windowsframebuffer.c
index 9c49fca8852f..925d2c640fdb 100644
--- a/src/video/windows/SDL_windowsframebuffer.c
+++ b/src/video/windows/SDL_windowsframebuffer.c
@@ -31,6 +31,9 @@ int WIN_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void
     size_t size;
     LPBITMAPINFO info;
     HBITMAP hbm;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     /* Free the old framebuffer surface */
     if (data->mdc) {
@@ -78,10 +81,10 @@ int WIN_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void
     }
 
     /* Fill in the size information */
-    *pitch = (((window->w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
-    info->bmiHeader.biWidth = window->w;
-    info->bmiHeader.biHeight = -window->h; /* negative for topdown bitmap */
-    info->bmiHeader.biSizeImage = (DWORD)window->h * (*pitch);
+    *pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
+    info->bmiHeader.biWidth = w;
+    info->bmiHeader.biHeight = -h; /* negative for topdown bitmap */
+    info->bmiHeader.biSizeImage = (DWORD)h * (*pitch);
 
     data->mdc = CreateCompatibleDC(data->hdc);
     data->hbm = CreateDIBSection(data->hdc, info, DIB_RGB_COLORS, pixels, NULL, 0);
diff --git a/src/video/x11/SDL_x11framebuffer.c b/src/video/x11/SDL_x11framebuffer.c
index b2ef7345eb11..466ded8493dc 100644
--- a/src/video/x11/SDL_x11framebuffer.c
+++ b/src/video/x11/SDL_x11framebuffer.c
@@ -55,6 +55,9 @@ int X11_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
     Display *display = data->videodata->display;
     XGCValues gcv;
     XVisualInfo vinfo;
+    int w, h;
+
+    SDL_GetWindowSizeInPixels(window, &w, &h);
 
     /* Free the old framebuffer surface */
     X11_DestroyWindowFramebuffer(_this, window);
@@ -77,14 +80,14 @@ int X11_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
     }
 
     /* Calculate pitch */
-    *pitch = (((window->w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
+    *pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
 
     /* Create the actual image */
 #ifndef NO_SHARED_MEMORY
     if (have_mitshm(display)) {
         XShmSegmentInfo *shminfo = &data->shminfo;
 
-        shminfo->shmid = shmget(IPC_PRIVATE, (size_t)window->h * (*pitch), IPC_CREAT | 0777);
+        shminfo->shmid = shmget(IPC_PRIVATE, (size_t)h * (*pitch), IPC_CREAT | 0777);
         if (shminfo->shmid >= 0) {
             shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
             shminfo->readOnly = False;
@@ -108,7 +111,7 @@ int X11_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
             data->ximage = X11_XShmCreateImage(display, data->visual,
                                                vinfo.depth, ZPixmap,
                                                shminfo->shmaddr, shminfo,
-                                               window->w, window->h);
+                                               w, h);
             if (!data->ximage) {
                 X11_XShmDetach(display, shminfo);
                 X11_XSync(display, False);
@@ -124,14 +127,14 @@ int X11_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
     }
 #endif /* not NO_SHARED_MEMORY */
 
-    *pixels = SDL_malloc((size_t)window->h * (*pitch));
+    *pixels = SDL_malloc((size_t)h * (*pitch));
     if (*pixels == NULL) {
         return SDL_OutOfMemory();
     }
 
     data->ximage = X11_XCreateImage(display, data->visual,
                                     vinfo.depth, ZPixmap, 0, (char *)(*pixels),
-                                    window->w, window->h, 32, 0);
+                                    w, h, 32, 0);
     if (!data->ximage) {
         SDL_free(*pixels);
         return SDL_SetError("Couldn't create XImage");
@@ -147,6 +150,10 @@ int X11_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects
     Display *display = data->videodata->display;
     int i;
     int x, y, w, h;
+    int window_w, window_h;
+
+    SDL_GetWindowSizeInPixels(window, &window_w, &window_h);
+
 #ifndef NO_SHARED_MEMORY
     if (data->use_mitshm) {
         for (i = 0; i < numrects; ++i) {
@@ -167,11 +174,11 @@ int X11_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects
                 y += h;
                 h += rects[i].y;
             }
-            if (x + w > window->w) {
-                w = window->w - x;
+            if (x + w > window_w) {
+                w = window_w - x;
             }
-            if (y + h > window->h) {
-                h = window->h - y;
+            if (y + h > window_h) {
+                h = window_h - y;
             }
 
             X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage,
@@ -198,11 +205,11 @@ int X11_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects
                 y += h;
                 h += rects[i].y;
             }
-            if (x + w > window->w) {
-                w = window->w - x;
+            if (x + w > window_w) {
+                w = window_w - x;
             }
-            if (y + h > window->h) {
-                h = window->h - y;
+            if (y + h > window_h) {
+                h = window_h - y;
             }
 
             X11_XPutImage(display, data->xwindow, data->gc, data->ximage,