diff options
| author | Johannes Schickel | 2012-08-29 06:58:22 -0700 | 
|---|---|---|
| committer | Johannes Schickel | 2012-08-29 06:58:22 -0700 | 
| commit | 933726a7e871fa9943cda9dc89cf91691a8f2ebf (patch) | |
| tree | beff4671e4d4ced9119b4bf7201adc4478faeeb5 | |
| parent | 6ab8db638e4a1d547ee67db067b5d6c3d6c940a4 (diff) | |
| parent | 47a7df2d0f9be2c13648a18c9de7b81e77e5d7fe (diff) | |
| download | scummvm-rg350-933726a7e871fa9943cda9dc89cf91691a8f2ebf.tar.gz scummvm-rg350-933726a7e871fa9943cda9dc89cf91691a8f2ebf.tar.bz2 scummvm-rg350-933726a7e871fa9943cda9dc89cf91691a8f2ebf.zip  | |
Merge pull request #263 from somaen/tgaloader
GRAPHICS: Add in a TGA-decoder
| -rw-r--r-- | graphics/decoders/tga.cpp | 419 | ||||
| -rw-r--r-- | graphics/decoders/tga.h | 100 | ||||
| -rw-r--r-- | graphics/module.mk | 3 | 
3 files changed, 521 insertions, 1 deletions
diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp new file mode 100644 index 0000000000..7d214a6f24 --- /dev/null +++ b/graphics/decoders/tga.cpp @@ -0,0 +1,419 @@ +/* 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. + */ + +/* Based on code from xoreos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +#include "common/util.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/error.h" + +#include "graphics/decoders/tga.h" + +namespace Graphics { + +TGADecoder::TGADecoder() { +	_colorMapSize = 0; +	_colorMapOrigin = 0; +	_colorMapLength = 0; +	_colorMapEntryLength = 0; +	_colorMap = NULL; +} + +TGADecoder::~TGADecoder() { +	destroy(); +} + +void TGADecoder::destroy() { +	_surface.free(); +	delete[] _colorMap; +} + +bool TGADecoder::loadStream(Common::SeekableReadStream &tga) { +	byte imageType, pixelDepth; +	bool success; +	success = readHeader(tga, imageType, pixelDepth); +	if (success) { +		switch (imageType) { +		case TYPE_BW: +		case TYPE_TRUECOLOR: +			success = readData(tga, imageType, pixelDepth); +			break; +		case TYPE_RLE_BW: +		case TYPE_RLE_TRUECOLOR: +		case TYPE_RLE_CMAP: +			success = readDataRLE(tga, imageType, pixelDepth); +			break; +		case TYPE_CMAP: +			success = readDataColorMapped(tga, imageType, pixelDepth); +			break; +		default: +			success = false; +			break; +		} +	} +	if (tga.err() || !success) { +		warning("Failed reading TGA-file"); +		return false; +	} +	return success; +} + +bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) { +	if (!tga.seek(0)) { +		warning("Failed reading TGA-file"); +		return false; +	} + +	// TGAs have an optional "id" string in the header +	uint32 idLength = tga.readByte(); + +	// Number of colors in the color map / palette +	int hasColorMap = tga.readByte(); + +	// Image type. See header for numeric constants +	imageType = tga.readByte(); + +	switch (imageType) { +	case TYPE_CMAP: +	case TYPE_TRUECOLOR: +	case TYPE_BW: +	case TYPE_RLE_CMAP: +	case TYPE_RLE_TRUECOLOR: +	case TYPE_RLE_BW: +		break; +	default: +		warning("Unsupported image type: %d", imageType); +		return false; +	} + +	// Color map specifications +	if (hasColorMap == 0) { +		tga.skip(5); +	} else { +		_colorMapOrigin = tga.readUint16LE(); +		_colorMapLength = tga.readUint16LE(); +		_colorMapEntryLength = tga.readByte(); +	} +	// Origin-defintions +	tga.skip(2 + 2); + +	// Image dimensions +	_surface.w = tga.readUint16LE(); +	_surface.h = tga.readUint16LE(); + +	// Bits per pixel +	pixelDepth = tga.readByte(); +	_surface.format.bytesPerPixel = pixelDepth / 8; + +	// Image descriptor +	byte imgDesc = tga.readByte(); +	int attributeBits = imgDesc & 0x0F; +	assert((imgDesc & 0x10) == 0); +	_originTop = (imgDesc & 0x20); + +	// Interleaving is not handled at this point +	//int interleave = (imgDesc & 0xC); +	if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) { +		if (pixelDepth == 8) { +			_format = PixelFormat::createFormatCLUT8(); +		} else { +			warning("Unsupported index-depth: %d", pixelDepth); +			return false; +		} +	} else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) { +		if (pixelDepth == 24) { +			_format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0); +		} else if (pixelDepth == 32) { +			_format = PixelFormat(4, 8, 8, 8, attributeBits, 16, 8, 0, 24); +		} else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) { +			// 16bpp TGA is ARGB1555 +			_format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15); +		} else { +			warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); +			return false; +		} +	} else if (imageType == TYPE_BW || TYPE_RLE_BW) { +		if (pixelDepth == 8) { +			_format = PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0); +		} else { +			warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); +			return false; +		} + +	} else { +		warning("Unsupported image type: %d", imageType); +		return false; +	} + +	// Skip the id string +	tga.skip(idLength); + +	if (hasColorMap) { +		return readColorMap(tga, imageType, pixelDepth); +	} +	return true; +} + +bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { +	_colorMap = new byte[3 * _colorMapLength]; +	for (int i = 0; i < _colorMapLength * 3; i += 3) { +		byte r, g, b, a; +		if (_colorMapEntryLength == 32) { +			PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24); +			uint32 color = tga.readUint32LE(); +			format.colorToARGB(color, a, r, g, b); +		} else if (_colorMapEntryLength == 24) { +			r = tga.readByte(); +			g = tga.readByte(); +			b = tga.readByte(); +		} else if (_colorMapEntryLength == 16) { +			PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15); +			uint16 color = tga.readUint16LE(); +			format.colorToARGB(color, a, r, g, b); +		} +#ifdef SCUMM_LITTLE_ENDIAN +		_colorMap[i] = r; +		_colorMap[i + 1] = g; +		_colorMap[i + 2] = b; +#else +		_colorMap[i] = b; +		_colorMap[i + 1] = g; +		_colorMap[i + 2] = r; +#endif +	} +	return true; +} + +// Additional information found from http://paulbourke.net/dataformats/tga/ +// With some details from the link referenced in the header. +bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { +	// TrueColor +	if (imageType == TYPE_TRUECOLOR) { +		_surface.create(_surface.w, _surface.h, _format); + +		if (pixelDepth == 16) { +			for (int i = 0; i < _surface.h; i++) { +				uint16 *dst; +				if (!_originTop) { +					dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1); +				} else { +					dst = (uint16 *)_surface.getBasePtr(0, i); +				} +				for (int j = 0; j < _surface.w; j++) { +					*dst++ = tga.readUint16LE(); +				} +			} +		} else if (pixelDepth == 32) { +			for (int i = 0; i < _surface.h; i++) { +				uint32 *dst; +				if (!_originTop) { +					dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1); +				} else { +					dst = (uint32 *)_surface.getBasePtr(0, i); +				} +				for (int j = 0; j < _surface.w; j++) { +					*dst++ = tga.readUint32LE(); +				} +			} +		} else if (pixelDepth == 24) { +			for (int i = 0; i < _surface.h; i++) { +				byte *dst; +				if (!_originTop) { +					dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); +				} else { +					dst = (byte *)_surface.getBasePtr(0, i); +				} +				for (int j = 0; j < _surface.w; j++) { +					byte r = tga.readByte(); +					byte g = tga.readByte(); +					byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN +					*dst++ = r; +					*dst++ = g; +					*dst++ = b; +#else +					*dst++ = b; +					*dst++ = g; +					*dst++ = r; +#endif +				} +			} +		} +		// Black/White +	} else if (imageType == TYPE_BW) { +		_surface.create(_surface.w, _surface.h, _format); + +		byte *data  = (byte *)_surface.pixels; +		uint32 count = _surface.w * _surface.h; + +		while (count-- > 0) { +			byte g = tga.readByte(); +			*data++ = g; +			*data++ = g; +			*data++ = g; +			*data++ = g; +		} +	} +	return true; +} + +bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) { +	// Color-mapped +	if (imageType == TYPE_CMAP) { +		_surface.create(_surface.w, _surface.h, _format); +		if (indexDepth == 8) { +			for (int i = 0; i < _surface.h; i++) { +				byte *dst; +				if (!_originTop) { +					dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); +				} else { +					dst = (byte *)_surface.getBasePtr(0, i); +				} +				for (int j = 0; j < _surface.w; j++) { +					byte index = tga.readByte(); +					*dst++ = index; +				} +			} +		} else if (indexDepth == 16) { +			warning("16 bit indexes not supported"); +			return false; +		} +	} else { +		return false; +	} +	return true; +} + +bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { +	// RLE-TrueColor / RLE-Black/White +	if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) { +		_surface.create(_surface.w, _surface.h, _format); +		uint32 count = _surface.w * _surface.h; +		byte *data = (byte *)_surface.pixels; + +		while (count > 0) { +			uint32 header = tga.readByte(); +			byte type = (header & 0x80) >> 7; +			uint32 rleCount = (header & 0x7F) + 1; + +			// RLE-packet +			if (type == 1) { +				if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { +					uint32 color = tga.readUint32LE(); +					while (rleCount-- > 0) { +						*((uint32 *)data) = color; +						data += 4; +						count--; +					} +				} else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { +					byte r = tga.readByte(); +					byte g = tga.readByte(); +					byte b = tga.readByte(); +					while (rleCount-- > 0) { +#ifdef SCUMM_LITTLE_ENDIAN +						*data++ = r; +						*data++ = g; +						*data++ = b; +#else +						*data++ = b; +						*data++ = g; +						*data++ = r; +#endif +						count--; +					} +				} else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { +					byte color = tga.readByte(); +					while (rleCount-- > 0) { +						*data++ = color; +						*data++ = color; +						*data++ = color; +						*data++ = color; +						count--; +					} +				} else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { +					byte index = tga.readByte(); +					while (rleCount-- > 0) { +						*data++ = index; +						count--; +					} +				} else { +					warning("Unhandled pixel-depth for image-type 10"); +					return false; +				} +				// Raw-packet +			} else if (type == 0) { +				if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { +					while (rleCount-- > 0) { +						uint32 color = tga.readUint32LE(); +						*((uint32 *)data) = color; +						data += 4; +						count--; +					} +				} else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { +					while (rleCount-- > 0) { +						byte r = tga.readByte(); +						byte g = tga.readByte(); +						byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN +						*data++ = r; +						*data++ = g; +						*data++ = b; +#else +						*data++ = b; +						*data++ = g; +						*data++ = r; +#endif +						count--; +					} +				} else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { +					while (rleCount-- > 0) { +						byte color = tga.readByte(); +						*data++ = color; +						*data++ = color; +						*data++ = color; +						*data++ = color; +						count--; +					} +				} else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { +					while (rleCount-- > 0) { +						byte index = tga.readByte(); +						*data++ = index; +						count--; +					} +				} else { +					warning("Unhandled pixel-depth for image-type 10"); +					return false; +				} +			} else { +				warning("Unknown header for RLE-packet %d", type); +				return false; +			} +		} +	} else { +		return false; +	} +	return true; +} + +} // End of namespace Graphics diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h new file mode 100644 index 0000000000..e8dd2b8411 --- /dev/null +++ b/graphics/decoders/tga.h @@ -0,0 +1,100 @@ +/* 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. + */ + +/* Based on code from eos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +/* + * TGA decoder used in engines: + *  - none + */ + +#ifndef GRAPHICS_DECODERS_TGA_H +#define GRAPHICS_DECODERS_TGA_H + +#include "graphics/surface.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +/** TarGa image-decoder + * The following variations of TGA are supported: + * - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes + * - Type 2 - 16/24/32 bpp Top AND Bottom origined. + * - Type 3 - Black/White images, 8bpp. + * - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only) + * - Type 10 - RLE-encoded TrueColor, 24/32bpp. + * - Type 11 - RLE-encoded Black/White, 8bpp. + * + * No images are returned with a palette, instead they are converted + * to 16 bpp for Type 1, or 32 bpp for Black/White-images. + */ +class TGADecoder : public ImageDecoder { +public: +	TGADecoder(); +	virtual ~TGADecoder(); +	virtual void destroy(); +	virtual const Surface *getSurface() const { +		return &_surface; +	}; +	virtual const byte *getPalette() const { return _colorMap; } +	virtual uint16 getPaletteColorCount() const { return _colorMapLength; } +	virtual bool loadStream(Common::SeekableReadStream &stream); +private: +	// Format-spec from: +	//http://www.ludorg.net/amnesia/TGA_File_Format_Spec.html +	enum { +	    TYPE_CMAP = 1, +	    TYPE_TRUECOLOR = 2, +	    TYPE_BW = 3, +	    TYPE_RLE_CMAP = 9, +	    TYPE_RLE_TRUECOLOR = 10, +	    TYPE_RLE_BW = 11 +	}; + +	// Color-map: +	bool _colorMapSize; +	byte *_colorMap; +	int16 _colorMapOrigin; +	int16 _colorMapLength; +	byte _colorMapEntryLength; + +	// Origin may be at the top, or bottom +	bool _originTop; + +	PixelFormat _format; +	Surface _surface; +	// Loading helpers +	bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth); +	bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); +	bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth); +	bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); +	bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_DECODERS_TGA_H diff --git a/graphics/module.mk b/graphics/module.mk index 281f904b38..e67efd2cf5 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -26,7 +26,8 @@ MODULE_OBJS := \  	decoders/bmp.o \  	decoders/jpeg.o \  	decoders/pict.o \ -	decoders/png.o +	decoders/png.o \ +	decoders/tga.o  ifdef USE_SCALERS  MODULE_OBJS += \  | 
