1
0
Fork 0
OpenTTD/src/screenshot_pcx.cpp

157 lines
3.9 KiB
C++

/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file screenshot_pcx.cpp PCX screenshots provider. */
#include "stdafx.h"
#include "core/endian_func.hpp"
#include "core/math_func.hpp"
#include "debug.h"
#include "fileio_func.h"
#include "screenshot_type.h"
#include "safeguards.h"
/** Definition of a PCX file header. */
struct PcxHeader {
uint8_t manufacturer;
uint8_t version;
uint8_t rle;
uint8_t bpp;
uint32_t unused;
uint16_t xmax, ymax;
uint16_t hdpi, vdpi;
uint8_t pal_small[16 * 3];
uint8_t reserved;
uint8_t planes;
uint16_t pitch;
uint16_t cpal;
uint16_t width;
uint16_t height;
uint8_t filler[54];
};
static_assert(sizeof(PcxHeader) == 128);
class ScreenshotProvider_Pcx : public ScreenshotProvider {
public:
ScreenshotProvider_Pcx() : ScreenshotProvider("pcx", "PCX", 20) {}
bool MakeImage(const char *name, const ScreenshotCallback &callb, uint w, uint h, int pixelformat, const Colour *palette) override
{
uint maxlines;
uint y;
PcxHeader pcx;
bool success;
if (pixelformat == 32) {
Debug(misc, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick another format.");
return false;
}
if (pixelformat != 8 || w == 0) return false;
auto of = FileHandle::Open(name, "wb");
if (!of.has_value()) return false;
auto &f = *of;
memset(&pcx, 0, sizeof(pcx));
/* setup pcx header */
pcx.manufacturer = 10;
pcx.version = 5;
pcx.rle = 1;
pcx.bpp = 8;
pcx.xmax = TO_LE16(w - 1);
pcx.ymax = TO_LE16(h - 1);
pcx.hdpi = TO_LE16(320);
pcx.vdpi = TO_LE16(320);
pcx.planes = 1;
pcx.cpal = TO_LE16(1);
pcx.width = pcx.pitch = TO_LE16(w);
pcx.height = TO_LE16(h);
/* write pcx header */
if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
return false;
}
/* use by default 64k temp memory */
maxlines = Clamp(65536 / w, 16, 128);
/* now generate the bitmap bits */
std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines); // by default generate 128 lines at a time.
y = 0;
do {
/* determine # lines to write */
uint n = std::min(h - y, maxlines);
uint i;
/* render the pixels into the buffer */
callb(buff.data(), y, w, n);
y += n;
/* write them to pcx */
for (i = 0; i != n; i++) {
const uint8_t *bufp = buff.data() + i * w;
uint8_t runchar = bufp[0];
uint runcount = 1;
uint j;
/* for each pixel... */
for (j = 1; j < w; j++) {
uint8_t ch = bufp[j];
if (ch != runchar || runcount >= 0x3f) {
if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
if (fputc(0xC0 | runcount, f) == EOF) {
return false;
}
}
if (fputc(runchar, f) == EOF) {
return false;
}
runcount = 0;
runchar = ch;
}
runcount++;
}
/* write remaining bytes.. */
if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
if (fputc(0xC0 | runcount, f) == EOF) {
return false;
}
}
if (fputc(runchar, f) == EOF) {
return false;
}
}
} while (y != h);
/* write 8-bit colour palette */
if (fputc(12, f) == EOF) {
return false;
}
/* Palette is word-aligned, copy it to a temporary byte array */
uint8_t tmp[256 * 3];
for (uint i = 0; i < 256; i++) {
tmp[i * 3 + 0] = palette[i].r;
tmp[i * 3 + 1] = palette[i].g;
tmp[i * 3 + 2] = palette[i].b;
}
success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
return success;
}
};
static ScreenshotProvider_Pcx s_screenshot_provider_pcx;