diff options
-rw-r--r-- | engines/sword25/gfx/image/pngloader.cpp | 51 | ||||
-rw-r--r-- | engines/sword25/gfx/image/renderedimage.cpp | 88 | ||||
-rw-r--r-- | engines/sword25/gfx/screenshot.cpp | 71 |
3 files changed, 88 insertions, 122 deletions
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp index 64bb9fef6a..6f370d8861 100644 --- a/engines/sword25/gfx/image/pngloader.cpp +++ b/engines/sword25/gfx/image/pngloader.cpp @@ -46,51 +46,6 @@ namespace Sword25 { -/** - * Load a NULL-terminated string from the given stream. - */ -static Common::String loadString(Common::ReadStream &in, uint maxSize = 999) { - Common::String result; - - while (!in.eos() && (result.size() < maxSize)) { - char ch = (char)in.readByte(); - if (ch == '\0') - break; - - result += ch; - } - - return result; -} - -/** - * Check if the given data is a savegame, and if so, locate the - * offset to the image data. - * @return offset to image data if fileDataPtr contains a savegame; 0 otherwise - */ -static uint findEmbeddedPNG(const byte *fileDataPtr, uint fileSize) { - if (fileSize < 100) - return 0; - if (memcmp(fileDataPtr, "BS25SAVEGAME", 12)) - return 0; - - // Read in the header - Common::MemoryReadStream stream(fileDataPtr, fileSize); - stream.seek(0, SEEK_SET); - - // Read header information of savegame - uint compressedGamedataSize; - loadString(stream); // Marker - loadString(stream); // Version - loadString(stream); // Description - Common::String gameSize = loadString(stream); - compressedGamedataSize = atoi(gameSize.c_str()); - loadString(stream); - - // Return the offset of where the thumbnail starts - return static_cast<uint>(stream.pos() + compressedGamedataSize); -} - #ifndef USE_INTERNAL_PNG_DECODER static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { const byte **ref = (const byte **)png_get_io_ptr(png_ptr); @@ -232,8 +187,7 @@ bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&unc } bool PNGLoader::decodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) { - uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize); - return doDecodeImage(fileDataPtr + pngOffset, fileSize - pngOffset, uncompressedDataPtr, width, height, pitch); + return doDecodeImage(fileDataPtr, fileSize, uncompressedDataPtr, width, height, pitch); } #ifndef USE_INTERNAL_PNG_DECODER @@ -280,8 +234,7 @@ bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &w } bool PNGLoader::imageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) { - uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize); - return doImageProperties(fileDataPtr + pngOffset, fileSize - pngOffset, width, height); + return doImageProperties(fileDataPtr, fileSize, width, height); } #else diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp index ba858e88d4..44d32f03f7 100644 --- a/engines/sword25/gfx/image/renderedimage.cpp +++ b/engines/sword25/gfx/image/renderedimage.cpp @@ -54,6 +54,75 @@ static Common::String generateSavegameFilename(uint slotID) { // CONSTRUCTION / DESTRUCTION // ----------------------------------------------------------------------------- +/** + * Load a NULL-terminated string from the given stream. + */ +static Common::String loadString(Common::SeekableReadStream &in, uint maxSize = 999) { + Common::String result; + + while (!in.eos() && (result.size() < maxSize)) { + char ch = (char)in.readByte(); + if (ch == '\0') + break; + + result += ch; + } + + return result; +} + +static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSize, bool &isPNG) { + byte *pFileData; + Common::SaveFileManager *sfm = g_system->getSavefileManager(); + int slotNum = atoi(filename.c_str() + filename.size() - 3); + Common::InSaveFile *file = sfm->openForLoading(generateSavegameFilename(slotNum)); + + // Seek to the actual PNG image + loadString(*file); // Marker (BS25SAVEGAME) + loadString(*file); // Version + loadString(*file); // Description + uint32 compressedGamedataSize = atoi(loadString(*file).c_str()); + loadString(*file); // Uncompressed game data size + file->skip(compressedGamedataSize); // Skip the game data and move to the thumbnail itself + uint32 thumbnailStart = file->pos(); + + fileSize = file->size() - thumbnailStart; + + // Check if the thumbnail is in our own format, or a PNG file. + uint32 header = file->readUint32BE(); + isPNG = (header != MKTAG('S','C','R','N')); + file->seek(-4, SEEK_CUR); + + pFileData = new byte[fileSize]; + file->read(pFileData, fileSize); + delete file; + + return pFileData; +} + +// TODO: Move this method into a more generic image loading class, together with the PNG reading code +static bool decodeThumbnail(const byte *pFileData, uint fileSize, byte *&pUncompressedData, int &width, int &height, int &pitch) { + const byte *src = pFileData + 4; // skip header + width = READ_LE_UINT16(src); src += 2; + height = READ_LE_UINT16(src); src += 2; + pitch = width * 4; + + uint32 totalSize = pitch * height; + pUncompressedData = new byte[totalSize]; + uint32 *dst = (uint32 *)pUncompressedData; // treat as uint32, for pixelformat output + const Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24); + byte r, g, b; + + for (uint32 i = 0; i < totalSize / 4; i++) { + r = *src++; + g = *src++; + b = *src++; + *dst++ = format.RGBToColor(r, g, b); + } + + return true; +} + RenderedImage::RenderedImage(const Common::String &filename, bool &result) : _data(0), _width(0), @@ -69,15 +138,10 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) : byte *pFileData; uint fileSize; + bool isPNG = true; + if (filename.hasPrefix("/saves")) { - // A savegame thumbnail - Common::SaveFileManager *sfm = g_system->getSavefileManager(); - int slotNum = atoi(filename.c_str() + filename.size() - 3); - Common::InSaveFile *file = sfm->openForLoading(generateSavegameFilename(slotNum)); - fileSize = file->size(); - pFileData = new byte[fileSize]; - file->read(pFileData, fileSize); - delete file; + pFileData = readSavegameThumbnail(filename, fileSize, isPNG); } else { pFileData = pPackage->getFile(filename, &fileSize); } @@ -98,7 +162,12 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) : // Uncompress the image int pitch; - if (!PNGLoader::decodeImage(pFileData, fileSize, _data, _width, _height, pitch)) { + if (isPNG) + result = PNGLoader::decodeImage(pFileData, fileSize, _data, _width, _height, pitch); + else + result = decodeThumbnail(pFileData, fileSize, _data, _width, _height, pitch); + + if (!result) { error("Could not decode image."); delete[] pFileData; return; @@ -109,7 +178,6 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) : _doCleanup = true; - result = true; return; } diff --git a/engines/sword25/gfx/screenshot.cpp b/engines/sword25/gfx/screenshot.cpp index a5516d051f..bfe732a596 100644 --- a/engines/sword25/gfx/screenshot.cpp +++ b/engines/sword25/gfx/screenshot.cpp @@ -36,83 +36,28 @@ #include "common/textconsole.h" #include "sword25/gfx/screenshot.h" #include "sword25/kernel/filesystemutil.h" -#include <png.h> namespace Sword25 { -#include "common/pack-start.h" -struct RGB_PIXEL { - byte red; - byte green; - byte blue; -} PACKED_STRUCT; -#include "common/pack-end.h" - -void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) { - static_cast<Common::WriteStream *>(png_get_io_ptr(png_ptr))->write(data, length); -} - -void userFlushFn(png_structp png_ptr) { -} - bool Screenshot::saveToFile(Graphics::Surface *data, Common::WriteStream *stream) { - // Reserve buffer space - RGB_PIXEL *pixelBuffer = new RGB_PIXEL[data->w * data->h]; - // Convert the RGBA data to RGB const byte *pSrc = (const byte *)data->getBasePtr(0, 0); - RGB_PIXEL *pDest = pixelBuffer; + + // Write our own custom header + stream->writeUint32BE(MKTAG('S','C','R','N')); // SCRN, short for "Screenshot" + stream->writeUint16LE(data->w); + stream->writeUint16LE(data->h); for (uint y = 0; y < data->h; y++) { for (uint x = 0; x < data->w; x++) { uint32 srcPixel = READ_LE_UINT32(pSrc); pSrc += sizeof(uint32); - pDest->red = (srcPixel >> 16) & 0xff; - pDest->green = (srcPixel >> 8) & 0xff; - pDest->blue = srcPixel & 0xff; - ++pDest; + stream->writeByte((srcPixel >> 16) & 0xff); // R + stream->writeByte((srcPixel >> 8) & 0xff); // G + stream->writeByte(srcPixel & 0xff); // B } } - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - error("Could not create PNG write-struct."); - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - error("Could not create PNG info-struct."); - - // The compression buffer must be large enough to the entire image. - // This ensures that only an IDAT chunk is created. - // When buffer size is used 110% of the raw data size to be sure. - png_set_compression_buffer_size(png_ptr, (data->w * data->h * 3 * 110) / 100); - - // Initialise PNG-Info structure - png_set_IHDR(png_ptr, info_ptr, - data->w, // Width - data->h, // Height - 8, // Bits depth - PNG_COLOR_TYPE_RGB, // Color type - PNG_INTERLACE_NONE, // No interlacing - PNG_COMPRESSION_TYPE_DEFAULT, // Compression type - PNG_FILTER_TYPE_DEFAULT); // Filter Type - - // Rowpointer erstellen - png_bytep *rowPointers = new png_bytep[data->h]; - for (uint i = 0; i < data->h; i++) { - rowPointers[i] = (png_bytep)&pixelBuffer[data->w * i]; - } - png_set_rows(png_ptr, info_ptr, &rowPointers[0]); - - // Write out the png data to the file - png_set_write_fn(png_ptr, (void *)stream, userWriteFn, userFlushFn); - png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); - - png_destroy_write_struct(&png_ptr, &info_ptr); - - delete[] pixelBuffer; - delete[] rowPointers; - return true; } |