Removed some 'throw(...)' declarations, which are deprecated in the latest version

of the C++ standard.

Fixed bug when using snprintf; we must use BSPF_snprintf, since the function
has different names in different OS's.

Optimized loading of PNG images in the RomInfoWidget so that memory allocations
aren't continuously being done.  Basically, memory is allocated once and then
only re-allocated if a new image is larger than all previous ones.  This can
increase memory use slightly, as the image data stays around between image loads,
and it remembers the largest image loaded.  But if you images are all mostly the
same size, it should hurt memory usage too much.  And it really helps on certain
systems where repeated (re)-allocations can cause problems.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2261 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2011-07-03 21:52:33 +00:00
parent 377765f565
commit f549cf9661
7 changed files with 129 additions and 72 deletions

View File

@ -25,32 +25,32 @@
#include "PNGLibrary.hxx" #include "PNGLibrary.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::PNGLibrary(const string& filename) PNGLibrary::PNGLibrary()
: myFilename(filename)
{ {
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::~PNGLibrary() PNGLibrary::~PNGLibrary()
{ {
delete[] ReadInfo.buffer;
delete[] ReadInfo.line;
delete[] ReadInfo.row_pointers;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::readImage(const FrameBuffer& fb, FBSurface& surface) bool PNGLibrary::readImage(const string& filename,
throw(const char*) const FrameBuffer& fb, FBSurface& surface)
{ {
#define readImageERROR(s) { err_message = s; goto done; } #define readImageERROR(s) { err_message = s; goto done; }
png_structp png_ptr = NULL; png_structp png_ptr = NULL;
png_infop info_ptr = NULL; png_infop info_ptr = NULL;
png_bytep* row_pointers = NULL; png_uint_32 iwidth, iheight;
png_uint_32 iwidth, iheight, ipitch;
uInt8* buffer = NULL;
int bit_depth, color_type, interlace_type; int bit_depth, color_type, interlace_type;
const char* err_message = NULL; const char* err_message = NULL;
ifstream* in = new ifstream(myFilename.c_str(), ios_base::binary); ifstream in(filename.c_str(), ios_base::binary);
if(!in || !in->is_open()) if(!in.is_open())
readImageERROR("No image found"); readImageERROR("No image found");
// Create the PNG loading context structure // Create the PNG loading context structure
@ -65,7 +65,7 @@ bool PNGLibrary::readImage(const FrameBuffer& fb, FBSurface& surface)
readImageERROR("Couldn't create image information for PNG file"); readImageERROR("Couldn't create image information for PNG file");
// Set up the input control // Set up the input control
png_set_read_fn(png_ptr, in, png_read_data); png_set_read_fn(png_ptr, &in, png_read_data);
// Read PNG header info // Read PNG header info
png_read_info(png_ptr, info_ptr); png_read_info(png_ptr, info_ptr);
@ -97,34 +97,27 @@ bool PNGLibrary::readImage(const FrameBuffer& fb, FBSurface& surface)
readImageERROR("Unknown format in PNG image"); readImageERROR("Unknown format in PNG image");
} }
// Create space for the entire image (3 bytes per pixel in RGB format) // Create/initialize storage area for the current image
ipitch = iwidth * 3; if(!allocateStorage(iwidth, iheight))
buffer = new uInt8[ipitch * iheight];
if(buffer == NULL)
readImageERROR("Not enough memory to read PNG file"); readImageERROR("Not enough memory to read PNG file");
// The PNG read function expects an array of rows, not a single 1-D array // The PNG read function expects an array of rows, not a single 1-D array
row_pointers = new png_bytep[iheight]; for(uInt32 irow = 0, offset = 0; irow < ReadInfo.height; ++irow, offset += ReadInfo.pitch)
for(uInt32 irow = 0, offset = 0; irow < iheight; ++irow, offset += ipitch) ReadInfo.row_pointers[irow] = (png_bytep) (uInt8*)ReadInfo.buffer + offset;
row_pointers[irow] = (png_bytep) (uInt8*)buffer + offset;
// Read the entire image in one go // Read the entire image in one go
png_read_image(png_ptr, row_pointers); png_read_image(png_ptr, ReadInfo.row_pointers);
delete[] row_pointers;
// We're finished reading // We're finished reading
png_read_end(png_ptr, info_ptr); png_read_end(png_ptr, info_ptr);
// Scale image to surface dimensions // Scale image to surface dimensions
scaleImagetoSurface(iwidth, iheight, buffer, fb, surface); scaleImagetoSurface(fb, surface);
// Cleanup // Cleanup
done: done:
if(png_ptr) if(png_ptr)
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : (png_infopp)0, (png_infopp)0); png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : (png_infopp)0, (png_infopp)0);
if(in)
in->close();
delete[] buffer;
if(err_message) if(err_message)
throw err_message; throw err_message;
@ -133,8 +126,49 @@ done:
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffer, bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
const FrameBuffer& fb, FBSurface& surface) {
// Create space for the entire image (3 bytes per pixel in RGB format)
uInt32 req_buffer_size = w * h * 3;
if(req_buffer_size > ReadInfo.buffer_size)
{
delete[] ReadInfo.buffer;
ReadInfo.buffer = new uInt8[req_buffer_size];
if(ReadInfo.buffer == NULL)
return false;
ReadInfo.buffer_size = req_buffer_size;
}
uInt32 req_line_size = w * 3;
if(req_line_size > ReadInfo.line_size)
{
delete[] ReadInfo.line;
ReadInfo.line = new uInt32[req_line_size];
if(ReadInfo.line == NULL)
return false;
ReadInfo.line_size = req_line_size;
}
uInt32 req_row_size = h;
if(req_row_size > ReadInfo.row_size)
{
delete[] ReadInfo.row_pointers;
ReadInfo.row_pointers = new png_bytep[req_row_size];
if(ReadInfo.row_pointers == NULL)
return false;
ReadInfo.row_size = req_row_size;
}
ReadInfo.width = w;
ReadInfo.height = h;
ReadInfo.pitch = w * 3;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
{ {
// Figure out the original zoom level of the snapshot // Figure out the original zoom level of the snapshot
// All snapshots generated by Stella are at most some multiple of 320 // All snapshots generated by Stella are at most some multiple of 320
@ -144,34 +178,32 @@ void PNGLibrary::scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffe
// The following calculation will work up to approx. 16x zoom level, // The following calculation will work up to approx. 16x zoom level,
// but since Stella only generates snapshots at up to 10x, we should // but since Stella only generates snapshots at up to 10x, we should
// be fine for a while ... // be fine for a while ...
uInt32 izoom = uInt32(ceil(iwidth/320.0)), uInt32 izoom = uInt32(ceil(ReadInfo.width/320.0)),
szoom = surface.getWidth()/320; szoom = surface.getWidth()/320;
uInt32 sw = iwidth / izoom * szoom, uInt32 sw = ReadInfo.width / izoom * szoom,
sh = iheight / izoom * szoom; sh = ReadInfo.height / izoom * szoom;
sw = BSPF_min(sw, surface.getWidth()); sw = BSPF_min(sw, surface.getWidth());
sh = BSPF_min(sh, surface.getHeight()); sh = BSPF_min(sh, surface.getHeight());
surface.setWidth(sw); surface.setWidth(sw);
surface.setHeight(sh); surface.setHeight(sh);
// Decompress the image, and scale it correctly // Decompress the image, and scale it correctly
uInt32 ipitch = iwidth * 3; // bytes per line of the actual PNG image uInt32 buf_offset = ReadInfo.pitch * izoom;
uInt32* line = new uInt32[ipitch];
uInt32 buf_offset = ipitch * izoom;
uInt32 i_offset = 3 * izoom; uInt32 i_offset = 3 * izoom;
// We can only scan at most the height of the image to the constraints of // We can only scan at most the height of the image to the constraints of
// the surface height (some multiple of 256) // the surface height (some multiple of 256)
iheight = BSPF_min(iheight, izoom * 256); uInt32 iheight = BSPF_min((uInt32)ReadInfo.height, izoom * 256);
// Grab each non-duplicate row of data from the image // Grab each non-duplicate row of data from the image
uInt8* buffer = ReadInfo.buffer;
for(uInt32 irow = 0, srow = 0; irow < iheight; irow += izoom, buffer += buf_offset) for(uInt32 irow = 0, srow = 0; irow < iheight; irow += izoom, buffer += buf_offset)
{ {
// Scale the image data into the temporary line buffer // Scale the image data into the temporary line buffer
uInt8* i_ptr = buffer; uInt8* i_ptr = buffer;
uInt32* l_ptr = line; uInt32* l_ptr = ReadInfo.line;
for(uInt32 icol = 0; icol < iwidth; icol += izoom, i_ptr += i_offset) 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 = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
uInt32 xstride = szoom; uInt32 xstride = szoom;
@ -182,9 +214,8 @@ void PNGLibrary::scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffe
// Then fill the surface with those bytes // Then fill the surface with those bytes
uInt32 ystride = szoom; uInt32 ystride = szoom;
while(ystride--) while(ystride--)
surface.drawPixels(line, 0, srow++, sw); surface.drawPixels(ReadInfo.line, 0, srow++, sw);
} }
delete[] line;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -219,3 +250,8 @@ void PNGLibrary::png_user_error(png_structp ctx, png_const_charp str)
{ {
cout << "PNGLibrary error: " << str << endl; cout << "PNGLibrary error: " << str << endl;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::ReadInfoType PNGLibrary::ReadInfo = {
NULL, NULL, 0, 0, 0, NULL, 0, 0, 0
};

View File

@ -37,44 +37,60 @@ class FBSurface;
class PNGLibrary class PNGLibrary
{ {
public: public:
/** Access a PNG image with the given filename. */ PNGLibrary();
PNGLibrary(const string& filename);
~PNGLibrary(); ~PNGLibrary();
/** /**
Read a PNG image from the previously specified file into a Read a PNG image from the specified file into a FBSurface structure,
FBSurface structure, scaling the image to the surface bounds. scaling the image to the surface bounds.
@param fb The main framebuffer of the application @param filename The filename to load from
@param surface The FBSurface into which to place the PNG data @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 @return On success, the FBSurface containing image data and a
result of true, otherwise an exception is thrown. result of true, otherwise a const char* exception is thrown
containing a more detailed error message.
*/ */
bool readImage(const FrameBuffer& fb, FBSurface& surface) throw(const char*); bool readImage(const string& filename, const FrameBuffer& fb, FBSurface& surface);
private: private:
// The following data remains between invocations of allocateStorage,
// and is only changed when absolutely necessary.
typedef struct {
uInt8* buffer;
png_bytep* row_pointers;
png_uint_32 width, height, pitch;
uInt32* line;
uInt32 buffer_size, line_size, row_size;
} ReadInfoType;
static ReadInfoType ReadInfo;
/** /**
Scale the PNG data from 'buffer' into the FBSurface. For now, scaling Allocate memory for PNG read operations. This is used to provide a
basic memory manager, so that we don't constantly allocate and deallocate
memory for each image loaded.
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.
*/
bool allocateStorage(png_uint_32 iwidth, png_uint_32 iheight);
/**
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). is done on integer boundaries only (ie, 1x, 2x, etc up or down).
@param iwidth Width of the PNG data (3 bytes per pixel)
@param iheight Number of row of PNG data
@param buffer The PNG data
@param fb The main framebuffer of the application @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
*/ */
static void scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffer, void scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface);
const FrameBuffer& fb, FBSurface& surface);
static void png_read_data(png_structp ctx, png_bytep area, png_size_t size); 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_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx); static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str); static void png_user_warn(png_structp ctx, png_const_charp str);
static void png_user_error(png_structp ctx, png_const_charp str); static void png_user_error(png_structp ctx, png_const_charp str);
private:
string myFilename;
}; };
#endif #endif

View File

@ -320,29 +320,29 @@ string Debugger::valueToString(int value, BaseFormat outputBase) const
case kBASE_10: // base 10: 3 or 5 bytes (depending on value) case kBASE_10: // base 10: 3 or 5 bytes (depending on value)
if(value < 0x100) if(value < 0x100)
snprintf(vToS_buf, 4, "%3d", value); BSPF_snprintf(vToS_buf, 4, "%3d", value);
else else
snprintf(vToS_buf, 6, "%5d", value); BSPF_snprintf(vToS_buf, 6, "%5d", value);
break; break;
case kBASE_16_1: // base 16: 1 byte wide case kBASE_16_1: // base 16: 1 byte wide
snprintf(vToS_buf, 2, "%1X", value); BSPF_snprintf(vToS_buf, 2, "%1X", value);
break; break;
case kBASE_16_2: // base 16: 2 bytes wide case kBASE_16_2: // base 16: 2 bytes wide
snprintf(vToS_buf, 3, "%02X", value); BSPF_snprintf(vToS_buf, 3, "%02X", value);
break; break;
case kBASE_16_4: // base 16: 4 bytes wide case kBASE_16_4: // base 16: 4 bytes wide
snprintf(vToS_buf, 5, "%04X", value); BSPF_snprintf(vToS_buf, 5, "%04X", value);
break; break;
case kBASE_16: // base 16: 2, 4, 8 bytes (depending on value) case kBASE_16: // base 16: 2, 4, 8 bytes (depending on value)
default: default:
if(value < 0x100) if(value < 0x100)
snprintf(vToS_buf, 3, "%02X", value); BSPF_snprintf(vToS_buf, 3, "%02X", value);
else if(value < 0x10000) else if(value < 0x10000)
snprintf(vToS_buf, 5, "%04X", value); BSPF_snprintf(vToS_buf, 5, "%04X", value);
else else
snprintf(vToS_buf, 9, "%08X", value); BSPF_snprintf(vToS_buf, 9, "%08X", value);
break; break;
} }

View File

@ -46,7 +46,7 @@ Thumbulator::~Thumbulator()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Thumbulator::run( void ) throw(const string&) string Thumbulator::run( void )
{ {
reset(); reset();
for(;;) for(;;)
@ -70,7 +70,6 @@ string Thumbulator::run( void ) throw(const string&)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg) inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg)
throw(const string&)
{ {
statusMsg << "Thumb ARM emulation fatal error: " << endl statusMsg << "Thumb ARM emulation fatal error: " << endl
<< opcode << "(" << HEX8 << v1 << "), " << msg << endl; << opcode << "(" << HEX8 << v1 << "), " << msg << endl;
@ -81,7 +80,6 @@ inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* ms
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2, inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2,
const char* msg) const char* msg)
throw(const string&)
{ {
statusMsg << "Thumb ARM emulation fatal error: " << endl statusMsg << "Thumb ARM emulation fatal error: " << endl
<< opcode << "(" << HEX8 << v1 << "," << v2 << "), " << msg << endl; << opcode << "(" << HEX8 << v1 << "," << v2 << "), " << msg << endl;

View File

@ -66,14 +66,14 @@ class Thumbulator
~Thumbulator(); ~Thumbulator();
/** /**
Run the ARM code, and return when finished. An exception is thrown Run the ARM code, and return when finished. A string exception is
in case of any fatal errors/aborts, containing the actual error, thrown in case of any fatal errors/aborts, containing the actual error,
and the contents of the registers at that point in time. and the contents of the registers at that point in time.
@return The results of any debugging output (if enabled), @return The results of any debugging output (if enabled),
otherwise an empty string otherwise an empty string
*/ */
string run() throw(const string&); string run();
private: private:
uInt32 read_register ( uInt32 reg ); uInt32 read_register ( uInt32 reg );
@ -93,8 +93,12 @@ class Thumbulator
void do_cflag_bit ( uInt32 x ); void do_cflag_bit ( uInt32 x );
void do_vflag_bit ( uInt32 x ); void do_vflag_bit ( uInt32 x );
int fatalError(const char* opcode, uInt32 v1, const char* msg) throw(const string&); // Throw a string exception containing an error referencing the given
int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg) throw(const string&); // message and variables
// Note that the return value is never used in these methods
int fatalError(const char* opcode, uInt32 v1, const char* msg);
int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg);
void dump_counters ( void ); void dump_counters ( void );
void dump_regs( void ); void dump_regs( void );
int execute ( void ); int execute ( void );

View File

@ -115,9 +115,8 @@ void RomInfoWidget::parseProperties()
// Read the PNG file // Read the PNG file
try try
{ {
PNGLibrary png(filename);
mySurfaceIsValid = mySurfaceIsValid =
png.readImage(instance().frameBuffer(), *mySurface); myPNGLib.readImage(filename, instance().frameBuffer(), *mySurface);
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

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