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

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


SDL1 zooming works as earlier, but with SDL2, ST-low resolution
is always doubled, and zoom factor applied after that.

"renderQuality" configuration option is now ignored and a suitable
value is set automatically 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                |  29 ++++++++-
 src/screen.c                 | 110 ++++++++++++++++++++++++++++-------
 6 files changed, 126 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 89df4025..ea0d8c78 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..cbb197ab 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,34 @@ 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
+			/* double ST-low always so that resulting screen size
+			 * is approximately same size with same zoom factor
+			 * regardless of the machine or monitor type
+			 */
+			ConfigureParams.Screen.nMaxWidth *= 2;
+			ConfigureParams.Screen.nMaxHeight *= 2;
+			ConfigureParams.Screen.nZoomFactor = zoom;
+#else
+			/* zoom factor only for ST-low mode */
 			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..4ab396de 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;
+		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


--------------69CD54D5487FA4DE087D9377
Content-Type: text/x-patch; charset=UTF-8;
 name="0001-Optimize-CNF-SCREEN-struct-by-grouping-ints-bools.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename*0="0001-Optimize-CNF-SCREEN-struct-by-grouping-ints-bools.patch"



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