Added SDL_RenderSetLogicalSize() and SDL_RenderGetLogicalSize()
authorSam Lantinga <slouken@libsdl.org>
Mon, 01 Oct 2012 22:30:07 -0700
changeset 6530 ba5de88eab60
parent 6529 9094fcfd378d
child 6531 1ab2e34b90cc
Added SDL_RenderSetLogicalSize() and SDL_RenderGetLogicalSize()
include/SDL_render.h
src/render/SDL_render.c
src/render/SDL_sysrender.h
--- a/include/SDL_render.h	Mon Oct 01 21:57:09 2012 -0700
+++ b/include/SDL_render.h	Mon Oct 01 22:30:07 2012 -0700
@@ -418,6 +418,40 @@
                                                 SDL_Texture *texture);
 
 /**
+ *  \brief Set device independent resolution for rendering
+ *
+ *  \param w      The width of the logical resolution
+ *  \param h      The height of the logical resolution
+ *
+ *  This function uses the viewport and scaling functionality to allow a fixed logical
+ *  resolution for rendering, regardless of the actual output resolution.  If the actual
+ *  output resolution doesn't have the same aspect ratio the output rendering will be
+ *  centered within the output display.
+ *
+ *  If the output display is a window, mouse events in the window will be filtered
+ *  and scaled so they seem to arrive within the logical resolution.
+ *
+ *  \note If this function results in scaling or subpixel drawing by the 
+ *        rendering backend, it will be handled using the appropriate
+ *        quality hints.
+ *
+ *  \sa SDL_RenderGetLogicalSize()
+ *  \sa SDL_RenderSetScale()
+ *  \sa SDL_RenderSetViewport()
+ */
+extern DECLSPEC int SDLCALL SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h);
+
+/**
+ *  \brief Get device independent resolution for rendering
+ *
+ *  \param w      A pointer filled with the width of the logical resolution
+ *  \param h      A pointer filled with the height of the logical resolution
+ *
+ *  \sa SDL_RenderSetLogicalSize()
+ */
+extern DECLSPEC void SDLCALL SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *y);
+
+/**
  *  \brief Set the drawing area for rendering on the current target.
  *
  *  \param rect The rectangle representing the drawing area, or NULL to set the viewport to the entire target.
@@ -426,12 +460,17 @@
  *
  *  \note When the window is resized, the current viewport is automatically
  *        centered within the new window size.
+ *
+ *  \sa SDL_RenderGetViewport()
+ *  \sa SDL_RenderSetLogicalSize()
  */
 extern DECLSPEC int SDLCALL SDL_RenderSetViewport(SDL_Renderer * renderer,
                                                   const SDL_Rect * rect);
 
 /**
  *  \brief Get the drawing area for the current target.
+ *
+ *  \sa SDL_RenderSetViewport()
  */
 extern DECLSPEC void SDLCALL SDL_RenderGetViewport(SDL_Renderer * renderer,
                                                    SDL_Rect * rect);
@@ -449,6 +488,9 @@
  *  \note If this results in scaling or subpixel drawing by the 
  *        rendering backend, it will be handled using the appropriate
  *        quality hints.  For best results use integer scaling factors.
+ *
+ *  \sa SDL_RenderGetScale()
+ *  \sa SDL_RenderSetLogicalSize()
  */
 extern DECLSPEC int SDLCALL SDL_RenderSetScale(SDL_Renderer * renderer,
                                                float scaleX, float scaleY);
@@ -458,6 +500,8 @@
  *
  *  \param scaleX A pointer filled in with the horizontal scaling factor
  *  \param scaleY A pointer filled in with the vertical scaling factor
+ *
+ *  \sa SDL_RenderSetScale()
  */
 extern DECLSPEC void SDLCALL SDL_RenderGetScale(SDL_Renderer * renderer,
                                                float *scaleX, float *scaleY);
--- a/src/render/SDL_render.c	Mon Oct 01 21:57:09 2012 -0700
+++ b/src/render/SDL_render.c	Mon Oct 01 22:30:07 2012 -0700
@@ -70,6 +70,8 @@
 static char renderer_magic;
 static char texture_magic;
 
+static int UpdateLogicalSize(SDL_Renderer *renderer);
+
 int
 SDL_GetNumRenderDrivers(void)
 {
@@ -101,21 +103,27 @@
             }
 
             if (event->window.event == SDL_WINDOWEVENT_RESIZED) {
-                /* Try to keep the previous viewport centered */
-                int w, h;
+                if (renderer->logical_w) {
+                    /* We'll update the renderer in the SIZE_CHANGED event */
+                } else {
+                    /* Try to keep the previous viewport centered */
+                    int w, h;
 
-                SDL_GetWindowSize(window, &w, &h);
-                if (renderer->target) {
-                    renderer->viewport_backup.x = (w - renderer->viewport_backup.w) / 2;
-                    renderer->viewport_backup.y = (h - renderer->viewport_backup.h) / 2;
-                } else {
-                    renderer->viewport.x = (w - renderer->viewport.w) / 2;
-                    renderer->viewport.y = (h - renderer->viewport.h) / 2;
-                    renderer->UpdateViewport(renderer);
+                    SDL_GetWindowSize(window, &w, &h);
+                    if (renderer->target) {
+                        renderer->viewport_backup.x = (w - renderer->viewport_backup.w) / 2;
+                        renderer->viewport_backup.y = (h - renderer->viewport_backup.h) / 2;
+                    } else {
+                        renderer->viewport.x = (w - renderer->viewport.w) / 2;
+                        renderer->viewport.y = (h - renderer->viewport.h) / 2;
+                        renderer->UpdateViewport(renderer);
+                    }
                 }
                 renderer->resized = SDL_TRUE;
             } else if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
-                if (!renderer->resized) {
+                if (renderer->logical_w) {
+                    UpdateLogicalSize(renderer);
+                } else if (!renderer->resized) {
                     /* Window was programmatically resized, reset viewport */
                     int w, h;
 
@@ -148,6 +156,21 @@
                 }
             }
         }
+    } else if (event->type == SDL_MOUSEMOTION) {
+        if (renderer->logical_w) {
+            event->motion.x -= renderer->viewport.x;
+            event->motion.y -= renderer->viewport.y;
+            event->motion.x = (int)(event->motion.x / renderer->scale.x);
+            event->motion.y = (int)(event->motion.y / renderer->scale.y);
+        }
+    } else if (event->type == SDL_MOUSEBUTTONDOWN ||
+               event->type == SDL_MOUSEBUTTONUP) {
+        if (renderer->logical_w) {
+            event->button.x -= renderer->viewport.x;
+            event->button.y -= renderer->viewport.y;
+            event->button.x = (int)(event->button.x / renderer->scale.x);
+            event->button.y = (int)(event->button.y / renderer->scale.y);
+        }
     }
     return 0;
 }
@@ -245,6 +268,8 @@
         renderer->window = window;
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
+        renderer->logical_w = 0;
+        renderer->logical_h = 0;
 
         if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
             renderer->hidden = SDL_TRUE;
@@ -921,6 +946,88 @@
     return 0;
 }
 
+static int
+UpdateLogicalSize(SDL_Renderer *renderer)
+{
+    int w, h;
+    float want_aspect;
+    float real_aspect;
+    float scale;
+    SDL_Rect viewport;
+
+    if (renderer->window) {
+        SDL_GetWindowSize(renderer->window, &w, &h);
+    } else {
+        /* FIXME */
+        SDL_SetError("Internal error: No way to get output resolution");
+        return -1;
+    }
+
+    want_aspect = (float)renderer->logical_w / renderer->logical_h;
+    real_aspect = (float)w / h;
+
+    /* Clear the scale because we're setting viewport in output coordinates */
+    SDL_RenderSetScale(renderer, 1.0f, 1.0f);
+
+    if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
+        /* The aspect ratios are the same, just scale appropriately */
+        scale = (float)w / renderer->logical_w;
+        SDL_RenderSetViewport(renderer, NULL);
+    } else if (want_aspect > real_aspect) {
+        /* We want a wider aspect ratio than is available - letterbox it */
+        scale = (float)w / renderer->logical_w;
+        viewport.x = 0;
+        viewport.w = w;
+        viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
+        viewport.y = (h - viewport.h) / 2;
+        SDL_RenderSetViewport(renderer, &viewport);
+    } else {
+        /* We want a narrower aspect ratio than is available - use side-bars */
+        scale = (float)h / renderer->logical_h;
+        viewport.y = 0;
+        viewport.h = h;
+        viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
+        viewport.x = (w - viewport.w) / 2;
+        SDL_RenderSetViewport(renderer, &viewport);
+    }
+
+    /* Set the new scale */
+    SDL_RenderSetScale(renderer, scale, scale);
+}
+
+int
+SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
+{
+    CHECK_RENDERER_MAGIC(renderer, -1);
+
+    if (!w || !h) {
+        /* Clear any previous logical resolution */
+        renderer->logical_w = 0;
+        renderer->logical_h = 0;
+        SDL_RenderSetViewport(renderer, NULL);
+        SDL_RenderSetScale(renderer, 1.0f, 1.0f);
+        return 0;
+    }
+
+    renderer->logical_w = w;
+    renderer->logical_h = h;
+
+    return UpdateLogicalSize(renderer);
+}
+
+void
+SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
+{
+    CHECK_RENDERER_MAGIC(renderer, );
+
+    if (w) {
+        *w = renderer->logical_w;
+    }
+    if (h) {
+        *h = renderer->logical_h;
+    }
+}
+
 int
 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
 {
--- a/src/render/SDL_sysrender.h	Mon Oct 01 21:57:09 2012 -0700
+++ b/src/render/SDL_sysrender.h	Mon Oct 01 22:30:07 2012 -0700
@@ -123,6 +123,10 @@
     SDL_bool hidden;
     SDL_bool resized;
 
+    /* The logical resolution for rendering */
+    int logical_w;
+    int logical_h;
+
     /* The drawable area within the window */
     SDL_Rect viewport;
     SDL_Rect viewport_backup;