Reorganization of the PNG handling; it didn't make sense to have loading

one class and saving in another.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2588 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2013-01-28 20:39:50 +00:00
parent 266f6c47c1
commit b0db10e969
10 changed files with 250 additions and 293 deletions

View File

@ -17,11 +17,17 @@
// $Id$
//============================================================================
#include <zlib.h>
#include <fstream>
#include <cstring>
#include <sstream>
#include <cmath>
#include "FrameBuffer.hxx"
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "Props.hxx"
#include "TIA.hxx"
#include "Version.hxx"
#include "PNGLibrary.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -38,7 +44,7 @@ PNGLibrary::~PNGLibrary()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::readImage(const string& filename,
bool PNGLibrary::loadImage(const string& filename,
const FrameBuffer& fb, FBSurface& surface)
{
#define readImageERROR(s) { err_message = s; goto done; }
@ -125,6 +131,140 @@ done:
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveImage(const string& filename,
const FrameBuffer& framebuffer,
const Properties& props)
{
ofstream out(filename.c_str(), ios_base::binary);
if(!out.is_open())
return "ERROR: Couldn't create snapshot 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];
// 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
}
return saveBufferToPNG(out, buffer, width, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveImage(const string& filename,
const FrameBuffer& framebuffer, 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";
uInt32 width = tia.width(), height = tia.height();
uInt8* buffer = new uInt8[(width*3*2 + 1) * height];
// Fill the buffer with pixels from the mediasrc
uInt8 r, g, b;
uInt8* buf_ptr = buffer;
for(uInt32 y = 0; y < height; ++y)
{
*buf_ptr++ = 0; // first byte of row is filter type
for(uInt32 x = 0; x < width; ++x)
{
uInt32 pixel = framebuffer.tiaPixel(y*width+x);
framebuffer.getRGB(pixel, &r, &g, &b);
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
}
}
return saveBufferToPNG(out, buffer, width << 1, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveBufferToPNG(ofstream& out, uInt8* buffer,
uInt32 width, uInt32 height,
const Properties& props,
const string& effectsInfo)
{
uInt8* compmem = (uInt8*) NULL;
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;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
{
@ -218,6 +358,53 @@ void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::writePNGChunk(ofstream& out, const char* type,
uInt8* data, int size)
{
// 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];
// Write the header
out.write((const char*)temp, 8);
// 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);
}
// 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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
{

View File

@ -24,7 +24,10 @@
class FrameBuffer;
class FBSurface;
class Properties;
class TIA;
#include <fstream>
#include "bspf.hxx"
/**
@ -38,13 +41,13 @@ class PNGLibrary
{
public:
PNGLibrary();
~PNGLibrary();
virtual ~PNGLibrary();
/**
Read a PNG image from the specified file into a FBSurface structure,
scaling the image to the surface bounds.
@param filename The filename to load from
@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
@ -52,7 +55,30 @@ class PNGLibrary
result of true, otherwise a const char* exception is thrown
containing a more detailed error message.
*/
bool readImage(const string& filename, const FrameBuffer& fb, FBSurface& surface);
bool loadImage(const string& filename, const FrameBuffer& fb, FBSurface& surface);
/**
Save the current TIA image to a PNG file using data from the Framebuffer.
Any postprocessing/filtering will be included.
@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
*/
string saveImage(const string& filename, const FrameBuffer& framebuffer,
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.
@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
*/
string saveImage(const string& filename, const FrameBuffer& framebuffer,
const TIA& tia, const Properties& props);
private:
// The following data remains between invocations of allocateStorage,
@ -86,6 +112,13 @@ class PNGLibrary
*/
void scaleImagetoSurface(const FrameBuffer& fb, 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);
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);

View File

@ -1,209 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2013 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#include <zlib.h>
#include <fstream>
#include <cstring>
#include <sstream>
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "Props.hxx"
#include "TIA.hxx"
#include "Version.hxx"
#include "Snapshot.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Snapshot::savePNG(const FrameBuffer& framebuffer, const Properties& props,
const string& filename)
{
ofstream out(filename.c_str(), ios_base::binary);
if(!out.is_open())
return "ERROR: Couldn't create snapshot 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];
// 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
}
return saveBufferToPNG(out, buffer, width, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Snapshot::savePNG(const FrameBuffer& framebuffer, const TIA& tia,
const Properties& props, const string& filename)
{
ofstream out(filename.c_str(), ios_base::binary);
if(!out.is_open())
return "ERROR: Couldn't create snapshot file";
uInt32 width = tia.width(), height = tia.height();
uInt8* buffer = new uInt8[(width*3*2 + 1) * height];
// Fill the buffer with pixels from the mediasrc
uInt8 r, g, b;
uInt8* buf_ptr = buffer;
for(uInt32 y = 0; y < height; ++y)
{
*buf_ptr++ = 0; // first byte of row is filter type
for(uInt32 x = 0; x < width; ++x)
{
uInt32 pixel = framebuffer.tiaPixel(y*width+x);
framebuffer.getRGB(pixel, &r, &g, &b);
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
}
}
return saveBufferToPNG(out, buffer, width << 1, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Snapshot::saveBufferToPNG(ofstream& out, uInt8* buffer,
uInt32 width, uInt32 height,
const Properties& props,
const string& effectsInfo)
{
uInt8* compmem = (uInt8*) NULL;
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;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::writePNGChunk(ofstream& out, const char* type,
uInt8* data, int size)
{
// 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];
// Write the header
out.write((const char*)temp, 8);
// 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);
}
// 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 Snapshot::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;
}

View File

@ -1,65 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2013 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#ifndef SNAPSHOT_HXX
#define SNAPSHOT_HXX
class Properties;
class FrameBuffer;
class TIA;
#include <fstream>
#include "bspf.hxx"
class Snapshot
{
public:
/**
Save the current TIA image to a PNG file using data from the Framebuffer.
Any postprocessing/filtering will be included.
@param framebuffer The framebuffer containing the image data
@param props The properties object containing info about the ROM
@param filename The filename of the PNG file
*/
static string savePNG(const FrameBuffer& framebuffer, const Properties& props,
const string& filename);
/**
Save the current TIA image to a PNG file using data directly from
the TIA framebuffer. No filtering or scaling will be included.
@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 of the PNG file
*/
static string savePNG(const FrameBuffer& framebuffer, const TIA& tia,
const Properties& props, const string& filename);
private:
static string saveBufferToPNG(ofstream& out, uInt8* buffer,
uInt32 width, uInt32 height,
const Properties& props,
const string& effectsInfo);
static void writePNGChunk(ofstream& out, const char* type, uInt8* data, int size);
static void writePNGText(ofstream& out, const string& key, const string& text);
};
#endif

View File

@ -9,8 +9,7 @@ MODULE_OBJS := \
src/common/FBSurfaceTIA.o \
src/common/PNGLibrary.o \
src/common/MouseControl.o \
src/common/RectList.o \
src/common/Snapshot.o
src/common/RectList.o
MODULE_DIRS += \
src/common

View File

@ -39,7 +39,6 @@
#include "ListWidget.hxx"
#include "ScrollBarWidget.hxx"
#include "Settings.hxx"
#include "Snapshot.hxx"
#include "Sound.hxx"
#include "StateManager.hxx"
#include "M6532.hxx"
@ -1986,9 +1985,10 @@ void EventHandler::takeSnapshot(uInt32 number)
// Now create a PNG snapshot
if(myOSystem->settings().getBool("ss1x"))
{
string msg = Snapshot::savePNG(myOSystem->frameBuffer(),
myOSystem->console().tia(),
myOSystem->console().properties(), filename);
string msg =
myOSystem->png().saveImage(filename, myOSystem->frameBuffer(),
myOSystem->console().tia(),
myOSystem->console().properties());
if(showmessage)
myOSystem->frameBuffer().showMessage(msg);
}
@ -1997,8 +1997,9 @@ void EventHandler::takeSnapshot(uInt32 number)
// Make sure we have a 'clean' image, with no onscreen messages
myOSystem->frameBuffer().enableMessages(false);
string msg = Snapshot::savePNG(myOSystem->frameBuffer(),
myOSystem->console().properties(), filename);
string msg =
myOSystem->png().saveImage(filename, myOSystem->frameBuffer(),
myOSystem->console().properties());
// Re-enable old messages
myOSystem->frameBuffer().enableMessages(true);

View File

@ -90,6 +90,7 @@ OSystem::OSystem()
myDebugger(NULL),
myCheatManager(NULL),
myStateManager(NULL),
myPNGLib(NULL),
myQuitLoop(false),
myRomFile(""),
myRomMD5(""),
@ -196,6 +197,7 @@ OSystem::~OSystem()
delete myEventHandler;
delete mySerialPort;
delete myPNGLib;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -304,6 +306,9 @@ bool OSystem::create()
// Let the random class know about us; it needs access to getTicks()
Random::setSystem(this);
// Create PNG handler
myPNGLib = new PNGLibrary();
return true;
}

View File

@ -41,6 +41,7 @@ namespace GUI {
#include "Array.hxx"
#include "FrameBuffer.hxx"
#include "PNGLibrary.hxx"
#include "bspf.hxx"
struct Resolution {
@ -172,6 +173,13 @@ class OSystem
*/
StateManager& state() const { return *myStateManager; }
/**
Get the PNG handler of the system.
@return The PNGlib object
*/
PNGLibrary& png() const { return *myPNGLib; }
/**
This method should be called to load the current settings from an rc file.
It first loads the settings from the config file, then informs subsystems
@ -557,6 +565,9 @@ class OSystem
// Pointer to the StateManager object
StateManager* myStateManager;
// PNG object responsible for loading/saving PNG images
PNGLibrary* myPNGLib;
// The list of log messages
string myLogMessages;

View File

@ -19,7 +19,6 @@
#include "FrameBuffer.hxx"
#include "OSystem.hxx"
#include "PNGLibrary.hxx"
#include "Settings.hxx"
#include "Widget.hxx"
@ -116,7 +115,7 @@ void RomInfoWidget::parseProperties()
try
{
mySurfaceIsValid =
myPNGLib.readImage(filename, instance().frameBuffer(), *mySurface);
instance().png().loadImage(filename, instance().frameBuffer(), *mySurface);
}
catch(const char* msg)
{

View File

@ -26,7 +26,6 @@
#include "Widget.hxx"
#include "Command.hxx"
#include "StringList.hxx"
#include "PNGLibrary.hxx"
#include "bspf.hxx"
@ -52,9 +51,6 @@ class RomInfoWidget : public Widget
FBSurface* mySurface;
int mySurfaceID;
// PNG object responsible for actually loading the PNG image
PNGLibrary myPNGLib;
// How much to zoom the PNG image
int myZoomLevel;