diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index d0765c372..61928d965 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -17,11 +17,17 @@ // $Id$ //============================================================================ +#include #include +#include +#include #include -#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) { diff --git a/src/common/PNGLibrary.hxx b/src/common/PNGLibrary.hxx index 4e00d38ee..b77794255 100644 --- a/src/common/PNGLibrary.hxx +++ b/src/common/PNGLibrary.hxx @@ -24,7 +24,10 @@ class FrameBuffer; class FBSurface; +class Properties; +class TIA; +#include #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); diff --git a/src/common/Snapshot.cxx b/src/common/Snapshot.cxx deleted file mode 100644 index 2b3be7a7d..000000000 --- a/src/common/Snapshot.cxx +++ /dev/null @@ -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 -#include -#include -#include - -#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; -} diff --git a/src/common/Snapshot.hxx b/src/common/Snapshot.hxx deleted file mode 100644 index b4de94178..000000000 --- a/src/common/Snapshot.hxx +++ /dev/null @@ -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 -#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 diff --git a/src/common/module.mk b/src/common/module.mk index 9e91715f3..0c99bcf14 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -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 diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index aea84954a..0c2cc14fa 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -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); diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 5b73afb15..2e9792ced 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -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; } diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index 85fe1f357..1c877d451 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -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; diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 618f7eeaf..da0327953 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -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) { diff --git a/src/gui/RomInfoWidget.hxx b/src/gui/RomInfoWidget.hxx index edf2d802e..73f528679 100644 --- a/src/gui/RomInfoWidget.hxx +++ b/src/gui/RomInfoWidget.hxx @@ -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;