diff options
| -rw-r--r-- | graphics/pict.cpp | 289 | ||||
| -rw-r--r-- | graphics/pict.h | 50 | 
2 files changed, 256 insertions, 83 deletions
| diff --git a/graphics/pict.cpp b/graphics/pict.cpp index 80bcb7a71e..f768ecdf3e 100644 --- a/graphics/pict.cpp +++ b/graphics/pict.cpp @@ -45,10 +45,164 @@ PictDecoder::~PictDecoder() {  	delete _jpeg;  } +#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PictDecoder::b, c)) + +void PictDecoder::setupOpcodesCommon() { +	OPCODE(0x0000, o_nop, "NOP"); +	OPCODE(0x0001, o_clip, "Clip"); +	OPCODE(0x0003, o_txFont, "TxFont"); +	OPCODE(0x0004, o_txFace, "TxFace"); +	OPCODE(0x0007, o_pnSize, "PnSize"); +	OPCODE(0x000D, o_txSize, "TxSize"); +	OPCODE(0x0010, o_txRatio, "TxRatio"); +	OPCODE(0x0011, o_versionOp, "VersionOp"); +	OPCODE(0x001E, o_nop, "DefHilite"); +	OPCODE(0x0028, o_longText, "LongText"); +	OPCODE(0x00A1, o_longComment, "LongComment"); +	OPCODE(0x00FF, o_opEndPic, "OpEndPic"); +	OPCODE(0x0C00, o_headerOp, "HeaderOp"); +} + +void PictDecoder::setupOpcodesNormal() { +	setupOpcodesCommon(); +	OPCODE(0x0098, on_packBitsRect, "PackBitsRect"); +	OPCODE(0x009A, on_directBitsRect, "DirectBitsRect"); +	OPCODE(0x8200, on_compressedQuickTime, "CompressedQuickTime"); +} + +void PictDecoder::setupOpcodesQuickTime() { +	setupOpcodesCommon(); +	OPCODE(0x0098, oq_packBitsRect, "PackBitsRect"); +	OPCODE(0x009A, oq_directBitsRect, "DirectBitsRect"); +	OPCODE(0x8200, oq_compressedQuickTime, "CompressedQuickTime"); +} + +#undef OPCODE + +void PictDecoder::o_nop(Common::SeekableReadStream *) { +	// Nothing to do +} + +void PictDecoder::o_clip(Common::SeekableReadStream *stream) { +	// Ignore +	stream->skip(stream->readUint16BE() - 2); +} + +void PictDecoder::o_txFont(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +} + +void PictDecoder::o_txFace(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readByte(); +} + +void PictDecoder::o_pnSize(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +	stream->readUint16BE(); +} + +void PictDecoder::o_txSize(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +} + +void PictDecoder::o_txRatio(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +	stream->readUint16BE(); +	stream->readUint16BE(); +	stream->readUint16BE(); +} + +void PictDecoder::o_versionOp(Common::SeekableReadStream *stream) { +	// We only support v2 extended +	if (stream->readUint16BE() != 0x02FF) +		error("Unknown PICT version"); +} + +void PictDecoder::o_longText(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +	stream->readUint16BE(); +	stream->skip(stream->readByte()); +} + +void PictDecoder::o_longComment(Common::SeekableReadStream *stream) { +	// Ignore +	stream->readUint16BE(); +	stream->skip(stream->readUint16BE()); +} + +void PictDecoder::o_opEndPic(Common::SeekableReadStream *stream) { +	// We've reached the end of the picture +	_continueParsing = false; +} + +void PictDecoder::o_headerOp(Common::SeekableReadStream *stream) { +	// Read the basic header, but we don't really have to do anything with it +	/* uint16 version = */ stream->readUint16BE(); +	stream->readUint16BE(); // Reserved +	/* uint32 hRes = */ stream->readUint32BE(); +	/* uint32 vRes = */ stream->readUint32BE(); +	Common::Rect origResRect; +	origResRect.top = stream->readUint16BE(); +	origResRect.left = stream->readUint16BE(); +	origResRect.bottom = stream->readUint16BE(); +	origResRect.right = stream->readUint16BE(); +	stream->readUint32BE(); // Reserved +} + +void PictDecoder::on_packBitsRect(Common::SeekableReadStream *stream) { +	// Unpack data (8bpp or lower) +	unpackBitsRect(stream, true); +} + +void PictDecoder::on_directBitsRect(Common::SeekableReadStream *stream) { +	// Unpack data (16bpp or higher) +	unpackBitsRect(stream, false); +} + +void PictDecoder::on_compressedQuickTime(Common::SeekableReadStream *stream) { +	// OK, here's the fun. We get to completely change how QuickDraw draws +	// the data in PICT files. + +	// Swap out the opcodes to the new ones +	_opcodes.clear(); +	setupOpcodesQuickTime(); + +	// We'll decode the first QuickTime data from here, but the QuickTime-specific +	// opcodes will take over from here on out. Normal opcodes, signing off. +	decodeCompressedQuickTime(stream); +} + +void PictDecoder::oq_packBitsRect(Common::SeekableReadStream *stream) { +	// Skip any data here (8bpp or lower) +	skipBitsRect(stream, true); +} + +void PictDecoder::oq_directBitsRect(Common::SeekableReadStream *stream) { +	// Skip any data here (16bpp or higher) +	skipBitsRect(stream, false); +} + +void PictDecoder::oq_compressedQuickTime(Common::SeekableReadStream *stream) { +	// Just pass the data along +	decodeCompressedQuickTime(stream); +} +  Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) {  	assert(stream); +	// Initialize opcodes to their normal state +	_opcodes.clear(); +	setupOpcodesNormal(); +  	_outputSurface = 0; +	_continueParsing = true; +	memset(_palette, 0, sizeof(_palette));  	uint16 fileSize = stream->readUint16BE(); @@ -63,70 +217,41 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale  	_imageRect.bottom = stream->readUint16BE();  	_imageRect.right = stream->readUint16BE();  	_imageRect.debugPrint(0, "PICT Rect:"); -	_isPaletted = false;  	// NOTE: This is only a subset of the full PICT format. -	//     - Only V2 Images Supported +	//     - Only V2 (Extended) Images Supported  	//     - CompressedQuickTime (JPEG) compressed data is supported  	//     - DirectBitsRect/PackBitsRect compressed data is supported -	for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size(); opNum++) { +	for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size() && _continueParsing; opNum++) { +		// PICT v2 opcodes are two bytes  		uint16 opcode = stream->readUint16BE(); -		debug(2, "Found PICT opcode %04x", opcode);  		if (opNum == 0 && opcode != 0x0011) -			error ("Cannot find PICT version opcode"); +			error("Cannot find PICT version opcode");  		else if (opNum == 1 && opcode != 0x0C00) -			error ("Cannot find PICT header opcode"); - -		if (opcode == 0x0000) {        // Nop -			stream->readUint16BE(); // Unknown -		} else if (opcode == 0x0001) { // Clip -			// Ignore -			uint16 clipSize = stream->readUint16BE(); -			stream->seek(clipSize - 2, SEEK_CUR); -		} else if (opcode == 0x0007) { // PnSize -			// Ignore -			stream->readUint16BE(); -			stream->readUint16BE(); -		} else if (opcode == 0x0011) { // VersionOp -			uint16 version = stream->readUint16BE(); -			if (version != 0x02FF) -				error ("Unknown PICT version"); -		} else if (opcode == 0x001E) { // DefHilite -			// Ignore, Contains no Data -		} else if (opcode == 0x0098) { // PackBitsRect -			decodeDirectBitsRect(stream, true); -			_isPaletted = true; -		} else if (opcode == 0x009A) { // DirectBitsRect -			decodeDirectBitsRect(stream, false); -		} else if (opcode == 0x00A1) { // LongComment -			stream->readUint16BE(); -			uint16 dataSize = stream->readUint16BE(); -			stream->seek(dataSize, SEEK_CUR); -		} else if (opcode == 0x00FF) { // OpEndPic -			stream->readUint16BE(); -			break; -		} else if (opcode == 0x0C00) { // HeaderOp -			/* uint16 version = */ stream->readUint16BE(); -			stream->readUint16BE(); // Reserved -			/* uint32 hRes = */ stream->readUint32BE(); -			/* uint32 vRes = */ stream->readUint32BE(); -			Common::Rect origResRect; -			origResRect.top = stream->readUint16BE(); -			origResRect.left = stream->readUint16BE(); -			origResRect.bottom = stream->readUint16BE(); -			origResRect.right = stream->readUint16BE(); -			stream->readUint32BE(); // Reserved -		} else if (opcode == 0x8200) { // CompressedQuickTime -			decodeCompressedQuickTime(stream); -			break; -		} else { -			warning("Unknown PICT opcode %04x", opcode); +			error("Cannot find PICT header opcode"); + +		// Since opcodes are word-aligned, we need to mark our starting +		// position here. +		uint32 startPos = stream->pos(); + +		for (uint32 i = 0; i < _opcodes.size(); i++) { +			if (_opcodes[i].op == opcode) { +				debug(4, "Running PICT opcode %04x '%s'", opcode, _opcodes[i].desc); +				(this->*(_opcodes[i].proc))(stream); +				break; +			} else if (i == _opcodes.size() - 1) { +				// Unknown opcode; attempt to continue forward +				warning("Unknown PICT opcode %04x", opcode); +			}  		} + +		// Align +		stream->skip((stream->pos() - startPos) & 1);  	}  	// If we got a palette throughout this nonsense, go and grab it -	if (palette && _isPaletted) +	if (palette)  		memcpy(palette, _palette, 256 * 3);  	return _outputSurface; @@ -155,21 +280,18 @@ PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream,  	return pixMap;  } -struct DirectBitsRectData { +struct PackBitsRectData {  	PictDecoder::PixMap pixMap;  	Common::Rect srcRect;  	Common::Rect dstRect;  	uint16 mode;  }; -void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, bool hasPalette) { +void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette) {  	static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); -	// Clear the palette -	memset(_palette, 0, sizeof(_palette)); - -	DirectBitsRectData directBitsData; -	directBitsData.pixMap = readPixMap(stream, !hasPalette); +	PackBitsRectData packBitsData; +	packBitsData.pixMap = readPixMap(stream, !hasPalette);  	// Read in the palette if there is one present  	if (hasPalette) { @@ -186,47 +308,47 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, bool  		}  	} -	directBitsData.srcRect.top = stream->readUint16BE(); -	directBitsData.srcRect.left = stream->readUint16BE(); -	directBitsData.srcRect.bottom = stream->readUint16BE(); -	directBitsData.srcRect.right = stream->readUint16BE(); -	directBitsData.dstRect.top = stream->readUint16BE(); -	directBitsData.dstRect.left = stream->readUint16BE(); -	directBitsData.dstRect.bottom = stream->readUint16BE(); -	directBitsData.dstRect.right = stream->readUint16BE(); -	directBitsData.mode = stream->readUint16BE(); +	packBitsData.srcRect.top = stream->readUint16BE(); +	packBitsData.srcRect.left = stream->readUint16BE(); +	packBitsData.srcRect.bottom = stream->readUint16BE(); +	packBitsData.srcRect.right = stream->readUint16BE(); +	packBitsData.dstRect.top = stream->readUint16BE(); +	packBitsData.dstRect.left = stream->readUint16BE(); +	packBitsData.dstRect.bottom = stream->readUint16BE(); +	packBitsData.dstRect.right = stream->readUint16BE(); +	packBitsData.mode = stream->readUint16BE(); -	uint16 width = directBitsData.srcRect.width(); -	uint16 height = directBitsData.srcRect.height(); +	uint16 width = packBitsData.srcRect.width(); +	uint16 height = packBitsData.srcRect.height();  	byte bytesPerPixel = 0; -	if (directBitsData.pixMap.pixelSize <= 8) +	if (packBitsData.pixMap.pixelSize <= 8)  		bytesPerPixel = 1; -	else if (directBitsData.pixMap.pixelSize == 32) +	else if (packBitsData.pixMap.pixelSize == 32)  		bytesPerPixel = 3;  	else -		bytesPerPixel = directBitsData.pixMap.pixelSize / 8; +		bytesPerPixel = packBitsData.pixMap.pixelSize / 8;  	_outputSurface = new Graphics::Surface();  	_outputSurface->create(width, height, (bytesPerPixel == 1) ? PixelFormat::createFormatCLUT8() : _pixelFormat);  	byte *buffer = new byte[width * height * bytesPerPixel];  	// Read in amount of data per row -	for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) { +	for (uint16 i = 0; i < packBitsData.pixMap.bounds.height(); i++) {  		// NOTE: Compression 0 is "default". The format in SCI games is packed when 0.  		// In the future, we may need to have something to set the  "default" packing  		// format, but this is good for now. -		if (directBitsData.pixMap.packType == 1 || directBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit) +		if (packBitsData.pixMap.packType == 1 || packBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit)  			// TODO: Finish this. Hasn't been needed (yet).  			error("Unpacked DirectBitsRect data (padded)"); -		} else if (directBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit) +		} else if (packBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit)  			// TODO: Finish this. Hasn't been needed (yet).  			error("Unpacked DirectBitsRect data (not padded)"); -		} else if (directBitsData.pixMap.packType == 0 || directBitsData.pixMap.packType > 2) { // Packed -			uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); -			decodeDirectBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), directBitsData.pixMap.pixelSize, bytesPerPixel); +		} else if (packBitsData.pixMap.packType == 0 || packBitsData.pixMap.packType > 2) { // Packed +			uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); +			unpackBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, packBitsData.pixMap.rowBytes, stream->readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel);  		}  	} @@ -264,7 +386,7 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, bool  	delete[] buffer;  } -void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) { +void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) {  	uint32 dataDecoded = 0;  	byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1; @@ -299,11 +421,16 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl  		dataDecoded += length / 4;  	if (length != dataDecoded) -		warning("Mismatched DirectBits read (%d/%d)", dataDecoded, length); +		warning("Mismatched PackBits read (%d/%d)", dataDecoded, length);  	delete data;  } +void PictDecoder::skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette) { +	// TODO +	error("TODO: PICT-QuickTime mode: skip PackBitsRect/DirectBitsRect"); +} +  void PictDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {  	switch (bitsPerPixel) {  	case 1: diff --git a/graphics/pict.h b/graphics/pict.h index 485c88b733..b426c6ee35 100644 --- a/graphics/pict.h +++ b/graphics/pict.h @@ -23,6 +23,7 @@  #ifndef GRAPHICS_PICT_H  #define GRAPHICS_PICT_H +#include "common/array.h"  #include "common/rect.h"  #include "common/scummsys.h" @@ -37,6 +38,8 @@ namespace Graphics {  class JPEG;  struct Surface; +#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream *stream) +  class PictDecoder {  public:  	PictDecoder(Graphics::PixelFormat pixelFormat); @@ -70,13 +73,56 @@ private:  	byte _palette[256 * 3];  	bool _isPaletted;  	Graphics::Surface *_outputSurface; +	bool _continueParsing; -	void decodeDirectBitsRect(Common::SeekableReadStream *stream, bool hasPalette); -	void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel); +	// Utility Functions +	void unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette); +	void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel); +	void skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette);  	void decodeCompressedQuickTime(Common::SeekableReadStream *stream);  	void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel); + +	// Opcodes +	typedef void (PictDecoder::*OpcodeProcPICT)(Common::SeekableReadStream *stream); +	struct PICTOpcode { +		PICTOpcode() { op = 0; proc = 0; desc = 0; } +		PICTOpcode(uint16 o, OpcodeProcPICT p, const char *d) { op = o; proc = p; desc = d; } +		uint16 op; +		OpcodeProcPICT proc; +		const char *desc; +	}; +	Common::Array<PICTOpcode> _opcodes; + +	// Common Opcodes +	void setupOpcodesCommon(); +	DECLARE_OPCODE(o_nop); +	DECLARE_OPCODE(o_clip); +	DECLARE_OPCODE(o_txFont); +	DECLARE_OPCODE(o_txFace); +	DECLARE_OPCODE(o_pnSize); +	DECLARE_OPCODE(o_txSize); +	DECLARE_OPCODE(o_txRatio); +	DECLARE_OPCODE(o_versionOp); +	DECLARE_OPCODE(o_longText); +	DECLARE_OPCODE(o_longComment); +	DECLARE_OPCODE(o_opEndPic); +	DECLARE_OPCODE(o_headerOp); + +	// Regular-mode Opcodes +	void setupOpcodesNormal(); +	DECLARE_OPCODE(on_packBitsRect); +	DECLARE_OPCODE(on_directBitsRect); +	DECLARE_OPCODE(on_compressedQuickTime); + +	// QuickTime-mode Opcodes +	void setupOpcodesQuickTime(); +	DECLARE_OPCODE(oq_packBitsRect); +	DECLARE_OPCODE(oq_directBitsRect); +	DECLARE_OPCODE(oq_compressedQuickTime);  }; +#undef DECLARE_OPCODE +  } // End of namespace Graphics  #endif | 
