render: allow render targets to use logical presentation.

Fixes https://github.com/libsdl-org/sdl2-compat/issues/279
This commit is contained in:
Ryan C. Gordon 2025-02-27 17:28:47 -05:00
parent 281f0fae1c
commit 35e8cf8ee6
3 changed files with 296 additions and 219 deletions

View file

@ -490,6 +490,9 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Rende
* This returns the true output size in pixels, ignoring any render targets or * This returns the true output size in pixels, ignoring any render targets or
* logical size and presentation. * logical size and presentation.
* *
* For the output size of the current rendering target, with logical size
* adjustments, use SDL_GetCurrentRenderOutputSize() instead.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param w a pointer filled in with the width in pixels. * \param w a pointer filled in with the width in pixels.
* \param h a pointer filled in with the height in pixels. * \param h a pointer filled in with the height in pixels.
@ -508,9 +511,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderOutputSize(SDL_Renderer *renderer,
* Get the current output size in pixels of a rendering context. * Get the current output size in pixels of a rendering context.
* *
* If a rendering target is active, this will return the size of the rendering * If a rendering target is active, this will return the size of the rendering
* target in pixels, otherwise if a logical size is set, it will return the * target in pixels, otherwise return the value of SDL_GetRenderOutputSize().
* logical size, otherwise it will return the value of *
* SDL_GetRenderOutputSize(). * Rendering target or not, the output will be adjusted by the current
* logical presentation state, dictated by SDL_SetRenderLogicalPresentation().
* *
* \param renderer the rendering context. * \param renderer the rendering context.
* \param w a pointer filled in with the current width. * \param w a pointer filled in with the current width.
@ -1318,6 +1322,11 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_Texture *texture);
* To stop rendering to a texture and render to the window again, call this * To stop rendering to a texture and render to the window again, call this
* function with a NULL `texture`. * function with a NULL `texture`.
* *
* Viewport, cliprect, scale, and logical presentation are unique to each
* render target. Get and set functions for these states apply to the current
* render target set by this function, and those states persist on each target
* when the current render target changes.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param texture the targeted texture, which must be created with the * \param texture the targeted texture, which must be created with the
* `SDL_TEXTUREACCESS_TARGET` flag, or NULL to render to the * `SDL_TEXTUREACCESS_TARGET` flag, or NULL to render to the
@ -1351,25 +1360,39 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderTarget(SDL_Renderer *renderer, SDL
extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_GetRenderTarget(SDL_Renderer *renderer); extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_GetRenderTarget(SDL_Renderer *renderer);
/** /**
* Set a device independent resolution and presentation mode for rendering. * Set a device-independent resolution and presentation mode for rendering.
* *
* This function sets the width and height of the logical rendering output. * This function sets the width and height of the logical rendering output.
* The renderer will act as if the window is always the requested dimensions, * The renderer will act as if the current render target is always the
* scaling to the actual window resolution as necessary. * requested dimensions, scaling to the actual resolution as necessary.
* *
* This can be useful for games that expect a fixed size, but would like to * This can be useful for games that expect a fixed size, but would like to
* scale the output to whatever is available, regardless of how a user resizes * scale the output to whatever is available, regardless of how a user resizes
* a window, or if the display is high DPI. * a window, or if the display is high DPI.
* *
* Logical presentation can be used with both render target textures
* and the renderer's window; the state is unique to each render target, and
* this function sets the state for the current render target. It might be
* useful to draw to a texture that matches the window dimensions with logical
* presentation enabled, and then draw that texture across the entire window
* with logical presentation disabled. Be careful not to render both with
* logical presentation enabled, however, as this could produce
* double-letterboxing, etc.
*
* You can disable logical coordinates by setting the mode to * You can disable logical coordinates by setting the mode to
* SDL_LOGICAL_PRESENTATION_DISABLED, and in that case you get the full pixel * SDL_LOGICAL_PRESENTATION_DISABLED, and in that case you get the full pixel
* resolution of the output window; it is safe to toggle logical presentation * resolution of the render target; it is safe to toggle logical presentation
* during the rendering of a frame: perhaps most of the rendering is done to * during the rendering of a frame: perhaps most of the rendering is done to
* specific dimensions but to make fonts look sharp, the app turns off logical * specific dimensions but to make fonts look sharp, the app turns off logical
* presentation while drawing text. * presentation while drawing text, for example.
* *
* Letterboxing will only happen if logical presentation is enabled during * For the renderer's window, letterboxing is drawn into the framebuffer
* SDL_RenderPresent; be sure to reenable it first if you were using it. * if logical presentation is enabled during SDL_RenderPresent; be sure to
* reenable it before presenting if you were toggling it, otherwise the
* letterbox areas might have artifacts from previous frames (or artifacts
* from external overlays, etc). Letterboxing is never drawn into texture
* render targets; be sure to call SDL_RenderClear() before drawing into
* the texture so the letterboxing areas are cleared, if appropriate.
* *
* You can convert coordinates in an event into rendering coordinates using * You can convert coordinates in an event into rendering coordinates using
* SDL_ConvertEventToRenderCoordinates(). * SDL_ConvertEventToRenderCoordinates().
@ -1397,6 +1420,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderLogicalPresentation(SDL_Renderer *
* This function gets the width and height of the logical rendering output, or * This function gets the width and height of the logical rendering output, or
* the output size in pixels if a logical resolution is not enabled. * the output size in pixels if a logical resolution is not enabled.
* *
* Each render target has its own logical presentation state. This function
* gets the state for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param w an int to be filled with the width. * \param w an int to be filled with the width.
* \param h an int to be filled with the height. * \param h an int to be filled with the height.
@ -1420,6 +1446,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderLogicalPresentation(SDL_Renderer *
* presentation is disabled, it will fill the rectangle with the output size, * presentation is disabled, it will fill the rectangle with the output size,
* in pixels. * in pixels.
* *
* Each render target has its own logical presentation state. This function
* gets the rectangle for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param rect a pointer filled in with the final presentation rectangle, may * \param rect a pointer filled in with the final presentation rectangle, may
* be NULL. * be NULL.
@ -1536,6 +1565,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ConvertEventToRenderCoordinates(SDL_Rendere
* *
* The area's width and height must be >= 0. * The area's width and height must be >= 0.
* *
* Each render target has its own viewport. This function sets the viewport
* for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param rect the SDL_Rect structure representing the drawing area, or NULL * \param rect the SDL_Rect structure representing the drawing area, or NULL
* to set the viewport to the entire target. * to set the viewport to the entire target.
@ -1554,6 +1586,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderViewport(SDL_Renderer *renderer, c
/** /**
* Get the drawing area for the current target. * Get the drawing area for the current target.
* *
* Each render target has its own viewport. This function gets the viewport
* for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param rect an SDL_Rect structure filled in with the current drawing area. * \param rect an SDL_Rect structure filled in with the current drawing area.
* \returns true on success or false on failure; call SDL_GetError() for more * \returns true on success or false on failure; call SDL_GetError() for more
@ -1575,6 +1610,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderViewport(SDL_Renderer *renderer, S
* whether you should restore a specific rectangle or NULL. Note that the * whether you should restore a specific rectangle or NULL. Note that the
* viewport is always reset when changing rendering targets. * viewport is always reset when changing rendering targets.
* *
* Each render target has its own viewport. This function checks the viewport
* for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \returns true if the viewport was set to a specific rectangle, or false if * \returns true if the viewport was set to a specific rectangle, or false if
* it was set to NULL (the entire target). * it was set to NULL (the entire target).
@ -1613,6 +1651,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderSafeArea(SDL_Renderer *renderer, S
/** /**
* Set the clip rectangle for rendering on the specified target. * Set the clip rectangle for rendering on the specified target.
* *
* Each render target has its own clip rectangle. This function
* sets the cliprect for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param rect an SDL_Rect structure representing the clip area, relative to * \param rect an SDL_Rect structure representing the clip area, relative to
* the viewport, or NULL to disable clipping. * the viewport, or NULL to disable clipping.
@ -1631,6 +1672,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderClipRect(SDL_Renderer *renderer, c
/** /**
* Get the clip rectangle for the current target. * Get the clip rectangle for the current target.
* *
* Each render target has its own clip rectangle. This function
* gets the cliprect for the current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param rect an SDL_Rect structure filled in with the current clipping area * \param rect an SDL_Rect structure filled in with the current clipping area
* or an empty rectangle if clipping is disabled. * or an empty rectangle if clipping is disabled.
@ -1647,7 +1691,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderClipRect(SDL_Renderer *renderer, c
extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect); extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect);
/** /**
* Get whether clipping is enabled on the given renderer. * Get whether clipping is enabled on the given render target.
*
* Each render target has its own clip rectangle. This function
* checks the cliprect for the current render target.
* *
* \param renderer the rendering context. * \param renderer the rendering context.
* \returns true if clipping is enabled or false if not; call SDL_GetError() * \returns true if clipping is enabled or false if not; call SDL_GetError()
@ -1673,6 +1720,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderClipEnabled(SDL_Renderer *renderer);
* will be handled using the appropriate quality hints. For best results use * will be handled using the appropriate quality hints. For best results use
* integer scaling factors. * integer scaling factors.
* *
* Each render target has its own scale. This function sets the scale for the
* current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param scaleX the horizontal scaling factor. * \param scaleX the horizontal scaling factor.
* \param scaleY the vertical scaling factor. * \param scaleY the vertical scaling factor.
@ -1690,6 +1740,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderScale(SDL_Renderer *renderer, floa
/** /**
* Get the drawing scale for the current target. * Get the drawing scale for the current target.
* *
* Each render target has its own scale. This function gets the scale for the
* current render target.
*
* \param renderer the rendering context. * \param renderer the rendering context.
* \param scaleX a pointer filled in with the horizontal scaling factor. * \param scaleX a pointer filled in with the horizontal scaling factor.
* \param scaleY a pointer filled in with the vertical scaling factor. * \param scaleY a pointer filled in with the vertical scaling factor.

View file

@ -472,17 +472,18 @@ static bool QueueCmdSetClipRect(SDL_Renderer *renderer)
{ {
bool result = true; bool result = true;
SDL_Rect clip_rect = renderer->view->pixel_clip_rect; const SDL_RenderViewState *view = renderer->view;
SDL_Rect clip_rect = view->pixel_clip_rect;
if (!renderer->cliprect_queued || if (!renderer->cliprect_queued ||
renderer->view->clipping_enabled != renderer->last_queued_cliprect_enabled || view->clipping_enabled != renderer->last_queued_cliprect_enabled ||
SDL_memcmp(&clip_rect, &renderer->last_queued_cliprect, sizeof(clip_rect)) != 0) { SDL_memcmp(&clip_rect, &renderer->last_queued_cliprect, sizeof(clip_rect)) != 0) {
SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
if (cmd) { if (cmd) {
cmd->command = SDL_RENDERCMD_SETCLIPRECT; cmd->command = SDL_RENDERCMD_SETCLIPRECT;
cmd->data.cliprect.enabled = renderer->view->clipping_enabled; cmd->data.cliprect.enabled = view->clipping_enabled;
SDL_copyp(&cmd->data.cliprect.rect, &clip_rect); SDL_copyp(&cmd->data.cliprect.rect, &clip_rect);
SDL_copyp(&renderer->last_queued_cliprect, &clip_rect); SDL_copyp(&renderer->last_queued_cliprect, &clip_rect);
renderer->last_queued_cliprect_enabled = renderer->view->clipping_enabled; renderer->last_queued_cliprect_enabled = view->clipping_enabled;
renderer->cliprect_queued = true; renderer->cliprect_queued = true;
} else { } else {
result = false; result = false;
@ -737,9 +738,9 @@ static void UpdateMainViewDimensions(SDL_Renderer *renderer)
if (renderer->window) { if (renderer->window) {
SDL_GetWindowSize(renderer->window, &window_w, &window_h); SDL_GetWindowSize(renderer->window, &window_w, &window_h);
} }
SDL_GetRenderOutputSize(renderer, &renderer->output_pixel_w, &renderer->output_pixel_h);
renderer->main_view.pixel_w = renderer->output_pixel_w; SDL_GetRenderOutputSize(renderer, &renderer->main_view.pixel_w, &renderer->main_view.pixel_h);
renderer->main_view.pixel_h = renderer->output_pixel_h;
if (window_w > 0 && window_h > 0) { if (window_w > 0 && window_h > 0) {
renderer->dpi_scale.x = (float)renderer->main_view.pixel_w / window_w; renderer->dpi_scale.x = (float)renderer->main_view.pixel_w / window_w;
renderer->dpi_scale.y = (float)renderer->main_view.pixel_h / window_h; renderer->dpi_scale.y = (float)renderer->main_view.pixel_h / window_h;
@ -833,7 +834,10 @@ static bool SDL_RendererEventWatch(void *userdata, SDL_Event *event)
if (event->type == SDL_EVENT_WINDOW_RESIZED || if (event->type == SDL_EVENT_WINDOW_RESIZED ||
event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED || event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
event->type == SDL_EVENT_WINDOW_METAL_VIEW_RESIZED) { event->type == SDL_EVENT_WINDOW_METAL_VIEW_RESIZED) {
SDL_RenderViewState *view = renderer->view;
renderer->view = &renderer->main_view; // only update the main_view (the window framebuffer) for window changes.
UpdateLogicalPresentation(renderer); UpdateLogicalPresentation(renderer);
renderer->view = view; // put us back on whatever the current render target's actual view is.
} else if (event->type == SDL_EVENT_WINDOW_HIDDEN) { } else if (event->type == SDL_EVENT_WINDOW_HIDDEN) {
renderer->hidden = true; renderer->hidden = true;
} else if (event->type == SDL_EVENT_WINDOW_SHOWN) { } else if (event->type == SDL_EVENT_WINDOW_SHOWN) {
@ -1245,11 +1249,12 @@ bool SDL_GetCurrentRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
const SDL_RenderViewState *view = renderer->view;
if (w) { if (w) {
*w = renderer->view->pixel_w; *w = view->pixel_w;
} }
if (h) { if (h) {
*h = renderer->view->pixel_h; *h = view->pixel_h;
} }
return true; return true;
} }
@ -2565,37 +2570,45 @@ SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer)
static void UpdateLogicalPresentation(SDL_Renderer *renderer) static void UpdateLogicalPresentation(SDL_Renderer *renderer)
{ {
if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) { SDL_RenderViewState *view = renderer->view;
renderer->main_view.logical_offset.x = renderer->main_view.logical_offset.y = 0.0f; const bool is_main_view = (view == &renderer->main_view);
renderer->main_view.logical_scale.x = renderer->main_view.logical_scale.y = 1.0f; const float logical_w = view->logical_w;
renderer->main_view.current_scale.x = renderer->main_view.scale.x; // skip the multiplications against 1.0f. const float logical_h = view->logical_h;
renderer->main_view.current_scale.y = renderer->main_view.scale.y; int iwidth, iheight;
UpdateMainViewDimensions(renderer);
UpdatePixelClipRect(renderer, &renderer->main_view); if (renderer->target) {
return; // All done! iwidth = (int)renderer->target->w;
iheight = (int)renderer->target->h;
} else {
SDL_GetRenderOutputSize(renderer, &iwidth, &iheight);
} }
int iwidth, iheight; view->logical_src_rect.x = 0.0f;
SDL_GetRenderOutputSize(renderer, &iwidth, &iheight); view->logical_src_rect.y = 0.0f;
view->logical_src_rect.w = logical_w;
view->logical_src_rect.h = logical_h;
if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) {
view->logical_dst_rect.x = 0.0f;
view->logical_dst_rect.y = 0.0f;
view->logical_dst_rect.w = iwidth;
view->logical_dst_rect.h = iheight;
view->logical_offset.x = view->logical_offset.y = 0.0f;
view->logical_scale.x = view->logical_scale.y = 1.0f;
view->current_scale.x = view->scale.x; // skip the multiplications against 1.0f.
view->current_scale.y = view->scale.y;
} else {
const float output_w = (float)iwidth; const float output_w = (float)iwidth;
const float output_h = (float)iheight; const float output_h = (float)iheight;
const float logical_w = renderer->logical_w;
const float logical_h = renderer->logical_h;
const float want_aspect = logical_w / logical_h; const float want_aspect = logical_w / logical_h;
const float real_aspect = output_w / output_h; const float real_aspect = output_w / output_h;
renderer->logical_src_rect.x = 0.0f;
renderer->logical_src_rect.y = 0.0f;
renderer->logical_src_rect.w = logical_w;
renderer->logical_src_rect.h = logical_h;
if ((logical_w <= 0.0f) || (logical_h <= 0.0f)) { if ((logical_w <= 0.0f) || (logical_h <= 0.0f)) {
renderer->logical_dst_rect.x = 0.0f; view->logical_dst_rect.x = 0.0f;
renderer->logical_dst_rect.y = 0.0f; view->logical_dst_rect.y = 0.0f;
renderer->logical_dst_rect.w = output_w; view->logical_dst_rect.w = output_w;
renderer->logical_dst_rect.h = output_h; view->logical_dst_rect.h = output_h;
} else if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) { } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) {
float scale; float scale;
if (want_aspect > real_aspect) { if (want_aspect > real_aspect) {
scale = (float)((int)output_w / (int)logical_w); // This an integer division! scale = (float)((int)output_w / (int)logical_w); // This an integer division!
@ -2607,79 +2620,84 @@ static void UpdateLogicalPresentation(SDL_Renderer *renderer)
scale = 1.0f; scale = 1.0f;
} }
renderer->logical_dst_rect.w = SDL_floorf(logical_w * scale); view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
renderer->logical_dst_rect.x = (output_w - renderer->logical_dst_rect.w) / 2.0f; view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
renderer->logical_dst_rect.h = SDL_floorf(logical_h * scale); view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
renderer->logical_dst_rect.y = (output_h - renderer->logical_dst_rect.h) / 2.0f; view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
} else if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_STRETCH || } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_STRETCH || SDL_fabsf(want_aspect - real_aspect) < 0.0001f) {
SDL_fabsf(want_aspect - real_aspect) < 0.0001f) { view->logical_dst_rect.x = 0.0f;
renderer->logical_dst_rect.x = 0.0f; view->logical_dst_rect.y = 0.0f;
renderer->logical_dst_rect.y = 0.0f; view->logical_dst_rect.w = output_w;
renderer->logical_dst_rect.w = output_w; view->logical_dst_rect.h = output_h;
renderer->logical_dst_rect.h = output_h;
} else if (want_aspect > real_aspect) { } else if (want_aspect > real_aspect) {
if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) {
// We want a wider aspect ratio than is available - letterbox it // We want a wider aspect ratio than is available - letterbox it
const float scale = output_w / logical_w; const float scale = output_w / logical_w;
renderer->logical_dst_rect.x = 0.0f; view->logical_dst_rect.x = 0.0f;
renderer->logical_dst_rect.w = output_w; view->logical_dst_rect.w = output_w;
renderer->logical_dst_rect.h = SDL_floorf(logical_h * scale); view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
renderer->logical_dst_rect.y = (output_h - renderer->logical_dst_rect.h) / 2.0f; view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
} else { // renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN
/* We want a wider aspect ratio than is available - /* We want a wider aspect ratio than is available -
zoom so logical height matches the real height zoom so logical height matches the real height
and the width will grow off the screen and the width will grow off the screen
*/ */
const float scale = output_h / logical_h; const float scale = output_h / logical_h;
renderer->logical_dst_rect.y = 0.0f; view->logical_dst_rect.y = 0.0f;
renderer->logical_dst_rect.h = output_h; view->logical_dst_rect.h = output_h;
renderer->logical_dst_rect.w = SDL_floorf(logical_w * scale); view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
renderer->logical_dst_rect.x = (output_w - renderer->logical_dst_rect.w) / 2.0f; view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
} }
} else { } else {
if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) {
// We want a narrower aspect ratio than is available - use side-bars // We want a narrower aspect ratio than is available - use side-bars
const float scale = output_h / logical_h; const float scale = output_h / logical_h;
renderer->logical_dst_rect.y = 0.0f; view->logical_dst_rect.y = 0.0f;
renderer->logical_dst_rect.h = output_h; view->logical_dst_rect.h = output_h;
renderer->logical_dst_rect.w = SDL_floorf(logical_w * scale); view->logical_dst_rect.w = SDL_floorf(logical_w * scale);
renderer->logical_dst_rect.x = (output_w - renderer->logical_dst_rect.w) / 2.0f; view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f;
} else { // renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN
/* We want a narrower aspect ratio than is available - /* We want a narrower aspect ratio than is available -
zoom so logical width matches the real width zoom so logical width matches the real width
and the height will grow off the screen and the height will grow off the screen
*/ */
const float scale = output_w / logical_w; const float scale = output_w / logical_w;
renderer->logical_dst_rect.x = 0.0f; view->logical_dst_rect.x = 0.0f;
renderer->logical_dst_rect.w = output_w; view->logical_dst_rect.w = output_w;
renderer->logical_dst_rect.h = SDL_floorf(logical_h * scale); view->logical_dst_rect.h = SDL_floorf(logical_h * scale);
renderer->logical_dst_rect.y = (output_h - renderer->logical_dst_rect.h) / 2.0f; view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f;
} }
} }
renderer->main_view.logical_scale.x = (logical_w > 0.0f) ? renderer->logical_dst_rect.w / logical_w : 0.0f; view->logical_scale.x = (logical_w > 0.0f) ? view->logical_dst_rect.w / logical_w : 0.0f;
renderer->main_view.logical_scale.y = (logical_h > 0.0f) ? renderer->logical_dst_rect.h / logical_h : 0.0f; view->logical_scale.y = (logical_h > 0.0f) ? view->logical_dst_rect.h / logical_h : 0.0f;
renderer->main_view.current_scale.x = renderer->main_view.scale.x * renderer->main_view.logical_scale.x; view->current_scale.x = view->scale.x * view->logical_scale.x;
renderer->main_view.current_scale.y = renderer->main_view.scale.y * renderer->main_view.logical_scale.y; view->current_scale.y = view->scale.y * view->logical_scale.y;
renderer->main_view.logical_offset.x = renderer->logical_dst_rect.x; view->logical_offset.x = view->logical_dst_rect.x;
renderer->main_view.logical_offset.y = renderer->logical_dst_rect.y; view->logical_offset.y = view->logical_dst_rect.y;
}
UpdateMainViewDimensions(renderer); // this will replace pixel_w and pixel_h while making sure the dpi_scale is right. if (is_main_view) {
renderer->main_view.pixel_w = (int) renderer->logical_dst_rect.w; // This makes sure the dpi_scale is right. It also sets pixel_w and pixel_h, but we're going to change them directly below here.
renderer->main_view.pixel_h = (int) renderer->logical_dst_rect.h; UpdateMainViewDimensions(renderer);
UpdatePixelViewport(renderer, &renderer->main_view); }
UpdatePixelClipRect(renderer, &renderer->main_view);
view->pixel_w = (int) view->logical_dst_rect.w;
view->pixel_h = (int) view->logical_dst_rect.h;
UpdatePixelViewport(renderer, view);
UpdatePixelClipRect(renderer, view);
} }
bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode) bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode)
{ {
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
renderer->logical_presentation_mode = mode; SDL_RenderViewState *view = renderer->view;
renderer->logical_w = w; view->logical_presentation_mode = mode;
renderer->logical_h = h; view->logical_w = w;
view->logical_h = h;
UpdateLogicalPresentation(renderer); UpdateLogicalPresentation(renderer);
@ -2696,9 +2714,10 @@ bool SDL_GetRenderLogicalPresentation(SDL_Renderer *renderer, int *w, int *h, SD
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
SETVAL(w, renderer->logical_w); const SDL_RenderViewState *view = renderer->view;
SETVAL(h, renderer->logical_h); SETVAL(w, view->logical_w);
SETVAL(mode, renderer->logical_presentation_mode); SETVAL(h, view->logical_h);
SETVAL(mode, view->logical_presentation_mode);
#undef SETVAL #undef SETVAL
@ -2714,21 +2733,14 @@ bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rec
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
if (rect) { if (rect) {
if (renderer->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) { SDL_copyp(rect, &renderer->view->logical_dst_rect);
rect->x = 0.0f;
rect->y = 0.0f;
rect->w = (float)renderer->output_pixel_w;
rect->h = (float)renderer->output_pixel_h;
} else {
SDL_copyp(rect, &renderer->logical_dst_rect);
}
} }
return true; return true;
} }
static void SDL_RenderLogicalBorders(SDL_Renderer *renderer) static void SDL_RenderLogicalBorders(SDL_Renderer *renderer, const SDL_FRect *dst)
{ {
const SDL_FRect *dst = &renderer->logical_dst_rect; const SDL_RenderViewState *view = renderer->view;
if (dst->x > 0.0f || dst->y > 0.0f) { if (dst->x > 0.0f || dst->y > 0.0f) {
SDL_BlendMode saved_blend_mode = renderer->blendMode; SDL_BlendMode saved_blend_mode = renderer->blendMode;
@ -2743,11 +2755,11 @@ static void SDL_RenderLogicalBorders(SDL_Renderer *renderer)
rect.x = 0.0f; rect.x = 0.0f;
rect.y = 0.0f; rect.y = 0.0f;
rect.w = dst->x; rect.w = dst->x;
rect.h = (float)renderer->view->pixel_h; rect.h = (float)view->pixel_h;
SDL_RenderFillRect(renderer, &rect); SDL_RenderFillRect(renderer, &rect);
rect.x = dst->x + dst->w; rect.x = dst->x + dst->w;
rect.w = (float)renderer->view->pixel_w - rect.x; rect.w = (float)view->pixel_w - rect.x;
SDL_RenderFillRect(renderer, &rect); SDL_RenderFillRect(renderer, &rect);
} }
@ -2756,12 +2768,12 @@ static void SDL_RenderLogicalBorders(SDL_Renderer *renderer)
rect.x = 0.0f; rect.x = 0.0f;
rect.y = 0.0f; rect.y = 0.0f;
rect.w = (float)renderer->view->pixel_w; rect.w = (float)view->pixel_w;
rect.h = dst->y; rect.h = dst->y;
SDL_RenderFillRect(renderer, &rect); SDL_RenderFillRect(renderer, &rect);
rect.y = dst->y + dst->h; rect.y = dst->y + dst->h;
rect.h = (float)renderer->view->pixel_h - rect.y; rect.h = (float)view->pixel_h - rect.y;
SDL_RenderFillRect(renderer, &rect); SDL_RenderFillRect(renderer, &rect);
} }
@ -2772,17 +2784,19 @@ static void SDL_RenderLogicalBorders(SDL_Renderer *renderer)
static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer) static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer)
{ {
const SDL_RendererLogicalPresentation mode = renderer->logical_presentation_mode; SDL_assert(renderer->view == &renderer->main_view);
SDL_RenderViewState *view = &renderer->main_view;
const SDL_RendererLogicalPresentation mode = view->logical_presentation_mode;
if (mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { if (mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) {
// save off some state we're going to trample. // save off some state we're going to trample.
SDL_assert(renderer->view == &renderer->main_view); const int logical_w = view->logical_w;
SDL_RenderViewState *view = &renderer->main_view; const int logical_h = view->logical_h;
const int logical_w = renderer->logical_w;
const int logical_h = renderer->logical_h;
const float scale_x = view->scale.x; const float scale_x = view->scale.x;
const float scale_y = view->scale.y; const float scale_y = view->scale.y;
const bool clipping_enabled = view->clipping_enabled; const bool clipping_enabled = view->clipping_enabled;
SDL_Rect orig_viewport, orig_cliprect; SDL_Rect orig_viewport, orig_cliprect;
const SDL_FRect logical_dst_rect = view->logical_dst_rect;
SDL_copyp(&orig_viewport, &view->viewport); SDL_copyp(&orig_viewport, &view->viewport);
if (clipping_enabled) { if (clipping_enabled) {
@ -2798,10 +2812,10 @@ static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer)
SDL_SetRenderScale(renderer, 1.0f, 1.0f); SDL_SetRenderScale(renderer, 1.0f, 1.0f);
// draw the borders. // draw the borders.
SDL_RenderLogicalBorders(renderer); SDL_RenderLogicalBorders(renderer, &logical_dst_rect);
// now set everything back. // now set everything back.
renderer->logical_presentation_mode = mode; view->logical_presentation_mode = mode;
SDL_SetRenderViewport(renderer, &orig_viewport); SDL_SetRenderViewport(renderer, &orig_viewport);
if (clipping_enabled) { if (clipping_enabled) {
SDL_SetRenderClipRect(renderer, &orig_cliprect); SDL_SetRenderClipRect(renderer, &orig_cliprect);
@ -2819,14 +2833,14 @@ static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx,
window_dy *= renderer->dpi_scale.y; window_dy *= renderer->dpi_scale.y;
// Convert from pixels within the window to pixels within the view // Convert from pixels within the window to pixels within the view
if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { const SDL_RenderViewState *view = &renderer->main_view;
const SDL_FRect *src = &renderer->logical_src_rect; if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
const SDL_FRect *dst = &renderer->logical_dst_rect; const SDL_FRect *src = &view->logical_src_rect;
const SDL_FRect *dst = &view->logical_dst_rect;
window_dx = (window_dx * src->w) / dst->w; window_dx = (window_dx * src->w) / dst->w;
window_dy = (window_dy * src->h) / dst->h; window_dy = (window_dy * src->h) / dst->h;
} }
const SDL_RenderViewState *view = &renderer->main_view;
window_dx /= view->scale.x; window_dx /= view->scale.x;
window_dy /= view->scale.y; window_dy /= view->scale.y;
@ -2846,14 +2860,14 @@ bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, flo
render_y = window_y * renderer->dpi_scale.y; render_y = window_y * renderer->dpi_scale.y;
// Convert from pixels within the window to pixels within the view // Convert from pixels within the window to pixels within the view
if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { const SDL_RenderViewState *view = &renderer->main_view;
const SDL_FRect *src = &renderer->logical_src_rect; if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
const SDL_FRect *dst = &renderer->logical_dst_rect; const SDL_FRect *src = &view->logical_src_rect;
const SDL_FRect *dst = &view->logical_dst_rect;
render_x = ((render_x - dst->x) * src->w) / dst->w; render_x = ((render_x - dst->x) * src->w) / dst->w;
render_y = ((render_y - dst->y) * src->h) / dst->h; render_y = ((render_y - dst->y) * src->h) / dst->h;
} }
const SDL_RenderViewState *view = &renderer->main_view;
render_x = (render_x / view->scale.x) - view->viewport.x; render_x = (render_x / view->scale.x) - view->viewport.x;
render_y = (render_y / view->scale.y) - view->viewport.y; render_y = (render_y / view->scale.y) - view->viewport.y;
@ -2875,9 +2889,9 @@ bool SDL_RenderCoordinatesToWindow(SDL_Renderer *renderer, float x, float y, flo
y = (view->viewport.y + y) * view->scale.y; y = (view->viewport.y + y) * view->scale.y;
// Convert from render coordinates to pixels within the window // Convert from render coordinates to pixels within the window
if (renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) {
const SDL_FRect *src = &renderer->logical_src_rect; const SDL_FRect *src = &view->logical_src_rect;
const SDL_FRect *dst = &renderer->logical_dst_rect; const SDL_FRect *dst = &view->logical_dst_rect;
x = dst->x + ((x * dst->w) / src->w); x = dst->x + ((x * dst->w) / src->w);
y = dst->y + ((y * dst->h) / src->h); y = dst->y + ((y * dst->h) / src->h);
} }
@ -2968,18 +2982,17 @@ bool SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect)
{ {
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
SDL_RenderViewState *view = renderer->view;
if (rect) { if (rect) {
if ((rect->w < 0) || (rect->h < 0)) { if ((rect->w < 0) || (rect->h < 0)) {
return SDL_SetError("rect has a negative size"); return SDL_SetError("rect has a negative size");
} }
SDL_copyp(&renderer->view->viewport, rect); SDL_copyp(&view->viewport, rect);
} else { } else {
renderer->view->viewport.x = 0; view->viewport.x = view->viewport.y = 0;
renderer->view->viewport.y = 0; view->viewport.w = view->viewport.h = -1;
renderer->view->viewport.w = -1;
renderer->view->viewport.h = -1;
} }
UpdatePixelViewport(renderer, renderer->view); UpdatePixelViewport(renderer, view);
return QueueCmdSetViewport(renderer); return QueueCmdSetViewport(renderer);
} }
@ -3001,7 +3014,7 @@ bool SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect)
} else { } else {
rect->w = (int)SDL_ceilf(view->pixel_w / view->current_scale.x); rect->w = (int)SDL_ceilf(view->pixel_w / view->current_scale.x);
} }
if (renderer->view->viewport.h >= 0) { if (view->viewport.h >= 0) {
rect->h = view->viewport.h; rect->h = view->viewport.h;
} else { } else {
rect->h = (int)SDL_ceilf(view->pixel_h / view->current_scale.y); rect->h = (int)SDL_ceilf(view->pixel_h / view->current_scale.y);
@ -3014,11 +3027,8 @@ bool SDL_RenderViewportSet(SDL_Renderer *renderer)
{ {
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
if (renderer->view->viewport.w >= 0 && const SDL_RenderViewState *view = renderer->view;
renderer->view->viewport.h >= 0) { return (view->viewport.w >= 0 && view->viewport.h >= 0);
return true;
}
return false;
} }
static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect) static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect)
@ -3094,14 +3104,15 @@ bool SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
{ {
CHECK_RENDERER_MAGIC(renderer, false) CHECK_RENDERER_MAGIC(renderer, false)
SDL_RenderViewState *view = renderer->view;
if (rect && rect->w >= 0 && rect->h >= 0) { if (rect && rect->w >= 0 && rect->h >= 0) {
renderer->view->clipping_enabled = true; view->clipping_enabled = true;
SDL_copyp(&renderer->view->clip_rect, rect); SDL_copyp(&view->clip_rect, rect);
} else { } else {
renderer->view->clipping_enabled = false; view->clipping_enabled = false;
SDL_zero(renderer->view->clip_rect); SDL_zero(view->clip_rect);
} }
UpdatePixelClipRect(renderer, renderer->view); UpdatePixelClipRect(renderer, view);
return QueueCmdSetClipRect(renderer); return QueueCmdSetClipRect(renderer);
} }
@ -3132,17 +3143,18 @@ bool SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY)
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
if (renderer->view->scale.x == scaleX && SDL_RenderViewState *view = renderer->view;
renderer->view->scale.y == scaleY) {
if ((view->scale.x == scaleX) && (view->scale.y == scaleY)) {
return true; return true;
} }
renderer->view->scale.x = scaleX; view->scale.x = scaleX;
renderer->view->scale.y = scaleY; view->scale.y = scaleY;
renderer->view->current_scale.x = scaleX * renderer->view->logical_scale.x; view->current_scale.x = scaleX * view->logical_scale.x;
renderer->view->current_scale.y = scaleY * renderer->view->logical_scale.y; view->current_scale.y = scaleY * view->logical_scale.y;
UpdatePixelViewport(renderer, renderer->view); UpdatePixelViewport(renderer, view);
UpdatePixelClipRect(renderer, renderer->view); UpdatePixelClipRect(renderer, view);
// The scale affects the existing viewport and clip rectangle // The scale affects the existing viewport and clip rectangle
result &= QueueCmdSetViewport(renderer); result &= QueueCmdSetViewport(renderer);
@ -3161,11 +3173,13 @@ bool SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY)
CHECK_RENDERER_MAGIC(renderer, false); CHECK_RENDERER_MAGIC(renderer, false);
const SDL_RenderViewState *view = renderer->view;
if (scaleX) { if (scaleX) {
*scaleX = renderer->view->scale.x; *scaleX = view->scale.x;
} }
if (scaleY) { if (scaleY) {
*scaleY = renderer->view->scale.y; *scaleY = view->scale.y;
} }
return true; return true;
} }
@ -3350,8 +3364,9 @@ static bool RenderPointsWithRects(SDL_Renderer *renderer, const SDL_FPoint *fpoi
return false; return false;
} }
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
for (i = 0; i < count; ++i) { for (i = 0; i < count; ++i) {
frects[i].x = fpoints[i].x * scale_x; frects[i].x = fpoints[i].x * scale_x;
frects[i].y = fpoints[i].y * scale_y; frects[i].y = fpoints[i].y * scale_y;
@ -3386,7 +3401,8 @@ bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int coun
} }
#endif #endif
if ((renderer->view->current_scale.x != 1.0f) || (renderer->view->current_scale.y != 1.0f)) { const SDL_RenderViewState *view = renderer->view;
if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) {
result = RenderPointsWithRects(renderer, points, count); result = RenderPointsWithRects(renderer, points, count);
} else { } else {
result = QueueCmdDrawPoints(renderer, points, count); result = QueueCmdDrawPoints(renderer, points, count);
@ -3406,7 +3422,8 @@ bool SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float
static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2, int y2, bool draw_last) static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2, int y2, bool draw_last)
{ {
const int MAX_PIXELS = SDL_max(renderer->view->pixel_w, renderer->view->pixel_h) * 4; const SDL_RenderViewState *view = renderer->view;
const int MAX_PIXELS = SDL_max(view->pixel_w, view->pixel_h) * 4;
int i, deltax, deltay, numpixels; int i, deltax, deltay, numpixels;
int d, dinc1, dinc2; int d, dinc1, dinc2;
int x, xinc1, xinc2; int x, xinc1, xinc2;
@ -3419,7 +3436,7 @@ static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2,
/* the backend might clip this further to the clipping rect, but we /* the backend might clip this further to the clipping rect, but we
just want a basic safety against generating millions of points for just want a basic safety against generating millions of points for
massive lines. */ massive lines. */
viewport = renderer->view->pixel_viewport; viewport = view->pixel_viewport;
viewport.x = 0; viewport.x = 0;
viewport.y = 0; viewport.y = 0;
if (!SDL_GetRectAndLineIntersection(&viewport, &x1, &y1, &x2, &y2)) { if (!SDL_GetRectAndLineIntersection(&viewport, &x1, &y1, &x2, &y2)) {
@ -3488,7 +3505,7 @@ static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2,
} }
} }
if ((renderer->view->current_scale.x != 1.0f) || (renderer->view->current_scale.y != 1.0f)) { if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) {
result = RenderPointsWithRects(renderer, points, numpixels); result = RenderPointsWithRects(renderer, points, numpixels);
} else { } else {
result = QueueCmdDrawPoints(renderer, points, numpixels); result = QueueCmdDrawPoints(renderer, points, numpixels);
@ -3501,8 +3518,9 @@ static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2,
static bool RenderLinesWithRectsF(SDL_Renderer *renderer, const SDL_FPoint *points, const int count) static bool RenderLinesWithRectsF(SDL_Renderer *renderer, const SDL_FPoint *points, const int count)
{ {
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
SDL_FRect *frect; SDL_FRect *frect;
SDL_FRect *frects; SDL_FRect *frects;
int i, nrects = 0; int i, nrects = 0;
@ -3589,11 +3607,12 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count
} }
#endif #endif
const bool islogical = ((renderer->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) && (renderer->view == &renderer->main_view)); SDL_RenderViewState *view = renderer->view;
const bool islogical = ((view == &renderer->main_view) && (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED));
if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) { if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) {
const float scale_x = renderer->view->current_scale.x; const float scale_x = view->current_scale.x;
const float scale_y = renderer->view->current_scale.y; const float scale_y = view->current_scale.y;
bool isstack1; bool isstack1;
bool isstack2; bool isstack2;
float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1); float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1);
@ -3712,7 +3731,7 @@ bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count
} else if (renderer->line_method == SDL_RENDERLINEMETHOD_POINTS) { } else if (renderer->line_method == SDL_RENDERLINEMETHOD_POINTS) {
result = RenderLinesWithRectsF(renderer, points, count); result = RenderLinesWithRectsF(renderer, points, count);
} else if (renderer->view->scale.x != 1.0f || renderer->view->scale.y != 1.0f) { /* we checked for logical scale elsewhere. */ } else if (view->scale.x != 1.0f || view->scale.y != 1.0f) { /* we checked for logical scale elsewhere. */
result = RenderLinesWithRectsF(renderer, points, count); result = RenderLinesWithRectsF(renderer, points, count);
} else { } else {
result = QueueCmdDrawLines(renderer, points, count); result = QueueCmdDrawLines(renderer, points, count);
@ -3817,8 +3836,9 @@ bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int cou
return false; return false;
} }
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
for (i = 0; i < count; ++i) { for (i = 0; i < count; ++i) {
frects[i].x = rects[i].x * scale_x; frects[i].x = rects[i].x * scale_x;
frects[i].y = rects[i].y * scale_y; frects[i].y = rects[i].y * scale_y;
@ -3835,8 +3855,9 @@ bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int cou
static bool SDL_RenderTextureInternal(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) static bool SDL_RenderTextureInternal(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)
{ {
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
const bool use_rendergeometry = (!renderer->QueueCopy); const bool use_rendergeometry = (!renderer->QueueCopy);
bool result; bool result;
@ -3976,8 +3997,9 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture,
texture->last_command_generation = renderer->render_command_generation; texture->last_command_generation = renderer->render_command_generation;
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
{ {
float xy[8]; float xy[8];
@ -4110,8 +4132,9 @@ bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture,
texture->last_command_generation = renderer->render_command_generation; texture->last_command_generation = renderer->render_command_generation;
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
const bool use_rendergeometry = (!renderer->QueueCopyEx); const bool use_rendergeometry = (!renderer->QueueCopyEx);
if (use_rendergeometry) { if (use_rendergeometry) {
@ -4242,10 +4265,11 @@ static bool SDL_RenderTextureTiled_Wrap(SDL_Renderer *renderer, SDL_Texture *tex
xy[6] = minx; xy[6] = minx;
xy[7] = maxy; xy[7] = maxy;
const SDL_RenderViewState *view = renderer->view;
return QueueCmdGeometry(renderer, texture, return QueueCmdGeometry(renderer, texture,
xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride,
num_vertices, indices, num_indices, size_indices, num_vertices, indices, num_indices, size_indices,
renderer->view->current_scale.x, renderer->view->current_scale.y, SDL_TEXTURE_ADDRESS_WRAP); view->current_scale.x, view->current_scale.y, SDL_TEXTURE_ADDRESS_WRAP);
} }
static bool SDL_RenderTextureTiled_Iterate(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) static bool SDL_RenderTextureTiled_Iterate(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect)
@ -4596,8 +4620,9 @@ static bool SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer,
float texw = 0.0f, texh = 0.0f; float texw = 0.0f, texh = 0.0f;
SDL_BlendMode blendMode = SDL_BLENDMODE_NONE; SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
float r = 0, g = 0, b = 0, a = 0; float r = 0, g = 0, b = 0, a = 0;
const float scale_x = renderer->view->current_scale.x; const SDL_RenderViewState *view = renderer->view;
const float scale_y = renderer->view->current_scale.y; const float scale_x = view->current_scale.x;
const float scale_y = view->current_scale.y;
// Save // Save
SDL_GetRenderDrawBlendMode(renderer, &blendMode); SDL_GetRenderDrawBlendMode(renderer, &blendMode);
@ -4997,10 +5022,11 @@ bool SDL_RenderGeometryRaw(SDL_Renderer *renderer,
} }
#endif #endif
const SDL_RenderViewState *view = renderer->view;
return QueueCmdGeometry(renderer, texture, return QueueCmdGeometry(renderer, texture,
xy, xy_stride, color, color_stride, uv, uv_stride, xy, xy_stride, color, color_stride, uv, uv_stride,
num_vertices, indices, num_indices, size_indices, num_vertices, indices, num_indices, size_indices,
renderer->view->current_scale.x, renderer->view->current_scale.y, view->current_scale.x, view->current_scale.y,
texture_address_mode); texture_address_mode);
} }

View file

@ -65,8 +65,15 @@ typedef struct SDL_RenderViewState
SDL_Rect pixel_clip_rect; SDL_Rect pixel_clip_rect;
bool clipping_enabled; bool clipping_enabled;
SDL_FPoint scale; SDL_FPoint scale;
// Support for logical output coordinates
SDL_RendererLogicalPresentation logical_presentation_mode;
int logical_w, logical_h;
SDL_FRect logical_src_rect;
SDL_FRect logical_dst_rect;
SDL_FPoint logical_scale; SDL_FPoint logical_scale;
SDL_FPoint logical_offset; SDL_FPoint logical_offset;
SDL_FPoint current_scale; // this is just `scale * logical_scale`, precalculated, since we use it a lot. SDL_FPoint current_scale; // this is just `scale * logical_scale`, precalculated, since we use it a lot.
} SDL_RenderViewState; } SDL_RenderViewState;
@ -248,18 +255,9 @@ struct SDL_Renderer
Uint64 simulate_vsync_interval_ns; Uint64 simulate_vsync_interval_ns;
Uint64 last_present; Uint64 last_present;
// Support for logical output coordinates
SDL_RendererLogicalPresentation logical_presentation_mode;
int logical_w, logical_h;
SDL_FRect logical_src_rect;
SDL_FRect logical_dst_rect;
SDL_RenderViewState *view; SDL_RenderViewState *view;
SDL_RenderViewState main_view; SDL_RenderViewState main_view;
// Cache the output size in pixels
int output_pixel_w, output_pixel_h;
// The window pixel to point coordinate scale // The window pixel to point coordinate scale
SDL_FPoint dpi_scale; SDL_FPoint dpi_scale;