Maelstrom: Use a render target to enable fading and scaling control

From 8d4c82732ea5a226e92c87cbeee1565c85163007 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 22 Nov 2025 22:48:45 -0800
Subject: [PATCH] Use a render target to enable fading and scaling control

---
 game/init.cpp              |  5 +--
 screenlib/SDL_FrameBuf.cpp | 89 ++++++++++++++++----------------------
 screenlib/SDL_FrameBuf.h   | 13 ++----
 3 files changed, 43 insertions(+), 64 deletions(-)

diff --git a/game/init.cpp b/game/init.cpp
index 277ed600..779fb84b 100644
--- a/game/init.cpp
+++ b/game/init.cpp
@@ -839,9 +839,8 @@ int DoInitializations(Uint32 window_flags)
 	/* Create the UI manager */
 	ui = new MaelstromUI(screen, prefs);
 
-	if (window_flags & SDL_WINDOW_FULLSCREEN) {
-		ui->SetPanelTransition(PANEL_TRANSITION_FADE);
-	}
+	/* Fade transition between panels */
+	ui->SetPanelTransition(PANEL_TRANSITION_FADE);
 
 	/* -- Throw up our intro screen */
 	if (ui->GetPanelTransition() == PANEL_TRANSITION_FADE) {
diff --git a/screenlib/SDL_FrameBuf.cpp b/screenlib/SDL_FrameBuf.cpp
index f7ecbf4e..c52c9d9f 100644
--- a/screenlib/SDL_FrameBuf.cpp
+++ b/screenlib/SDL_FrameBuf.cpp
@@ -34,15 +34,12 @@
 /* Constructors cannot fail. :-/ */
 FrameBuf::FrameBuf() : ErrorBase()
 {
-	/* Initialize various variables to null state */
-	window = NULL;
-	renderer = NULL;
-	faded = 0;
 }
 
 int
 FrameBuf::Init(int width, int height, Uint32 window_flags, SDL_Surface *icon)
 {
+	/* Create the window */
 	window = SDL_CreateWindow(NULL, width, height, window_flags);
 	if (!window) {
 		SetError("Couldn't create %dx%d window: %s", 
@@ -50,26 +47,40 @@ FrameBuf::Init(int width, int height, Uint32 window_flags, SDL_Surface *icon)
 		return(-1);
 	}
 
+	/* Set the icon, if any */
+	if ( icon ) {
+		SDL_SetWindowIcon(window, icon);
+	}
+
+	/* Create the renderer */
 	renderer = SDL_CreateRenderer(window, NULL);
 	if (!renderer) {
 		SetError("Couldn't create renderer: %s", SDL_GetError());
 		return(-1);
 	}
 
-	/* Set the icon, if any */
-	if ( icon ) {
-		SDL_SetWindowIcon(window, icon);
-	}
-
 	/* Set the output area */
 	SDL_SetRenderLogicalPresentation(renderer, width, height, SDL_LOGICAL_PRESENTATION_LETTERBOX);
 	UpdateWindowSize(width, height);
 
+	/* Create the render target */
+	target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_TARGET, width, height);
+	if (!target) {
+		SetError("Couldn't create target: %s", SDL_GetError());
+		return(-1);
+	}
+	//SDL_SetTextureScaleMode(target, SDL_SCALEMODE_PIXELART);
+
+	SDL_SetRenderTarget(renderer, target);
+
 	return(0);
 }
 
 FrameBuf::~FrameBuf()
 {
+	if (target) {
+		SDL_DestroyTexture(target);
+	}
 	if (renderer) {
 		SDL_DestroyRenderer(renderer);
 	}
@@ -186,7 +197,18 @@ FrameBuf::StretchBlit(const SDL_Rect *_dstrect, SDL_Texture *src, const SDL_Rect
 void
 FrameBuf::Update(void)
 {
+	/* Make sure resize events are seen before drawing to the screen */
+	SDL_PumpEvents();
+
+	SDL_SetRenderTarget(renderer, NULL);
+
+	SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+	SDL_RenderClear(renderer);
+
+	SDL_RenderTexture(renderer, target, NULL, NULL);
 	SDL_RenderPresent(renderer);
+
+	SDL_SetRenderTarget(renderer, target);
 }
 
 void
@@ -196,24 +218,16 @@ FrameBuf::Fade(void)
 	return;
 #else
 	const int max = 32;
-	Uint16 ramp[256];   
+	Uint8 value;
 
-	for ( int j = 1; j <= max; j++ ) {
-		int v = faded ? j : max - j + 1;
-		for ( int i = 0; i < 256; i++ ) {
-			ramp[i] = (i * v / max) << 8;
-		}
-		// FIXME SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
+	for ( int i = 1; i <= max; ++i ) {
+		int v = faded ? i : max - i;
+		value = (Uint8)(255 * v / max);
+		SDL_SetTextureColorMod(target, value, value, value);
+		Update();
 		SDL_Delay(10);
 	}
 	faded = !faded;
-
-	if ( faded ) {
-		for ( int i = 0; i < 256; i++ ) {
-			ramp[i] = 0;
-		}
-		// FIXME SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
-	}
 #endif
 } 
 
@@ -318,32 +332,3 @@ FrameBuf::FreeImage(SDL_Texture *image)
 	SDL_DestroyTexture(image);
 }
 
-SDL_Texture *
-FrameBuf::CreateRenderTarget(int w, int h)
-{
-	SDL_Texture *texture;
-
-	texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_XRGB8888,
-				    SDL_TEXTUREACCESS_TARGET, w, h);
-	if (!texture) {
-		SetError("Couldn't create target texture: %s", SDL_GetError());
-		return NULL;
-	}
-	return texture;
-}
-
-int
-FrameBuf::SetRenderTarget(SDL_Texture *texture)
-{
-	if (!SDL_SetRenderTarget(renderer, texture)) {
-		SetError("Couldn't set render target: %s", SDL_GetError());
-		return(-1);
-	}
-	return 0;
-}
-
-void
-FrameBuf::FreeRenderTarget(SDL_Texture *texture)
-{
-	SDL_DestroyTexture(texture);
-}
diff --git a/screenlib/SDL_FrameBuf.h b/screenlib/SDL_FrameBuf.h
index 710e56c5..d5ecab7a 100644
--- a/screenlib/SDL_FrameBuf.h
+++ b/screenlib/SDL_FrameBuf.h
@@ -181,12 +181,6 @@ class FrameBuf : public ErrorBase {
 	}
 	void FreeImage(SDL_Texture *image);
 
-	/* Create a render target */
-	SDL_Texture *CreateRenderTarget(int w, int h);
-	int SetRenderTarget(SDL_Texture *texture);
-	void FreeRenderTarget(SDL_Texture *texture);
-	
-
 	/* Screen dump routines */
 	int ScreenDump(const char *prefix, int x, int y, int w, int h);
 
@@ -204,9 +198,10 @@ class FrameBuf : public ErrorBase {
 
 private:
 	/* The current display */
-	SDL_Window *window;
-	SDL_Renderer *renderer;
-	int faded;
+	SDL_Window *window = nullptr;
+	SDL_Renderer *renderer = nullptr;
+	SDL_Texture *target = nullptr;
+	bool faded = false;
 	SDL_Rect rect;
 	SDL_FRect clip;
 	SDL_Rect output;