From 434ba135ee61cd6d60c7569cdb519891c4ecb95c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 27 Mar 2026 18:41:47 -0700
Subject: [PATCH] Wrap the screen in the vertical direction in zoom mode
---
game/game.cpp | 171 ++++++++++++++++++++++++++++++---------
game/game.h | 4 +
screenlib/SDL_FrameBuf.h | 6 ++
screenlib/UIManager.cpp | 7 +-
4 files changed, 150 insertions(+), 38 deletions(-)
diff --git a/game/game.cpp b/game/game.cpp
index 97550c6b..dd8ff193 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -616,48 +616,58 @@ GamePanelDelegate::UpdateZoom()
}
if (zoom) {
- float scale = (float)GAME_WIDTH / rect.w;
- int x = (int)SDL_round(rect.x * scale);
- int y = (int)SDL_round(rect.y * scale);
- int height = (int)SDL_round(rect.h * scale);
- ui->SetPosition(x, y);
- ui->SetSize(GAME_WIDTH, height);
+ StartZoom(rect);
} else {
- ui->SetPosition(0, 0);
- ui->SetSize(GAME_WIDTH, GAME_HEIGHT);
+ StopZoom();
}
- m_zoom = zoom;
+}
+
+void
+GamePanelDelegate::StartZoom(const SDL_Rect &rect)
+{
+ SDL_Renderer *renderer = screen->GetRenderer();
+ float scale = (float)GAME_WIDTH / rect.w;
+ int x = (int)SDL_round(rect.x * scale);
+ int y = (int)SDL_round(rect.y * scale);
+ int height = (int)SDL_round(rect.h * scale);
+ ui->SetPosition(x, y);
+ ui->SetSize(GAME_WIDTH, height);
+
+ if (!m_texture) {
+ m_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_TARGET, GAME_WIDTH, GAME_HEIGHT);
+ }
+
+ m_zoom = true;
+}
+
+void
+GamePanelDelegate::StopZoom()
+{
+ ui->SetPosition(0, 0);
+ ui->SetSize(GAME_WIDTH, GAME_HEIGHT);
+
+ if (m_texture) {
+ SDL_DestroyTexture(m_texture);
+ m_texture = nullptr;
+ }
+
+ m_zoom = false;
}
void
GamePanelDelegate::StartZoomedDrawing()
{
SDL_Renderer *renderer = screen->GetRenderer();
- SDL_SetRenderLogicalPresentation(renderer, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
- int w = 0, h = 0;
- SDL_GetCurrentRenderOutputSize(renderer, &w, &h);
- float scale = (float)w / GAME_WIDTH;
- int playerX, playerY;
- gPlayers[0]->GetPos(&playerX, &playerY);
- GetRenderCoordinates(playerX, playerY);
- playerY += (32 / 2);
+ // Don't clip the top and bottom
+ screen->GetClip(&m_savedClip);
+ SDL_Rect clip = m_savedClip;
+ clip.y = 0;
+ clip.h = GAME_HEIGHT;
+ screen->ClipBlit(&clip);
- SDL_SetRenderScale(renderer, scale, scale);
-
- int half_viewable_height = (int)(gScrnRect.h / 2 * ((float)gScrnRect.w / gScrnRect.h) / (((float)w / h)));
- int shortfall = (int)(gScrnRect.h - (h / scale));
- SDL_Rect viewport;
- viewport.w = w;
- viewport.h = h;
- viewport.x = 0;
- viewport.y = -playerY + half_viewable_height;
- if (viewport.y > 0) {
- viewport.y = 0;
- } else if (viewport.y < -shortfall) {
- viewport.y = -shortfall;
- }
- SDL_SetRenderViewport(renderer, &viewport);
+ SDL_SetRenderTarget(renderer, m_texture);
+ screen->Clear();
}
void
@@ -665,9 +675,88 @@ GamePanelDelegate::StopZoomedDrawing()
{
SDL_Renderer *renderer = screen->GetRenderer();
- SDL_SetRenderScale(renderer, 1.0f, 1.0f);
- SDL_SetRenderViewport(renderer, nullptr);
+ SDL_SetRenderTarget(renderer, nullptr);
+ SDL_SetRenderLogicalPresentation(renderer, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
+
+ int w = 0, h = 0;
+ SDL_GetRenderOutputSize(renderer, &w, &h);
+
+ int playerX, playerY;
+ gPlayers[0]->GetPos(&playerX, &playerY);
+ GetRenderCoordinates(playerX, playerY);
+ playerX += (32 / 2);
+ playerY += (32 / 2);
+
+ float scale = (float)GAME_WIDTH / w;
+ SDL_Rect src;
+ src.w = GAME_WIDTH;
+ src.h = (int)SDL_roundf(h * scale);
+ src.x = 0;
+ src.y = playerY - src.h / 2;
+ float minu = (float)src.x / m_texture->w;
+ float minv = (float)src.y / m_texture->h;
+ float maxu = (float)(src.x + src.w) / m_texture->w;
+ float maxv = (float)(src.y + src.h) / m_texture->h;
+
+ SDL_FRect dst;
+ dst.x = 0.0f;
+ dst.y = 0.0f;
+ dst.w = (float)w;
+ dst.h = (float)h;
+
+ SDL_FColor color = { 1.0f, 1.0f, 1.0f, 1.0f };
+ SDL_Vertex verts[6];
+ SDL_Vertex *vert = verts;
+ /* 0 */
+ vert->position.x = dst.x;
+ vert->position.y = dst.y;
+ vert->color = color;
+ vert->tex_coord.x = minu;
+ vert->tex_coord.y = minv;
+ vert++;
+ /* 1 */
+ vert->position.x = dst.x + dst.w;
+ vert->position.y = dst.y;
+ vert->color = color;
+ vert->tex_coord.x = maxu;
+ vert->tex_coord.y = minv;
+ vert++;
+ /* 2 */
+ vert->position.x = dst.x + dst.w;
+ vert->position.y = dst.y + dst.h;
+ vert->color = color;
+ vert->tex_coord.x = maxu;
+ vert->tex_coord.y = maxv;
+ vert++;
+ /* 0 */
+ vert->position.x = dst.x;
+ vert->position.y = dst.y;
+ vert->color = color;
+ vert->tex_coord.x = minu;
+ vert->tex_coord.y = minv;
+ vert++;
+ /* 2 */
+ vert->position.x = dst.x + dst.w;
+ vert->position.y = dst.y + dst.h;
+ vert->color = color;
+ vert->tex_coord.x = maxu;
+ vert->tex_coord.y = maxv;
+ vert++;
+ /* 3 */
+ vert->position.x = dst.x;
+ vert->position.y = dst.y + dst.h;
+ vert->color = color;
+ vert->tex_coord.x = minu;
+ vert->tex_coord.y = maxv;
+ vert++;
+
+ SDL_SetRenderTextureAddressMode(renderer, SDL_TEXTURE_ADDRESS_WRAP, SDL_TEXTURE_ADDRESS_WRAP);
+ SDL_RenderGeometry(renderer, m_texture, verts, 6, NULL, 0);
+ SDL_SetRenderTextureAddressMode(renderer, SDL_TEXTURE_ADDRESS_AUTO, SDL_TEXTURE_ADDRESS_AUTO);
+
SDL_SetRenderLogicalPresentation(renderer, ui->X() + ui->Width() + ui->X(), ui->Y() + ui->Height() + ui->Y(), SDL_LOGICAL_PRESENTATION_LETTERBOX);
+
+ screen->ClipBlit(&m_savedClip);
}
/* ----------------------------------------------------------------- */
@@ -1346,10 +1435,9 @@ GamePanelDelegate::GameOver()
DisableRemoteInput();
- ui->SetPosition(0, 0);
- ui->SetSize(GAME_WIDTH, GAME_HEIGHT);
-
ui->ShowPanel(PANEL_GAMEOVER);
+
+ StopZoom();
}
/* ----------------------------------------------------------------- */
@@ -1370,4 +1458,13 @@ void RenderSprite(UITexture *sprite, int x, int y, int w, int h)
w = (int)(((float)w * gScrnRect.w) / GAME_WIDTH);
h = (int)(((float)h * gScrnRect.h) / GAME_HEIGHT);
screen->QueueBlit(sprite->Texture(), x, y, w, h, DOCLIP);
+
+ // Render the other side of the sprite
+ if (y < 0) {
+ y += GAME_HEIGHT;
+ screen->QueueBlit(sprite->Texture(), x, y, w, h, DOCLIP);
+ } else if ((y + h) > GAME_HEIGHT) {
+ y -= GAME_HEIGHT;
+ screen->QueueBlit(sprite->Texture(), x, y, w, h, DOCLIP);
+ }
}
diff --git a/game/game.h b/game/game.h
index 80243cd5..6c2aea98 100644
--- a/game/game.h
+++ b/game/game.h
@@ -42,6 +42,8 @@ class GamePanelDelegate : public UIPanelDelegate
protected:
void UpdateZoom();
+ void StartZoom(const SDL_Rect &rect);
+ void StopZoom();
void StartZoomedDrawing();
void StopZoomedDrawing();
void DrawStatus(Bool first);
@@ -101,6 +103,8 @@ class GamePanelDelegate : public UIPanelDelegate
} m_state;
bool m_zoom;
+ SDL_Texture *m_texture = nullptr;
+ SDL_Rect m_savedClip;
};
/* ----------------------------------------------------------------- */
diff --git a/screenlib/SDL_FrameBuf.h b/screenlib/SDL_FrameBuf.h
index dfb2f5d9..118e6798 100644
--- a/screenlib/SDL_FrameBuf.h
+++ b/screenlib/SDL_FrameBuf.h
@@ -66,6 +66,12 @@ class FrameBuf : public ErrorBase {
m_clip.w = (float)cliprect->w;
m_clip.h = (float)cliprect->h;
}
+ void GetClip(SDL_Rect *cliprect) {
+ cliprect->x = (int)m_clip.x;
+ cliprect->y = (int)m_clip.y;
+ cliprect->w = (int)m_clip.w;
+ cliprect->h = (int)m_clip.h;
+ }
void SetLogicalSize(int width, int height);
/* Event Routines */
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 43d7f9c6..33bd179f 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -403,10 +403,15 @@ UIManager::Draw(bool tick)
// Run the tick before we draw in case it changes drawing state
if (tick) {
for (i = 0; i < m_visible.length(); ++i) {
- UIPanel* panel = m_visible[i];
+ UIPanel *panel = m_visible[i];
panel->Poll();
panel->Tick();
+
+ if (i >= m_visible.length() || panel != m_visible[i]) {
+ // This panel was hidden, don't draw it
+ return;
+ }
}
}