diff options
Diffstat (limited to 'engines/m4/graphics.cpp')
| -rw-r--r-- | engines/m4/graphics.cpp | 1074 | 
1 files changed, 1074 insertions, 0 deletions
| diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp new file mode 100644 index 0000000000..beda178344 --- /dev/null +++ b/engines/m4/graphics.cpp @@ -0,0 +1,1074 @@ +/* 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 "common/file.h" +#include "common/endian.h" +#include "common/system.h" +#include "common/util.h" +#include "common/ptr.h" + +#include "m4/globals.h" +#include "m4/graphics.h" +#include "m4/sprite.h" +#include "m4/m4.h" +#include "m4/compression.h" + +namespace M4 { + +RGBList::RGBList(int numEntries, RGB8 *srcData, bool freeData) { +	_size = numEntries; +	assert(numEntries <= 256); + +	if (srcData == NULL) { +		_data = new RGB8[numEntries]; +		_freeData = true; +	} else { +		_data = srcData; +		_freeData = freeData; +	} + +	_palIndexes = new byte[numEntries]; +	Common::set_to(&_palIndexes[0], &_palIndexes[numEntries], 0); +} + +RGBList::~RGBList() { +	if (_freeData) +		delete[] _data; +	delete[] _palIndexes; +} + +//-------------------------------------------------------------------------- + +#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2) + +void M4Surface::loadCodesM4(Common::SeekableReadStream *source) { +	if (!source) { +		free(); +		return; +	} + +	uint16 widthVal = source->readUint16LE(); +	uint16 heightVal = source->readUint16LE(); + +	create(widthVal, heightVal, 1); +	source->read(pixels, widthVal * heightVal); +} + +void M4Surface::loadCodesMads(Common::SeekableReadStream *source) { +	if (!source) { +		free(); +		return; +	} + +	uint16 widthVal = 320; +	uint16 heightVal = 156; +	byte *walkMap = new byte[source->size()]; + +	create(widthVal, heightVal, 1); +	source->read(walkMap, source->size()); + +	byte *ptr = (byte *)getBasePtr(0, 0); + +	for (int y = 0; y < heightVal; y++) { +		for (int x = 0; x < widthVal; x++) { +			int ofs = x + (y * widthVal); +			if ((walkMap[ofs / 8] << (ofs % 8)) & 0x80) +				*ptr++ = 1;		// walkable +			else +				*ptr++ = 0; +		} +	} +} + +// Sprite related methods + +void M4Surface::vLine(int x, int y1, int y2) { +	Graphics::Surface::vLine(x, y1, y2, _color); +} + +void M4Surface::hLine(int x1, int x2, int y) { +	Graphics::Surface::hLine(x1, y, x2, _color); +} + +void M4Surface::vLineXor(int x, int y1, int y2) { +	// Clipping +	if (x < 0 || x >= w) +		return; + +	if (y2 < y1) +		SWAP(y2, y1); + +	if (y1 < 0) +		y1 = 0; +	if (y2 >= h) +		y2 = h - 1; + +	byte *ptr = (byte *)getBasePtr(x, y1); +	while (y1++ <= y2) { +		*ptr ^= 0xFF; +		ptr += pitch; +	} + +} + +void M4Surface::hLineXor(int x1, int x2, int y) { +	// Clipping +	if (y < 0 || y >= h) +		return; + +	if (x2 < x1) +		SWAP(x2, x1); + +	if (x1 < 0) +		x1 = 0; +	if (x2 >= w) +		x2 = w - 1; + +	if (x2 < x1) +		return; + +	byte *ptr = (byte *)getBasePtr(x1, y); +	while (x1++ <= x2) +		*ptr++ ^= 0xFF; + +} + +void M4Surface::line(int x1, int y1, int x2, int y2, byte color) { +	Graphics::Surface::drawLine(x1, y1, x2, y2, color); +} + + +void M4Surface::frameRect(int x1, int y1, int x2, int y2) { +	Graphics::Surface::frameRect(Common::Rect(x1, y1, x2, y2), _color); +} + +void M4Surface::fillRect(int x1, int y1, int x2, int y2) { +	Graphics::Surface::fillRect(Common::Rect(x1, y1, x2, y2), _color); +} + +void M4Surface::drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &clipRect) { + +	enum { +		kStatusSkip, +		kStatusScale, +		kStatusDraw +	}; + +	// NOTE: The current clipping code assumes that the top left corner of the clip +	// rectangle is always 0, 0 +	assert(clipRect.top == 0 && clipRect.left == 0); + +	// TODO: Put err* and scaled* into SpriteInfo +	int errX = info.hotX * info.scaleX % 100; +	int errY = info.hotY * info.scaleY % 100; +	int scaledWidth = scaleValue(info.width, info.scaleX, errX); +	int scaledHeight = scaleValue(info.height, info.scaleY, errY); + +	/* +	printf("M4Surface::drawSprite() info.width = %d; info.scaleX = %d; info.height = %d; info.scaleY = %d; scaledWidth = %d; scaledHeight = %d\n", +		info.width, info.scaleX, info.height, info.scaleY, scaledWidth, scaledHeight); fflush(stdout); +	*/ +	 +	int clipX = 0, clipY = 0; +	// Clip the sprite's width and height according to the clip rectangle's dimensions +	// This clips the sprite to the bottom and right +	if (x >= 0) { +		scaledWidth = MIN<int>(x + scaledWidth, clipRect.right) - x; +	} else { +		clipX = x; +		scaledWidth = x + scaledWidth; +	} +	if (y >= 0) { +		scaledHeight = MIN<int>(y + scaledHeight, clipRect.bottom) - y; +	} else { +		clipY = y; +		scaledHeight = y + scaledHeight; +	} + +	//printf("M4Surface::drawSprite() width = %d; height = %d; scaledWidth = %d; scaledHeight = %d\n", info.width, info.height, scaledWidth, scaledHeight); fflush(stdout); + +	// Check if sprite is inside the screen. If it's not, there's no need to draw it +	if (scaledWidth + x <= 0 || scaledHeight + y <= 0)	// check left and top (in case x,y are negative) +		return; +	if (scaledWidth <= 0 || scaledHeight <= 0)			// check right and bottom +		return; +	int heightAmt = scaledHeight; + +	byte *src = info.sprite->getData(); +	byte *dst = getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY); + +	int status = kStatusSkip; +	byte *scaledLineBuf = new byte[scaledWidth]; + +	while (heightAmt > 0) { + +		if (status == kStatusSkip) { +			// Skip line +			errY -= info.scaleY; +			if (errY < 0) +				status = kStatusScale; +			else +				src += info.width; +		} else { + +			if (status == kStatusScale) { +				// Scale current line +				byte *lineDst = scaledLineBuf; +				int curErrX = errX; +				int widthVal = scaledWidth; +				byte *tempSrc = src; +				int startX = clipX; +				while (widthVal > 0) { +					byte pixel = *tempSrc++; +					curErrX -= info.scaleX; +					while (curErrX < 0) { +						if (startX == 0) { +							*lineDst++ = pixel; +							widthVal--; +						} else { +							startX++; +						} +						curErrX += 100; +					} +				} +				src += info.width; +				status = kStatusDraw; +			} + +			if (status == kStatusDraw && clipY == 0) { +				// Draw previously scaled line +				// TODO Implement different drawing types (depth, shadow etc.) +				byte *tempDst = dst; +				for (int lineX = 0; lineX < scaledWidth; lineX++) { +					byte pixel = scaledLineBuf[lineX]; + +					if (info.encoding & 0x80) { + +						if (pixel == 0x80) { +							pixel = 0; +						} else { +							byte destPixel = *tempDst; +							byte r, g, b; +							r = CLIP((info.palette[destPixel].r * pixel) >> 10, 0, 31); +							g = CLIP((info.palette[destPixel].g * pixel) >> 10, 0, 31); +							b = CLIP((info.palette[destPixel].b * pixel) >> 10, 0, 31); +							pixel = info.inverseColorTable[(b << 10) | (g << 5) | r]; +						} +					} + +					if (pixel) +						*tempDst = pixel; + +					tempDst++; +				} +				dst += pitch; +				heightAmt--; +				// TODO depth etc. +				//depthAddress += Destination -> Width; + +				errY += 100; +				if (errY >= 0) +					status = kStatusSkip; +			} else if (status == kStatusDraw && clipY < 0) { +				clipY++; + +				errY += 100; +				if (errY >= 0) +					status = kStatusSkip; +			} + +		} + +	} +	 +	delete[] scaledLineBuf; + +} + +// Surface methods + +byte *M4Surface::getData() { +	return (byte *)pixels; +} + +byte *M4Surface::getBasePtr(int x, int y) { +	return (byte *)Graphics::Surface::getBasePtr(x, y); +} + +void M4Surface::freeData() { +} + +void M4Surface::empty() { +	Common::set_to((byte *) pixels, (byte *) pixels + w * h, _vm->_palette->BLACK); +} + +void M4Surface::frameRect(const Common::Rect &r, uint8 color) { +	Graphics::Surface::frameRect(r, color); +} + +void M4Surface::fillRect(const Common::Rect &r, uint8 color) { +	Graphics::Surface::fillRect(r, color); +} + +void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, +						 int transparentColor) { +	// Validation of the rectangle and position +	if ((destX >= w) || (destY >= h)) +		return; + +	Common::Rect copyRect = srcBounds; +	if (destX < 0) { +		copyRect.left += -destX; +		destX = 0; +	} else if (destX + copyRect.width() > w) { +		copyRect.right -= destX + copyRect.width() - w; +	} +	if (destY < 0) { +		copyRect.top += -destY; +		destY = 0; +	} else if (destY + copyRect.height() > h) { +		copyRect.bottom -= destY + copyRect.height() - h; +	} + +	if (!copyRect.isValidRect()) +		return; + +	// Copy the specified area + +	byte *data = src->getData(); +	byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left); +	byte *destPtr = (byte *)pixels + (destY * width()) + destX; + +	for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { +		if (transparentColor == -1) +			// No transparency, so copy line over +			Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr); +		else { +			// Copy each byte one at a time checking for the transparency color +			for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) +				if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr]; +		} + +		srcPtr += src->width(); +		destPtr += width(); +	} + +	src->freeData(); +} + +void M4Surface::loadBackgroundRiddle(const char *sceneName) { +	char resourceName[20]; +	Common::SeekableReadStream *stream; +	// Loads a Riddle scene +	sprintf(resourceName, "%s.tt", sceneName); +	stream = _vm->_resourceManager->get(resourceName);	 +	m4LoadBackground(stream); +	_vm->_resourceManager->toss(resourceName); +} + +void M4Surface::loadBackground(int sceneNumber, RGBList **palData) { +	this->empty();		// clear previous scene + +	if (_vm->isM4() || (_vm->getGameType() == GType_RexNebular)) { +		char resourceName[20]; +		Common::SeekableReadStream *stream; + +		if (_vm->getGameType() == GType_RexNebular) { +			// Load Rex Nebular screen +			sprintf(resourceName, "rm%d.art", sceneNumber); +			stream = _vm->_resourceManager->get(resourceName);	 +			rexLoadBackground(stream, palData); +		} else { +			// Loads M4 game scene +			if (palData) +				*palData = NULL; +			sprintf(resourceName, "%i.tt", sceneNumber); +			stream = _vm->_resourceManager->get(resourceName);	 +			m4LoadBackground(stream); +		} +		  +		_vm->_resourceManager->toss(resourceName); + +	} else { +		madsLoadBackground(sceneNumber, palData); +	} +} + +void M4Surface::madsLoadBackground(int roomNumber, RGBList **palData) { +	// Get a MadsPack reference to the tile set and mapping +	char resourceName[20]; +	int i; + +	// Uncompressed tile map resource +	sprintf(resourceName, "rm%d.mm", roomNumber); +	MadsPack tileMapFile(resourceName, _vm); +	Common::SeekableReadStream *mapStream = tileMapFile.getItemStream(0); + +	// Get the details of the tiles and map +	mapStream->readUint32LE(); +	int tileCountX = mapStream->readUint16LE(); +	int tileCountY = mapStream->readUint16LE(); +	int tileWidthMap = mapStream->readUint16LE(); +	int tileHeightMap = mapStream->readUint16LE(); +	int screenWidth = mapStream->readUint16LE(); +	int screenHeight = mapStream->readUint16LE(); +	int tileCountMap = tileCountX * tileCountY; +	delete mapStream; + +	// Obtain tile map information +	typedef Common::List<Common::SharedPtr<M4Surface> > TileSetList; +	typedef TileSetList::iterator TileSetIterator; +	TileSetList tileSet; +	uint16 *tileMap = new uint16[tileCountMap]; +	mapStream = tileMapFile.getItemStream(1); +	for (i = 0; i < tileCountMap; ++i) +		tileMap[i] = mapStream->readUint16LE(); +	delete mapStream; +	_vm->res()->toss(resourceName); + +	// -------------------------------------------------------------------------------- + +	// Tile map data, which needs to be kept compressed, as the tile offsets refer to +	// the compressed data. Each tile is then uncompressed separately +	sprintf(resourceName, "rm%d.tt", roomNumber); +	Common::SeekableReadStream *tileDataComp = _vm->_resourceManager->get(resourceName); +	MadsPack tileData(tileDataComp); +	Common::SeekableReadStream *tileDataUncomp = tileData.getItemStream(0); + +	// Validate that the data matches between the tiles and tile map file and is valid +	int tileCount = tileDataUncomp->readUint16LE(); +	int tileWidth = tileDataUncomp->readUint16LE(); +	int tileHeight = tileDataUncomp->readUint16LE(); +	delete tileDataUncomp; +	assert(tileCountMap == tileCount); +	assert(tileWidth == tileWidthMap); +	assert(tileHeight == tileHeightMap); +	assert(screenWidth == _vm->_screen->width()); +	assert(screenHeight <= _vm->_screen->height()); + +	// -------------------------------------------------------------------------------- + +	// Get the palette to use +	tileDataUncomp = tileData.getItemStream(2); +	// Set palette +	if (!palData) { +		_vm->_palette->setMadsPalette(tileDataUncomp, 4); +	} else { +		int numColors; +		RGB8 *rgbList = _vm->_palette->decodeMadsPalette(tileDataUncomp, &numColors); +		*palData = new RGBList(numColors, rgbList, true); +	} +	delete tileDataUncomp; + +	// -------------------------------------------------------------------------------- + +	// Get tile data + +	tileDataUncomp = tileData.getItemStream(1); +	FabDecompressor fab; +	uint32 compressedTileDataSize = 0; + +	for (i = 0; i < tileCount; i++) { +		tileDataUncomp->seek(i * 4, SEEK_SET); +		uint32 tileOfs = tileDataUncomp->readUint32LE(); +		M4Surface* newTile = new M4Surface(tileWidth, tileHeight); + +		if (i == tileCount - 1) +			compressedTileDataSize = tileDataComp->size() - tileOfs; +		else +			compressedTileDataSize = tileDataUncomp->readUint32LE() - tileOfs; + +		//printf("Tile: %i, compressed size: %i\n", i, compressedTileDataSize); + +		newTile->empty(); + +		byte *compressedTileData = new byte[compressedTileDataSize]; + +		tileDataComp->seek(tileData.getDataOffset() + tileOfs, SEEK_SET); +		tileDataComp->read(compressedTileData, compressedTileDataSize); + +		fab.decompress(compressedTileData, compressedTileDataSize, (byte*)newTile->pixels, tileWidth * tileHeight); +		tileSet.push_back(TileSetList::value_type(newTile)); +		delete[] compressedTileData; +	} + +	delete tileDataUncomp; + +	// -------------------------------------------------------------------------------- + +	// Loop through the mapping data to place the tiles on the screen +	 +	uint16 *tIndex = &tileMap[0]; +	for (int y = 0; y < tileCountY; y++) { +		for (int x = 0; x < tileCountX; x++) { +			int tileIndex = *tIndex++; +			assert(tileIndex < tileCount); +			TileSetIterator tile = tileSet.begin(); +			for (i = 0; i < tileIndex; i++) +				++tile; +			((*tile).get())->copyTo(this, x * tileWidth, y * tileHeight); +		} +	} +	tileSet.clear(); +	_vm->res()->toss(resourceName); +} + +void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData) { +	MadsPack packData(source); +	Common::MemoryReadStream *sourceUnc = packData.getItemStream(0); + +	int sceneWidth = sourceUnc->readUint16LE(); +	int sceneHeight = sourceUnc->readUint16LE(); +	int sceneSize = sceneWidth * sceneHeight; +	if (sceneWidth > this->width()) { +		warning("Background width is %i, too large to fit in screen. Setting it to %i", sceneWidth, this->width()); +		sceneWidth = this->width(); +		sceneSize = sceneWidth * sceneHeight; +	} +	if (sceneHeight > this->height()) { +		warning("Background height is %i, too large to fit in screen.Setting it to %i", sceneHeight, this->height()); +		sceneHeight = this->height(); +		sceneSize = sceneWidth * sceneHeight; +	} + +	// Set palette +	if (!palData) { +		_vm->_palette->setMadsPalette(sourceUnc, 4); +	} else { +		int numColors; +		RGB8 *rgbList = _vm->_palette->decodeMadsPalette(sourceUnc, &numColors); +		*palData = new RGBList(numColors, rgbList, true); +	} +	delete sourceUnc; + +	// Get the raw data for the background +	sourceUnc = packData.getItemStream(1); +	assert((int)sourceUnc->size() >= sceneSize); + +	byte *pData = (byte *)pixels; +	sourceUnc->read(pData, sceneSize); +	 +	freeData(); +	delete sourceUnc; +} + +#undef COL_TRANS + +void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) { +	M4Surface *tileBuffer = new M4Surface(); +	uint curTileX = 0, curTileY = 0; +	int clipX = 0, clipY = 0; +	RGB8 palette[256]; + +	source->readUint32LE();			// magic, unused +	/*uint32 size =*/ source->readUint32LE(); +	uint32 widthVal = source->readUint32LE(); +	uint32 heightVal = source->readUint32LE(); +	uint32 tilesX = source->readUint32LE(); +	uint32 tilesY = source->readUint32LE(); +	uint32 tileWidth = source->readUint32LE(); +	uint32 tileHeight = source->readUint32LE(); +	uint8 blackIndex = 0; + +	// Debug +	//printf("loadBackground(): %dx%d picture (%d bytes) - %dx%d tiles of size %dx%d\n",  +	//	   widthVal, heightVal, size, tilesX, tilesY, tileWidth, tileHeight); +	 +	// BGR data, which is converted to RGB8 +	for (uint i = 0; i < 256; i++) { +		palette[i].b = source->readByte() << 2; +		palette[i].g = source->readByte() << 2; +		palette[i].r = source->readByte() << 2; +		palette[i].u = source->readByte() << 2; + +		if ((blackIndex == 0) && !palette[i].r && !palette[i].g && !palette[i].b) +			blackIndex = i; +	} +	 +	_vm->_palette->setPalette(palette, 0, 256); + +	// resize or create the surface +	// note that the height of the scene in game scenes is smaller than 480, as the bottom part of the +	// screen is the inventory +	assert(width() == (int)widthVal); +	//printf("width(): %d, widthVal: %d, height(): %d, heightVal: %d\n", width(), widthVal, height(), heightVal); + +	tileBuffer->create(tileWidth, tileHeight, 1); + +	for (curTileY = 0; curTileY < tilesY; curTileY++) { +		clipY = MIN(heightVal, (1 + curTileY) * tileHeight) - (curTileY * tileHeight); + +		for (curTileX = 0; curTileX < tilesX; curTileX++) { +			clipX = MIN(widthVal, (1 + curTileX) * tileWidth) - (curTileX * tileWidth); + +			// Read a tile and copy it to the destination surface +			source->read(tileBuffer->pixels, tileWidth * tileHeight); +			Common::Rect srcBounds(0, 0, clipX, clipY); +			copyFrom(tileBuffer, srcBounds, curTileX * tileWidth, curTileY * tileHeight); +		} +	} + +	if (heightVal < (uint)height()) +		fillRect(Common::Rect(0, heightVal, width(), height()), blackIndex); + +	delete tileBuffer; +} + +void M4Surface::madsloadInterface(int index, RGBList **palData) { +	char resourceName[20]; +	sprintf(resourceName, "i%d.int", index); +	MadsPack intFile(resourceName, _vm); +	RGB8 *palette = new RGB8[16]; + +	// Chunk 0, palette +	Common::SeekableReadStream *intStream = intFile.getItemStream(0); +	 +	for (int i = 0; i < 16; i++) { +		palette[i].r = intStream->readByte() << 2; +		palette[i].g = intStream->readByte() << 2; +		palette[i].b = intStream->readByte() << 2; +		intStream->readByte(); +		intStream->readByte(); +		intStream->readByte(); +	} +	*palData = new RGBList(16, palette, true); +	delete intStream; + +	// Chunk 1, data +	intStream = intFile.getItemStream(1); +	create(320, 44, 1); +	intStream->read(pixels, 320 * 44); +	delete intStream; +} + +void M4Surface::translate(RGBList *list, bool isTransparent) { +	byte *p = getBasePtr(0, 0); +	byte *palIndexes = list->palIndexes(); + +	for (int i = 0; i < width() * height(); ++i, ++p) { +		if (!isTransparent || (*p != 0)) { +			assert(*p < list->size()); +			*p = palIndexes[*p]; +		} +	} + +	freeData(); +} + +//-------------------------------------------------------------------------- +// Palette class +// + +#define GREEN_START 32 +#define NUM_GREENS 32 +#define GREEN_END (GREEN_START + NUM_GREENS - 1) +#define NORMAL_START 64 +#define NORMAL_END 255 +#define NUM_NORMAL (NORMAL_END - NORMAL_START + 1) + +// Support function for creating a list of palette indexes to change entries in the shaded range to + +static void makeTranslationList(RGB8 *palData, byte transList[NUM_GREENS]) { +	int i, j, minDistance; +	byte bestIndex; + +	for (i = 0; i < NUM_GREENS; ++i) { +		bestIndex = NORMAL_START; +		minDistance = 255; + +		uint8 findCol = palData[GREEN_START + i].g; + +		// Find the closest matching palette color +		for (j = NORMAL_START; j <= NORMAL_END; ++j) { +			int greenVal = palData[j].g; +			if (ABS(findCol - greenVal) < minDistance) { +				minDistance = ABS(findCol - greenVal); +				bestIndex = j; +			} + +			if (minDistance == 0) +				break; +		} +			 +		transList[i] = bestIndex; +	} +} + +// Support function for fading in or out + +static void fadeRange(M4Engine *vm, RGB8 *srcPal, RGB8 *destPal,  int startIndex, int endIndex,  +					 int numSteps, uint delayAmount) { +	RGB8 tempPal[256]; + +	// perform the fade +	for(int stepCtr = 1; stepCtr <= numSteps; ++stepCtr) { +		// Delay the specified amount +		uint32 startTime = g_system->getMillis(); +		while ((g_system->getMillis() - startTime) < delayAmount) { +			vm->_events->handleEvents(); +			g_system->delayMillis(10); +		} + +		for (int i = startIndex; i <= endIndex; ++i) { +			// Handle the intermediate rgb values for fading +			tempPal[i].r = (byte) (srcPal[i].r + (destPal[i].r - srcPal[i].r) * stepCtr / numSteps);    +			tempPal[i].g = (byte) (srcPal[i].g + (destPal[i].g - srcPal[i].g) * stepCtr / numSteps);  +			tempPal[i].b = (byte) (srcPal[i].b + (destPal[i].b - srcPal[i].b) * stepCtr / numSteps);  +		} +		 +		vm->_palette->setPalette(&tempPal[startIndex], startIndex, endIndex - startIndex + 1); +		vm->_viewManager->refreshAll(); +	} + +	// Make sure the end palette exactly matches what is wanted +	vm->_palette->setPalette(&destPal[startIndex], startIndex, endIndex - startIndex + 1); +} + +Palette::Palette(M4Engine *vm) : _vm(vm) { +	reset(); +	_fading_in_progress = false; +	Common::set_to(&_usageCount[0], &_usageCount[256], 0); +} + +void Palette::setPalette(const byte *colors, uint start, uint num) { +	g_system->setPalette(colors, start, num); +	reset(); +} + +void Palette::setPalette(const RGB8 *colors, uint start, uint num) { +	g_system->setPalette((const byte *)colors, start, num); +	reset(); +} + +void Palette::grabPalette(byte *colors, uint start, uint num) { +	g_system->grabPalette(colors, start, num); +	reset(); +} + +uint8 Palette::palIndexFromRgb(byte r, byte g, byte b, RGB8 *paletteData) { +	byte index = 0; +	int32 minDist = 0x7fffffff; +	RGB8 palData[256]; +	int Rdiff, Gdiff, Bdiff; + +	if (paletteData == NULL) { +		g_system->grabPalette((byte *)palData, 0, 256); +		paletteData = &palData[0]; +	} + +	for (int palIndex = 0; palIndex < 256; ++palIndex) { +		Rdiff = r - paletteData[palIndex].r; +		Gdiff = g - paletteData[palIndex].g; +		Bdiff = b - paletteData[palIndex].b; + +		if (Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff < minDist) { +			minDist = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff; +			index = (uint8)palIndex; +		} +	} + +	return (uint8)index; +} + +void Palette::reset() { +	RGB8 palData[256]; +	g_system->grabPalette((byte *)palData, 0, 256); + +	BLACK = palIndexFromRgb(0, 0, 0, palData); +	BLUE = palIndexFromRgb(0, 0, 255, palData); +	GREEN = palIndexFromRgb(0, 255, 0, palData); +	CYAN = palIndexFromRgb(0, 255, 255, palData); +	RED = palIndexFromRgb(255, 0, 0, palData); +	VIOLET = palIndexFromRgb(255, 0, 255, palData); +	BROWN = palIndexFromRgb(168, 84, 84, palData); +	LIGHT_GRAY = palIndexFromRgb(168, 168, 168, palData); +	DARK_GRAY = palIndexFromRgb(84, 84, 84, palData); +	LIGHT_BLUE = palIndexFromRgb(0, 0, 127, palData); +	LIGHT_GREEN = palIndexFromRgb(0, 127, 0, palData); +	LIGHT_CYAN = palIndexFromRgb(0, 127, 127, palData); +	LIGHT_RED = palIndexFromRgb(84, 0, 0, palData); +	PINK = palIndexFromRgb(84, 0, 0, palData); +	YELLOW = palIndexFromRgb(0, 84, 84, palData); +	WHITE = palIndexFromRgb(255, 255, 255, palData); +} + +void Palette::fadeToGreen(int numSteps, uint delayAmount) { +	if (_fading_in_progress) +		return; +	_fading_in_progress = true; +	byte translationList[NUM_GREENS]; + +	int i; +	byte *tempP; +	uint8 greenAmount = 0; +	RGB8 *srcPalette = (RGB8 *) &_originalPalette[0]; +	RGB8 *destPalette = (RGB8 *) &_fadedPalette[0]; + +	_vm->_palette->grabPalette(srcPalette, 0, 256); + +	// Create the destination 'greenish' palette to fade to by setting the green component +	// to the average of the RGB bytes, and leaving the Red and Blue parts as 0 + +	Common::copy(&srcPalette[0], &srcPalette[256], &destPalette[0]); +	for (i = 32; i < 256; ++i) { +		byte luminance = (byte)((destPalette[i].r + destPalette[i].g + destPalette[i].b) / 3); +		destPalette[i].g = MIN((byte)255, luminance); +		destPalette[i].r = destPalette[i].b = 0; +	} + +	// Handle the actual fading +	fadeRange(_vm, srcPalette, destPalette, 21, 255, numSteps, delayAmount); + +	// Create a translation table to be used in translating pixels in the game surface +	// using palette indexes in the range the range #32-63 into values from #64-255 + +	makeTranslationList(destPalette, translationList); +	 +	// Use palette indexes from #32-63 for the range of possible shades + +	for (i = GREEN_START; i <= GREEN_END; ++i, greenAmount += 8) { +		destPalette[i].g = greenAmount; +		destPalette[i].r = destPalette[i].b = 0; +	} + +	// Remap all pixels into the #32-63 range +	 +	tempP = _vm->_scene->getData(); +	for (int pixelCtr = 0; pixelCtr < _vm->_scene->width() * _vm->_scene->height();  +			++pixelCtr, ++tempP) { +		// If pixel is in #32-63 range already, remap to higher palette entries +		if ((*tempP >= GREEN_START) && (*tempP <= GREEN_END))  +			*tempP = translationList[*tempP - GREEN_START]; + +		*tempP = (uint8) (GREEN_START + (destPalette[*tempP].g >> 3)); +	} + +	_vm->_palette->setPalette(&destPalette[GREEN_START], GREEN_START, NUM_GREENS); +	_vm->_viewManager->refreshAll(); +	_fading_in_progress = false; +} + +void Palette::fadeFromGreen(int numSteps, uint delayAmount, bool fadeToBlack) { +	if (_fading_in_progress) +		return; +	_fading_in_progress = true; +	RGB8 blackPalette[256]; +	RGB8 *fadedPalette = (RGB8 *) &_fadedPalette[0]; +	RGB8 *destPalette = (RGB8 *) &_originalPalette[0]; + +	if (fadeToBlack) { +		Common::set_to((byte *)&blackPalette[0], (byte *)&blackPalette[256], 0); +		destPalette = &blackPalette[0]; +	} + +	// Initially restore the faded palette  +	_vm->_palette->setPalette(fadedPalette, 0, 256); +	_vm->_viewManager->refreshAll(); + +	// Restore the pixel data from the original screen +	_vm->_scene->update(); + +	// Handle the actual fading +	fadeRange(_vm, fadedPalette, destPalette, GREEN_START, NORMAL_END, numSteps, delayAmount); + +	_fading_in_progress = false; +} + +void Palette::fadeIn(int numSteps, uint delayAmount, RGBList *destPalette) { +	fadeIn(numSteps, delayAmount, destPalette->data(), destPalette->size()); +} + +void Palette::fadeIn(int numSteps, uint delayAmount, RGB8 *destPalette, int numColors) { +	if (_fading_in_progress) +		return; + +	_fading_in_progress = true; +	RGB8 blackPalette[256]; +	Common::set_to((byte *)&blackPalette[0], (byte *)&blackPalette[256], 0); + +	// Initially set the black palette +	_vm->_palette->setPalette(blackPalette, 0, numColors); + +	// Handle the actual fading +	fadeRange(_vm, blackPalette, destPalette, 0, numColors - 1, numSteps, delayAmount); + +	_fading_in_progress = false; +} + +RGB8 *Palette::decodeMadsPalette(Common::SeekableReadStream *palStream, int *numColors) { +	*numColors = palStream->readUint16LE(); +	assert(*numColors <= 252); + +	RGB8 *palData = new RGB8[*numColors]; +	Common::set_to((byte *)&palData[0], (byte *)&palData[*numColors], 0); + +	for (int i = 0; i < *numColors; ++i) { +		byte r = palStream->readByte(); +		byte g = palStream->readByte(); +		byte b = palStream->readByte(); +		palData[i].r = VGA_COLOR_TRANS(r); +		palData[i].g = VGA_COLOR_TRANS(g); +		palData[i].b = VGA_COLOR_TRANS(b); + +		// The next 3 bytes are unused +		palStream->skip(3); +	} + +	return palData; +} + +int Palette::setMadsPalette(Common::SeekableReadStream *palStream, int indexStart) { +	int colorCount; +	RGB8 *palData = Palette::decodeMadsPalette(palStream, &colorCount); +	_vm->_palette->setPalette(palData, indexStart, colorCount); +	delete palData; +	return colorCount; +} + +void Palette::setMadsSystemPalette() { +	// Rex Nebular default system palette +	resetColorCounts(); + +	RGB8 palData[4]; +	palData[0].r = palData[0].g = palData[0].b = 0; +	palData[1].r = palData[1].g = palData[1].b = 0x54; +	palData[2].r = palData[2].g = palData[2].b = 0xb4; +	palData[3].r = palData[3].g = palData[3].b = 0xff; +	 +	setPalette(palData, 0, 4); +	blockRange(0, 4); +} + +void Palette::resetColorCounts() { +	Common::set_to(&_usageCount[0], &_usageCount[256], 0); +} + +void Palette::blockRange(int startIndex, int size) { +	// Use a reference count of -1 to signal a palette index shouldn't be used +	Common::set_to(&_usageCount[startIndex], &_usageCount[startIndex + size], -1); +} + +void Palette::addRange(RGBList *list) { +	RGB8 *data = list->data(); +	byte *palIndexes = list->palIndexes(); +	RGB8 palData[256]; +	g_system->grabPalette((byte *)&palData[0], 0, 256); +	bool paletteChanged = false; +	 +	for (int colIndex = 0; colIndex < list->size(); ++colIndex) { +		// Scan through for an existing copy of the RGB value +		int palIndex = -1;  +		while (++palIndex < 256) { +			if (_usageCount[palIndex] <= 0) +				// Palette index is to be skipped +				continue; + +			if ((palData[palIndex].r == data[colIndex].r) &&  +				(palData[palIndex].g == data[colIndex].g) && +				(palData[palIndex].b == data[colIndex].b))  +				// Match found +				break; +		} + +		if (palIndex == 256) { +			// No match found, so find a free slot to use +			palIndex = -1; +			while (++palIndex < 256) { +				if (_usageCount[palIndex] == 0) +					break; +			} + +			if (palIndex == 256)  +				error("addRange - Ran out of palette space to allocate"); + +			palData[palIndex].r = data[colIndex].r; +			palData[palIndex].g = data[colIndex].g; +			palData[palIndex].b = data[colIndex].b; +			paletteChanged = true; +		} + +		palIndexes[colIndex] = palIndex; +		++_usageCount[palIndex]; +	} + +	if (paletteChanged) { +		g_system->setPalette((byte *)&palData[0], 0, 256); +		reset(); +	} +} + +void Palette::deleteRange(RGBList *list) { +	// Release the reference count on each of the palette entries +	for (int colIndex = 0; colIndex < list->size(); ++colIndex) { +		int palIndex = list->palIndexes()[colIndex]; +		assert(_usageCount[palIndex] > 0); +		--_usageCount[palIndex]; +	} +} + +void Palette::deleteAllRanges() { +	for (int colIndex = 0; colIndex < 255; ++colIndex) +		_usageCount[colIndex] = 0; +} + +//-------------------------------------------------------------------------- +// Support methods + +void decompressRle(byte *rleData, int rleSize, byte *celData, int w, int h) { +	byte *src = rleData; +	byte *dst = celData; +	byte len; +	while (1) { +		len = *src++; +		if (len == 0) { +			len = *src++; +			if (len <= 2) { +				if (len == 1) // end of sprite marker +					break; +			} else { +				while (len--) +					*dst++ = *src++; +			} +		} else { +			while (len--) +				*dst++ = *src; +			*src++; +		} +	} +} + +int scaleValue(int value, int scale, int err) { +	int scaled = 0; +	while (value--) { +		err -= scale; +		while (err < 0) { +			scaled++; +			err += 100; +		} +	} +	return scaled; +} + +} // End of namespace M4 | 
