diff --git a/src/common/filesystem/include/fs_files.h b/src/common/filesystem/include/fs_files.h index 7702f68de3f..77a385fb5bb 100644 --- a/src/common/filesystem/include/fs_files.h +++ b/src/common/filesystem/include/fs_files.h @@ -384,6 +384,7 @@ class FileWriter static FileWriter *Open(const char *filename); virtual size_t Write(const void *buffer, size_t len); + virtual void Flush(); virtual ptrdiff_t Tell(); virtual ptrdiff_t Seek(ptrdiff_t offset, int mode); size_t Printf(const char *fmt, ...); diff --git a/src/common/filesystem/source/files.cpp b/src/common/filesystem/source/files.cpp index ac058111981..6e2a74a5d1d 100644 --- a/src/common/filesystem/source/files.cpp +++ b/src/common/filesystem/source/files.cpp @@ -465,6 +465,15 @@ size_t FileWriter::Write(const void *buffer, size_t len) } } + +void FileWriter::Flush() +{ + if (File != nullptr) + { + fflush(File); + } +} + ptrdiff_t FileWriter::Tell() { if (File != nullptr) diff --git a/src/common/textures/m_png.cpp b/src/common/textures/m_png.cpp index 62c3fa98719..1c57e951f18 100644 --- a/src/common/textures/m_png.cpp +++ b/src/common/textures/m_png.cpp @@ -46,7 +46,6 @@ #include -#include "m_crc32.h" #include "m_swap.h" #if __has_include("c_cvars.h") #include "c_cvars.h" @@ -58,114 +57,8 @@ #include "vectors.h" -// MACROS ------------------------------------------------------------------ - -// The maximum size of an IDAT chunk ZDoom will write. This is also the -// size of the compression buffer it allocates on the stack. -#define PNG_WRITE_SIZE 32768 - // TYPES ------------------------------------------------------------------- -struct IHDR -{ - uint32_t Width; - uint32_t Height; - uint8_t BitDepth; - uint8_t ColorType; - uint8_t Compression; - uint8_t Filter; - uint8_t Interlace; -}; - - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static inline void MakeChunk (void *where, uint32_t type, size_t len); -static inline void StuffPalette (const PalEntry *from, uint8_t *to); -static bool WriteIDAT (FileWriter *file, const uint8_t *data, int len); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -// allow this to compile without CVARs. -#if __has_include("c_cvars.h") -CUSTOM_CVAR(Int, png_level, 5, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 0) - self = 0; - else if (self > 9) - self = 9; -} -CVAR(Float, png_gamma, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -#else -const int png_level = 5; -const float png_gamma = 0; -#endif - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// M_CreatePNG -// -// Passed a newly-created file, writes the PNG signature and IHDR, gAMA, and -// PLTE chunks. Returns true if everything went as expected. -// -//========================================================================== - -bool M_CreatePNG (FileWriter *file, const uint8_t *buffer, const PalEntry *palette, - ESSType color_type, int width, int height, int pitch, float gamma) -{ - uint8_t work[8 + // signature - 12+2*4+5 + // IHDR - 12+4 + // gAMA - 12+256*3]; // PLTE - uint32_t *const sig = (uint32_t *)&work[0]; - IHDR *const ihdr = (IHDR *)&work[8 + 8]; - uint32_t *const gama = (uint32_t *)((uint8_t *)ihdr + 2*4+5 + 12); - uint8_t *const plte = (uint8_t *)gama + 4 + 12; - size_t work_len; - - sig[0] = MAKE_ID(137,'P','N','G'); - sig[1] = MAKE_ID(13,10,26,10); - - ihdr->Width = BigLong(width); - ihdr->Height = BigLong(height); - ihdr->BitDepth = 8; - ihdr->ColorType = color_type == SS_PAL ? 3 : 2; - ihdr->Compression = 0; - ihdr->Filter = 0; - ihdr->Interlace = 0; - MakeChunk (ihdr, MAKE_ID('I','H','D','R'), 2*4+5); - - // Assume a display exponent of 2.2 (100000/2.2 ~= 45454.5) - *gama = BigLong (int (45454.5f * (png_gamma == 0.f ? gamma : png_gamma))); - MakeChunk (gama, MAKE_ID('g','A','M','A'), 4); - - if (color_type == SS_PAL) - { - StuffPalette (palette, plte); - MakeChunk (plte, MAKE_ID('P','L','T','E'), 256*3); - work_len = sizeof(work); - } - else - { - work_len = sizeof(work) - (12+256*3); - } - - if (file->Write (work, work_len) != work_len) - return false; - - return M_SaveBitmap (buffer, color_type, width, height, pitch, file); -} - //========================================================================== // // M_CreateDummyPNG @@ -187,72 +80,39 @@ bool M_CreateDummyPNG (FileWriter *file) return file->Write (dummyPNG, sizeof(dummyPNG)) == sizeof(dummyPNG); } - //========================================================================== // -// M_FinishPNG -// -// Writes an IEND chunk to a PNG file. The file is left opened. +// PNG Reading // //========================================================================== -bool M_FinishPNG (FileWriter *file) +static void PNGRead(png_structp readp, png_bytep dest, png_size_t count) { - static const uint8_t iend[12] = { 0,0,0,0,73,69,78,68,174,66,96,130 }; - return file->Write (iend, 12) == 12; -} - -//========================================================================== -// -// M_AppendPNGText -// -// Appends a PNG tEXt chunk to the file -// -//========================================================================== + FileReader * fr = static_cast(png_get_io_ptr(readp)); -bool M_AppendPNGText (FileWriter *file, const char *keyword, const char *text) -{ - struct { uint32_t len, id; char key[80]; } head; - int len = (int)strlen (text); - int keylen = min ((int)strlen (keyword), 79); - uint32_t crc; - - head.len = BigLong(len + keylen + 1); - head.id = MAKE_ID('t','E','X','t'); - memset (&head.key, 0, sizeof(head.key)); - strncpy (head.key, keyword, keylen); - head.key[keylen] = 0; - - if ((int)file->Write (&head, keylen + 9) == keylen + 9 && - (int)file->Write (text, len) == len) + if(fr->Read(dest, count) != count) { - crc = CalcCRC32 ((uint8_t *)&head+4, keylen + 5); - if (len != 0) - { - crc = AddCRC32 (crc, (uint8_t *)text, len); - } - crc = BigLong(crc); - return file->Write (&crc, 4) == 4; + png_error(readp, "PNG file too short"); } - return false; } -//========================================================================== -// -// PNG READING -// -//========================================================================== - -static void PNGRead(png_structp readp, png_bytep dest, png_size_t count) +static void PNGWrite(png_structp readp, png_bytep dest, png_size_t count) { - FileReader * fr = static_cast(png_get_io_ptr(readp)); + FileWriter * fw = static_cast(png_get_io_ptr(readp)); - if(fr->Read(dest, count) != count) + if(fw->Write(dest, count) != count) { png_error(readp, "PNG file too short"); } } +static void PNGFlush(png_structp readp) +{ + FileWriter * fw = static_cast(png_get_io_ptr(readp)); + + fw->Flush(); +} + static bool Internal_ReadPNG (FileReader * fr, FBitmap * out, int(*callback)(png_structp, png_unknown_chunkp), void * data, int start) { TArray rows; @@ -316,7 +176,7 @@ static bool Internal_ReadPNG (FileReader * fr, FBitmap * out, int(*callback)(png rows.Resize(height); - for(int i = 0; i < height; i++) + for(unsigned i = 0; i < height; i++) { rows[i] = out->GetPixels() + (4 * width * i); } @@ -418,25 +278,6 @@ bool M_ReadPNG(FileReader &&fr, FBitmap * out) return Internal_ReadPNG(&png, out, nullptr, nullptr, 8); } -// PRIVATE CODE ------------------------------------------------------------ - -//========================================================================== -// -// MakeChunk -// -// Prepends the chunk length and type and appends the chunk's CRC32. -// There must be 8 bytes available before the chunk passed and 4 bytes -// after the chunk. -// -//========================================================================== - -static inline void MakeChunk (void *where, uint32_t type, size_t len) -{ - uint8_t *const data = (uint8_t *)where; - *(uint32_t *)(data - 8) = BigLong ((unsigned int)len); - *(uint32_t *)(data - 4) = type; - *(uint32_t *)(data + len) = BigLong ((unsigned int)CalcCRC32 (data-4, (unsigned int)(len+4))); -} //========================================================================== // @@ -446,7 +287,7 @@ static inline void MakeChunk (void *where, uint32_t type, size_t len) // //========================================================================== -static void StuffPalette (const PalEntry *from, uint8_t *to) +static void StuffPalette(const PalEntry *from, uint8_t *to) { for (int i = 256; i > 0; --i) { @@ -458,160 +299,97 @@ static void StuffPalette (const PalEntry *from, uint8_t *to) } } -#define SelectFilter(x,y,z) 0 - //========================================================================== // -// M_SaveBitmap -// -// Given a bitmap, creates one or more IDAT chunks in the given file. -// Returns true on success. +// PNG Writing // //========================================================================== -bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height, int pitch, FileWriter *file) +bool M_WritePNG(const uint8_t * buffer, uint32_t width, uint32_t height, const PalEntry (*palette)[256], double gamma, bool bgra, bool upside_down, const TArray> &text, FileWriter &out) { - TArray temprow_storage; - - static const unsigned temprow_count = 1; + TArray rows; + TArray txt; + TArray pal; - const unsigned temprow_size = 1 + width * 3; - temprow_storage.Resize(temprow_size * temprow_count); + png_structp writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - Byte* temprow[temprow_count]; - - for (unsigned i = 0; i < temprow_count; ++i) + if(!writep) { - temprow[i] = &temprow_storage[temprow_size * i]; + return false; } - TArray array(PNG_WRITE_SIZE, true); - auto buffer = array.data(); - z_stream stream; - int err; - int y; + png_infop infop = png_create_info_struct(writep); - stream.next_in = Z_NULL; - stream.avail_in = 0; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - err = deflateInit (&stream, png_level); + if(!infop) + { + png_destroy_write_struct(&writep, nullptr); + return false; + } - if (err != Z_OK) + if(setjmp(png_jmpbuf(writep))) { + png_destroy_write_struct(&writep, &infop); return false; } - y = height; - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + png_set_write_fn(writep, &out, &PNGWrite, &PNGFlush); + + png_set_compression_level(writep, Z_BEST_COMPRESSION); - temprow[0][0] = 0; + png_set_IHDR(writep, infop, width, height, 8, palette ? PNG_COLOR_TYPE_PALETTE : (bgra ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - while (y-- > 0 && err == Z_OK) + if(palette) { - switch (color_type) - { - case SS_PAL: - memcpy(&temprow[0][1], from, width); - // always use filter type 0 for paletted images - stream.next_in = temprow[0]; - stream.avail_in = width + 1; - break; - - case SS_RGB: - memcpy(&temprow[0][1], from, width*3); - stream.next_in = temprow[SelectFilter(temprow, prior, width)]; - stream.avail_in = width * 3 + 1; - break; - - case SS_BGRA: - for (int x = 0; x < width; ++x) - { - temprow[0][x*3 + 1] = from[x*4 + 2]; - temprow[0][x*3 + 2] = from[x*4 + 1]; - temprow[0][x*3 + 3] = from[x*4]; - } - stream.next_in = temprow[SelectFilter(temprow, prior, width)]; - stream.avail_in = width * 3 + 1; - break; - } + pal.Resize(3 * 256); + StuffPalette(*palette, pal.Data()); + png_set_PLTE(writep, infop, (const png_color*) pal.Data(), 256); + } - from += pitch; + png_set_gAMA(writep, infop, gamma); - err = deflate (&stream, (y == 0) ? Z_FINISH : 0); - if (err != Z_OK) - { - break; - } - while (stream.avail_out == 0) - { - if (!WriteIDAT (file, buffer, sizeof(buffer))) - { - return false; - } - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); - if (stream.avail_in != 0) - { - err = deflate (&stream, (y == 0) ? Z_FINISH : 0); - if (err != Z_OK) - { - break; - } - } - } + uint8_t trans = 0; + + png_set_tRNS(writep, infop, &trans, 1, 0); + + txt.Resize(text.Size()); + for(unsigned i = 0; i < text.Size(); i++) + { + txt[i].compression = -1; + txt[i].key = (char*) text[i].first.GetChars(); + txt[i].text = (char*) text[i].second.GetChars(); + txt[i].text_length = text[i].second.Len(); + txt[i].itxt_length = 0; + txt[i].lang = nullptr; + txt[i].lang_key = nullptr; } - while (err == Z_OK) + png_set_text(writep, infop, txt.Data(), txt.Size()); + + rows.Resize(height); + + int bpp = palette ? 1 : (bgra ? 4 : 3); + + if(upside_down) { - err = deflate (&stream, Z_FINISH); - if (err != Z_OK) - { - break; - } - if (stream.avail_out == 0) + for(unsigned i = height; i > 0; i--) { - if (!WriteIDAT (file, buffer, sizeof(buffer))) - { - return false; - } - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + rows[i] = buffer + (bpp * width * (i - 1)); } } - - deflateEnd (&stream); - - if (err != Z_STREAM_END) + else { - return false; + for(unsigned i = 0; i < height; i++) + { + rows[i] = buffer + (bpp * width * i); + } } - return WriteIDAT (file, buffer, sizeof(buffer)-stream.avail_out); -} - -//========================================================================== -// -// WriteIDAT -// -// Writes a single IDAT chunk to the file. Returns true on success. -// -//========================================================================== -static bool WriteIDAT (FileWriter *file, const uint8_t *data, int len) -{ - uint32_t foo[2], crc; + png_set_rows(writep, infop, (png_bytepp) rows.Data()); + png_write_png(writep, infop, (!palette && bgra) ? PNG_TRANSFORM_BGR : 0, nullptr); + png_write_end(writep, infop); - foo[0] = BigLong (len); - foo[1] = MAKE_ID('I','D','A','T'); - crc = CalcCRC32 ((uint8_t *)&foo[1], 4); - crc = BigLong ((unsigned int)AddCRC32 (crc, data, len)); + png_destroy_write_struct(&writep, &infop); - if (file->Write (foo, 8) != 8 || - file->Write (data, len) != (size_t)len || - file->Write (&crc, 4) != 4) - { - return false; - } return true; } + diff --git a/src/common/textures/m_png.h b/src/common/textures/m_png.h index 2ee0d4df9c2..d233cdf3e5c 100644 --- a/src/common/textures/m_png.h +++ b/src/common/textures/m_png.h @@ -33,38 +33,13 @@ ** */ +#include +#include #include #include "zstring.h" #include "files.h" #include "palentry.h" - -// Screenshot buffer image data types -enum ESSType -{ - SS_PAL, - SS_RGB, - SS_BGRA -}; - -// PNG Writing -------------------------------------------------------------- - -// Start writing an 8-bit palettized PNG file. -// The passed file should be a newly created file. -// This function writes the PNG signature and the IHDR, gAMA, PLTE, and IDAT -// chunks. -bool M_CreatePNG (FileWriter *file, const uint8_t *buffer, const PalEntry *pal, - ESSType color_type, int width, int height, int pitch, float gamma); - -// Creates a grayscale 1x1 PNG file. Used for savegames without savepics. -bool M_CreateDummyPNG (FileWriter *file); - -// Adds a tEXt chunk to a PNG file started with M_CreatePNG. -bool M_AppendPNGText (FileWriter *file, const char *keyword, const char *text); - -// Appends the IEND chunk to a PNG file. -bool M_FinishPNG (FileWriter *file); - -bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height, int pitch, FileWriter *file); +#include "tarray.h" // PNG Reading -------------------------------------------------------------- @@ -80,4 +55,18 @@ class FGameTexture; FGameTexture *PNGTexture_CreateFromFile(FileSys::FileReader &&fr, const FString &filename); +// PNG Writing -------------------------------------------------------------- + +enum ESSType +{ + SS_PAL, + SS_RGB, + SS_BGRA +}; + +//palette = null, buffer must be rgb or bgra, 24/32 bpp +//palette = non-null, buffer must be indices, 8 bpp +bool M_WritePNG(const uint8_t * buffer, uint32_t width, uint32_t height, const PalEntry (*palette)[256], double gamma, bool bgra, bool upside_down, const TArray> &text, FileWriter &out); +bool M_CreateDummyPNG (FileWriter *file); + #endif diff --git a/src/g_game.cpp b/src/g_game.cpp index 105b16aa74c..cce560c46fa 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2333,7 +2333,7 @@ static void PutSaveComment (FSerializer &arc) arc.AddString("Comment", comment.GetChars()); } -static void PutSavePic (FileWriter *file, int width, int height) +static void PutSavePic (FileWriter *file, int width, int height, const TArray> &text) { if (width <= 0 || height <= 0 || !storesavepic) { @@ -2343,7 +2343,7 @@ static void PutSavePic (FileWriter *file, int width, int height) { D_Render([&]() { - WriteSavePic(&players[consoleplayer], file, width, height); + WriteSavePic(&players[consoleplayer], file, width, height, text); }, false); } } @@ -2403,16 +2403,19 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c savegameglobals.OpenWriter(save_formatted); SaveVersion = SAVEVER; - PutSavePic(&savepic, SAVEPICWIDTH, SAVEPICHEIGHT); - mysnprintf(buf, countof(buf), GAMENAME " %s", GetVersionString()); + // put some basic info into the PNG so that this isn't lost when the image gets extracted. - M_AppendPNGText(&savepic, "Software", buf); - M_AppendPNGText(&savepic, "Title", description); - M_AppendPNGText(&savepic, "Current Map", primaryLevel->MapName.GetChars()); - M_FinishPNG(&savepic); + FString software(GAMENAME " "); + software += GetVersionString(); + + TArray> text; + text.Push({"Software", software}); + text.Push({"Title", description}); + text.Push({"Current Map", primaryLevel->MapName}); + PutSavePic(&savepic, SAVEPICWIDTH, SAVEPICHEIGHT, text); int ver = SAVEVER; - savegameinfo.AddString("Software", buf) + savegameinfo.AddString("Software", software.GetChars()) .AddString("Engine", GAMESIG) ("Save Version", ver) .AddString("Title", description) diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 888a76d9b0a..8a11dc156b5 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -335,17 +335,15 @@ void M_LoadDefaults () // // WritePNGfile // -void WritePNGfile (FileWriter *file, const uint8_t *buffer, const PalEntry *palette, - ESSType color_type, int width, int height, int pitch, float gamma) +void WritePNGfile (FileWriter *file, const uint8_t *buffer, ESSType color_type, int width, int height, bool flip, float gamma) { FString software(GAMENAME " "); - software += GetVersionString(); + TArray> text; + text.Push({"Software", software}); - if (!M_CreatePNG (file, buffer, palette, color_type, width, height, pitch, gamma) || - !M_AppendPNGText (file, "Software", software.GetChars()) || - !M_FinishPNG (file)) + if (!M_WritePNG(buffer, width, height, nullptr, gamma, color_type == SS_BGRA, flip, text, *file)) { Printf ("%s\n", GStrings.GetString("TXT_SCREENSHOTERR")); } @@ -454,8 +452,7 @@ void M_ScreenShot (const char *filename) return; } - WritePNGfile(file, buffer.Data(), nullptr, color_type, - screen->GetWidth(), screen->GetHeight(), pitch, gamma); + WritePNGfile(file, buffer.Data(), color_type, screen->GetWidth(), screen->GetHeight(), pitch < 0, gamma); delete file; diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index 25051040a64..4d326cd62fa 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -205,11 +205,11 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou // //=========================================================================== -void WriteSavePic(player_t* player, FileWriter* file, int width, int height) +void WriteSavePic(player_t* player, FileWriter* file, int width, int height, const TArray> &text) { if (!V_IsHardwareRenderer()) { - SWRenderer->WriteSavePic(player, file, width, height); + SWRenderer->WriteSavePic(player, file, width, height, text); } else { @@ -271,16 +271,7 @@ void WriteSavePic(player_t* player, FileWriter* file, int width, int height) scr[i + 2] = uint8_t(scr[i + 2] * iblendfac + blend.Z); } - uint8_t * scrP = scr.Data(); - - int pitch = width * 3; - if(screen->FlipSavePic()) - { - scrP += ((height - 1) * width * 3); - pitch *= -1; - } - - M_CreatePNG(file, scrP, nullptr , SS_RGB, width, height, pitch, vid_gamma); + M_WritePNG(scr.Data(), width, height, nullptr, vid_gamma, false, screen->FlipSavePic(), text, *file); // Switch back the screen render buffers screen->SetViewportRects(nullptr); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index ce848e29710..8723d6acf20 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -327,7 +327,7 @@ struct HWDrawInfo void CleanSWDrawer(); sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); -void WriteSavePic(player_t* player, FileWriter* file, int width, int height); +void WriteSavePic(player_t* player, FileWriter* file, int width, int height, const TArray> &text); sector_t* RenderView(player_t* player); diff --git a/src/rendering/swrenderer/r_renderer.h b/src/rendering/swrenderer/r_renderer.h index 7d873f5f0bb..d51d84e81cf 100644 --- a/src/rendering/swrenderer/r_renderer.h +++ b/src/rendering/swrenderer/r_renderer.h @@ -26,7 +26,7 @@ struct FRenderer virtual void RenderView(player_t *player, DCanvas *target, void *videobuffer, int bufferpitch) = 0; // renders view to a savegame picture - virtual void WriteSavePic(player_t *player, FileWriter *file, int width, int height) = 0; + virtual void WriteSavePic(player_t *player, FileWriter *file, int width, int height, const TArray> &text) = 0; // draws player sprites with hardware acceleration (only useful for software rendering) virtual void DrawRemainingPlayerSprites() = 0; diff --git a/src/rendering/swrenderer/r_swrenderer.cpp b/src/rendering/swrenderer/r_swrenderer.cpp index 4fa23b21502..82d8fc05c3b 100644 --- a/src/rendering/swrenderer/r_swrenderer.cpp +++ b/src/rendering/swrenderer/r_swrenderer.cpp @@ -198,7 +198,7 @@ void FSoftwareRenderer::RenderView(player_t *player, DCanvas *target, void *vide }); } -void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) +void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height, const TArray> &text) { DCanvas pic(width, height, false); @@ -215,7 +215,7 @@ void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int wi // Apply the screen blend to the palette. The colormap related parts get skipped here because these are already part of the image. DoBlending(GPalette.BaseColors, palette, 256, uint8_t(blend.X), uint8_t(blend.Y), uint8_t(blend.Z), uint8_t(blend.W * 255)); - M_CreatePNG(file, pic.GetPixels(), palette , SS_PAL, width, height, width, vid_gamma); + M_WritePNG(pic.GetPixels(), width, height, &palette, vid_gamma, false, false, text, *file); } void FSoftwareRenderer::DrawRemainingPlayerSprites() diff --git a/src/rendering/swrenderer/r_swrenderer.h b/src/rendering/swrenderer/r_swrenderer.h index 1e26a6a060c..5f94cafd913 100644 --- a/src/rendering/swrenderer/r_swrenderer.h +++ b/src/rendering/swrenderer/r_swrenderer.h @@ -15,7 +15,7 @@ struct FSoftwareRenderer : public FRenderer void RenderView(player_t *player, DCanvas *target, void *videobuffer, int bufferpitch) override; // renders view to a savegame picture - void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; + void WriteSavePic (player_t *player, FileWriter *file, int width, int height, const TArray> &text) override; // draws player sprites with hardware acceleration (only useful for software rendering) void DrawRemainingPlayerSprites() override;