mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 17:49:43 +01:00
Add support for internal screenshots
"But people already have screenshot tools", you might protest. The rationale is simple: If you play with any video setting other than 1x windowed (no stretching and no letterbox), then your screenshot will be too big if you want the internal resolution of 320x240, and downscaling will be an inconvenience. The point is to make screenshots based off of internal resolution so they are always pixel perfect and ideally never have to be altered once taken. I've added the keybind of F6 to do this. Right now it saves to a temporary test location with the same filename; future commits will save to properly-timestamped filenames.
This commit is contained in:
parent
060fe6938d
commit
f05827f268
9 changed files with 170 additions and 1 deletions
|
@ -316,7 +316,6 @@ add_library(lodepng-static STATIC ${PNG_SRC})
|
||||||
target_compile_definitions(lodepng-static PRIVATE
|
target_compile_definitions(lodepng-static PRIVATE
|
||||||
-DLODEPNG_NO_COMPILE_ALLOCATORS
|
-DLODEPNG_NO_COMPILE_ALLOCATORS
|
||||||
-DLODEPNG_NO_COMPILE_DISK
|
-DLODEPNG_NO_COMPILE_DISK
|
||||||
-DLODEPNG_NO_COMPILE_ENCODER
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(c-hashmap-static STATIC ${CHM_SRC})
|
add_library(c-hashmap-static STATIC ${CHM_SRC})
|
||||||
|
|
|
@ -48,6 +48,7 @@ static char* basePath = NULL;
|
||||||
static char writeDir[MAX_PATH] = {'\0'};
|
static char writeDir[MAX_PATH] = {'\0'};
|
||||||
static char saveDir[MAX_PATH] = {'\0'};
|
static char saveDir[MAX_PATH] = {'\0'};
|
||||||
static char levelDir[MAX_PATH] = {'\0'};
|
static char levelDir[MAX_PATH] = {'\0'};
|
||||||
|
static char screenshotDir[MAX_PATH] = {'\0'};
|
||||||
static char mainLangDir[MAX_PATH] = {'\0'};
|
static char mainLangDir[MAX_PATH] = {'\0'};
|
||||||
static bool isMainLangDirFromRepo = false;
|
static bool isMainLangDirFromRepo = false;
|
||||||
static bool doesLangDirExist = false;
|
static bool doesLangDirExist = false;
|
||||||
|
@ -260,6 +261,15 @@ int FILESYSTEM_init(char *argvZero, char* baseDir, char *assetsPath, char* langD
|
||||||
mkdir(levelDir, 0777);
|
mkdir(levelDir, 0777);
|
||||||
vlog_info("Level directory: %s", levelDir);
|
vlog_info("Level directory: %s", levelDir);
|
||||||
|
|
||||||
|
/* Store full screenshot directory */
|
||||||
|
SDL_snprintf(screenshotDir, sizeof(screenshotDir), "%s%s%s",
|
||||||
|
writeDir,
|
||||||
|
"screenshots",
|
||||||
|
pathSep
|
||||||
|
);
|
||||||
|
mkdir(screenshotDir, 0777);
|
||||||
|
vlog_info("Screenshot directory: %s", screenshotDir);
|
||||||
|
|
||||||
basePath = SDL_GetBasePath();
|
basePath = SDL_GetBasePath();
|
||||||
|
|
||||||
if (basePath == NULL)
|
if (basePath == NULL)
|
||||||
|
@ -799,6 +809,38 @@ static PHYSFS_sint64 read_bytes(
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FILESYSTEM_saveFile(const char* name, const unsigned char* data, const size_t len)
|
||||||
|
{
|
||||||
|
if (!isInit)
|
||||||
|
{
|
||||||
|
vlog_warn("Filesystem not initialized! Not writing just to be safe.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PHYSFS_File* handle = PHYSFS_openWrite(name);
|
||||||
|
if (handle == NULL)
|
||||||
|
{
|
||||||
|
vlog_error(
|
||||||
|
"Could not open PHYSFS handle for %s: %s",
|
||||||
|
name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PHYSFS_sint64 bytes_written = PHYSFS_writeBytes(handle, data, len);
|
||||||
|
if ((size_t) bytes_written != len)
|
||||||
|
{
|
||||||
|
vlog_warn("%s: Number of bytes written is not as expected", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int success = PHYSFS_close(handle);
|
||||||
|
if (success == 0)
|
||||||
|
{
|
||||||
|
vlog_error("%s: Could not close handle", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void FILESYSTEM_loadFileToMemory(
|
void FILESYSTEM_loadFileToMemory(
|
||||||
const char *name,
|
const char *name,
|
||||||
unsigned char **mem,
|
unsigned char **mem,
|
||||||
|
|
|
@ -32,6 +32,7 @@ void FILESYSTEM_unmountAssets(void);
|
||||||
bool FILESYSTEM_isAssetMounted(const char* filename);
|
bool FILESYSTEM_isAssetMounted(const char* filename);
|
||||||
bool FILESYSTEM_areAssetsInSameRealDir(const char* filenameA, const char* filenameB);
|
bool FILESYSTEM_areAssetsInSameRealDir(const char* filenameA, const char* filenameB);
|
||||||
|
|
||||||
|
bool FILESYSTEM_saveFile(const char* name, const unsigned char* data, size_t len);
|
||||||
void FILESYSTEM_loadFileToMemory(const char *name, unsigned char **mem,
|
void FILESYSTEM_loadFileToMemory(const char *name, unsigned char **mem,
|
||||||
size_t *len);
|
size_t *len);
|
||||||
void FILESYSTEM_loadAssetToMemory(
|
void FILESYSTEM_loadAssetToMemory(
|
||||||
|
|
|
@ -111,6 +111,7 @@ void Graphics::init(void)
|
||||||
tempShakeTexture = NULL;
|
tempShakeTexture = NULL;
|
||||||
backgroundTexture = NULL;
|
backgroundTexture = NULL;
|
||||||
foregroundTexture = NULL;
|
foregroundTexture = NULL;
|
||||||
|
tempScreenshot = NULL;
|
||||||
towerbg = TowerBG();
|
towerbg = TowerBG();
|
||||||
titlebg = TowerBG();
|
titlebg = TowerBG();
|
||||||
trinketr = 0;
|
trinketr = 0;
|
||||||
|
@ -220,6 +221,7 @@ void Graphics::destroy_buffers(void)
|
||||||
VVV_freefunc(SDL_DestroyTexture, titlebg.texture);
|
VVV_freefunc(SDL_DestroyTexture, titlebg.texture);
|
||||||
VVV_freefunc(SDL_FreeSurface, tempFilterSrc);
|
VVV_freefunc(SDL_FreeSurface, tempFilterSrc);
|
||||||
VVV_freefunc(SDL_FreeSurface, tempFilterDest);
|
VVV_freefunc(SDL_FreeSurface, tempFilterDest);
|
||||||
|
VVV_freefunc(SDL_FreeSurface, tempScreenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::drawspritesetcol(int x, int y, int t, int c)
|
void Graphics::drawspritesetcol(int x, int y, int t, int c)
|
||||||
|
|
|
@ -332,6 +332,7 @@ public:
|
||||||
SDL_Texture* backgroundTexture;
|
SDL_Texture* backgroundTexture;
|
||||||
SDL_Texture* foregroundTexture;
|
SDL_Texture* foregroundTexture;
|
||||||
SDL_Texture* tempScrollingTexture;
|
SDL_Texture* tempScrollingTexture;
|
||||||
|
SDL_Surface* tempScreenshot;
|
||||||
|
|
||||||
TowerBG towerbg;
|
TowerBG towerbg;
|
||||||
TowerBG titlebg;
|
TowerBG titlebg;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "Alloc.h"
|
#include "Alloc.h"
|
||||||
#include "FileSystemUtils.h"
|
#include "FileSystemUtils.h"
|
||||||
|
#include "Graphics.h"
|
||||||
#include "GraphicsUtil.h"
|
#include "GraphicsUtil.h"
|
||||||
#include "Localization.h"
|
#include "Localization.h"
|
||||||
#include "Vlogging.h"
|
#include "Vlogging.h"
|
||||||
|
@ -20,6 +21,13 @@ extern "C"
|
||||||
const unsigned char* in,
|
const unsigned char* in,
|
||||||
size_t insize
|
size_t insize
|
||||||
);
|
);
|
||||||
|
extern unsigned lodepng_encode24(
|
||||||
|
unsigned char** out,
|
||||||
|
size_t* outsize,
|
||||||
|
const unsigned char* image,
|
||||||
|
unsigned w,
|
||||||
|
unsigned h
|
||||||
|
);
|
||||||
extern const char* lodepng_error_text(unsigned code);
|
extern const char* lodepng_error_text(unsigned code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,3 +477,52 @@ void GraphicsResources::destroy(void)
|
||||||
VVV_freefunc(SDL_FreeSurface, im_sprites_surf);
|
VVV_freefunc(SDL_FreeSurface, im_sprites_surf);
|
||||||
VVV_freefunc(SDL_FreeSurface, im_flipsprites_surf);
|
VVV_freefunc(SDL_FreeSurface, im_flipsprites_surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SaveImage(const SDL_Surface* surface, const char* filename)
|
||||||
|
{
|
||||||
|
unsigned char* out;
|
||||||
|
size_t outsize;
|
||||||
|
unsigned int error;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
error = lodepng_encode24(
|
||||||
|
&out, &outsize,
|
||||||
|
(const unsigned char*) surface->pixels,
|
||||||
|
surface->w, surface->h
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
vlog_error("Could not save image: %s", lodepng_error_text(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = FILESYSTEM_saveFile(filename, out, outsize);
|
||||||
|
SDL_free(out);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
vlog_error("Could not save image");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveScreenshot(void)
|
||||||
|
{
|
||||||
|
bool success = TakeScreenshot(&graphics.tempScreenshot);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
vlog_error("Could not take screenshot");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: Timestamp in filename
|
||||||
|
success = SaveImage(graphics.tempScreenshot, "screenshots/test.png");
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlog_info("Saved screenshot");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -257,3 +257,60 @@ void ApplyFilter(SDL_Surface** src, SDL_Surface** dest)
|
||||||
|
|
||||||
SDL_UpdateTexture(graphics.gameTexture, NULL, (*dest)->pixels, (*dest)->pitch);
|
SDL_UpdateTexture(graphics.gameTexture, NULL, (*dest)->pixels, (*dest)->pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TakeScreenshot(SDL_Surface** surface)
|
||||||
|
{
|
||||||
|
if (surface == NULL)
|
||||||
|
{
|
||||||
|
SDL_assert(0 && "surface is NULL!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
int result = graphics.query_texture(
|
||||||
|
graphics.gameTexture, NULL, NULL, &width, &height
|
||||||
|
);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*surface == NULL)
|
||||||
|
{
|
||||||
|
*surface = SDL_CreateRGBSurface(0, width, height, 24, 0, 0, 0, 0);
|
||||||
|
if (*surface == NULL)
|
||||||
|
{
|
||||||
|
WHINE_ONCE_ARGS(
|
||||||
|
("Could not create temporary surface: %s", SDL_GetError())
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*surface)->w != width || (*surface)->h != height)
|
||||||
|
{
|
||||||
|
SDL_assert(0 && "Width and height of surface and texture mismatch!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = graphics.set_render_target(graphics.gameTexture);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = SDL_RenderReadPixels(
|
||||||
|
gameScreen.m_renderer, NULL, SDL_PIXELFORMAT_RGB24,
|
||||||
|
(*surface)->pixels, (*surface)->pitch
|
||||||
|
);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
WHINE_ONCE_ARGS(
|
||||||
|
("Could not read pixels from renderer: %s", SDL_GetError())
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -14,4 +14,6 @@ SDL_Color ReadPixel(const SDL_Surface* surface, int x, int y);
|
||||||
void UpdateFilter(void);
|
void UpdateFilter(void);
|
||||||
void ApplyFilter(SDL_Surface** src, SDL_Surface** dest);
|
void ApplyFilter(SDL_Surface** src, SDL_Surface** dest);
|
||||||
|
|
||||||
|
bool TakeScreenshot(SDL_Surface** surface);
|
||||||
|
|
||||||
#endif /* GRAPHICSUTIL_H */
|
#endif /* GRAPHICSUTIL_H */
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "GlitchrunnerMode.h"
|
#include "GlitchrunnerMode.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
|
#include "GraphicsUtil.h"
|
||||||
#include "Localization.h"
|
#include "Localization.h"
|
||||||
#include "LocalizationStorage.h"
|
#include "LocalizationStorage.h"
|
||||||
#include "Music.h"
|
#include "Music.h"
|
||||||
|
@ -17,6 +18,8 @@
|
||||||
#include "UTF8.h"
|
#include "UTF8.h"
|
||||||
#include "Vlogging.h"
|
#include "Vlogging.h"
|
||||||
|
|
||||||
|
bool SaveScreenshot(void);
|
||||||
|
|
||||||
int inline KeyPoll::getThreshold(void)
|
int inline KeyPoll::getThreshold(void)
|
||||||
{
|
{
|
||||||
switch (sensitivity)
|
switch (sensitivity)
|
||||||
|
@ -181,6 +184,11 @@ void KeyPoll::Poll(void)
|
||||||
music.playef(Sound_COIN);
|
music.playef(Sound_COIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.key.keysym.sym == SDLK_F6 && !evt.key.repeat)
|
||||||
|
{
|
||||||
|
SaveScreenshot();
|
||||||
|
}
|
||||||
|
|
||||||
BUTTONGLYPHS_keyboard_set_active(true);
|
BUTTONGLYPHS_keyboard_set_active(true);
|
||||||
|
|
||||||
if (textentry())
|
if (textentry())
|
||||||
|
|
Loading…
Reference in a new issue