diff options
| -rw-r--r-- | engines/sword25/gfx/image/imgloader.cpp | 13 | ||||
| -rw-r--r-- | graphics/decoders/png.cpp (renamed from graphics/png.cpp) | 290 | ||||
| -rw-r--r-- | graphics/decoders/png.h (renamed from graphics/png.h) | 113 | ||||
| -rw-r--r-- | graphics/module.mk | 4 | 
4 files changed, 202 insertions, 218 deletions
| diff --git a/engines/sword25/gfx/image/imgloader.cpp b/engines/sword25/gfx/image/imgloader.cpp index 1df0fba70c..e103626416 100644 --- a/engines/sword25/gfx/image/imgloader.cpp +++ b/engines/sword25/gfx/image/imgloader.cpp @@ -33,18 +33,19 @@  #include "sword25/gfx/image/image.h"  #include "sword25/gfx/image/imgloader.h"  #include "graphics/pixelformat.h" -#include "graphics/png.h" +#include "graphics/decoders/png.h"  namespace Sword25 {  bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {  	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 + +	Graphics::PNGDecoder png; +	if (!png.loadStream(*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); +	const Graphics::Surface *sourceSurface = png.getSurface(); +	Graphics::Surface *pngSurface = sourceSurface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), png.getPalette());  	width = pngSurface->w;  	height = pngSurface->h; @@ -53,7 +54,7 @@ bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&un  	pngSurface->free();  	delete pngSurface; -	delete png; +	delete fileStr;  	// Signal success  	return true; diff --git a/graphics/png.cpp b/graphics/decoders/png.cpp index 156c405780..b87b6fdc7a 100644 --- a/graphics/png.cpp +++ b/graphics/decoders/png.cpp @@ -20,7 +20,7 @@   *   */ -#include "graphics/png.h" +#include "graphics/decoders/png.h"  #include "graphics/pixelformat.h"  #include "graphics/surface.h" @@ -98,113 +98,27 @@ enum PNGFilters {  	kFilterPaeth   = 4  }; -PNG::PNG() : _compressedBuffer(0), _compressedBufferSize(0), -			_unfilteredSurface(0), _transparentColorSpecified(false) { +PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0), +			_transparentColorSpecified(false), _outputSurface(0) {  } -PNG::~PNG() { -	if (_unfilteredSurface) { -		_unfilteredSurface->free(); -		delete _unfilteredSurface; -	} +PNGDecoder::~PNGDecoder() { +	destroy();  } -Graphics::Surface *PNG::getSurface(const PixelFormat &format) { -	Graphics::Surface *output = new Graphics::Surface(); -	output->create(_unfilteredSurface->w, _unfilteredSurface->h, format); -	byte *src = (byte *)_unfilteredSurface->pixels; -	byte a = 0xFF; -	byte bpp = _unfilteredSurface->format.bytesPerPixel; - -	if (_header.colorType != kIndexed) { -		if (_header.colorType == kTrueColor ||  -			_header.colorType == kTrueColorWithAlpha) { -			if (bpp != 3 && bpp != 4) -				error("Unsupported truecolor PNG format"); -		} else if (_header.colorType == kGrayScale || -				   _header.colorType == kGrayScaleWithAlpha) { -			if (bpp != 1 && bpp != 2) -				error("Unsupported grayscale PNG format"); -		} - -		for (uint16 i = 0; i < output->h; i++) { -			for (uint16 j = 0; j < output->w; j++) { -				uint32 result = 0; - -				switch (bpp) { -				case 1:	// Grayscale -					if (_transparentColorSpecified) -						a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; -					result = format.ARGBToColor(    a, src[0], src[0], src[0]); -					break; -				case 2: // Grayscale + alpha -					result = 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 = format.ARGBToColor(     a, src[0], src[1], src[2]); -					break; -				case 4: // RGBA -					result = format.ARGBToColor(src[3], src[0], src[1], src[2]); -					break; -				} - -				if (format.bytesPerPixel == 2) 	// 2bpp -					*((uint16 *)output->getBasePtr(j, i)) = (uint16)result; -				else	// 4bpp -					*((uint32 *)output->getBasePtr(j, i)) = result; - -				src += bpp; -			} -		} -	} else { -		byte index, r, g, b; -		uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth); - -		// Convert the indexed surface to the target pixel format -		for (uint16 i = 0; i < output->h; i++) { -			int data = 0; -			int bitCount = 8; -			byte *src1 = src; - -			for (uint16 j = 0; j < output->w; j++) { -				if (bitCount == 8) { -					data = *src; -					src++; -				} - -				index = (data & mask) >> (8 - _header.bitDepth); -				data = (data << _header.bitDepth) & 0xff; -				bitCount -= _header.bitDepth; - -				if (bitCount == 0) -					bitCount = 8; - -				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 = src1 + output->w; -		} +void PNGDecoder::destroy() { +	if (_outputSurface) { +		_outputSurface->free(); +		delete _outputSurface; +		_outputSurface = 0;  	} - -	return output;  } -bool PNG::read(Common::SeekableReadStream *str) { +bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { +	destroy(); +  	uint32 chunkLength = 0, chunkType = 0; -	_stream = str; +	_stream = &stream;  	// First, check the PNG signature  	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) { @@ -249,8 +163,10 @@ bool PNG::read(Common::SeekableReadStream *str) {  				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(); +			_stream->read(_palette, _paletteEntries * 3); +			memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency));  			break;  		case kChunkIEND:  			// End of stream @@ -269,7 +185,6 @@ bool PNG::read(Common::SeekableReadStream *str) {  	}  	// We no longer need the file stream, thus close it here -	delete _stream;  	_stream = 0;  	// Unpack the compressed buffer @@ -295,7 +210,7 @@ bool PNG::read(Common::SeekableReadStream *str) {   * 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) { +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); @@ -315,7 +230,7 @@ byte PNG::paethPredictor(int16 a, int16 b, int16 c) {   *   * Taken from lodePNG   */ -void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { +void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) {  	uint16 i;  	switch (filterType) { @@ -370,33 +285,29 @@ void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLin  } -void PNG::constructImage() { +int PNGDecoder::getBytesPerPixel() const { +	return (getNumColorChannels() * _header.bitDepth + 7) / 8; +} + +void PNGDecoder::constructImage() {  	assert (_header.bitDepth != 0); -	byte *dest; -	byte *scanLine; -	byte *prevLine = 0; -	byte filterType; +	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; - -	if (_unfilteredSurface) { -		_unfilteredSurface->free(); -		delete _unfilteredSurface; -	} -	_unfilteredSurface = new Graphics::Surface(); -	// TODO/FIXME: It seems we can not properly determine the format here. But maybe there is a way... -	_unfilteredSurface->create(_header.width, _header.height, PixelFormat((getNumColorChannels() * _header.bitDepth + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0)); -	scanLine = new byte[_unfilteredSurface->pitch]; -	dest = (byte *)_unfilteredSurface->getBasePtr(0, 0); +	byte *scanLine = new byte[scanLineWidth]; +	byte *prevLine = 0;  	switch(_header.interlaceType) {  	case kNonInterlaced: -		for (uint16 y = 0; y < _unfilteredSurface->h; y++) { -			filterType = _imageData->readByte(); +		for (uint16 y = 0; y < _header.height; y++) { +			byte filterType = _imageData->readByte();  			_imageData->read(scanLine, scanLineWidth); -			unfilterScanLine(dest, scanLine, prevLine, _unfilteredSurface->format.bytesPerPixel, filterType, scanLineWidth); +			unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth);  			prevLine = dest; -			dest += _unfilteredSurface->pitch; +			dest += pitch;  		}  		break;  	case kInterlaced: @@ -409,9 +320,123 @@ void PNG::constructImage() {  	}  	delete[] scanLine; + +	constructOutput(unfilteredSurface); +	delete[] unfilteredSurface;  } -void PNG::readHeaderChunk() { +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 + +	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(); +} + +void PNGDecoder::constructOutput(const byte *surface) { +	_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"); +		} + +		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; +			} +		} +	} 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; +		} +	} +} + +void PNGDecoder::readHeaderChunk() {  	_header.width = _stream->readUint32BE();  	_header.height = _stream->readUint32BE();  	_header.bitDepth = _stream->readByte(); @@ -431,7 +456,7 @@ void PNG::readHeaderChunk() {  	_header.interlaceType = (PNGInterlaceType)_stream->readByte();  } -byte PNG::getNumColorChannels() { +byte PNGDecoder::getNumColorChannels() const {  	switch (_header.colorType) {  	case kGrayScale:  		return 1; // Gray @@ -448,16 +473,7 @@ byte PNG::getNumColorChannels() {  	}  } -void PNG::readPaletteChunk() { -	for (uint16 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) { +void PNGDecoder::readTransparencyChunk(uint32 chunkLength) {  	_transparentColorSpecified = true;  	switch(_header.colorType) { @@ -472,8 +488,8 @@ void PNG::readTransparencyChunk(uint32 chunkLength) {  		_transparentColor[2] = _stream->readUint16BE();  		break;  	case kIndexed: -		for (uint32 i = 0; i < chunkLength; i++) -			_palette[i * 4 + 3] = _stream->readByte(); +		_stream->read(_paletteTransparency, chunkLength); +  		// A transparency chunk may have less entries  		// than the palette entries. The remaining ones  		// are unmodified (set to 255). Check here: diff --git a/graphics/png.h b/graphics/decoders/png.h index 078c76fc6b..1da0bea1ab 100644 --- a/graphics/png.h +++ b/graphics/decoders/png.h @@ -53,6 +53,7 @@  #include "common/scummsys.h"  #include "common/textconsole.h" +#include "graphics/decoders/image_decoder.h"  namespace Common {  class SeekableReadStream; @@ -61,82 +62,44 @@ class SeekableReadStream;  namespace Graphics {  struct Surface; - -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 { +class PNGDecoder : public ImageDecoder {  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, given a pointer to -	 * an RGBA palette array (4 x 256). -	 */ -	void getPalette(byte *palette, uint16 &entries) { -		if (_header.colorType != kIndexed) -			error("Palette requested for a non-indexed PNG"); -		for (int i = 0; i < 256; i++) { -			palette[0 + i * 4] = _palette[0 + i * 4];	// R -			palette[1 + i * 4] = _palette[1 + i * 4];	// G -			palette[2 + i * 4] = _palette[2 + i * 4];	// B -			palette[3 + i * 4] = _palette[3 + i * 4];	// A -		} -		entries = _paletteEntries; -	} +	PNGDecoder(); +	~PNGDecoder(); + +	bool loadStream(Common::SeekableReadStream &stream); +	void destroy(); +	const Graphics::Surface *getSurface() const { return _outputSurface; } +	const byte *getPalette() const { return _palette; }  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(); +	byte getNumColorChannels() const;  	void readPaletteChunk();  	void readTransparencyChunk(uint32 chunkLength); @@ -152,7 +115,8 @@ private:  	PNGHeader _header; -	byte _palette[256 * 4];	// RGBA +	byte _palette[256 * 3];	// RGB +	byte _paletteTransparency[256];  	uint16 _paletteEntries;  	uint16 _transparentColor[3];  	bool _transparentColorSpecified; @@ -160,9 +124,12 @@ private:  	byte *_compressedBuffer;  	uint32 _compressedBufferSize; -	Graphics::Surface *_unfilteredSurface; +	Graphics::Surface *_outputSurface; +	Graphics::PixelFormat findPixelFormat() const; +	int getBytesPerPixel() const; +	void constructOutput(const byte *surface);  }; -} // End of Graphics namespace +} // End of namespace Graphics  #endif // GRAPHICS_PNG_H diff --git a/graphics/module.mk b/graphics/module.mk index d9f3802a68..281f904b38 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -13,7 +13,6 @@ MODULE_OBJS := \  	fonts/winfont.o \  	iff.o \  	maccursor.o \ -	png.o \  	primitives.o \  	scaler.o \  	scaler/thumbnail_intern.o \ @@ -26,7 +25,8 @@ MODULE_OBJS := \  	yuv_to_rgb.o \  	decoders/bmp.o \  	decoders/jpeg.o \ -	decoders/pict.o +	decoders/pict.o \ +	decoders/png.o  ifdef USE_SCALERS  MODULE_OBJS += \ | 
