[Scummvm-cvs-logs] scummvm master -> 05eb6eecfce5a049a100872ba4a4fb414cf913a5

lordhoto lordhoto at gmail.com
Tue Aug 21 02:13:17 CEST 2012


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
af05b1b80e GRAPHICS: Reimplement the PNG-decoder using libpng
05eb6eecfc Merge pull request #262 from somaen/pngwithlibpng


Commit: af05b1b80edfc8674845d6e42c9872fea9eeb381
    https://github.com/scummvm/scummvm/commit/af05b1b80edfc8674845d6e42c9872fea9eeb381
Author: Einar Johan Trøan Sømåen (einarjohants at gmail.com)
Date: 2012-08-20T12:14:59-07:00

Commit Message:
GRAPHICS: Reimplement the PNG-decoder using libpng

Changed paths:
    configure
    graphics/decoders/png.cpp
    graphics/decoders/png.h



diff --git a/configure b/configure
index 06492ff..f98c1c9 100755
--- a/configure
+++ b/configure
@@ -97,7 +97,7 @@ _sndio=auto
 _timidity=auto
 _zlib=auto
 _sparkle=auto
-_png=no
+_png=auto
 _theoradec=auto
 _faad=auto
 _fluidsynth=auto
diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp
index 492c697..f047189 100644
--- a/graphics/decoders/png.cpp
+++ b/graphics/decoders/png.cpp
@@ -19,87 +19,23 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  */
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/scummsys.h"
+
+#ifdef USE_PNG
+#include <png.h>
+#endif
 
 #include "graphics/decoders/png.h"
 
 #include "graphics/pixelformat.h"
 #include "graphics/surface.h"
 
-#include "common/endian.h"
-#include "common/memstream.h"
 #include "common/stream.h"
-#include "common/types.h"
-#include "common/util.h"
-#include "common/zlib.h"
-
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-/*
-LodePNG version 20101211
-
-Copyright (c) 2005-2010 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-    1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgment in the product documentation would be
-    appreciated but is not required.
-
-    2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as being the original software.
-
-    3. This notice may not be removed or altered from any source
-    distribution.
-*/
 
 namespace Graphics {
 
-enum PNGChunks {
-	// == Critical chunks =====================================================
-	kChunkIHDR = MKTAG('I','H','D','R'),	// Image header
-	kChunkIDAT = MKTAG('I','D','A','T'),	// Image data
-	kChunkPLTE = MKTAG('P','L','T','E'),	// Palette
-	kChunkIEND = MKTAG('I','E','N','D'),	// Image trailer
-	// == Ancillary chunks ====================================================
-	kChunktRNS = MKTAG('t','R','N','S')	// 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
-};
-
-PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0),
-			_transparentColorSpecified(false), _outputSurface(0), _paletteEntries(0) {
+PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0) {
 }
 
 PNGDecoder::~PNGDecoder() {
@@ -112,16 +48,43 @@ void PNGDecoder::destroy() {
 		delete _outputSurface;
 		_outputSurface = 0;
 	}
+	delete[] _palette;
+	_palette = NULL;
+}
+
+#ifdef USE_PNG
+// libpng-error-handling:
+void pngError(png_structp pngptr, png_const_charp errorMsg) {
+	error("%s", errorMsg);
+}
 
-	_paletteEntries = 0;
+void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
+	warning("%s", warningMsg);
 }
 
+// libpng-I/O-helper:
+void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
+	void *readIOptr = png_get_io_ptr(pngPtr);
+	Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
+	stream->read(data, length);
+}
+#endif
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
 bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
+#ifdef USE_PNG
 	destroy();
 
-	uint32 chunkLength = 0, chunkType = 0;
 	_stream = &stream;
-
+	
 	// First, check the PNG signature
 	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
 		delete _stream;
@@ -132,374 +95,143 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &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 = (byte *)malloc(_compressedBufferSize);
-				_stream->read(_compressedBuffer, chunkLength);
-			} else {
-				// Expand the buffer
-				uint32 prevSize = _compressedBufferSize;
-				_compressedBufferSize += chunkLength;
-				byte *tmp = new byte[prevSize];
-				memcpy(tmp, _compressedBuffer, prevSize);
-				free(_compressedBuffer);
-				_compressedBuffer = (byte *)malloc(_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;
-			_stream->read(_palette, _paletteEntries * 3);
-			memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency));
-			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
+	// The following is based on the guide provided in:
+	//http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3
+	//http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
+	// along with the png-loading code used in the sword25-engine.
+	png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+	if (!pngPtr) {
+		delete _stream;
+		return false;
 	}
-
-	// We no longer need the file stream, thus close it here
-	_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 PNGDecoder::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 PNGDecoder::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");
+	png_infop infoPtr = png_create_info_struct(pngPtr);
+	if (!infoPtr) {
+		png_destroy_read_struct(&pngPtr, NULL, NULL);
+		delete _stream;
+		return false;
 	}
-
-}
-
-int PNGDecoder::getBytesPerPixel() const {
-	return (getNumColorChannels() * _header.bitDepth + 7) / 8;
-}
-
-void PNGDecoder::constructImage() {
-	assert (_header.bitDepth != 0);
-
-	int bytesPerPixel = getBytesPerPixel();
-	int pitch = bytesPerPixel * _header.width;
-	byte *unfilteredSurface = new byte[pitch * _header.height];
-	byte *dest = unfilteredSurface;
-	uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8;
-	byte *scanLine = new byte[scanLineWidth];
-	byte *prevLine = 0;
-
-	switch(_header.interlaceType) {
-	case kNonInterlaced:
-		for (uint16 y = 0; y < _header.height; y++) {
-			byte filterType = _imageData->readByte();
-			_imageData->read(scanLine, scanLineWidth);
-			unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth);
-			prevLine = dest;
-			dest += 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;
+	png_infop endInfo = png_create_info_struct(pngPtr);
+	if (!endInfo) {
+		png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+		delete _stream;
+		return false;
 	}
 
-	delete[] scanLine;
+	png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
+	// TODO: The manual says errors should be handled via setjmp
 
-	constructOutput(unfilteredSurface);
-	delete[] unfilteredSurface;
-}
+	png_set_read_fn(pngPtr, _stream, pngReadFromStream);
+	png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE);
+	// We already verified the PNG-header
+	png_set_sig_bytes(pngPtr, 8);
 
-Graphics::PixelFormat PNGDecoder::findPixelFormat() const {
-	// Try to find the best pixel format based on what we have here
-	// Which is basically 8bpp for paletted non-transparent
-	// and 32bpp for everything else
+	// Read PNG header
+	png_read_info(pngPtr, infoPtr);
 
-	switch (_header.colorType) {
-	case kIndexed:
-		if (!_transparentColorSpecified)
-			return Graphics::PixelFormat::createFormatCLUT8();
-		// fall through
-	case kGrayScale:
-	case kTrueColor:
-	case kGrayScaleWithAlpha:
-	case kTrueColorWithAlpha:
-		// We'll go with standard RGBA 32-bit
-		return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
-	}
-
-	error("Unknown PNG color type");
-	return Graphics::PixelFormat();
-}
+	// No handling for unknown chunks yet.
+	int bitDepth, colorType, width, height, interlaceType;
+	png_uint_32 w, h;
+	png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL);
+	width = w;
+	height = h;
 
-void PNGDecoder::constructOutput(const byte *surface) {
+	// Allocate memory for the final image data.
+	// To keep memory framentation low this happens before allocating memory for temporary image data.
 	_outputSurface = new Graphics::Surface();
-	_outputSurface->create(_header.width, _header.height, findPixelFormat());
 
-	const byte *src = surface;
-	byte a = 0xFF;
-	int bytesPerPixel = getBytesPerPixel();
-
-	if (_header.colorType != kIndexed) {
-		if (_header.colorType == kTrueColor || 
-			_header.colorType == kTrueColorWithAlpha) {
-			if (bytesPerPixel != 3 && bytesPerPixel != 4)
-				error("Unsupported truecolor PNG format");
-		} else if (_header.colorType == kGrayScale ||
-				   _header.colorType == kGrayScaleWithAlpha) {
-			if (bytesPerPixel != 1 && bytesPerPixel != 2)
-				error("Unsupported grayscale PNG format");
+	// Images of all color formats except PNG_COLOR_TYPE_PALETTE
+	// will be transformed into ARGB images
+	if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
+		int numPalette = 0;
+		png_colorp palette = NULL;
+		uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette);
+		if (success != PNG_INFO_PLTE) {
+			png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+			return false;
 		}
-
-		for (uint16 i = 0; i < _outputSurface->h; i++) {
-			for (uint16 j = 0; j < _outputSurface->w; j++) {
-				uint32 result = 0;
-
-				switch (bytesPerPixel) {
-				case 1:	// Grayscale
-					if (_transparentColorSpecified)
-						a = (src[0] == _transparentColor[0]) ? 0 : 0xFF;
-					result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]);
-					break;
-				case 2: // Grayscale + alpha
-					result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]);
-					break;
-				case 3: // RGB
-					if (_transparentColorSpecified) {
-						bool isTransparentColor = (src[0] == _transparentColor[0] &&
-												   src[1] == _transparentColor[1] &&
-												   src[2] == _transparentColor[2]);
-						a = isTransparentColor ? 0 : 0xFF;
-					}
-
-					result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]);
-					break;
-				case 4: // RGBA
-					result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]);
-					break;
-				}
-
-				*((uint32 *)_outputSurface->getBasePtr(j, i)) = result;
-				src += bytesPerPixel;
-			}
+		_paletteColorCount = numPalette;
+		_palette = new byte[_paletteColorCount * 3];
+		for (int i = 0; i < _paletteColorCount; i++) {
+			_palette[(i * 3)] = palette[i].red;
+			_palette[(i * 3) + 1] = palette[i].green;
+			_palette[(i * 3) + 2] = palette[i].blue;
+			
 		}
+		_outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+		png_set_packing(pngPtr);
 	} else {
-		uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth);
-
-		// Convert the indexed surface to the target pixel format
-		for (uint16 i = 0; i < _outputSurface->h; i++) {
-			int data = 0;
-			int bitCount = 8;
-			const byte *src1 = src;
-
-			for (uint16 j = 0; j < _outputSurface->w; j++) {
-				if (bitCount == 8) {
-					data = *src;
-					src++;
-				}
-
-				byte index = (data & mask) >> (8 - _header.bitDepth);
-				data = (data << _header.bitDepth) & 0xff;
-				bitCount -= _header.bitDepth;
-
-				if (bitCount == 0)
-					bitCount = 8;
-
-				if (_transparentColorSpecified) {
-					byte r = _palette[index * 3 + 0];
-					byte g = _palette[index * 3 + 1];
-					byte b = _palette[index * 3 + 2];
-					a = _paletteTransparency[index];
-					*((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b);
-				} else {
-					*((byte *)_outputSurface->getBasePtr(j, i)) = index;
-				}
-			}
-
-			src = src1 + _outputSurface->w;
+		_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+		if (!_outputSurface->pixels) {
+			error("Could not allocate memory for output image.");
 		}
-	}
-}
+		if (bitDepth == 16)
+			png_set_strip_16(pngPtr);
+		if (bitDepth < 8)
+			png_set_expand(pngPtr);
+		if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))
+			png_set_expand(pngPtr);
+		if (colorType == PNG_COLOR_TYPE_GRAY ||
+			colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+			png_set_gray_to_rgb(pngPtr);
+		
+		// PNGs are Big-Endian:
+#ifdef SCUMM_LITTLE_ENDIAN
+		png_set_bgr(pngPtr);
+		png_set_swap_alpha(pngPtr);
+		if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
+			png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE);
+#else
+		if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
+			png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
+#endif
 
-void PNGDecoder::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 PNGDecoder::getNumColorChannels() const {
-	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");
 	}
-}
+	
+	// After the transformations have been registered, the image data is read again.
+	png_read_update_info(pngPtr, infoPtr);
+	png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
+	width = w;
+	height = h;
+
+	if (interlaceType == PNG_INTERLACE_NONE) {
+		// PNGs without interlacing can simply be read row by row.
+		for (int i = 0; i < height; i++) {
+			png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL);
+		}
+	} else {
+		// PNGs with interlacing require us to allocate an auxillary
+		// buffer with pointers to all row starts.
+		
+		// Allocate row pointer buffer
+		png_bytep *rowPtr = new png_bytep[height];
+		if (!rowPtr) {
+			error("Could not allocate memory for row pointers.");
+		}
+		
+		// Initialize row pointers
+		for (int i = 0; i < height; i++)
+			rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i);
+		
+		// Read image data
+		png_read_image(pngPtr, rowPtr);
+		
+		// Free row pointer buffer
+		delete[] rowPtr;
+	}
+	
+	// Read additional data at the end.
+	png_read_end(pngPtr, NULL);
 
-void PNGDecoder::readTransparencyChunk(uint32 chunkLength) {
-	_transparentColorSpecified = true;
+	// Destroy libpng structures
+	png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
 
-	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:
-		_stream->read(_paletteTransparency, chunkLength);
+	// We no longer need the file stream, thus close it here
+	_stream = 0;
 
-		// 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");
-	}
+	return true;
+#else
+	return false;
+#endif
 }
 
 } // End of Graphics namespace
diff --git a/graphics/decoders/png.h b/graphics/decoders/png.h
index ca204f6..e52ddab 100644
--- a/graphics/decoders/png.h
+++ b/graphics/decoders/png.h
@@ -24,33 +24,12 @@
  * PNG decoder used in engines:
  *  - sword25
  * Dependencies:
- *  - zlib
+ *  - libpng
  */
 
 #ifndef GRAPHICS_PNG_H
 #define GRAPHICS_PNG_H
 
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-// 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.
-
 #include "common/scummsys.h"
 #include "common/textconsole.h"
 #include "graphics/decoders/image_decoder.h"
@@ -73,62 +52,13 @@ public:
 	void destroy();
 	const Graphics::Surface *getSurface() const { return _outputSurface; }
 	const byte *getPalette() const { return _palette; }
-	uint16 getPaletteColorCount() const { return _paletteEntries; }
-
+	uint16 getPaletteColorCount() const { return _paletteColorCount; }
 private:
-	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;
-	};
-
-	void readHeaderChunk();
-	byte getNumColorChannels() const;
-
-	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 * 3];	// RGB
-	byte _paletteTransparency[256];
-	uint16 _paletteEntries;
-	uint16 _transparentColor[3];
-	bool _transparentColorSpecified;
-
-	byte *_compressedBuffer;
-	uint32 _compressedBufferSize;
+	byte *_palette;
+	uint16 _paletteColorCount;
 
 	Graphics::Surface *_outputSurface;
-	Graphics::PixelFormat findPixelFormat() const;
-	int getBytesPerPixel() const;
-	void constructOutput(const byte *surface);
 };
 
 } // End of namespace Graphics


Commit: 05eb6eecfce5a049a100872ba4a4fb414cf913a5
    https://github.com/scummvm/scummvm/commit/05eb6eecfce5a049a100872ba4a4fb414cf913a5
Author: Johannes Schickel (lordhoto at gmail.com)
Date: 2012-08-20T17:12:52-07:00

Commit Message:
Merge pull request #262 from somaen/pngwithlibpng

GRAPHICS: Reimplement the PNG-decoder using libpng

Changed paths:
    configure
    graphics/decoders/png.cpp
    graphics/decoders/png.h









More information about the Scummvm-git-logs mailing list