mirror of https://github.com/stella-emu/stella.git
OK, I originally intended to fix FrameBuffer::scanline for SDL2,
which I though a just a one or two-liner equivalent to glReadPixels. Turns out that SDL2 doesn't return 24-bit RGB data (only 16 or 32-bit), so the PNGLibrary had to massage the data. In the process, converted the PNG saving functions to actually use PNG library functions, which greatly simplified converting packing from 32 -> 24-bit. Historically, the PNG save functions were written before libpng was a requirement, so they were hand-rolled. Once libpng was integrated into the codebase, it didn't make much sense to not use it anymore. So an expected two-line code fix became this commit ... git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2919 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
a0aa1c8e0a
commit
d275773413
|
@ -234,15 +234,12 @@ FBSurface* FrameBufferSDL2::createSurface(uInt32 w, uInt32 h,
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBufferSDL2::scanline(uInt32 row, uInt8* data) const
|
||||
void FrameBufferSDL2::readPixels(const GUI::Rect& rect,
|
||||
uInt8* pixels, uInt32 pitch) const
|
||||
{
|
||||
#if 0 //FIXSDL
|
||||
// Invert the row, since OpenGL rows start at the bottom
|
||||
// of the framebuffer
|
||||
const GUI::Rect& image = imageRect();
|
||||
row = image.height() + image.y() - row - 1;
|
||||
SDL_Rect r;
|
||||
r.x = rect.x(); r.y = rect.y();
|
||||
r.w = rect.width(); r.h = rect.height();
|
||||
|
||||
p_gl.PixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
p_gl.ReadPixels(image.x(), row, image.width(), 1, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
#endif
|
||||
SDL_RenderReadPixels(myRenderer, &r, SDL_PIXELFORMAT_ARGB8888, pixels, pitch);
|
||||
}
|
||||
|
|
|
@ -96,12 +96,16 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
bool isDoubleBuffered() const { return myDblBufferedFlag; }
|
||||
|
||||
/**
|
||||
This method is called to get the specified scanline data.
|
||||
This method is called to get the specified ARGB data from the viewable
|
||||
FrameBuffer area. Note that this isn't the same as any internal
|
||||
surfaces that may be in use; it should return the actual data as it
|
||||
is currently seen onscreen.
|
||||
|
||||
@param row The row we are looking for
|
||||
@param data The actual pixel data (in bytes)
|
||||
@param rect The bounding rectangle for the buffer
|
||||
@param buffer The actual pixel data in ARGB8888 format
|
||||
@param pitch The pitch (in bytes) for the pixel data
|
||||
*/
|
||||
void scanline(uInt32 row, uInt8* data) const;
|
||||
void readPixels(const GUI::Rect& rect, uInt8* buffer, uInt32 pitch) const;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
// $Id$
|
||||
//============================================================================
|
||||
|
||||
#include <zlib.h>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
@ -31,7 +30,8 @@
|
|||
#include "PNGLibrary.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
PNGLibrary::PNGLibrary()
|
||||
PNGLibrary::PNGLibrary(const FrameBuffer& fb)
|
||||
: myFB(fb)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,9 @@ PNGLibrary::~PNGLibrary()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PNGLibrary::loadImage(const string& filename,
|
||||
const FrameBuffer& fb, FBSurface& surface)
|
||||
void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
||||
{
|
||||
#define readImageERROR(s) { err_message = s; goto done; }
|
||||
#define loadImageERROR(s) { err_message = s; goto done; }
|
||||
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
@ -57,18 +56,18 @@ bool PNGLibrary::loadImage(const string& filename,
|
|||
|
||||
ifstream in(filename.c_str(), ios_base::binary);
|
||||
if(!in.is_open())
|
||||
readImageERROR("No image found");
|
||||
loadImageERROR("No image found");
|
||||
|
||||
// Create the PNG loading context structure
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
png_user_error, png_user_warn);
|
||||
if(png_ptr == NULL)
|
||||
readImageERROR("Couldn't allocate memory for PNG file");
|
||||
loadImageERROR("Couldn't allocate memory for PNG file");
|
||||
|
||||
// Allocate/initialize the memory for image information. REQUIRED.
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if(info_ptr == NULL)
|
||||
readImageERROR("Couldn't create image information for PNG file");
|
||||
loadImageERROR("Couldn't create image information for PNG file");
|
||||
|
||||
// Set up the input control
|
||||
png_set_read_fn(png_ptr, &in, png_read_data);
|
||||
|
@ -92,20 +91,20 @@ bool PNGLibrary::loadImage(const string& filename,
|
|||
}
|
||||
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
{
|
||||
readImageERROR("Greyscale PNG images not supported");
|
||||
loadImageERROR("Greyscale PNG images not supported");
|
||||
}
|
||||
else if(color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
{
|
||||
readImageERROR("Paletted PNG images not supported");
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
else if(color_type != PNG_COLOR_TYPE_RGB)
|
||||
{
|
||||
readImageERROR("Unknown format in PNG image");
|
||||
loadImageERROR("Unknown format in PNG image");
|
||||
}
|
||||
|
||||
// Create/initialize storage area for the current image
|
||||
if(!allocateStorage(iwidth, iheight))
|
||||
readImageERROR("Not enough memory to read PNG file");
|
||||
loadImageERROR("Not enough memory to read PNG file");
|
||||
|
||||
// The PNG read function expects an array of rows, not a single 1-D array
|
||||
for(uInt32 irow = 0, offset = 0; irow < ReadInfo.height; ++irow, offset += ReadInfo.pitch)
|
||||
|
@ -118,7 +117,7 @@ bool PNGLibrary::loadImage(const string& filename,
|
|||
png_read_end(png_ptr, info_ptr);
|
||||
|
||||
// Scale image to surface dimensions
|
||||
scaleImagetoSurface(fb, surface);
|
||||
scaleImagetoSurface(surface);
|
||||
|
||||
// Cleanup
|
||||
done:
|
||||
|
@ -127,61 +126,136 @@ done:
|
|||
|
||||
if(err_message)
|
||||
throw err_message;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string PNGLibrary::saveImage(const string& filename,
|
||||
const FrameBuffer& framebuffer,
|
||||
const Properties& props)
|
||||
void PNGLibrary::saveImage(const string& filename, const Properties& props)
|
||||
{
|
||||
#define saveImageERROR(s) { err_message = s; goto done; }
|
||||
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
const char* err_message = NULL;
|
||||
const GUI::Rect& imageR = myFB.imageRect();
|
||||
png_uint_32 width = imageR.width(), height = imageR.height();
|
||||
png_bytep image = new png_byte[width * height * 4];
|
||||
png_bytep row_pointers[height];
|
||||
|
||||
ofstream out(filename.c_str(), ios_base::binary);
|
||||
if(!out.is_open())
|
||||
return "ERROR: Couldn't create snapshot file";
|
||||
saveImageERROR("ERROR: Couldn't create snapshot file");
|
||||
|
||||
// Create the PNG saving context structure
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
png_user_error, png_user_warn);
|
||||
if(png_ptr == NULL)
|
||||
saveImageERROR("Couldn't allocate memory for PNG file");
|
||||
|
||||
// Get actual image dimensions. which are not always the same
|
||||
// as the framebuffer dimensions
|
||||
const GUI::Rect& image = framebuffer.imageRect();
|
||||
uInt32 width = image.width(), height = image.height(),
|
||||
pitch = width * 3;
|
||||
uInt8* buffer = new uInt8[(pitch + 1) * height];
|
||||
// Allocate/initialize the memory for image information. REQUIRED.
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if(info_ptr == NULL)
|
||||
saveImageERROR("Couldn't create image information for PNG file");
|
||||
|
||||
// Fill the buffer with scanline data
|
||||
uInt8* buf_ptr = buffer;
|
||||
for(uInt32 row = 0; row < height; row++)
|
||||
{
|
||||
*buf_ptr++ = 0; // first byte of row is filter type
|
||||
framebuffer.scanline(row, buf_ptr); // get another scanline
|
||||
buf_ptr += pitch; // add pitch
|
||||
}
|
||||
// Set up the output control
|
||||
png_set_write_fn(png_ptr, &out, png_write_data, png_io_flush);
|
||||
|
||||
return saveBufferToPNG(out, buffer, width, height, props,
|
||||
framebuffer.tiaSurface().effectsInfo());
|
||||
// Write PNG header info
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
// Write comments
|
||||
writeComments(png_ptr, info_ptr, props);
|
||||
|
||||
// Write the file header information. REQUIRED
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
// Pack pixels into bytes
|
||||
png_set_packing(png_ptr);
|
||||
|
||||
// Swap location of alpha bytes from ARGB to RGBA
|
||||
png_set_swap_alpha(png_ptr);
|
||||
|
||||
// Pack ARGB into RGB
|
||||
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
|
||||
|
||||
// Flip BGR pixels to RGB
|
||||
png_set_bgr(png_ptr);
|
||||
|
||||
// Get framebuffer surface pixel data (we get ABGR format)
|
||||
myFB.readPixels(imageR, image, width*4);
|
||||
|
||||
// Set up pointers into "image" byte array
|
||||
for(png_uint_32 k = 0; k < height; ++k)
|
||||
row_pointers[k] = image + k*width*4;
|
||||
|
||||
// Write the entire image in one go
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
// We're finished writing
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
|
||||
// Cleanup
|
||||
done:
|
||||
if(png_ptr)
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
if(image)
|
||||
delete[] image;
|
||||
if(err_message)
|
||||
throw err_message;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string PNGLibrary::saveImage(const string& filename,
|
||||
const FrameBuffer& framebuffer, const TIA& tia,
|
||||
const Properties& props)
|
||||
void PNGLibrary::saveImage(const string& filename, const TIA& tia,
|
||||
const Properties& props)
|
||||
{
|
||||
ofstream out(filename.c_str(), ios_base::binary);
|
||||
if(!out.is_open())
|
||||
return "ERROR: Couldn't create snapshot file";
|
||||
#define saveImageERROR(s) { err_message = s; goto done; }
|
||||
|
||||
uInt32 width = tia.width(), height = tia.height();
|
||||
uInt8* buffer = new uInt8[(width*3*2 + 1) * height];
|
||||
|
||||
// Fill the buffer with pixels from the mediasrc
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
const char* err_message = NULL;
|
||||
png_uint_32 tiaw = tia.width(), width = tiaw*2, height = tia.height();
|
||||
png_bytep image = new png_byte[width * height * 3];
|
||||
png_bytep row_pointers[height];
|
||||
uInt8 r, g, b;
|
||||
uInt8* buf_ptr = buffer;
|
||||
uInt8* buf_ptr = image;
|
||||
|
||||
ofstream out(filename.c_str(), ios_base::binary);
|
||||
if(!out.is_open())
|
||||
saveImageERROR("ERROR: Couldn't create snapshot file");
|
||||
|
||||
// Create the PNG saving context structure
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
png_user_error, png_user_warn);
|
||||
if(png_ptr == NULL)
|
||||
saveImageERROR("Couldn't allocate memory for PNG file");
|
||||
|
||||
// Allocate/initialize the memory for image information. REQUIRED.
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if(info_ptr == NULL)
|
||||
saveImageERROR("Couldn't create image information for PNG file");
|
||||
|
||||
// Set up the output control
|
||||
png_set_write_fn(png_ptr, &out, png_write_data, png_io_flush);
|
||||
|
||||
// Write PNG header info
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
// Write comments
|
||||
writeComments(png_ptr, info_ptr, props);
|
||||
|
||||
// Write the file header information. REQUIRED
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
// Fill the buffer with pixels from the tia, scaled 2x horizontally
|
||||
for(uInt32 y = 0; y < height; ++y)
|
||||
{
|
||||
*buf_ptr++ = 0; // first byte of row is filter type
|
||||
for(uInt32 x = 0; x < width; ++x)
|
||||
for(uInt32 x = 0; x < tiaw; ++x)
|
||||
{
|
||||
uInt32 pixel = framebuffer.tiaSurface().pixel(y*width+x);
|
||||
framebuffer.getRGB(pixel, &r, &g, &b);
|
||||
uInt32 pixel = myFB.tiaSurface().pixel(y*tiaw+x);
|
||||
myFB.getRGB(pixel, &r, &g, &b);
|
||||
*buf_ptr++ = r;
|
||||
*buf_ptr++ = g;
|
||||
*buf_ptr++ = b;
|
||||
|
@ -189,80 +263,24 @@ string PNGLibrary::saveImage(const string& filename,
|
|||
*buf_ptr++ = g;
|
||||
*buf_ptr++ = b;
|
||||
}
|
||||
// Set up pointers into "image" byte array
|
||||
buf_ptr = row_pointers[y] = image + y*width*3;
|
||||
}
|
||||
|
||||
return saveBufferToPNG(out, buffer, width << 1, height, props,
|
||||
framebuffer.tiaSurface().effectsInfo());
|
||||
}
|
||||
// Write the entire image in one go
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string PNGLibrary::saveBufferToPNG(ofstream& out, uInt8* buffer,
|
||||
uInt32 width, uInt32 height,
|
||||
const Properties& props,
|
||||
const string& effectsInfo)
|
||||
{
|
||||
uInt8* compmem = (uInt8*) NULL;
|
||||
// We're finished writing
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
|
||||
try
|
||||
{
|
||||
// PNG file header
|
||||
uInt8 header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
|
||||
out.write((const char*)header, 8);
|
||||
|
||||
// PNG IHDR
|
||||
uInt8 ihdr[13];
|
||||
ihdr[0] = width >> 24; // width
|
||||
ihdr[1] = width >> 16;
|
||||
ihdr[2] = width >> 8;
|
||||
ihdr[3] = width & 0xFF;
|
||||
ihdr[4] = height >> 24; // height
|
||||
ihdr[5] = height >> 16;
|
||||
ihdr[6] = height >> 8;
|
||||
ihdr[7] = height & 0xFF;
|
||||
ihdr[8] = 8; // 8 bits per sample (24 bits per pixel)
|
||||
ihdr[9] = 2; // PNG_COLOR_TYPE_RGB
|
||||
ihdr[10] = 0; // PNG_COMPRESSION_TYPE_DEFAULT
|
||||
ihdr[11] = 0; // PNG_FILTER_TYPE_DEFAULT
|
||||
ihdr[12] = 0; // PNG_INTERLACE_NONE
|
||||
writePNGChunk(out, "IHDR", ihdr, 13);
|
||||
|
||||
// Compress the data with zlib
|
||||
uLongf compmemsize = (uLongf)((height * (width + 1) * 3 * 1.001 + 1) + 12);
|
||||
compmem = new uInt8[compmemsize];
|
||||
if(compmem == NULL ||
|
||||
(compress(compmem, &compmemsize, buffer, height * (width * 3 + 1)) != Z_OK))
|
||||
throw "ERROR: Couldn't compress PNG";
|
||||
|
||||
// Write the compressed framebuffer data
|
||||
writePNGChunk(out, "IDAT", compmem, compmemsize);
|
||||
|
||||
// Add some info about this snapshot
|
||||
ostringstream text;
|
||||
text << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
|
||||
<< BSPF_ARCH << "]";
|
||||
|
||||
writePNGText(out, "Software", text.str());
|
||||
writePNGText(out, "ROM Name", props.get(Cartridge_Name));
|
||||
writePNGText(out, "ROM MD5", props.get(Cartridge_MD5));
|
||||
writePNGText(out, "TV Effects", effectsInfo);
|
||||
|
||||
// Finish up
|
||||
writePNGChunk(out, "IEND", 0, 0);
|
||||
|
||||
// Clean up
|
||||
if(buffer) delete[] buffer;
|
||||
if(compmem) delete[] compmem;
|
||||
out.close();
|
||||
|
||||
return "Snapshot saved";
|
||||
}
|
||||
catch(const char* msg)
|
||||
{
|
||||
if(buffer) delete[] buffer;
|
||||
if(compmem) delete[] compmem;
|
||||
out.close();
|
||||
return msg;
|
||||
}
|
||||
// Cleanup
|
||||
done:
|
||||
if(png_ptr)
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
if(image)
|
||||
delete[] image;
|
||||
if(err_message)
|
||||
throw err_message;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -308,7 +326,7 @@ bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
|
||||
void PNGLibrary::scaleImagetoSurface(FBSurface& surface)
|
||||
{
|
||||
// Figure out the original zoom level of the snapshot
|
||||
// All snapshots generated by Stella are at most some multiple of 320
|
||||
|
@ -345,7 +363,7 @@ void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
|
|||
uInt32* l_ptr = ReadInfo.line;
|
||||
for(uInt32 icol = 0; icol < ReadInfo.width; icol += izoom, i_ptr += i_offset)
|
||||
{
|
||||
uInt32 pixel = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
|
||||
uInt32 pixel = myFB.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
|
||||
uInt32 xstride = szoom;
|
||||
while(xstride--)
|
||||
*l_ptr++ = pixel;
|
||||
|
@ -359,50 +377,30 @@ void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PNGLibrary::writePNGChunk(ofstream& out, const char* type,
|
||||
uInt8* data, int size)
|
||||
void PNGLibrary::writeComments(png_structp png_ptr, png_infop info_ptr,
|
||||
const Properties& props)
|
||||
{
|
||||
// Stuff the length/type into the buffer
|
||||
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];
|
||||
// Pre-processor voodoo to make the code shorter
|
||||
#define CONVERT_TO_PNGTEXT(_idx, _key, _text) \
|
||||
char key##_idx[] = _key; \
|
||||
char text##_idx[256]; \
|
||||
strncpy(text##_idx, _text.c_str(), 255); \
|
||||
text_ptr[_idx].key = key##_idx; \
|
||||
text_ptr[_idx].text = text##_idx; \
|
||||
text_ptr[_idx].compression = PNG_TEXT_COMPRESSION_NONE; \
|
||||
text_ptr[_idx].text_length = 0;
|
||||
|
||||
// Write the header
|
||||
out.write((const char*)temp, 8);
|
||||
ostringstream version;
|
||||
version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
|
||||
<< BSPF_ARCH << "]";
|
||||
|
||||
// Append the actual data
|
||||
uInt32 crc = crc32(0, temp + 4, 4);
|
||||
if(size > 0)
|
||||
{
|
||||
out.write((const char*)data, size);
|
||||
crc = crc32(crc, data, size);
|
||||
}
|
||||
png_text text_ptr[4];
|
||||
CONVERT_TO_PNGTEXT(0, "Software", version.str());
|
||||
CONVERT_TO_PNGTEXT(1, "ROM Name", props.get(Cartridge_Name));
|
||||
CONVERT_TO_PNGTEXT(2, "ROM MD5", props.get(Cartridge_MD5));
|
||||
CONVERT_TO_PNGTEXT(3, "TV Effects", myFB.tiaSurface().effectsInfo());
|
||||
|
||||
// Write the CRC
|
||||
temp[0] = crc >> 24;
|
||||
temp[1] = crc >> 16;
|
||||
temp[2] = crc >> 8;
|
||||
temp[3] = crc;
|
||||
out.write((const char*)temp, 4);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PNGLibrary::writePNGText(ofstream& out, const string& key, const string& text)
|
||||
{
|
||||
int length = key.length() + 1 + text.length() + 1;
|
||||
uInt8* data = new uInt8[length];
|
||||
|
||||
strcpy((char*)data, key.c_str());
|
||||
strcpy((char*)data + key.length() + 1, text.c_str());
|
||||
|
||||
writePNGChunk(out, "tEXt", data, length-1);
|
||||
delete[] data;
|
||||
png_set_text(png_ptr, info_ptr, text_ptr, 4);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -40,7 +40,7 @@ class TIA;
|
|||
class PNGLibrary
|
||||
{
|
||||
public:
|
||||
PNGLibrary();
|
||||
PNGLibrary(const FrameBuffer& fb);
|
||||
virtual ~PNGLibrary();
|
||||
|
||||
/**
|
||||
|
@ -48,39 +48,44 @@ class PNGLibrary
|
|||
scaling the image to the surface bounds.
|
||||
|
||||
@param filename The filename to load the PNG image
|
||||
@param fb The main framebuffer of the application
|
||||
@param surface The FBSurface into which to place the PNG data
|
||||
|
||||
@return On success, the FBSurface containing image data and a
|
||||
result of true, otherwise a const char* exception is thrown
|
||||
containing a more detailed error message.
|
||||
@return On success, the FBSurface containing image data, otherwise a
|
||||
const char* exception is thrown containing a more detailed
|
||||
error message.
|
||||
*/
|
||||
bool loadImage(const string& filename, const FrameBuffer& fb, FBSurface& surface);
|
||||
void loadImage(const string& filename, FBSurface& surface);
|
||||
|
||||
/**
|
||||
Save the current TIA image to a PNG file using data from the Framebuffer.
|
||||
Any postprocessing/filtering will be included.
|
||||
Save the current FrameBuffer image to a PNG file. Note that in most cases
|
||||
this will be a TIA image, but it could actually be used for *any* mode.
|
||||
|
||||
@param filename The filename to save the PNG image
|
||||
@param framebuffer The framebuffer containing the image data
|
||||
@param props The properties object containing info about the ROM
|
||||
@param filename The filename to save the PNG image
|
||||
@param props The properties object containing info about the ROM
|
||||
|
||||
@return On success, the PNG file has been saved to 'filename',
|
||||
otherwise a const char* exception is thrown containing a
|
||||
more detailed error message.
|
||||
*/
|
||||
string saveImage(const string& filename, const FrameBuffer& framebuffer,
|
||||
const Properties& props);
|
||||
void saveImage(const string& filename, const Properties& props);
|
||||
|
||||
/**
|
||||
Save the current TIA image to a PNG file using data directly from
|
||||
the TIA framebuffer. No filtering or scaling will be included.
|
||||
the internal TIA buffer. No filtering or scaling will be included.
|
||||
|
||||
@param filename The filename to save the PNG image
|
||||
@param framebuffer The framebuffer containing the image data
|
||||
@param mediasrc Source of the raw TIA data
|
||||
@param props The properties object containing info about the ROM
|
||||
@param filename The filename to save the PNG image
|
||||
@param tia Source of the raw TIA data
|
||||
@param props The properties object containing info about the ROM
|
||||
|
||||
@return On success, the PNG file has been saved to 'filename',
|
||||
otherwise a const char* exception is thrown containing a
|
||||
more detailed error message.
|
||||
*/
|
||||
string saveImage(const string& filename, const FrameBuffer& framebuffer,
|
||||
const TIA& tia, const Properties& props);
|
||||
void saveImage(const string& filename, const TIA& tia, const Properties& props);
|
||||
|
||||
private:
|
||||
const FrameBuffer& myFB;
|
||||
|
||||
// The following data remains between invocations of allocateStorage,
|
||||
// and is only changed when absolutely necessary.
|
||||
typedef struct {
|
||||
|
@ -100,6 +105,9 @@ class PNGLibrary
|
|||
The method fills the 'ReadInfo' struct with valid memory locations
|
||||
dependent on the given dimensions. If memory has been previously
|
||||
allocated and it can accommodate the given dimensions, it is used directly.
|
||||
|
||||
@param iwidth The width of the PNG image
|
||||
@param iheight The height of the PNG image
|
||||
*/
|
||||
bool allocateStorage(png_uint_32 iwidth, png_uint_32 iheight);
|
||||
|
||||
|
@ -107,18 +115,17 @@ class PNGLibrary
|
|||
Scale the PNG data from 'ReadInfo' into the FBSurface. For now, scaling
|
||||
is done on integer boundaries only (ie, 1x, 2x, etc up or down).
|
||||
|
||||
@param fb The main framebuffer of the application
|
||||
@param surface The FBSurface into which to place the PNG data
|
||||
@param surface The FBSurface into which to place the PNG data
|
||||
*/
|
||||
void scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface);
|
||||
void scaleImagetoSurface(FBSurface& surface);
|
||||
|
||||
string saveBufferToPNG(ofstream& out, uInt8* buffer,
|
||||
uInt32 width, uInt32 height,
|
||||
const Properties& props,
|
||||
const string& effectsInfo);
|
||||
void writePNGChunk(ofstream& out, const char* type, uInt8* data, int size);
|
||||
void writePNGText(ofstream& out, const string& key, const string& text);
|
||||
/**
|
||||
Write PNG tEXt chunks to the image.
|
||||
*/
|
||||
void writeComments(png_structp png_ptr, png_infop info_ptr,
|
||||
const Properties& props);
|
||||
|
||||
/** PNG library callback functions */
|
||||
static void png_read_data(png_structp ctx, png_bytep area, png_size_t size);
|
||||
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size);
|
||||
static void png_io_flush(png_structp ctx);
|
||||
|
|
|
@ -771,8 +771,7 @@ int PromptWidget::vprintf(const char *format, va_list argptr)
|
|||
void PromptWidget::putchar(int c)
|
||||
{
|
||||
putcharIntern(c);
|
||||
|
||||
setDirty(); draw(); // FIXME - not nice to redraw the full console just for one char!
|
||||
setDirty(); draw();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -153,7 +153,7 @@ void EventHandler::reset(State state)
|
|||
// We wait a little while, since 'hold' events may be present, and we want
|
||||
// time for the ROM to process them
|
||||
if(state == S_EMULATE)
|
||||
SDL_AddTimer(500, resetEventsCallback, (void*)this); //FIXSDL
|
||||
SDL_AddTimer(500, resetEventsCallback, (void*)this);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1907,26 +1907,39 @@ void EventHandler::takeSnapshot(uInt32 number)
|
|||
// Now create a PNG snapshot
|
||||
if(myOSystem.settings().getBool("ss1x"))
|
||||
{
|
||||
string msg =
|
||||
myOSystem.png().saveImage(filename, myOSystem.frameBuffer(),
|
||||
myOSystem.console().tia(),
|
||||
myOSystem.console().properties());
|
||||
string message = "Snapshot saved";
|
||||
try
|
||||
{
|
||||
myOSystem.png().saveImage(filename, myOSystem.console().tia(),
|
||||
myOSystem.console().properties());
|
||||
}
|
||||
catch(const char* msg)
|
||||
{
|
||||
message = msg;
|
||||
}
|
||||
|
||||
if(showmessage)
|
||||
myOSystem.frameBuffer().showMessage(msg);
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure we have a 'clean' image, with no onscreen messages
|
||||
myOSystem.frameBuffer().enableMessages(false);
|
||||
|
||||
string msg =
|
||||
myOSystem.png().saveImage(filename, myOSystem.frameBuffer(),
|
||||
myOSystem.console().properties());
|
||||
string message = "Snapshot saved";
|
||||
try
|
||||
{
|
||||
myOSystem.png().saveImage(filename, myOSystem.console().properties());
|
||||
}
|
||||
catch(const char* msg)
|
||||
{
|
||||
message = msg;
|
||||
}
|
||||
|
||||
// Re-enable old messages
|
||||
myOSystem.frameBuffer().enableMessages(true);
|
||||
if(showmessage)
|
||||
myOSystem.frameBuffer().showMessage(msg);
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -535,14 +535,6 @@ void FrameBuffer::refresh()
|
|||
case EventHandler::S_PAUSE:
|
||||
invalidate();
|
||||
drawTIA();
|
||||
#if 0 // FIXME: eliminate stuttering in TIA mode; do we really need this?
|
||||
if(isDoubleBuffered())
|
||||
{
|
||||
postFrameUpdate();
|
||||
invalidate();
|
||||
drawTIA();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EventHandler::S_MENU:
|
||||
|
|
|
@ -352,15 +352,16 @@ class FrameBuffer
|
|||
virtual bool isDoubleBuffered() const = 0;
|
||||
|
||||
/**
|
||||
This method is called to get the specified scanline data from the
|
||||
viewable FrameBuffer area. Note that this isn't the same as any
|
||||
internal surfaces that may be in use; it should return the actual
|
||||
data as it is currently seen onscreen.
|
||||
This method is called to get the specified ARGB data from the viewable
|
||||
FrameBuffer area. Note that this isn't the same as any internal
|
||||
surfaces that may be in use; it should return the actual data as it
|
||||
is currently seen onscreen.
|
||||
|
||||
@param row The row we are looking for
|
||||
@param data The actual pixel data (in bytes)
|
||||
@param rect The bounding rectangle for the buffer
|
||||
@param buffer The actual pixel data in ARGB8888 format
|
||||
@param pitch The pitch (in bytes) for the pixel data
|
||||
*/
|
||||
virtual void scanline(uInt32 row, uInt8* data) const = 0;
|
||||
virtual void readPixels(const GUI::Rect& rect, uInt8* buffer, uInt32 pitch) const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
|
@ -219,7 +219,7 @@ bool OSystem::create()
|
|||
Random::setSystem(this);
|
||||
|
||||
// Create PNG handler
|
||||
myPNGLib = new PNGLibrary();
|
||||
myPNGLib = new PNGLibrary(*myFrameBuffer);
|
||||
|
||||
// Create ZIP handler
|
||||
myZipHandler = new ZipHandler();
|
||||
|
|
|
@ -99,7 +99,7 @@ void RomInfoWidget::parseProperties()
|
|||
|
||||
// Initialize to empty properties entry
|
||||
mySurfaceErrorMsg = "";
|
||||
mySurfaceIsValid = false;
|
||||
mySurfaceIsValid = true;
|
||||
myRomInfo.clear();
|
||||
|
||||
// Get a valid filename representing a snapshot file for this rom
|
||||
|
@ -109,8 +109,7 @@ void RomInfoWidget::parseProperties()
|
|||
// Read the PNG file
|
||||
try
|
||||
{
|
||||
mySurfaceIsValid =
|
||||
instance().png().loadImage(filename, instance().frameBuffer(), *mySurface);
|
||||
instance().png().loadImage(filename, *mySurface);
|
||||
}
|
||||
catch(const char* msg)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue