diff --git a/include/SDL_render.h b/include/SDL_render.h index 7ec45010d..aca3065b9 100644 --- a/include/SDL_render.h +++ b/include/SDL_render.h @@ -417,6 +417,40 @@ extern DECLSPEC SDL_bool SDLCALL SDL_RenderTargetSupported(SDL_Renderer *rendere extern DECLSPEC int SDLCALL SDL_SetRenderTarget(SDL_Renderer *renderer, 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. * @@ -426,12 +460,17 @@ extern DECLSPEC int SDLCALL SDL_SetRenderTarget(SDL_Renderer *renderer, * * \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 @@ extern DECLSPEC void SDLCALL SDL_RenderGetViewport(SDL_Renderer * renderer, * \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 @@ extern DECLSPEC int SDLCALL SDL_RenderSetScale(SDL_Renderer * renderer, * * \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); diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 1cd270021..9bafeacb1 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -70,6 +70,8 @@ static const SDL_RenderDriver *render_drivers[] = { static char renderer_magic; static char texture_magic; +static int UpdateLogicalSize(SDL_Renderer *renderer); + int SDL_GetNumRenderDrivers(void) { @@ -101,21 +103,27 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) } if (event->window.event == SDL_WINDOWEVENT_RESIZED) { - /* 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; + if (renderer->logical_w) { + /* We'll update the renderer in the SIZE_CHANGED event */ } else { - renderer->viewport.x = (w - renderer->viewport.w) / 2; - renderer->viewport.y = (h - renderer->viewport.h) / 2; - renderer->UpdateViewport(renderer); + /* 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); + } } 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 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) } } } + } 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 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) 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 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 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) { diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 07d83315c..e3c99d3c9 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -123,6 +123,10 @@ struct SDL_Renderer 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;