[PATCH 2/7] Support floating point zoom factors with SDL2 |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/hatari-devel Archives
]
- Subject: [PATCH 2/7] Support floating point zoom factors with SDL2
- From: Eero Tamminen <oak@xxxxxxxxxxxxxx>
- Date: Mon, 2 Mar 2020 22:40:40 +0200
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"