diff options
-rw-r--r-- | engines/sword25/gfx/image/pngloader.cpp | 37 | ||||
-rw-r--r-- | graphics/module.mk | 1 | ||||
-rw-r--r-- | graphics/png.cpp | 449 | ||||
-rw-r--r-- | graphics/png.h | 162 |
4 files changed, 647 insertions, 2 deletions
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp index cdff0012c2..f54b45254b 100644 --- a/engines/sword25/gfx/image/pngloader.cpp +++ b/engines/sword25/gfx/image/pngloader.cpp @@ -32,13 +32,23 @@ * */ +// Define to use ScummVM's PNG decoder, instead of libpng +#define USE_INTERNAL_PNG_DECODER + +#ifndef USE_INTERNAL_PNG_DECODER // Disable symbol overrides so that we can use png.h #define FORBIDDEN_SYMBOL_ALLOW_ALL +#endif #include "common/memstream.h" #include "sword25/gfx/image/image.h" #include "sword25/gfx/image/pngloader.h" +#ifndef USE_INTERNAL_PNG_DECODER #include <png.h> +#else +#include "graphics/pixelformat.h" +#include "graphics/png.h" +#endif namespace Sword25 { @@ -87,6 +97,7 @@ static uint findEmbeddedPNG(const byte *fileDataPtr, uint fileSize) { 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); memcpy(data, *ref, length); @@ -96,9 +107,10 @@ static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t l static bool doIsCorrectImageFormat(const byte *fileDataPtr, uint fileSize) { return (fileSize > 8) && png_check_sig(const_cast<byte *>(fileDataPtr), 8); } - +#endif bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) { +#ifndef USE_INTERNAL_PNG_DECODER png_structp png_ptr = NULL; png_infop info_ptr = NULL; @@ -202,7 +214,25 @@ bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&unc // Destroy libpng structures png_destroy_read_struct(&png_ptr, &info_ptr, NULL); +#else + Common::MemoryReadStream *fileStr = new Common::MemoryReadStream(fileDataPtr, fileSize, DisposeAfterUse::NO); + Graphics::PNG *png = new Graphics::PNG(); + if (!png->read(fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done + error("Error while reading PNG image"); + + Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24); + Graphics::Surface *pngSurface = png->getSurface(format); + width = pngSurface->w; + height = pngSurface->h; + uncompressedDataPtr = new byte[pngSurface->pitch * pngSurface->h]; + memcpy(uncompressedDataPtr, (byte *)pngSurface->pixels, pngSurface->pitch * pngSurface->h); + pngSurface->free(); + + delete pngSurface; + delete png; + +#endif // Signal success return true; } @@ -213,6 +243,7 @@ bool PNGLoader::decodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncom } bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) { +#ifndef USE_INTERNAL_PNG_DECODER // Check for valid PNG signature if (!doIsCorrectImageFormat(fileDataPtr, fileSize)) return false; @@ -249,7 +280,9 @@ bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &w // Die Strukturen freigeben png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - +#else + // We don't need to read the image properties here... +#endif return true; } diff --git a/graphics/module.mk b/graphics/module.mk index 827d2886f7..c962f0617d 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -15,6 +15,7 @@ MODULE_OBJS := \ imagedec.o \ jpeg.o \ pict.o \ + png.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ diff --git a/graphics/png.cpp b/graphics/png.cpp new file mode 100644 index 0000000000..cf19969b46 --- /dev/null +++ b/graphics/png.cpp @@ -0,0 +1,449 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "graphics/conversion.h" +#include "graphics/png.h" +#include "graphics/pixelformat.h" + +#include "common/endian.h" +#include "common/memstream.h" +#include "common/stream.h" +#include "common/util.h" +#include "common/zlib.h" + +namespace Graphics { + +enum PNGChunks { + // == Critical chunks ===================================================== + kChunkIHDR = MKID_BE('IHDR'), // Image header + kChunkIDAT = MKID_BE('IDAT'), // Image data + kChunkPLTE = MKID_BE('PLTE'), // Palette + kChunkIEND = MKID_BE('IEND'), // Image trailer + // == Ancillary chunks ==================================================== + kChunktRNS = MKID_BE('tRNS') // Transparency + // All of the other ancillary chunks are ignored. They're added here for + // reference only. + // cHRM - Primary chromacities and white point + // gAMA - Image gamma + // iCCP - Embedded ICC profile + // sBIT - Significant bits + // sRGB - Standard RGB color space + // tEXT - Textual data + // sTXt - Compressed textual data + // iTXt - International textual data + // bKGD - Background color + // hIST - Image histogram + // pHYs - Physical pixel dimensions + // sPLT - Suggested palette + // tIME - Image last-modification time +}; + +// Refer to http://www.w3.org/TR/PNG/#9Filters +enum PNGFilters { + kFilterNone = 0, + kFilterSub = 1, + kFilterUp = 2, + kFilterAverage = 3, + kFilterPaeth = 4 +}; + +#define PNG_HEADER(a, b, c, d) CONSTANT_LE_32(d | (c << 8) | (b << 16) | (a << 24)) + +PNG::PNG() : _compressedBuffer(0), _compressedBufferSize(0), + _unfilteredSurface(0), _transparentColorSpecified(false) { +} + +PNG::~PNG() { + if (_unfilteredSurface) { + _unfilteredSurface->free(); + delete _unfilteredSurface; + } +} + +Graphics::Surface *PNG::getSurface(const PixelFormat &format) { + Graphics::Surface *output = new Graphics::Surface(); + output->create(_unfilteredSurface->w, _unfilteredSurface->h, format.bytesPerPixel); + byte *src = (byte *)_unfilteredSurface->pixels; + byte a = 0xFF; + + if (_header.colorType != kIndexed) { + if (_header.colorType == kTrueColor || _header.colorType == kTrueColorWithAlpha) { + if (_unfilteredSurface->bytesPerPixel != 3 && _unfilteredSurface->bytesPerPixel != 4) + error("Unsupported truecolor PNG format"); + } else if (_header.colorType == kGrayScale || _header.colorType == kGrayScaleWithAlpha) { + if (_unfilteredSurface->bytesPerPixel != 1 && _unfilteredSurface->bytesPerPixel != 2) + error("Unsupported grayscale PNG format"); + } + + for (uint16 i = 0; i < output->h; i++) { + for (uint16 j = 0; j < output->w; j++) { + if (format.bytesPerPixel == 2) { + if (_unfilteredSurface->bytesPerPixel == 1) { // Grayscale + if (_transparentColorSpecified) + a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; + *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor( a, src[0], src[0], src[0]); + } else if (_unfilteredSurface->bytesPerPixel == 2) { // Grayscale + alpha + *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor(src[1], src[0], src[0], src[0]); + } else if (_unfilteredSurface->bytesPerPixel == 3) { // RGB + if (_transparentColorSpecified) { + bool isTransparentColor = (src[0] == _transparentColor[0] && + src[1] == _transparentColor[1] && + src[2] == _transparentColor[2]); + a = isTransparentColor ? 0 : 0xFF; + } + *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor( a, src[0], src[1], src[2]); + } else if (_unfilteredSurface->bytesPerPixel == 4) { // RGBA + *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor(src[3], src[0], src[1], src[2]); + } + } else { + if (_unfilteredSurface->bytesPerPixel == 1) { // Grayscale + if (_transparentColorSpecified) + a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; + *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor( a, src[0], src[0], src[0]); + } else if (_unfilteredSurface->bytesPerPixel == 2) { // Grayscale + alpha + *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor(src[1], src[0], src[0], src[0]); + } else if (_unfilteredSurface->bytesPerPixel == 3) { // RGB + if (_transparentColorSpecified) { + bool isTransparentColor = (src[0] == _transparentColor[0] && + src[1] == _transparentColor[1] && + src[2] == _transparentColor[2]); + a = isTransparentColor ? 0 : 0xFF; + } + *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor( a, src[0], src[1], src[2]); + } else if (_unfilteredSurface->bytesPerPixel == 4) { // RGBA + *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor(src[3], src[0], src[1], src[2]); + } + } + + src += _unfilteredSurface->bytesPerPixel; + } + } + } else { + byte index, r, g, b; + + // Convert the indexed surface to the target pixel format + for (uint16 i = 0; i < output->h; i++) { + for (uint16 j = 0; j < output->w; j++) { + index = *src; + r = _palette[index * 4 + 0]; + g = _palette[index * 4 + 1]; + b = _palette[index * 4 + 2]; + a = _palette[index * 4 + 3]; + + if (format.bytesPerPixel == 2) + *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b); + else + *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b); + + src++; + } + } + } + + return output; +} + +bool PNG::read(Common::SeekableReadStream *str) { + uint32 chunkLength = 0, chunkType = 0; + _stream = str; + + // First, check the PNG signature + if (_stream->readUint32BE() != PNG_HEADER(0x89, 0x50, 0x4e, 0x47)) { + delete _stream; + return false; + } + if (_stream->readUint32BE() != PNG_HEADER(0x0d, 0x0a, 0x1a, 0x0a)) { + delete _stream; + return false; + } + + // Start reading chunks till we reach an IEND chunk + while (chunkType != kChunkIEND) { + // The chunk length does not include the type or CRC bytes + chunkLength = _stream->readUint32BE(); + chunkType = _stream->readUint32BE(); + + switch (chunkType) { + case kChunkIHDR: + readHeaderChunk(); + break; + case kChunkIDAT: + if (_compressedBufferSize == 0) { + _compressedBufferSize += chunkLength; + _compressedBuffer = new byte[_compressedBufferSize]; + _stream->read(_compressedBuffer, chunkLength); + } else { + // Expand the buffer + uint32 prevSize = _compressedBufferSize; + _compressedBufferSize += chunkLength; + byte *tmp = new byte[prevSize]; + memcpy(tmp, _compressedBuffer, prevSize); + delete[] _compressedBuffer; + _compressedBuffer = new byte[_compressedBufferSize]; + memcpy(_compressedBuffer, tmp, prevSize); + delete[] tmp; + _stream->read(_compressedBuffer + prevSize, chunkLength); + } + break; + case kChunkPLTE: // only available in indexed PNGs + if (_header.colorType != kIndexed) + error("A palette chunk has been found in a non-indexed PNG file"); + if (chunkLength % 3 != 0) + error("Palette chunk not divisible by 3"); + _paletteEntries = chunkLength / 3; + readPaletteChunk(); + break; + case kChunkIEND: + // End of stream + break; + case kChunktRNS: + readTransparencyChunk(chunkLength); + break; + default: + // Skip the chunk content + _stream->skip(chunkLength); + break; + } + + if (chunkType != kChunkIEND) + _stream->skip(4); // skip the chunk CRC checksum + } + + // We no longer need the file stream, thus close it here + delete _stream; + _stream = 0; + + // Unpack the compressed buffer + Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES); + _imageData = Common::wrapCompressedReadStream(compData); + + // Construct the final image + constructImage(); + + // Close the uncompressed stream, which will also delete the memory stream, + // and thus the original compressed buffer + delete _imageData; + + return true; +} + +/** + * Paeth predictor, used by PNG filter type 4 + * The parameters are of signed 16-bit integers, but should come + * from unsigned chars. The integers are only needed to make + * the paeth calculation correct. + * + * Taken from lodePNG, with a slight patch: + * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx + */ +byte PNG::paethPredictor(int16 a, int16 b, int16 c) { + int16 pa = ABS<int16>(b - c); + int16 pb = ABS<int16>(a - c); + int16 pc = ABS<int16>(a + b - c - c); + + if (pa <= MIN<int16>(pb, pc)) + return (byte)a; + else if (pb <= pc) + return (byte)b; + else + return (byte)c; +} + +/** + * Unfilters a filtered PNG scan line. + * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters + * Note that filters are always applied to bytes + * + * Taken from lodePNG + */ +void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { + uint16 i; + + switch (filterType) { + case kFilterNone: // no change + for (i = 0; i < length; i++) + dest[i] = scanLine[i]; + break; + case kFilterSub: // add the bytes to the left + for (i = 0; i < byteWidth; i++) + dest[i] = scanLine[i]; + for (i = byteWidth; i < length; i++) + dest[i] = scanLine[i] + dest[i - byteWidth]; + break; + case kFilterUp: // add the bytes of the above scanline + if (prevLine) { + for (i = 0; i < length; i++) + dest[i] = scanLine[i] + prevLine[i]; + } else { + for (i = 0; i < length; i++) + dest[i] = scanLine[i]; + } + break; + case kFilterAverage: // average value of the left and top left + if (prevLine) { + for (i = 0; i < byteWidth; i++) + dest[i] = scanLine[i] + prevLine[i] / 2; + for (i = byteWidth; i < length; i++) + dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2); + } else { + for (i = 0; i < byteWidth; i++) + dest[i] = scanLine[i]; + for (i = byteWidth; i < length; i++) + dest[i] = scanLine[i] + dest[i - byteWidth] / 2; + } + break; + case kFilterPaeth: // Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth + if (prevLine) { + for(i = 0; i < byteWidth; i++) + dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i] + for(i = byteWidth; i < length; i++) + dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth])); + } else { + for(i = 0; i < byteWidth; i++) + dest[i] = scanLine[i]; + for(i = byteWidth; i < length; i++) + dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth] + } + break; + default: + error("Unknown line filter"); + } + +} + +void PNG::constructImage() { + assert (_header.bitDepth != 0); + + byte *dest; + byte *scanLine; + byte *prevLine = 0; + byte filterType; + uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8; + + if (_unfilteredSurface) { + _unfilteredSurface->free(); + delete _unfilteredSurface; + } + _unfilteredSurface = new Graphics::Surface(); + _unfilteredSurface->create(_header.width, _header.height, (getNumColorChannels() * _header.bitDepth + 7) / 8); + scanLine = new byte[_unfilteredSurface->pitch]; + dest = (byte *)_unfilteredSurface->getBasePtr(0, 0); + + switch(_header.interlaceType) { + case kNonInterlaced: + for (uint16 y = 0; y < _unfilteredSurface->h; y++) { + filterType = _imageData->readByte(); + _imageData->read(scanLine, scanLineWidth); + unfilterScanLine(dest, scanLine, prevLine, _unfilteredSurface->bytesPerPixel, filterType, scanLineWidth); + prevLine = dest; + dest += _unfilteredSurface->pitch; + } + break; + case kInterlaced: + // Theoretically, this shouldn't be needed, as interlacing is only + // useful for web images. Interlaced PNG images require more complex + // handling, so unless having support for such images is needed, there + // is no reason to add support for them. + error("TODO: Support for interlaced PNG images"); + break; + } + + delete[] scanLine; +} + +void PNG::readHeaderChunk() { + _header.width = _stream->readUint32BE(); + _header.height = _stream->readUint32BE(); + _header.bitDepth = _stream->readByte(); + if (_header.bitDepth > 8) + error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)"); + _header.colorType = (PNGColorType)_stream->readByte(); + _header.compressionMethod = _stream->readByte(); + // Compression methods: http://www.w3.org/TR/PNG/#10Compression + // Only compression method 0 (deflate) is documented and supported + if (_header.compressionMethod != 0) + error("Unknown PNG compression method: %d", _header.compressionMethod); + _header.filterMethod = _stream->readByte(); + // Filter methods: http://www.w3.org/TR/PNG/#9Filters + // Only filter method 0 is documented and supported + if (_header.filterMethod != 0) + error("Unknown PNG filter method: %d", _header.filterMethod); + _header.interlaceType = (PNGInterlaceType)_stream->readByte(); +} + +byte PNG::getNumColorChannels() { + switch (_header.colorType) { + case kGrayScale: + return 1; // Gray + case kTrueColor: + return 3; // RGB + case kIndexed: + return 1; // Indexed + case kGrayScaleWithAlpha: + return 2; // Gray + Alpha + case kTrueColorWithAlpha: + return 4; // RGBA + default: + error("Unknown color type"); + } +} + +void PNG::readPaletteChunk() { + for (byte i = 0; i < _paletteEntries; i++) { + _palette[i * 4 + 0] = _stream->readByte(); // R + _palette[i * 4 + 1] = _stream->readByte(); // G + _palette[i * 4 + 2] = _stream->readByte(); // B + _palette[i * 4 + 3] = 0xFF; // Alpha, set in the tRNS chunk + } +} + +void PNG::readTransparencyChunk(uint32 chunkLength) { + _transparentColorSpecified = true; + + switch(_header.colorType) { + case kGrayScale: + _transparentColor[0] = _stream->readUint16BE(); + _transparentColor[1] = _transparentColor[0]; + _transparentColor[2] = _transparentColor[0]; + break; + case kTrueColor: + _transparentColor[0] = _stream->readUint16BE(); + _transparentColor[1] = _stream->readUint16BE(); + _transparentColor[2] = _stream->readUint16BE(); + break; + case kIndexed: + for (uint32 i = 0; i < chunkLength; i++) + _palette[i * 4 + 3] = _stream->readByte(); + // A transparency chunk may have less entries + // than the palette entries. The remaining ones + // are unmodified (set to 255). Check here: + // http://www.w3.org/TR/PNG/#11tRNS + break; + default: + error("Transparency chunk found in a PNG that has a separate transparency channel"); + } +} + +} // End of Graphics namespace diff --git a/graphics/png.h b/graphics/png.h new file mode 100644 index 0000000000..4de55d869b --- /dev/null +++ b/graphics/png.h @@ -0,0 +1,162 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* + * PNG decoder used in engines: + * - sword25 + */ + +#ifndef GRAPHICS_PNG_H +#define GRAPHICS_PNG_H + +#include "graphics/surface.h" + +// PNG decoder, based on the W3C specs: +// http://www.w3.org/TR/PNG/ +// and lodePNG: +// http://members.gamedev.net/lode/projects/LodePNG/ +// and the ysflight PNG decoder: +// http://homepage3.nifty.com/ysflight/pngdecoder/pngdecodere.html + +// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order + +// Note: At the moment, this decoder only supports non-interlaced images, and +// does not support truecolor/grayscale images with 16bit depth. +// +// Theoretically, interlaced images shouldn't be needed for games, as +// interlacing is only useful for images in websites. +// +// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can +// theoretically contain more than 16.7 millions of colors (the so-called "deep +// color" representation). In essence, each of the R, G, B and A components in +// them is specified with 2 bytes, instead of 1. However, the PNG specification +// always refers to color components with 1 byte each, so this part of the spec +// is a bit unclear. For now, these won't be supported, until a suitable sample +// is found. + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +enum PNGColorType { + kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16 + kTrueColor = 2, // bit depths: 8, 16 + kIndexed = 3, // bit depths: 1, 2, 4, 8 + kGrayScaleWithAlpha = 4, // bit depths: 8, 16 + kTrueColorWithAlpha = 6 // bit depths: 8, 16 +}; + +enum PNGInterlaceType { + kNonInterlaced = 0, + kInterlaced = 1 +}; + +struct PNGHeader { + uint32 width; + uint32 height; + byte bitDepth; + PNGColorType colorType; + byte compressionMethod; + byte filterMethod; + PNGInterlaceType interlaceType; +}; + +struct PixelFormat; + +class PNG { +public: + PNG(); + ~PNG(); + + /** + * Reads a PNG image from the specified stream + */ + bool read(Common::SeekableReadStream *str); + + /** + * Returns the information obtained from the PNG header. + */ + PNGHeader getHeader() const { return _header; } + + /** + * Returns the PNG image, formatted for the specified pixel format. + */ + Graphics::Surface *getSurface(const PixelFormat &format); + + /** + * Returns the indexed PNG8 image. Used for PNGs with an indexed 256 color + * palette, when they're shown on an 8-bit color screen, as no translation + * is taking place. + */ + Graphics::Surface *getIndexedSurface() { + if (_header.colorType != kIndexed) + error("Indexed surface requested for a non-indexed PNG"); + return _unfilteredSurface; + } + + /** + * Returns the palette of the specified PNG8 image + */ + void getPalette(byte *palette, byte &entries) { + if (_header.colorType != kIndexed) + error("Palette requested for a non-indexed PNG"); + palette = _palette; + entries = _paletteEntries; + } + +private: + void readHeaderChunk(); + byte getNumColorChannels(); + + void readPaletteChunk(); + void readTransparencyChunk(uint32 chunkLength); + + void constructImage(); + void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length); + byte paethPredictor(int16 a, int16 b, int16 c); + + // The original file stream + Common::SeekableReadStream *_stream; + // The unzipped image data stream + Common::SeekableReadStream *_imageData; + + PNGHeader _header; + + byte _palette[256 * 4]; // RGBA + byte _paletteEntries; + uint16 _transparentColor[3]; + bool _transparentColorSpecified; + + byte *_compressedBuffer; + uint32 _compressedBufferSize; + + Graphics::Surface *_unfilteredSurface; +}; + +} // End of Graphics namespace + +#endif // GRAPHICS_PNG_H |