#include "ScreenExporter.hpp"
#include <zlib.h>
#include <sstream>
#include <fstream>
#include "Log.hpp"
namespace ale {
static void writePNGChunk(std::ofstream& out, const char* type, uInt8* data,
int size) {
uInt8 temp[8];
temp[0] = size >> 24;
temp[1] = size >> 16;
temp[2] = size >> 8;
temp[3] = size;
temp[4] = type[0];
temp[5] = type[1];
temp[6] = type[2];
temp[7] = type[3];
out.write((const char*)temp, 8);
uInt32 crc = crc32(0, temp + 4, 4);
if (size > 0) {
out.write((const char*)data, size);
crc = crc32(crc, data, size);
}
temp[0] = crc >> 24;
temp[1] = crc >> 16;
temp[2] = crc >> 8;
temp[3] = crc;
out.write((const char*)temp, 4);
}
static void writePNGHeader(std::ofstream& out, const ALEScreen& screen,
bool doubleWidth = true) {
int width = doubleWidth ? screen.width() * 2 : screen.width();
int height = screen.height();
uInt8 header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
out.write((const char*)header, sizeof(header));
uInt8 ihdr[13];
ihdr[0] = (width >> 24) & 0xFF; ihdr[1] = (width >> 16) & 0xFF;
ihdr[2] = (width >> 8) & 0xFF;
ihdr[3] = (width >> 0) & 0xFF;
ihdr[4] = (height >> 24) & 0xFF; ihdr[5] = (height >> 16) & 0xFF;
ihdr[6] = (height >> 8) & 0xFF;
ihdr[7] = (height >> 0) & 0xFF;
ihdr[8] = 8; ihdr[9] = 2; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; writePNGChunk(out, "IHDR", ihdr, sizeof(ihdr));
}
static void writePNGData(std::ofstream& out, const ALEScreen& screen,
const ColourPalette& palette,
bool doubleWidth = true) {
int dataWidth = screen.width();
int width = doubleWidth ? dataWidth * 2 : dataWidth;
int height = screen.height();
int rowbytes = width * 3;
std::vector<uInt8> buffer((rowbytes + 1) * height, 0);
uInt8* buf_ptr = &buffer[0];
for (int i = 0; i < height; i++) {
*buf_ptr++ = 0; for (int j = 0; j < dataWidth; j++) {
int r, g, b;
palette.getRGB(screen.getArray()[i * dataWidth + j], r, g, b);
int jj = doubleWidth ? 2 * j : j;
buf_ptr[jj * 3 + 0] = r;
buf_ptr[jj * 3 + 1] = g;
buf_ptr[jj * 3 + 2] = b;
if (doubleWidth) {
jj = jj + 1;
buf_ptr[jj * 3 + 0] = r;
buf_ptr[jj * 3 + 1] = g;
buf_ptr[jj * 3 + 2] = b;
}
}
buf_ptr += rowbytes; }
uLongf compmemsize = (uLongf)((height * (width + 1) * 3 + 1) + 12);
std::vector<uInt8> compmem(compmemsize, 0);
if ((compress(&compmem[0], &compmemsize, &buffer[0],
height * (width * 3 + 1)) != Z_OK)) {
ale::Logger::Error << "Error: Couldn't compress PNG" << std::endl;
return;
}
writePNGChunk(out, "IDAT", &compmem[0], compmemsize);
}
static void writePNGEnd(std::ofstream& out) {
writePNGChunk(out, "IEND", 0, 0);
}
ScreenExporter::ScreenExporter(ColourPalette& palette)
: m_palette(palette), m_frame_number(0), m_frame_field_width(6) {}
ScreenExporter::ScreenExporter(ColourPalette& palette, const std::string& path)
: m_palette(palette), m_frame_number(0), m_frame_field_width(6),
m_path(path) {}
void ScreenExporter::save(const ALEScreen& screen,
const std::string& filename) const {
std::ofstream out(filename.c_str(), std::ios_base::binary);
if (!out.good()) {
ale::Logger::Error << "Could not open " << filename << " for writing"
<< std::endl;
return;
}
writePNGHeader(out, screen, true);
writePNGData(out, screen, m_palette, true);
writePNGEnd(out);
out.close();
}
void ScreenExporter::saveNext(const ALEScreen& screen) {
assert(m_path.size() > 0);
std::ostringstream oss;
oss << m_path << "/" << std::setw(m_frame_field_width) << std::setfill('0')
<< m_frame_number << ".png";
save(screen, oss.str());
m_frame_number++;
}
}