[PATCH 2/4] Support floating point zoom factors with SDL2

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


This will ignore renderQuality configuration option and instead set
automatically suitable value based on the difference between the
Hatari framebuffer and size of the window on which it will be rendered.

=> (smoothing) linear scaling is used for non-integer scaling values,
   (sharp) nearest pixel sampling otherwise.

If window resize requires scaling quality change, window's backing
texture is re-created (required to apply quality hint).

If user has disabled SDL2 sdlRenderer support, all of above is no-op.

TODO: remove renderQuality and sdlRenderer configuration options?
---
 src/configuration.c          |   2 +
 src/includes/configuration.h |   1 +
 src/includes/screen.h        |   1 +
 src/main.c                   |   8 ++-
 src/options.c                |  30 +++++++++-
 src/screen.c                 | 110 ++++++++++++++++++++++++++++-------
 6 files changed, 127 insertions(+), 25 deletions(-)

diff --git a/src/configuration.c b/src/configuration.c
index 87fe724d..62798053 100644
--- a/src/configuration.c
+++ b/src/configuration.c
@@ -99,6 +99,7 @@ static const struct Config_Tag configs_Screen[] =
 	{ "nMaxWidth", Int_Tag, &ConfigureParams.Screen.nMaxWidth },
 	{ "nMaxHeight", Int_Tag, &ConfigureParams.Screen.nMaxHeight },
 #if WITH_SDL2
+	{ "nZoomFactor", Float_Tag, &ConfigureParams.Screen.nZoomFactor },
 	{ "bUseSdlRenderer", Bool_Tag, &ConfigureParams.Screen.bUseSdlRenderer },
 	{ "nRenderScaleQuality", Int_Tag, &ConfigureParams.Screen.nRenderScaleQuality },
 	{ "bUseVsync", Bool_Tag, &ConfigureParams.Screen.bUseVsync },
@@ -840,6 +841,7 @@ void Configuration_SetDefault(void)
 	ConfigureParams.Screen.bForceMax = false;
 	ConfigureParams.Screen.DisableVideo = false;
 #if WITH_SDL2
+	ConfigureParams.Screen.nZoomFactor = 1.0;
 	ConfigureParams.Screen.bUseSdlRenderer = true;
 	ConfigureParams.Screen.nRenderScaleQuality = 0;
 	ConfigureParams.Screen.bUseVsync = false;
diff --git a/src/includes/configuration.h b/src/includes/configuration.h
index dbaf97ac..3c4f8868 100644
--- a/src/includes/configuration.h
+++ b/src/includes/configuration.h
@@ -304,6 +304,7 @@ typedef struct
   bool bResizable;
   bool bUseVsync;
   bool bUseSdlRenderer;
+  float nZoomFactor;
   int nRenderScaleQuality;
 #endif
   int nSpec512Threshold;
diff --git a/src/includes/screen.h b/src/includes/screen.h
index 505e8399..98c92d78 100644
--- a/src/includes/screen.h
+++ b/src/includes/screen.h
@@ -20,6 +20,7 @@ static inline int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors,
 }
 void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
 void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h);
+void Screen_SetTextureScale(int width, int height, int win_width, int win_height, bool bForceCreation);
 #define SDL_GRAB_OFF false
 #define SDL_GRAB_ON true
 #define SDL_WM_GrabInput SDL_SetRelativeMouseMode
diff --git a/src/main.c b/src/main.c
index dc801e3e..538f1faa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -656,12 +656,18 @@ void Main_EventHandler(void)
 				}
 				/* fall through */
 			case SDL_WINDOWEVENT_RESTORED:
-			case SDL_WINDOWEVENT_SIZE_CHANGED:
 				/* Note: any changes here should most likely
 				 * be done also in sdlgui.c::SDLGui_DoDialog()
 				 */
 				SDL_UpdateRect(sdlscrn, 0, 0, 0, 0);
 				break;
+			case SDL_WINDOWEVENT_SIZE_CHANGED:
+				/* internal & external window size changes */
+				Screen_SetTextureScale(sdlscrn->w, sdlscrn->h,
+						       event.window.data1,
+						       event.window.data2, false);
+				SDL_UpdateRect(sdlscrn, 0, 0, 0, 0);
+				break;
 				/* mouse & keyboard focus */
 			case SDL_WINDOWEVENT_ENTER:
 			case SDL_WINDOWEVENT_FOCUS_GAINED:
diff --git a/src/options.c b/src/options.c
index c40a1bd6..96a0f017 100644
--- a/src/options.c
+++ b/src/options.c
@@ -265,7 +265,11 @@ static const opt_t HatariOptions[] = {
 	{ OPT_SPEC512, NULL, "--spec512",
 	  "<x>", "Spec512 palette threshold (0 <= x <= 512, 0=disable)" },
 	{ OPT_ZOOM, "-z", "--zoom",
+#if WITH_SDL2
+	  "<x>", "Screen zoom factor (1.0 - 8.0)" },
+#else
 	  "<x>", "Double small resolutions (1=no, 2=yes)" },
+#endif
 	{ OPT_VIDEO_TIMING,   NULL, "--video-timing",
 	  "<x>", "Wakeup State for MMU/GLUE (x=ws1/ws2/ws3/ws4/random, default ws3)" },
 
@@ -1041,9 +1045,14 @@ static bool Opt_HandleArgument(const char *path)
  */
 bool Opt_ParseParameters(int argc, const char * const argv[])
 {
-	int ncpu, skips, zoom, planes, cpuclock, threshold, memsize, port, freq, temp, drive;
+	int ncpu, skips, planes, cpuclock, threshold, memsize, port, freq, temp, drive;
 	const char *errstr, *str;
 	int i, ok = true;
+#if WITH_SDL2
+	float zoom;
+#else
+	int zoom;
+#endif
 	int val;
 
 	/* Defaults for loading initial memory snap-shots */
@@ -1242,18 +1251,35 @@ bool Opt_ParseParameters(int argc, const char * const argv[])
 			break;
 
 		case OPT_ZOOM:
+#if WITH_SDL2
+			zoom = atof(argv[++i]);
+			if (zoom < 1.0 || zoom > 8.0)
+#else
 			zoom = atoi(argv[++i]);
-			if (zoom < 1)
+			if (zoom < 1 || zoom > 2)
+#endif
 			{
 				return Opt_ShowError(OPT_ZOOM, argv[i], "Invalid zoom value");
 			}
 			ConfigureParams.Screen.nMaxWidth = NUM_VISIBLE_LINE_PIXELS;
 			ConfigureParams.Screen.nMaxHeight = NUM_VISIBLE_LINES;
+#if WITH_SDL2
+			ConfigureParams.Screen.nZoomFactor = zoom;
+			if (zoom >= 1.0)
+			{
+				if (zoom >= 2.0)
+				{
+					ConfigureParams.Screen.nMaxWidth *= 2;
+					ConfigureParams.Screen.nMaxHeight *= 2;
+				}
+			}
+#else
 			if (zoom > 1)
 			{
 				ConfigureParams.Screen.nMaxWidth *= 2;
 				ConfigureParams.Screen.nMaxHeight *= 2;
 			}
+#endif
 			ConfigureParams.Screen.nMaxHeight += STATUSBAR_MAX_HEIGHT;
 			break;
 
diff --git a/src/screen.c b/src/screen.c
index 16249883..af611e54 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -311,6 +311,80 @@ static void Screen_FreeSDL2Resources(void)
 		sdlRenderer = NULL;
 	}
 }
+
+/*
+ * Create window backing texture when needed, with suitable scaling
+ * quality.
+ *
+ * Window size is affected by ZoomFactor setting and window resizes
+ * done by the user, and constrained by maximum window size setting
+ * and desktop size.
+ *
+ * Calculate scale factor for the given resulting window size, compared
+ * to the size of the SDL frame buffer rendered by Hatari, and based on
+ * that, set the render scaling quality hint to:
+ * - (sharp) nearest pixel sampling for integer zoom factors
+ * - (smoothing/blurring) linear filtering otherwise
+ *
+ * If hint value changes from earlier one (or force flag is used),
+ * window texture needs to be re-created to apply the scaling quality
+ * change.
+ */
+void Screen_SetTextureScale(int width, int height, int win_width, int win_height, bool bForce)
+{
+	static char prev_quality = '0';
+	float scale_w, scale_h, scale;
+	char quality;
+	int pfmt;
+
+	if (!(bUseSdlRenderer && sdlRenderer))
+		return;
+
+	scale_w = (float)win_width / width;
+	scale_h = (float)win_height / height;
+	scale = (scale_w + scale_h) / 2.0;
+
+	if (scale == floorf(scale))
+		quality = '0';	// nearest pixel
+	else
+		quality = '1';	// linear filtering
+
+	DEBUGPRINT(("%dx%d / %dx%d -> scale = %.1f / Render Scale Quality = %c\n",
+		    win_width, win_height, width, height, scale, quality));
+
+	if (bForce || quality != prev_quality)
+	{
+		char hint[2] = { quality, 0 };
+		prev_quality = quality;
+
+		/* show new value in options */
+		ConfigureParams.Screen.nRenderScaleQuality = quality - '0';
+
+		/* hint needs to be there before texture */
+		SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, hint, SDL_HINT_OVERRIDE);
+
+		if (sdlTexture)
+		{
+			SDL_DestroyTexture(sdlTexture);
+			sdlTexture = NULL;
+		}
+
+		if (sdlscrn->format->BitsPerPixel == 16)
+			pfmt = SDL_PIXELFORMAT_RGB565;
+		else
+			pfmt = SDL_PIXELFORMAT_RGB888;
+
+		sdlTexture = SDL_CreateTexture(sdlRenderer, pfmt,
+					       SDL_TEXTUREACCESS_STREAMING,
+					       width, height);
+		if (!sdlTexture)
+		{
+			fprintf(stderr, "ERROR: Failed to create %dx%d@%d texture!\n",
+			       width, height, sdlscrn->format->BitsPerPixel);
+			exit(-3);
+		}
+	}
+}
 #endif
 
 /**
@@ -323,10 +397,10 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 	char *psSdlVideoDriver;
 	bool bUseDummyMode;
 #if WITH_SDL2
-	static int nPrevRenderScaleQuality = 0;
 	static bool bPrevUseVsync = false;
 	static bool bPrevInFullScreen;
 	int win_width, win_height;
+	float scale = 1.0;
 
 	if (bitdepth == 0 || bitdepth == 24)
 		bitdepth = 32;
@@ -355,6 +429,12 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 	/* SDL Video attributes: */
 	win_width = width;
 	win_height = height;
+	if (bUseSdlRenderer)
+	{
+		scale = ConfigureParams.Screen.nZoomFactor / nScreenZoomX;
+		win_width *= scale;
+		win_height *= scale;
+	}
 	if (bInFullScreen)
 	{
 		sdlVideoFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_GRABBED;
@@ -392,13 +472,6 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 	}
 	bPrevInFullScreen = bInFullScreen;
 
-	/* Set SDL2 video hints */
-	if (nPrevRenderScaleQuality != ConfigureParams.Screen.nRenderScaleQuality)
-	{
-		char hint[2] = { '0' + ConfigureParams.Screen.nRenderScaleQuality, 0 };
-		SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, hint, SDL_HINT_OVERRIDE);
-		nPrevRenderScaleQuality = ConfigureParams.Screen.nRenderScaleQuality;
-	}
 	if (bPrevUseVsync != ConfigureParams.Screen.bUseVsync)
 	{
 		char hint[2] = { '0' + ConfigureParams.Screen.bUseVsync, 0 };
@@ -412,8 +485,8 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 #endif
 
 	/* Set new video mode */
-	DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height,
-	        bitdepth, bInFullScreen?"fullscreen":"windowed"));
+	DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s) -> window: %d x %d\n", width, height,
+	        bitdepth, (bInFullScreen ? "fullscreen" : "windowed"), win_width, win_height));
 
 	if (sdlWindow)
 	{
@@ -434,7 +507,7 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 	}
 	if (bUseSdlRenderer)
 	{
-		int rm, bm, gm, pfmt;
+		int rm, bm, gm;
 
 		sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
 		if (!sdlRenderer)
@@ -451,32 +524,25 @@ bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChan
 
 		if (bInFullScreen)
 			SDL_RenderSetLogicalSize(sdlRenderer, width, height);
+		else
+			SDL_RenderSetScale(sdlRenderer, scale, scale);
 
 		if (bitdepth == 16)
 		{
 			rm = 0xF800;
 			gm = 0x07E0;
 			bm = 0x001F;
-			pfmt = SDL_PIXELFORMAT_RGB565;
 		}
 		else
 		{
 			rm = 0x00FF0000;
 			gm = 0x0000FF00;
 			bm = 0x000000FF;
-			pfmt = SDL_PIXELFORMAT_RGB888;
 		}
 		sdlscrn = SDL_CreateRGBSurface(0, width, height, bitdepth,
 		                               rm, gm, bm, 0);
-		sdlTexture = SDL_CreateTexture(sdlRenderer, pfmt,
-		                               SDL_TEXTUREACCESS_STREAMING,
-		                               width, height);
-		if (!sdlTexture)
-		{
-			fprintf(stderr, "ERROR: Failed to create %dx%d@%d texture!\n",
-			       width, height, bitdepth);
-			exit(-3);
-		}
+
+		Screen_SetTextureScale(width, height, win_width, win_height, true);
 	}
 	else
 	{
-- 
2.20.1


--------------939C9DF5FDF12E3201491A6B
Content-Type: text/x-patch; charset=UTF-8;
 name="0001-Optimize-CNF-structs-by-grouping-ints-bools.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0001-Optimize-CNF-structs-by-grouping-ints-bools.patch"



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/