diff --git a/desktop_version/CMakeLists.txt b/desktop_version/CMakeLists.txt index 0d4b8a7e..9e60db8b 100644 --- a/desktop_version/CMakeLists.txt +++ b/desktop_version/CMakeLists.txt @@ -316,7 +316,6 @@ add_library(lodepng-static STATIC ${PNG_SRC}) target_compile_definitions(lodepng-static PRIVATE -DLODEPNG_NO_COMPILE_ALLOCATORS -DLODEPNG_NO_COMPILE_DISK - -DLODEPNG_NO_COMPILE_ENCODER ) add_library(c-hashmap-static STATIC ${CHM_SRC}) diff --git a/desktop_version/src/FileSystemUtils.cpp b/desktop_version/src/FileSystemUtils.cpp index d9813d56..f23f86d3 100644 --- a/desktop_version/src/FileSystemUtils.cpp +++ b/desktop_version/src/FileSystemUtils.cpp @@ -48,6 +48,7 @@ static char* basePath = NULL; static char writeDir[MAX_PATH] = {'\0'}; static char saveDir[MAX_PATH] = {'\0'}; static char levelDir[MAX_PATH] = {'\0'}; +static char screenshotDir[MAX_PATH] = {'\0'}; static char mainLangDir[MAX_PATH] = {'\0'}; static bool isMainLangDirFromRepo = false; static bool doesLangDirExist = false; @@ -260,6 +261,15 @@ int FILESYSTEM_init(char *argvZero, char* baseDir, char *assetsPath, char* langD mkdir(levelDir, 0777); 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(); if (basePath == NULL) @@ -799,6 +809,38 @@ static PHYSFS_sint64 read_bytes( 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( const char *name, unsigned char **mem, diff --git a/desktop_version/src/FileSystemUtils.h b/desktop_version/src/FileSystemUtils.h index 667f180c..e5c65031 100644 --- a/desktop_version/src/FileSystemUtils.h +++ b/desktop_version/src/FileSystemUtils.h @@ -32,6 +32,7 @@ void FILESYSTEM_unmountAssets(void); bool FILESYSTEM_isAssetMounted(const char* filename); 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, size_t *len); void FILESYSTEM_loadAssetToMemory( diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp index fefae038..02611a0f 100644 --- a/desktop_version/src/Graphics.cpp +++ b/desktop_version/src/Graphics.cpp @@ -111,6 +111,7 @@ void Graphics::init(void) tempShakeTexture = NULL; backgroundTexture = NULL; foregroundTexture = NULL; + tempScreenshot = NULL; towerbg = TowerBG(); titlebg = TowerBG(); trinketr = 0; @@ -220,6 +221,7 @@ void Graphics::destroy_buffers(void) VVV_freefunc(SDL_DestroyTexture, titlebg.texture); VVV_freefunc(SDL_FreeSurface, tempFilterSrc); VVV_freefunc(SDL_FreeSurface, tempFilterDest); + VVV_freefunc(SDL_FreeSurface, tempScreenshot); } void Graphics::drawspritesetcol(int x, int y, int t, int c) diff --git a/desktop_version/src/Graphics.h b/desktop_version/src/Graphics.h index ec201c62..10167520 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -332,6 +332,7 @@ public: SDL_Texture* backgroundTexture; SDL_Texture* foregroundTexture; SDL_Texture* tempScrollingTexture; + SDL_Surface* tempScreenshot; TowerBG towerbg; TowerBG titlebg; diff --git a/desktop_version/src/GraphicsResources.cpp b/desktop_version/src/GraphicsResources.cpp index 29f5358a..a74b6712 100644 --- a/desktop_version/src/GraphicsResources.cpp +++ b/desktop_version/src/GraphicsResources.cpp @@ -4,6 +4,7 @@ #include "Alloc.h" #include "FileSystemUtils.h" +#include "Graphics.h" #include "GraphicsUtil.h" #include "Localization.h" #include "Vlogging.h" @@ -20,6 +21,13 @@ extern "C" const unsigned char* in, 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); } @@ -469,3 +477,52 @@ void GraphicsResources::destroy(void) VVV_freefunc(SDL_FreeSurface, im_sprites_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; +} diff --git a/desktop_version/src/GraphicsUtil.cpp b/desktop_version/src/GraphicsUtil.cpp index 303e12f5..255e3f5e 100644 --- a/desktop_version/src/GraphicsUtil.cpp +++ b/desktop_version/src/GraphicsUtil.cpp @@ -257,3 +257,60 @@ void ApplyFilter(SDL_Surface** src, SDL_Surface** dest) 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; +} diff --git a/desktop_version/src/GraphicsUtil.h b/desktop_version/src/GraphicsUtil.h index cb59df98..ffa89b51 100644 --- a/desktop_version/src/GraphicsUtil.h +++ b/desktop_version/src/GraphicsUtil.h @@ -14,4 +14,6 @@ SDL_Color ReadPixel(const SDL_Surface* surface, int x, int y); void UpdateFilter(void); void ApplyFilter(SDL_Surface** src, SDL_Surface** dest); +bool TakeScreenshot(SDL_Surface** surface); + #endif /* GRAPHICSUTIL_H */ diff --git a/desktop_version/src/KeyPoll.cpp b/desktop_version/src/KeyPoll.cpp index 43a1831f..444bad0e 100644 --- a/desktop_version/src/KeyPoll.cpp +++ b/desktop_version/src/KeyPoll.cpp @@ -10,6 +10,7 @@ #include "Game.h" #include "GlitchrunnerMode.h" #include "Graphics.h" +#include "GraphicsUtil.h" #include "Localization.h" #include "LocalizationStorage.h" #include "Music.h" @@ -17,6 +18,8 @@ #include "UTF8.h" #include "Vlogging.h" +bool SaveScreenshot(void); + int inline KeyPoll::getThreshold(void) { switch (sensitivity) @@ -181,6 +184,11 @@ void KeyPoll::Poll(void) music.playef(Sound_COIN); } + if (evt.key.keysym.sym == SDLK_F6 && !evt.key.repeat) + { + SaveScreenshot(); + } + BUTTONGLYPHS_keyboard_set_active(true); if (textentry())