1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-16 16:09:42 +01:00
VVVVVV/desktop_version/src/Screen.cpp

422 lines
8.3 KiB
C++
Raw Normal View History

2020-01-01 21:29:24 +01:00
#include "Screen.h"
#include <SDL.h>
2020-01-01 21:29:24 +01:00
#include "FileSystemUtils.h"
#include "Game.h"
2020-01-01 21:29:24 +01:00
#include "GraphicsUtil.h"
#include "Vlogging.h"
2020-01-01 21:29:24 +01:00
// Used to create the window icon
extern "C"
{
extern unsigned lodepng_decode24(
unsigned char** out,
unsigned* w,
unsigned* h,
const unsigned char* in,
size_t insize
);
}
ScreenSettings::ScreenSettings(void)
{
windowWidth = 320;
windowHeight = 240;
fullscreen = false;
2021-08-31 21:08:46 +02:00
useVsync = true; // Now that uncapped is the default...
stretch = 0;
linearFilter = false;
badSignal = false;
}
void Screen::init(const ScreenSettings& settings)
{
m_window = NULL;
m_renderer = NULL;
m_screenTexture = NULL;
m_screen = NULL;
isWindowed = !settings.fullscreen;
stretchMode = settings.stretch;
isFiltered = settings.linearFilter;
vsync = settings.useVsync;
filterSubrect.x = 1;
filterSubrect.y = 1;
filterSubrect.w = 318;
filterSubrect.h = 238;
SDL_SetHintWithPriority(
SDL_HINT_RENDER_SCALE_QUALITY,
isFiltered ? "linear" : "nearest",
SDL_HINT_OVERRIDE
);
SDL_SetHintWithPriority(
SDL_HINT_RENDER_VSYNC,
vsync ? "1" : "0",
SDL_HINT_OVERRIDE
);
2020-01-01 21:29:24 +01:00
// Uncomment this next line when you need to debug -flibit
// SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, "software", SDL_HINT_OVERRIDE);
// FIXME: m_renderer is also created in resetRendererWorkaround()!
2020-01-01 21:29:24 +01:00
SDL_CreateWindowAndRenderer(
640,
480,
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI,
2020-01-01 21:29:24 +01:00
&m_window,
&m_renderer
);
SDL_SetWindowTitle(m_window, "VVVVVV");
LoadIcon();
2020-01-01 21:29:24 +01:00
// FIXME: This surface should be the actual backbuffer! -flibit
m_screen = SDL_CreateRGBSurface(
0,
320,
240,
32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000
);
// ALSO FIXME: This SDL_CreateTexture() is duplicated twice in this file!
2020-01-01 21:29:24 +01:00
m_screenTexture = SDL_CreateTexture(
m_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
320,
240
);
badSignalEffect = settings.badSignal;
ResizeScreen(settings.windowWidth, settings.windowHeight);
2020-01-01 21:29:24 +01:00
}
void Screen::destroy(void)
{
#define X(CLEANUP, POINTER) \
CLEANUP(POINTER); \
POINTER = NULL;
/* Order matters! */
X(SDL_DestroyTexture, m_screenTexture);
X(SDL_FreeSurface, m_screen);
X(SDL_DestroyRenderer, m_renderer);
X(SDL_DestroyWindow, m_window);
#undef X
}
void Screen::GetSettings(ScreenSettings* settings)
{
int width, height;
GetWindowSize(&width, &height);
settings->windowWidth = width;
settings->windowHeight = height;
settings->fullscreen = !isWindowed;
settings->useVsync = vsync;
settings->stretch = stretchMode;
settings->linearFilter = isFiltered;
settings->badSignal = badSignalEffect;
}
void Screen::LoadIcon(void)
{
#ifndef __APPLE__
unsigned char *fileIn;
size_t length;
unsigned char *data;
unsigned int width, height;
FILESYSTEM_loadAssetToMemory("VVVVVV.png", &fileIn, &length, false);
lodepng_decode24(&data, &width, &height, fileIn, length);
FILESYSTEM_freeMemory(&fileIn);
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
data,
width,
height,
24,
width * 3,
0x000000FF,
0x0000FF00,
0x00FF0000,
0x00000000
);
SDL_SetWindowIcon(m_window, icon);
SDL_FreeSurface(icon);
Reduce dependency on libc functions During 2.3 development, there's been a gradual shift to using SDL stdlib functions instead of libc functions, but there are still some libc functions (or the same libc function but from the STL) in the code. Well, this patch replaces all the rest of them in one fell swoop. SDL's stdlib can replace most of these, but its SDL_min() and SDL_max() are inadequate - they aren't really functions, they're more like macros with a nasty penchant for double-evaluation. So I just made my own VVV_min() and VVV_max() functions and placed them in Maths.h instead, then replaced all the previous usages of min(), max(), std::min(), std::max(), SDL_min(), and SDL_max() with VVV_min() and VVV_max(). Additionally, there's no SDL_isxdigit(), so I just implemented my own VVV_isxdigit(). SDL has SDL_malloc() and SDL_free(), but they have some refcounting built in to them, so in order to use them with LodePNG, I have to replace the malloc() and free() that LodePNG uses. Which isn't too hard, I did it in a new file called ThirdPartyDeps.c, and LodePNG is now compiled with the LODEPNG_NO_COMPILE_ALLOCATORS definition. Lastly, I also refactored the awful strcpy() and strcat() usages in PLATFORM_migrateSaveData() to use SDL_snprintf() instead. I know save migration is getting axed in 2.4, but it still bothers me to have something like that in the codebase otherwise. Without further ado, here is the full list of functions that the codebase now uses: - SDL_strlcpy() instead of strcpy() - SDL_strlcat() instead of strcat() - SDL_snprintf() instead of sprintf(), strcpy(), or strcat() (see above) - VVV_min() instead of min(), std::min(), or SDL_min() - VVV_max() instead of max(), std::max(), or SDL_max() - VVV_isxdigit() instead of isxdigit() - SDL_strcmp() instead of strcmp() - SDL_strcasecmp() instead of strcasecmp() or Win32 strcmpi() - SDL_strstr() instead of strstr() - SDL_strlen() instead of strlen() - SDL_sscanf() instead of sscanf() - SDL_getenv() instead of getenv() - SDL_malloc() instead of malloc() (replacing in LodePNG as well) - SDL_free() instead of free() (replacing in LodePNG as well)
2021-01-12 01:17:45 +01:00
SDL_free(data);
#endif /* __APPLE__ */
}
void Screen::ResizeScreen(int x, int y)
2020-01-01 21:29:24 +01:00
{
static int resX = 320;
static int resY = 240;
if (x != -1 && y != -1)
{
// This is a user resize!
resX = x;
resY = y;
}
if(!isWindowed)
{
int result = SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
if (result != 0)
{
vlog_error("Error: could not set the game to fullscreen mode: %s", SDL_GetError());
return;
}
2020-01-01 21:29:24 +01:00
}
else
{
int result = SDL_SetWindowFullscreen(m_window, 0);
if (result != 0)
{
vlog_error("Error: could not set the game to windowed mode: %s", SDL_GetError());
return;
}
2020-01-01 21:29:24 +01:00
if (x != -1 && y != -1)
{
SDL_SetWindowSize(m_window, resX, resY);
SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
}
if (stretchMode == 1)
{
int winX, winY;
2021-01-01 03:12:30 +01:00
GetWindowSize(&winX, &winY);
int result = SDL_RenderSetLogicalSize(m_renderer, winX, winY);
if (result != 0)
{
vlog_error("Error: could not set logical size: %s", SDL_GetError());
return;
}
result = SDL_RenderSetIntegerScale(m_renderer, SDL_FALSE);
if (result != 0)
{
vlog_error("Error: could not set scale: %s", SDL_GetError());
return;
}
2020-01-01 21:29:24 +01:00
}
else
{
SDL_RenderSetLogicalSize(m_renderer, 320, 240);
int result = SDL_RenderSetIntegerScale(m_renderer, (SDL_bool) (stretchMode == 2));
if (result != 0)
{
vlog_error("Error: could not set scale: %s", SDL_GetError());
return;
}
2020-01-01 21:29:24 +01:00
}
SDL_ShowWindow(m_window);
}
void Screen::ResizeToNearestMultiple(void)
{
int w, h;
GetWindowSize(&w, &h);
// Check aspect ratio first
bool using_width;
int usethisdimension, usethisratio;
if ((float) w / (float) h > 4.0 / 3.0)
{
// Width is bigger, so it's limited by height
usethisdimension = h;
usethisratio = 240;
using_width = false;
}
else
{
// Height is bigger, so it's limited by width. Or we're exactly 4:3 already
usethisdimension = w;
usethisratio = 320;
using_width = true;
}
int floor = (usethisdimension / usethisratio) * usethisratio;
int ceiling = floor + usethisratio;
int final_dimension;
if (usethisdimension - floor < ceiling - usethisdimension)
{
// Floor is nearest
final_dimension = floor;
}
else
{
// Ceiling is nearest. Or we're exactly on a multiple already
final_dimension = ceiling;
}
if (final_dimension == 0)
{
// We're way too small!
ResizeScreen(320, 240);
return;
}
if (using_width)
{
ResizeScreen(final_dimension, final_dimension / 4 * 3);
}
else
{
ResizeScreen(final_dimension * 4 / 3, final_dimension);
}
}
2020-01-01 21:29:24 +01:00
void Screen::GetWindowSize(int* x, int* y)
{
SDL_GetRendererOutputSize(m_renderer, x, y);
2020-01-01 21:29:24 +01:00
}
void Screen::UpdateScreen(SDL_Surface* buffer, SDL_Rect* rect )
{
if((buffer == NULL) && (m_screen == NULL) )
{
return;
}
2020-01-01 21:29:24 +01:00
if(badSignalEffect)
{
buffer = ApplyFilter(buffer);
}
2020-01-01 21:29:24 +01:00
ClearSurface(m_screen);
BlitSurfaceStandard(buffer,NULL,m_screen,rect);
2020-01-01 21:29:24 +01:00
if(badSignalEffect)
{
SDL_FreeSurface(buffer);
}
2020-01-01 21:29:24 +01:00
}
const SDL_PixelFormat* Screen::GetFormat(void)
2020-01-01 21:29:24 +01:00
{
return m_screen->format;
2020-01-01 21:29:24 +01:00
}
void Screen::FlipScreen(const bool flipmode)
2020-01-01 21:29:24 +01:00
{
SDL_RendererFlip flip_flags;
if (flipmode)
{
flip_flags = SDL_FLIP_VERTICAL;
}
else
{
flip_flags = SDL_FLIP_NONE;
}
2020-01-01 21:29:24 +01:00
SDL_UpdateTexture(
m_screenTexture,
NULL,
m_screen->pixels,
m_screen->pitch
);
SDL_RenderCopyEx(
2020-01-01 21:29:24 +01:00
m_renderer,
m_screenTexture,
isFiltered ? &filterSubrect : NULL,
NULL,
0.0,
NULL,
flip_flags
2020-01-01 21:29:24 +01:00
);
SDL_RenderPresent(m_renderer);
SDL_RenderClear(m_renderer);
ClearSurface(m_screen);
2020-01-01 21:29:24 +01:00
}
void Screen::toggleFullScreen(void)
2020-01-01 21:29:24 +01:00
{
isWindowed = !isWindowed;
ResizeScreen(-1, -1);
if (game.currentmenuname == Menu::graphicoptions)
{
/* Recreate menu to update "resize to nearest" */
game.createmenu(game.currentmenuname, true);
}
2020-01-01 21:29:24 +01:00
}
void Screen::toggleStretchMode(void)
2020-01-01 21:29:24 +01:00
{
stretchMode = (stretchMode + 1) % 3;
ResizeScreen(-1, -1);
}
void Screen::toggleLinearFilter(void)
2020-01-01 21:29:24 +01:00
{
isFiltered = !isFiltered;
SDL_SetHintWithPriority(
SDL_HINT_RENDER_SCALE_QUALITY,
isFiltered ? "linear" : "nearest",
SDL_HINT_OVERRIDE
);
2020-01-01 21:29:24 +01:00
SDL_DestroyTexture(m_screenTexture);
m_screenTexture = SDL_CreateTexture(
m_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
320,
240
);
}
void Screen::resetRendererWorkaround(void)
{
SDL_SetHintWithPriority(
SDL_HINT_RENDER_VSYNC,
vsync ? "1" : "0",
SDL_HINT_OVERRIDE
);
/* FIXME: This exists because SDL_HINT_RENDER_VSYNC is not dynamic!
* As a result, our only workaround is to tear down the renderer
* and recreate everything so that it can process the variable.
* -flibit
*/
if (m_renderer == NULL)
{
/* We haven't made it to init yet, don't worry about it */
return;
}
SDL_RendererInfo renderInfo;
SDL_GetRendererInfo(m_renderer, &renderInfo);
bool curVsync = (renderInfo.flags & SDL_RENDERER_PRESENTVSYNC) != 0;
if (vsync == curVsync)
{
return;
}
SDL_DestroyTexture(m_screenTexture);
SDL_DestroyRenderer(m_renderer);
m_renderer = SDL_CreateRenderer(m_window, -1, 0);
m_screenTexture = SDL_CreateTexture(
m_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
320,
240
);
/* Ugh, have to make sure to re-apply graphics options after doing the
* above, otherwise letterbox/integer won't be applied...
*/
ResizeScreen(-1, -1);
}