From ddce870dda3e4fdf3bb03d34952d362c3efc982c Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Thu, 7 Nov 2019 07:54:55 +0800 Subject: [PATCH] Handle scaling for SDL1 (#370) Only some handheld devices support auto-scaling. On desktop and some handhelds we need to downscale manually. Hardware auto-scaler check for jz4760 provided by @jbanes and @scooterpsu --- SourceX/DiabloUI/diabloui.cpp | 26 +++++---------- SourceX/dx.cpp | 19 ++++++++--- SourceX/miniwin/ddraw.cpp | 23 ++++++++++++- SourceX/miniwin/ddraw.h | 63 +++++++++++++++++++++++++++++++++++ SourceX/miniwin/dsound.h | 2 ++ SourceX/miniwin/misc.cpp | 15 +++++++++ SourceX/miniwin/misc_msg.cpp | 25 ++++++-------- SourceX/storm/storm.cpp | 1 + 8 files changed, 136 insertions(+), 38 deletions(-) diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index 4bca28986..41ca9e1c9 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -283,8 +283,14 @@ bool UiFocusNavigation(SDL_Event *event) } } - if (UiItemMouseEvents(event, gUiItems, gUiItemCnt)) - return true; + if (event->type == SDL_MOUSEBUTTONDOWN || event->type == SDL_MOUSEBUTTONUP) { + // In SDL2 mouse events already use logical coordinates. +#ifdef USE_SDL1 + OutputToLogical(&event->button.x, &event->button.y); +#endif + if (UiItemMouseEvents(event, gUiItems, gUiItemCnt)) + return true; + } return false; } @@ -782,21 +788,7 @@ void DrawMouse() return; SDL_GetMouseState(&MouseX, &MouseY); - -#ifndef USE_SDL1 - if (renderer) { - float scaleX; - SDL_RenderGetScale(renderer, &scaleX, NULL); - MouseX /= scaleX; - MouseY /= scaleX; - - SDL_Rect view; - SDL_RenderGetViewport(renderer, &view); - MouseX -= view.x; - MouseY -= view.y; - } -#endif - + OutputToLogical(&MouseX, &MouseY); DrawArt(MouseX, MouseY, &ArtCursor); } diff --git a/SourceX/dx.cpp b/SourceX/dx.cpp index 7b912c039..bb7437d8c 100644 --- a/SourceX/dx.cpp +++ b/SourceX/dx.cpp @@ -186,12 +186,21 @@ void BltFast(DWORD dwX, DWORD dwY, LPRECT lpSrcRect) static_cast(dwY), w, h }; - - // Convert from 8-bit to 32-bit - if (SDL_BlitSurface(pal_surface, &src_rect, GetOutputSurface(), &dst_rect) <= -1) { - ErrSdl(); + if (OutputRequiresScaling()) { + ScaleOutputRect(&dst_rect); + // Convert from 8-bit to 32-bit + SDL_Surface *tmp = SDL_ConvertSurface(pal_surface, GetOutputSurface()->format, 0); + if (SDL_BlitScaled(tmp, &src_rect, GetOutputSurface(), &dst_rect) <= -1) { + SDL_FreeSurface(tmp); + ErrSdl(); + } + SDL_FreeSurface(tmp); + } else { + // Convert from 8-bit to 32-bit + if (SDL_BlitSurface(pal_surface, &src_rect, GetOutputSurface(), &dst_rect) <= -1) { + ErrSdl(); + } } - bufferUpdated = true; } diff --git a/SourceX/miniwin/ddraw.cpp b/SourceX/miniwin/ddraw.cpp index fc9a3774e..0e18eab24 100644 --- a/SourceX/miniwin/ddraw.cpp +++ b/SourceX/miniwin/ddraw.cpp @@ -4,7 +4,8 @@ namespace dvl { extern SDL_Surface *renderer_texture_surface; // defined in dx.cpp -SDL_Surface *GetOutputSurface() { +SDL_Surface *GetOutputSurface() +{ #ifdef USE_SDL1 return SDL_GetVideoSurface(); #else @@ -14,4 +15,24 @@ SDL_Surface *GetOutputSurface() { #endif } +bool OutputRequiresScaling() +{ +#ifdef USE_SDL1 + return SCREEN_WIDTH != GetOutputSurface()->w || SCREEN_HEIGHT != GetOutputSurface()->h; +#else // SDL2, scaling handled by renderer. + return false; +#endif +} + +void ScaleOutputRect(SDL_Rect *rect) +{ + if (!OutputRequiresScaling()) + return; + const auto *surface = GetOutputSurface(); + rect->x = rect->x * surface->w / SCREEN_WIDTH; + rect->y = rect->y * surface->h / SCREEN_HEIGHT; + rect->w = rect->w * surface->w / SCREEN_WIDTH; + rect->h = rect->h * surface->h / SCREEN_HEIGHT; +} + } // namespace dvl diff --git a/SourceX/miniwin/ddraw.h b/SourceX/miniwin/ddraw.h index fb335cda7..360e4192e 100644 --- a/SourceX/miniwin/ddraw.h +++ b/SourceX/miniwin/ddraw.h @@ -1,5 +1,8 @@ +#pragma once + #include "devilution.h" #include +#include namespace dvl { @@ -18,4 +21,64 @@ extern bool bufferUpdated; // SDL2, upscale: Renderer texture surface. SDL_Surface *GetOutputSurface(); +// Whether the output surface requires software scaling. +// Always returns false on SDL2. +bool OutputRequiresScaling(); + +// Scales rect if necessary. +void ScaleOutputRect(SDL_Rect *rect); + +// Convert from output coordinates to logical (resolution-independent) coordinates. +template < + typename T, + typename = typename std::enable_if::value, T>::type> +void OutputToLogical(T *x, T *y) +{ +#ifndef USE_SDL1 + if (!renderer) + return; + float scaleX; + SDL_RenderGetScale(renderer, &scaleX, NULL); + *x /= scaleX; + *y /= scaleX; + + SDL_Rect view; + SDL_RenderGetViewport(renderer, &view); + *x -= view.x; + *y -= view.y; +#else + if (!OutputRequiresScaling()) + return; + const auto *surface = GetOutputSurface(); + *x = *x * SCREEN_WIDTH / surface->w; + *y = *y * SCREEN_HEIGHT / surface->h; +#endif +} + +template < + typename T, + typename = typename std::enable_if::value, T>::type> +void LogicalToOutput(T *x, T *y) +{ +#ifndef USE_SDL1 + if (!renderer) + return; + SDL_Rect view; + SDL_RenderGetViewport(renderer, &view); + *x += view.x; + *y += view.y; + + float scaleX; + SDL_RenderGetScale(renderer, &scaleX, NULL); + *x *= scaleX; + *y *= scaleX; +#else + if (!OutputRequiresScaling()) + return; + const auto *surface = GetOutputSurface(); + *x = *x * surface->w / SCREEN_WIDTH; + *y = *y * surface->h / SCREEN_HEIGHT; +#endif +} + } // namespace dvl diff --git a/SourceX/miniwin/dsound.h b/SourceX/miniwin/dsound.h index 0aa5e16b9..941aaef24 100644 --- a/SourceX/miniwin/dsound.h +++ b/SourceX/miniwin/dsound.h @@ -1,3 +1,5 @@ +#pragma once + #include "devilution.h" #include diff --git a/SourceX/miniwin/misc.cpp b/SourceX/miniwin/misc.cpp index e00066ecb..e3a77452a 100644 --- a/SourceX/miniwin/misc.cpp +++ b/SourceX/miniwin/misc.cpp @@ -13,6 +13,10 @@ #define strncasecmp _strnicmp #endif +#if defined(USE_SDL1) && defined(RETROFW) +#include +#endif + namespace dvl { DWORD last_error; @@ -143,8 +147,19 @@ bool SpawnWindow(LPCSTR lpWindowName, int nWidth, int nHeight) if (fullscreen) flags |= SDL_FULLSCREEN; SDL_WM_SetCaption(lpWindowName, WINDOW_ICON_NAME); +#ifndef RETROFW SDL_SetVideoMode(nWidth, nHeight, /*bpp=*/0, flags); +#else // RETROFW + // JZ4760 IPU scaler (e.g. on RG-300 v2/3) - automatic high-quality scaling. + if (access("/proc/jz/ipu_ratio", F_OK) == 0) { + SDL_SetVideoMode(nWidth, nHeight, /*bpp=*/0, flags); + } else { + // Other RetroFW devices have 320x480 screens with non-square pixels. + SDL_SetVideoMode(320, 480, /*bpp=*/0, flags); + } +#endif window = SDL_GetVideoSurface(); + SDL_Log("Output surface: %dx%d sw-scaling=%d bpp=%d", window->w, window->h, OutputRequiresScaling(), window->format->BitsPerPixel); if (grabInput) SDL_WM_GrabInput(SDL_GRAB_ON); atexit(SDL_VideoQuit); // Without this video mode is not restored after fullscreen. diff --git a/SourceX/miniwin/misc_msg.cpp b/SourceX/miniwin/misc_msg.cpp index 9202d9ab2..38937d496 100644 --- a/SourceX/miniwin/misc_msg.cpp +++ b/SourceX/miniwin/misc_msg.cpp @@ -8,6 +8,7 @@ #include "controls/controller_motion.h" #include "controls/game_controls.h" #include "controls/plrctrls.h" +#include "miniwin/ddraw.h" /** @file * * @@ -26,22 +27,8 @@ void SetCursorPos(int X, int Y) { mouseWarpingX = X; mouseWarpingY = Y; - -#ifndef USE_SDL1 - if (renderer) { - SDL_Rect view; - SDL_RenderGetViewport(renderer, &view); - X += view.x; - Y += view.y; - - float scaleX; - SDL_RenderGetScale(renderer, &scaleX, NULL); - X *= scaleX; - Y *= scaleX; - } -#endif - mouseWarping = true; + LogicalToOutput(&X, &Y); SDL_WarpMouseInWindow(window, X, Y); } @@ -403,6 +390,14 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter return true; } +#ifdef USE_SDL1 + if (e.type == SDL_MOUSEMOTION) { + OutputToLogical(&e.motion.x, &e.motion.y); + } else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) { + OutputToLogical(&e.button.x, &e.button.y); + } +#endif + if (movie_playing) { if (ShouldSkipMovie(e)) SetMouseLMBMessage(e, lpMsg); diff --git a/SourceX/storm/storm.cpp b/SourceX/storm/storm.cpp index 4099a2a08..94a0c7b1b 100644 --- a/SourceX/storm/storm.cpp +++ b/SourceX/storm/storm.cpp @@ -721,6 +721,7 @@ BOOL SVidPlayContinue(void) Uint32 format = SDL_GetWindowPixelFormat(window); SDL_Surface *tmp = SDL_ConvertSurfaceFormat(SVidSurface, format, 0); #endif + ScaleOutputRect(&pal_surface_offset); if (SDL_BlitScaled(tmp, NULL, GetOutputSurface(), &pal_surface_offset) <= -1) { SDL_Log(SDL_GetError()); return false;