diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index 3c7608101..191d5bb76 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -25,32 +25,32 @@ #include "PNGLibrary.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PNGLibrary::PNGLibrary(const string& filename) - : myFilename(filename) +PNGLibrary::PNGLibrary() { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PNGLibrary::~PNGLibrary() { + delete[] ReadInfo.buffer; + delete[] ReadInfo.line; + delete[] ReadInfo.row_pointers; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool PNGLibrary::readImage(const FrameBuffer& fb, FBSurface& surface) - throw(const char*) +bool PNGLibrary::readImage(const string& filename, + const FrameBuffer& fb, FBSurface& surface) { #define readImageERROR(s) { err_message = s; goto done; } png_structp png_ptr = NULL; png_infop info_ptr = NULL; - png_bytep* row_pointers = NULL; - png_uint_32 iwidth, iheight, ipitch; - uInt8* buffer = NULL; + png_uint_32 iwidth, iheight; int bit_depth, color_type, interlace_type; const char* err_message = NULL; - ifstream* in = new ifstream(myFilename.c_str(), ios_base::binary); - if(!in || !in->is_open()) + ifstream in(filename.c_str(), ios_base::binary); + if(!in.is_open()) readImageERROR("No image found"); // 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"); // 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 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"); } - // Create space for the entire image (3 bytes per pixel in RGB format) - ipitch = iwidth * 3; - buffer = new uInt8[ipitch * iheight]; - if(buffer == NULL) + // Create/initialize storage area for the current image + if(!allocateStorage(iwidth, iheight)) readImageERROR("Not enough memory to read PNG file"); // 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 < iheight; ++irow, offset += ipitch) - row_pointers[irow] = (png_bytep) (uInt8*)buffer + offset; + for(uInt32 irow = 0, offset = 0; irow < ReadInfo.height; ++irow, offset += ReadInfo.pitch) + ReadInfo.row_pointers[irow] = (png_bytep) (uInt8*)ReadInfo.buffer + offset; // Read the entire image in one go - png_read_image(png_ptr, row_pointers); - delete[] row_pointers; + png_read_image(png_ptr, ReadInfo.row_pointers); // We're finished reading png_read_end(png_ptr, info_ptr); // Scale image to surface dimensions - scaleImagetoSurface(iwidth, iheight, buffer, fb, surface); + scaleImagetoSurface(fb, surface); // Cleanup done: if(png_ptr) 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) throw err_message; @@ -133,8 +126,49 @@ done: } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void PNGLibrary::scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffer, - const FrameBuffer& fb, FBSurface& surface) +bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h) +{ + // 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 // 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, // but since Stella only generates snapshots at up to 10x, we should // be fine for a while ... - uInt32 izoom = uInt32(ceil(iwidth/320.0)), + uInt32 izoom = uInt32(ceil(ReadInfo.width/320.0)), szoom = surface.getWidth()/320; - uInt32 sw = iwidth / izoom * szoom, - sh = iheight / izoom * szoom; + uInt32 sw = ReadInfo.width / izoom * szoom, + sh = ReadInfo.height / izoom * szoom; sw = BSPF_min(sw, surface.getWidth()); sh = BSPF_min(sh, surface.getHeight()); surface.setWidth(sw); surface.setHeight(sh); // Decompress the image, and scale it correctly - uInt32 ipitch = iwidth * 3; // bytes per line of the actual PNG image - uInt32* line = new uInt32[ipitch]; - - uInt32 buf_offset = ipitch * izoom; + uInt32 buf_offset = ReadInfo.pitch * izoom; uInt32 i_offset = 3 * izoom; // We can only scan at most the height of the image to the constraints of // 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 + uInt8* buffer = ReadInfo.buffer; for(uInt32 irow = 0, srow = 0; irow < iheight; irow += izoom, buffer += buf_offset) { // Scale the image data into the temporary line buffer uInt8* i_ptr = buffer; - uInt32* l_ptr = line; - for(uInt32 icol = 0; icol < iwidth; icol += izoom, i_ptr += i_offset) + 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 xstride = szoom; @@ -182,9 +214,8 @@ void PNGLibrary::scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffe // Then fill the surface with those bytes uInt32 ystride = szoom; 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; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PNGLibrary::ReadInfoType PNGLibrary::ReadInfo = { + NULL, NULL, 0, 0, 0, NULL, 0, 0, 0 +}; diff --git a/src/common/PNGLibrary.hxx b/src/common/PNGLibrary.hxx index 6cfbe5af6..a6c4ec271 100644 --- a/src/common/PNGLibrary.hxx +++ b/src/common/PNGLibrary.hxx @@ -37,44 +37,60 @@ class FBSurface; class PNGLibrary { public: - /** Access a PNG image with the given filename. */ - PNGLibrary(const string& filename); + PNGLibrary(); ~PNGLibrary(); /** - Read a PNG image from the previously specified file into a - FBSurface structure, scaling the image to the surface bounds. + Read a PNG image from the specified file into a FBSurface structure, + scaling the image to the surface bounds. - @param fb The main framebuffer of the application - @param surface The FBSurface into which to place the PNG data + @param filename The filename to load from + @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 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: + // 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). - @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 surface The FBSurface into which to place the PNG data */ - static void scaleImagetoSurface(uInt32 iwidth, uInt32 iheight, uInt8* buffer, - const FrameBuffer& fb, FBSurface& surface); + void scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface); 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); static void png_user_warn(png_structp ctx, png_const_charp str); static void png_user_error(png_structp ctx, png_const_charp str); - - private: - string myFilename; }; #endif diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index a74645920..d92fbb0e1 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -320,29 +320,29 @@ string Debugger::valueToString(int value, BaseFormat outputBase) const case kBASE_10: // base 10: 3 or 5 bytes (depending on value) if(value < 0x100) - snprintf(vToS_buf, 4, "%3d", value); + BSPF_snprintf(vToS_buf, 4, "%3d", value); else - snprintf(vToS_buf, 6, "%5d", value); + BSPF_snprintf(vToS_buf, 6, "%5d", value); break; case kBASE_16_1: // base 16: 1 byte wide - snprintf(vToS_buf, 2, "%1X", value); + BSPF_snprintf(vToS_buf, 2, "%1X", value); break; case kBASE_16_2: // base 16: 2 bytes wide - snprintf(vToS_buf, 3, "%02X", value); + BSPF_snprintf(vToS_buf, 3, "%02X", value); break; case kBASE_16_4: // base 16: 4 bytes wide - snprintf(vToS_buf, 5, "%04X", value); + BSPF_snprintf(vToS_buf, 5, "%04X", value); break; case kBASE_16: // base 16: 2, 4, 8 bytes (depending on value) default: if(value < 0x100) - snprintf(vToS_buf, 3, "%02X", value); + BSPF_snprintf(vToS_buf, 3, "%02X", value); else if(value < 0x10000) - snprintf(vToS_buf, 5, "%04X", value); + BSPF_snprintf(vToS_buf, 5, "%04X", value); else - snprintf(vToS_buf, 9, "%08X", value); + BSPF_snprintf(vToS_buf, 9, "%08X", value); break; } diff --git a/src/emucore/Thumbulator.cxx b/src/emucore/Thumbulator.cxx index 3d1490d53..0b688993c 100644 --- a/src/emucore/Thumbulator.cxx +++ b/src/emucore/Thumbulator.cxx @@ -46,7 +46,7 @@ Thumbulator::~Thumbulator() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Thumbulator::run( void ) throw(const string&) +string Thumbulator::run( void ) { reset(); for(;;) @@ -70,7 +70,6 @@ string Thumbulator::run( void ) throw(const string&) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg) - throw(const string&) { statusMsg << "Thumb ARM emulation fatal error: " << 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, const char* msg) - throw(const string&) { statusMsg << "Thumb ARM emulation fatal error: " << endl << opcode << "(" << HEX8 << v1 << "," << v2 << "), " << msg << endl; diff --git a/src/emucore/Thumbulator.hxx b/src/emucore/Thumbulator.hxx index 6642223d3..592161b75 100644 --- a/src/emucore/Thumbulator.hxx +++ b/src/emucore/Thumbulator.hxx @@ -66,14 +66,14 @@ class Thumbulator ~Thumbulator(); /** - Run the ARM code, and return when finished. An exception is thrown - in case of any fatal errors/aborts, containing the actual error, + Run the ARM code, and return when finished. A string exception is + thrown in case of any fatal errors/aborts, containing the actual error, and the contents of the registers at that point in time. @return The results of any debugging output (if enabled), otherwise an empty string */ - string run() throw(const string&); + string run(); private: uInt32 read_register ( uInt32 reg ); @@ -93,8 +93,12 @@ class Thumbulator void do_cflag_bit ( uInt32 x ); void do_vflag_bit ( uInt32 x ); - int fatalError(const char* opcode, uInt32 v1, const char* msg) throw(const string&); - int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg) throw(const string&); + // Throw a string exception containing an error referencing the given + // 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_regs( void ); int execute ( void ); diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index bfb5dd182..1918a8510 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -115,9 +115,8 @@ void RomInfoWidget::parseProperties() // Read the PNG file try { - PNGLibrary png(filename); mySurfaceIsValid = - png.readImage(instance().frameBuffer(), *mySurface); + myPNGLib.readImage(filename, instance().frameBuffer(), *mySurface); } catch(const char* msg) { diff --git a/src/gui/RomInfoWidget.hxx b/src/gui/RomInfoWidget.hxx index 376ff5220..500fcd2a9 100644 --- a/src/gui/RomInfoWidget.hxx +++ b/src/gui/RomInfoWidget.hxx @@ -26,6 +26,7 @@ #include "Widget.hxx" #include "Command.hxx" #include "StringList.hxx" +#include "PNGLibrary.hxx" #include "bspf.hxx" @@ -51,6 +52,9 @@ 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;