Browse Source
PNG screenshots are also lossless and about half the size of the PCX screenshots.pull/7182/head
9 changed files with 272 additions and 131 deletions
@ -0,0 +1,153 @@
|
||||
#include "utils/surface_to_pcx.hpp" |
||||
|
||||
#include <cerrno> |
||||
#include <cstdint> |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
#include <string> |
||||
|
||||
#include <SDL.h> |
||||
#include <expected.hpp> |
||||
|
||||
#include "engine/surface.hpp" |
||||
#include "utils/pcx.hpp" |
||||
|
||||
namespace devilution { |
||||
namespace { |
||||
|
||||
tl::expected<void, std::string> CheckedFWrite(const void *ptr, size_t size, FILE *out) |
||||
{ |
||||
if (std::fwrite(ptr, size, 1, out) != 1) { |
||||
const char *errorMessage = std::strerror(errno); |
||||
if (errorMessage == nullptr) |
||||
errorMessage = ""; |
||||
return tl::make_unexpected(std::string("fwrite failed with: ").append(errorMessage)); |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Write the PCX-file header |
||||
* @param width Image width |
||||
* @param height Image height |
||||
* @param out File stream to write to |
||||
* @return True on success |
||||
*/ |
||||
tl::expected<void, std::string> WritePcxHeader(int16_t width, int16_t height, FILE *out) |
||||
{ |
||||
PCXHeader buffer; |
||||
|
||||
memset(&buffer, 0, sizeof(buffer)); |
||||
buffer.Manufacturer = 10; |
||||
buffer.Version = 5; |
||||
buffer.Encoding = 1; |
||||
buffer.BitsPerPixel = 8; |
||||
buffer.Xmax = SDL_SwapLE16(width - 1); |
||||
buffer.Ymax = SDL_SwapLE16(height - 1); |
||||
buffer.HDpi = SDL_SwapLE16(width); |
||||
buffer.VDpi = SDL_SwapLE16(height); |
||||
buffer.NPlanes = 1; |
||||
buffer.BytesPerLine = SDL_SwapLE16(width); |
||||
|
||||
return CheckedFWrite(&buffer, sizeof(buffer), out); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Write the current in-game palette to the PCX file |
||||
* @param palette Current palette |
||||
* @param out File stream for the PCX file. |
||||
* @return True if successful, else false |
||||
*/ |
||||
tl::expected<void, std::string> WritePcxPalette(SDL_Color *palette, FILE *out) |
||||
{ |
||||
uint8_t pcxPalette[1 + 256 * 3]; |
||||
|
||||
pcxPalette[0] = 12; |
||||
for (int i = 0; i < 256; i++) { |
||||
pcxPalette[1 + 3 * i + 0] = palette[i].r; |
||||
pcxPalette[1 + 3 * i + 1] = palette[i].g; |
||||
pcxPalette[1 + 3 * i + 2] = palette[i].b; |
||||
} |
||||
|
||||
return CheckedFWrite(pcxPalette, sizeof(pcxPalette), out); |
||||
} |
||||
|
||||
/**
|
||||
* @brief RLE compress the pixel data |
||||
* @param src Raw pixel buffer |
||||
* @param dst Output buffer |
||||
* @param width Width of pixel buffer |
||||
|
||||
* @return Output buffer |
||||
*/ |
||||
uint8_t *WritePcxLine(uint8_t *src, uint8_t *dst, int width) |
||||
{ |
||||
int rleLength; |
||||
|
||||
do { |
||||
const uint8_t rlePixel = *src; |
||||
src++; |
||||
rleLength = 1; |
||||
|
||||
width--; |
||||
|
||||
while (rlePixel == *src) { |
||||
if (rleLength >= 63) |
||||
break; |
||||
if (width == 0) |
||||
break; |
||||
rleLength++; |
||||
|
||||
width--; |
||||
src++; |
||||
} |
||||
|
||||
if (rleLength > 1 || rlePixel > 0xBF) { |
||||
*dst = rleLength | 0xC0; |
||||
dst++; |
||||
} |
||||
|
||||
*dst = rlePixel; |
||||
dst++; |
||||
} while (width > 0); |
||||
|
||||
return dst; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Write the pixel data to the PCX file |
||||
* |
||||
* @param buf Pixel data |
||||
* @param out File stream for the PCX file. |
||||
* @return True if successful, else false |
||||
*/ |
||||
tl::expected<void, std::string> WritePcxPixels(const Surface &buf, FILE *out) |
||||
{ |
||||
const int width = buf.w(); |
||||
const std::unique_ptr<uint8_t[]> pBuffer { new uint8_t[static_cast<size_t>(2 * width)] }; |
||||
uint8_t *pixels = buf.begin(); |
||||
for (int height = buf.h(); height > 0; height--) { |
||||
const uint8_t *pBufferEnd = WritePcxLine(pixels, pBuffer.get(), width); |
||||
pixels += buf.pitch(); |
||||
tl::expected<void, std::string> result = CheckedFWrite(pBuffer.get(), pBufferEnd - pBuffer.get(), out); |
||||
if (!result.has_value()) return result; |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
tl::expected<void, std::string> |
||||
WriteSurfaceToFilePcx(const Surface &buf, FILE *outStream) |
||||
{ |
||||
tl::expected<void, std::string> result = WritePcxHeader(buf.w(), buf.h(), outStream); |
||||
if (!result.has_value()) return result; |
||||
result = WritePcxPixels(buf, outStream); |
||||
if (!result.has_value()) return result; |
||||
result = WritePcxPalette(buf.surface->format->palette->colors, outStream); |
||||
if (!result.has_value()) return result; |
||||
std::fclose(outStream); |
||||
return {}; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,13 @@
|
||||
#include <cstdio> |
||||
#include <string> |
||||
|
||||
#include <expected.hpp> |
||||
|
||||
#include "engine/surface.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
tl::expected<void, std::string> |
||||
WriteSurfaceToFilePcx(const Surface &buf, FILE *outStream); |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,27 @@
|
||||
#include "utils/surface_to_png.hpp" |
||||
|
||||
#include <cstdio> |
||||
#include <string> |
||||
|
||||
#include <SDL.h> |
||||
#include <expected.hpp> |
||||
|
||||
#include "engine/surface.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
extern "C" int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); |
||||
|
||||
tl::expected<void, std::string> |
||||
WriteSurfaceToFilePng(const Surface &buf, FILE *outStream) |
||||
{ |
||||
SDL_RWops *rwops = SDL_RWFromFP(outStream, /*autoclose=*/SDL_TRUE); |
||||
if (rwops == nullptr || IMG_SavePNG_RW(buf.surface, rwops, /*freedst=*/1) != 0) { |
||||
tl::expected<void, std::string> result = tl::make_unexpected(std::string(SDL_GetError())); |
||||
SDL_ClearError(); |
||||
return result; |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,13 @@
|
||||
#include <cstdio> |
||||
#include <string> |
||||
|
||||
#include <expected.hpp> |
||||
|
||||
#include "engine/surface.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
tl::expected<void, std::string> |
||||
WriteSurfaceToFilePng(const Surface &buf, FILE *outStream); |
||||
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue