[Scummvm-cvs-logs] SF.net SVN: scummvm: [25345] scummvm/trunk/engines/scumm

eriktorbjorn at users.sourceforge.net eriktorbjorn at users.sourceforge.net
Sat Feb 3 06:50:36 CET 2007


Revision: 25345
          http://scummvm.svn.sourceforge.net/scummvm/?rev=25345&view=rev
Author:   eriktorbjorn
Date:     2007-02-02 21:50:35 -0800 (Fri, 02 Feb 2007)

Log Message:
-----------
Applied the revised NUT font renderer patch #1635584, which combines cyx's
patch for correct (not to mention more efficient) rendering of CMI's shadowed
letters, while retaining most of the memory savings of my original patch. This
time, SMUSH and INSANE fonts also benefit from it.

Modified Paths:
--------------
    scummvm/trunk/engines/scumm/charset.cpp
    scummvm/trunk/engines/scumm/insane/insane.cpp
    scummvm/trunk/engines/scumm/nut_renderer.cpp
    scummvm/trunk/engines/scumm/nut_renderer.h
    scummvm/trunk/engines/scumm/smush/smush_font.cpp

Modified: scummvm/trunk/engines/scumm/charset.cpp
===================================================================
--- scummvm/trunk/engines/scumm/charset.cpp	2007-02-03 01:21:49 UTC (rev 25344)
+++ scummvm/trunk/engines/scumm/charset.cpp	2007-02-03 05:50:35 UTC (rev 25345)
@@ -1626,7 +1626,7 @@
 	if (!_fr[id]) {
 		char fontname[11];
 		sprintf(fontname, "font%d.nut", id);
-		_fr[id] = new NutRenderer(_vm, fontname, true);
+		_fr[id] = new NutRenderer(_vm, fontname);
 	}
 	_current = _fr[id];
 	assert(_current);
@@ -1655,13 +1655,9 @@
 	if (chr == '@')
 		return;
 
-	shadow.left = _left - 1;
-	shadow.top = _top - 1;
+	shadow.left = _left;
+	shadow.top = _top;
 
-	// Note that the character is drawn with a shadow, so it is slightly
-	// larger than the advertised dimensions. See drawShadowChar() for
-	// details.
-
 	if (_firstChar) {
 		_str.left = (shadow.left >= 0) ? shadow.left : 0;
 		_str.top = (shadow.top >= 0) ? shadow.top : 0;
@@ -1676,8 +1672,8 @@
 	if (chr >= 256 && _vm->_useCJKMode)
 		width = _vm->_2byteWidth;
 
-	shadow.right = _left + width + 2;
-	shadow.bottom = _top + height + 2;
+	shadow.right = _left + width;
+	shadow.bottom = _top + height;
 
 	Graphics::Surface s;
 	if (!ignoreCharsetMask) {
@@ -1695,7 +1691,10 @@
 		drawTop -= _vm->_screenTop;
 	}
 
-	_current->drawShadowChar(s, chr, _left, drawTop, _color, _curId != 3);
+	if (chr >= 256 && _vm->_useCJKMode)
+		_current->draw2byte(s, chr, _left, drawTop, _color);
+	else
+		_current->drawChar(s, (byte)chr, _left, drawTop, _color);
 	_vm->markRectAsDirty(kMainVirtScreen, shadow);
 
 	if (_str.left > _left)

Modified: scummvm/trunk/engines/scumm/insane/insane.cpp
===================================================================
--- scummvm/trunk/engines/scumm/insane/insane.cpp	2007-02-03 01:21:49 UTC (rev 25344)
+++ scummvm/trunk/engines/scumm/insane/insane.cpp	2007-02-03 05:50:35 UTC (rev 25345)
@@ -65,12 +65,12 @@
 		readFileToMem("toranch.flu", &_smush_toranchFlu);
 		readFileToMem("minedriv.flu", &_smush_minedrivFlu);
 		readFileToMem("minefite.flu", &_smush_minefiteFlu);
-		_smush_bensgoggNut = new NutRenderer(_vm, "bensgogg.nut", false);
-		_smush_bencutNut = new NutRenderer(_vm, "bencut.nut", false);
+		_smush_bensgoggNut = new NutRenderer(_vm, "bensgogg.nut");
+		_smush_bencutNut = new NutRenderer(_vm, "bencut.nut");
 	}
 
-	_smush_iconsNut = new NutRenderer(_vm, "icons.nut", false);
-	_smush_icons2Nut = new NutRenderer(_vm, "icons2.nut", false);
+	_smush_iconsNut = new NutRenderer(_vm, "icons.nut");
+	_smush_icons2Nut = new NutRenderer(_vm, "icons2.nut");
 }
 
 Insane::~Insane(void) {

Modified: scummvm/trunk/engines/scumm/nut_renderer.cpp
===================================================================
--- scummvm/trunk/engines/scumm/nut_renderer.cpp	2007-02-03 01:21:49 UTC (rev 25344)
+++ scummvm/trunk/engines/scumm/nut_renderer.cpp	2007-02-03 05:50:35 UTC (rev 25345)
@@ -27,73 +27,27 @@
 
 namespace Scumm {
 
-NutRenderer::NutRenderer(ScummEngine *vm, const char *filename, bool bitmap) :
+NutRenderer::NutRenderer(ScummEngine *vm, const char *filename) :
 	_vm(vm),
-	_bitmapFont(bitmap),
 	_numChars(0),
+	_maxCharSize(0),
+	_charBuffer(0),
 	_decodedData(0) {
 	memset(_chars, 0, sizeof(_chars));
 	loadFont(filename);
 }
 
 NutRenderer::~NutRenderer() {
+	delete [] _charBuffer;
 	delete [] _decodedData;
 }
 
+void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
+
 void NutRenderer::codec1(byte *dst, const byte *src, int width, int height, int pitch) {
-	byte val, code;
-	int32 length;
-	int h = height, size_line;
-
-	for (h = 0; h < height; h++) {
-		size_line = READ_LE_UINT16(src);
-		src += 2;
-		byte bit = 0x80;
-		byte *dstPtrNext = dst + pitch;
-		while (size_line > 0) {
-			code = *src++;
-			size_line--;
-			length = (code >> 1) + 1;
-			if (code & 1) {
-				val = *src++;
-				size_line--;
-				if (_bitmapFont) {
-					for (int i = 0; i < length; i++) {
-						if (val)
-							*dst |= bit;
-						bit >>= 1;
-						if (!bit) {
-							bit = 0x80;
-							dst++;
-						}
-					}
-				} else {
-					if (val)
-						memset(dst, val, length);
-					dst += length;
-				}
-			} else {
-				size_line -= length;
-				while (length--) {
-					val = *src++;
-					if (_bitmapFont) {
-						if (val)
-							*dst |= bit;
-						bit >>= 1;
-						if (!bit) {
-							bit = 0x80;
-							dst++;
-						}
-					} else {
-						if (val)
-							*dst = val;
-						dst++;
-					}
-				}
-			}
-		}
-		dst = dstPtrNext;
-	}
+	smush_decode_codec1(dst, src, 0, 0, width, height, pitch);
+	for (int i = 0; i < width * height; i++)
+		_paletteMap[dst[i]] = 1;
 }
 
 void NutRenderer::codec21(byte *dst, const byte *src, int width, int height, int pitch) {
@@ -102,21 +56,9 @@
 		const byte *srcPtrNext = src + 2 + READ_LE_UINT16(src);
 		src += 2;
 		int len = width;
-		byte bit = 0x80;
 		do {
-			int i;
 			int offs = READ_LE_UINT16(src); src += 2;
-			if (_bitmapFont) {
-				for (i = 0; i < offs; i++) {
-					bit >>= 1;
-					if (!bit) {
-						bit = 0x80;
-						dst++;
-					}
-				}
-			} else {
-				dst += offs;
-			}
+			dst += offs;
 			len -= offs;
 			if (len <= 0) {
 				break;
@@ -130,20 +72,11 @@
 			//  src bytes equal to 255 are replaced by 0 in dst
 			//  src bytes equal to 1 are replaced by a color passed as an argument in the original function
 			//  other src bytes values are copied as-is
-			if (_bitmapFont) {
-				for (i = 0; i < w; i++) {
-					if (src[i])
-						*dst |= bit;
-					bit >>= 1;
-					if (!bit) {
-						bit = 0x80;
-						dst++;
-					}
-				}
-			} else {
-				memcpy(dst, src, w);
-				dst += w;
+			for (int i = 0; i < w; i++) {
+				_paletteMap[src[i]] = 1;
 			}
+			memcpy(dst, src, w);
+			dst += w;
 			src += w;
 		} while (len > 0);
 		dst = dstPtrNext;
@@ -181,28 +114,30 @@
 
 	uint32 offset = 0;
 	uint32 decodedLength = 0;
-	for (int l = 0; l < _numChars; l++) {
+	int l;
+	
+	_paletteMap = new byte[256];
+	for (l = 0; l < 256; l++) {
+		_paletteMap[l] = 0;
+	}
+
+	for (l = 0; l < _numChars; l++) {
 		offset += READ_BE_UINT32(dataSrc + offset + 4) + 16;
 		int width = READ_LE_UINT16(dataSrc + offset + 14);
 		int height = READ_LE_UINT16(dataSrc + offset + 16);
-		if (_bitmapFont) {
-			decodedLength += (((width + 7) / 8) * height);
-		} else {
-			decodedLength += (width * height);
-		}
+		int size = width * height;
+		decodedLength += size;
+		if (size > _maxCharSize)
+			_maxCharSize = size;
 	}
 
-	// If characters have transparency, then bytes just get skipped and
-	// so there may appear some garbage. That's why we have to fill it
-	// with zeroes first.
+	debug(1, "NutRenderer::loadFont('%s') - decodedLength = %d", filename, decodedLength);
 
 	_decodedData = new byte[decodedLength];
-	memset(_decodedData, 0, decodedLength);
-
 	byte *decodedPtr = _decodedData;
 
 	offset = 0;
-	for (int l = 0; l < _numChars; l++) {
+	for (l = 0; l < _numChars; l++) {
 		offset += READ_BE_UINT32(dataSrc + offset + 4) + 8;
 		if (READ_BE_UINT32(dataSrc + offset) != MKID_BE('FRME')) {
 			error("NutRenderer::loadFont(%s) there is no FRME chunk %d (offset %x)", filename, l, offset);
@@ -220,31 +155,110 @@
 		_chars[l].height = READ_LE_UINT16(dataSrc + offset + 16);
 		_chars[l].src = decodedPtr;
 
-		int pitch;
+		decodedPtr += (_chars[l].width * _chars[l].height);
 
-		if (_bitmapFont) {
-			pitch = (_chars[l].width + 7) / 8;
+		// If characters have transparency, then bytes just get skipped and
+		// so there may appear some garbage. That's why we have to fill it
+		// with a default color first.
+		if (codec == 44) {
+			memset(_chars[l].src, kSmush44TransparentColor, _chars[l].width * _chars[l].height);
+			_paletteMap[kSmush44TransparentColor] = 1;
+			_chars[l].transparency = kSmush44TransparentColor;
 		} else {
-			pitch = _chars[l].width;
+			memset(_chars[l].src, kDefaultTransparentColor, _chars[l].width * _chars[l].height);
+			_paletteMap[kDefaultTransparentColor] = 1;
+			_chars[l].transparency = kDefaultTransparentColor;
 		}
 
-		decodedPtr += (pitch * _chars[l].height);
-
 		const uint8 *fobjptr = dataSrc + offset + 22;
 		switch (codec) {
 		case 1:
-			codec1(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, pitch);
+			codec1(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
 			break;
 		case 21:
 		case 44:
-			codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, pitch);
+			codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
 			break;
 		default:
 			error("NutRenderer::loadFont: unknown codec: %d", codec);
 		}
 	}
 
+	// We have decoded the font. Now let's see if we can re-compress it to
+	// a more compact format. Start by counting the number of colors.
+
+	int numColors = 0;
+	for (l = 0; l < 256; l++) {
+		if (_paletteMap[l]) {
+			if (numColors < ARRAYSIZE(_palette)) {
+				_paletteMap[l] = numColors;
+				_palette[numColors] = l;
+			}
+			numColors++;
+		}
+	}
+
+	// Now _palette contains all the used colors, and _paletteMap maps the
+	// real color to the palette index.
+
+	if (numColors <= 2)
+		_bpp = 1;
+	else if (numColors <= 4)
+		_bpp = 2;
+	else if (numColors <= 16)
+		_bpp = 4;
+	else
+		_bpp = 8;
+
+	if (_bpp < 8) {
+		int compressedLength = 0;
+		for (l = 0; l < 256; l++) {
+			compressedLength += (((_bpp * _chars[l].width + 7) / 8) * _chars[l].height);
+		}
+
+		debug(1, "NutRenderer::loadFont('%s') - compressedLength = %d (%d bpp)", filename, compressedLength, _bpp);
+
+		byte *compressedData = new byte[compressedLength];
+		memset(compressedData, 0, compressedLength);
+
+		offset = 0;
+
+		for (l = 0; l < 256; l++) {
+			byte *src = _chars[l].src;
+			byte *dst = compressedData + offset;
+			int srcPitch = _chars[l].width;
+			int dstPitch = (_bpp * _chars[l].width + 7) / 8;
+
+			for (int h = 0; h < _chars[l].height; h++) {
+				byte bit = 0x80;
+				byte *nextDst = dst + dstPitch;
+				for (int w = 0; w < srcPitch; w++) {
+					byte color = _paletteMap[src[w]];
+					for (int i = 0; i < _bpp; i++) {
+						if (color & (1 << i))
+							*dst |= bit;
+						bit >>= 1;
+					}
+					if (!bit) {
+						bit = 0x80;
+						dst++;
+					}
+				}
+				src += srcPitch;
+				dst = nextDst;
+			}
+			_chars[l].src = compressedData + offset;
+			offset += (dstPitch * _chars[l].height);
+		}
+
+		delete [] _decodedData;
+		_decodedData = compressedData;
+
+		_charBuffer = new byte[_maxCharSize];
+	}
+
 	delete [] dataSrc;
+	delete [] _paletteMap;
 }
 
 int NutRenderer::getCharWidth(byte c) const {
@@ -267,46 +281,53 @@
 	return _chars[c].height;
 }
 
-void NutRenderer::drawShadowChar(const Graphics::Surface &s, int c, int x, int y, byte color, bool showShadow) {
+byte *NutRenderer::unpackChar(byte c) {
+	if (_bpp == 8)
+		return _chars[c].src;
 
-	// We draw the character a total of 7 times: 6 times shifted and in black
-	// for the shadow, and once in the right color and position. This way we
-	// achieve the exact look as the original CMI had.
-	// However, this is not how the original engine handled it. Char glyphs
-	// were compressed with codec 44. In the decoding routine, transparent
-	// pixels are skipped. Other pixels are just filled with the decoded color
-	// which can be equal to 0 (==shadow), 1 (==char color) or another value
-	// (255, 224) which is just copied as-is in the destination buffer.
+	byte *src = _chars[c].src;
+	int pitch = (_bpp * _chars[c].width + 7) / 8;
 
-	static const int offsetX[7] = { -1,  0, 1, 0, 1, 2, 0 };
-	static const int offsetY[7] = {  0, -1, 0, 1, 2, 1, 0 };
-	const int cTable[7] = { 0, 0, 0, 0, 0, 0, color };
-	int i = 0;
+	for (int ty = 0; ty < _chars[c].height; ty++) {
+		for (int tx = 0; tx < _chars[c].width; tx++) {
+			byte val;
+			int offset;
+			byte bit;
 
-	if (!showShadow)
-		i = 6;
+			switch (_bpp) {
+			case 1:
+				offset = tx / 8;
+				bit = 0x80 >> (tx % 8);
+				break;
+			case 2:
+				offset = tx / 4;
+				bit = 0x80 >> (2 * (tx % 4));
+				break;
+			default:
+				offset = tx / 2;
+				bit = 0x80 >> (4 * (tx % 2));
+				break;
+			}
 
-	for (; i < 7; i++) {
-		x += offsetX[i];
-		y += offsetY[i];
-		color = cTable[i];
+			val = 0;
 
-		if (c >= 256 && _vm->_useCJKMode)
-			draw2byte(s, c, x, y, color);
-		else
-			drawChar(s, (byte)c, x, y, color);
+			for (int i = 0; i < _bpp; i++) {
+				if (src[offset] & (bit >> i))
+					val |= (1 << i);
+			}
 
-		x -= offsetX[i];
-		y -= offsetY[i];
+			_charBuffer[ty * _chars[c].width + tx] = _palette[val];
+		}
+		src += pitch;
 	}
+
+	return _charBuffer;
 }
 
 void NutRenderer::drawFrame(byte *dst, int c, int x, int y) {
-	assert(!_bitmapFont);
-
 	const int width = MIN((int)_chars[c].width, _vm->_screenWidth - x);
 	const int height = MIN((int)_chars[c].height, _vm->_screenHeight - y);
-	const byte *src = _chars[c].src;
+	const byte *src = unpackChar(c);
 	const int srcPitch = _chars[c].width;
 	byte bits = 0;
 
@@ -339,15 +360,9 @@
 	byte *dst = (byte *)s.pixels + y * s.pitch + x;
 	const int width = MIN((int)_chars[c].width, s.w - x);
 	const int height = MIN((int)_chars[c].height, s.h - y);
-	const byte *src = _chars[c].src;
-	int srcPitch;
+	const byte *src = unpackChar(c);
+	int srcPitch = _chars[c].width;
 
-	if (_bitmapFont) {
-		srcPitch = (_chars[c].width + 7) / 8;
-	} else {
-		srcPitch = _chars[c].width;
-	}
-
 	const int minX = x < 0 ? -x : 0;
 	const int minY = y < 0 ? -y : 0;
 
@@ -361,17 +376,13 @@
 	}
 
 	for (int ty = minY; ty < height; ty++) {
-		int tx;
-
-		for (tx = minX; tx < width; tx++) {
-			if (_bitmapFont) {
-				if (src[tx / 8] & (0x80 >> (tx % 8))) {
+		for (int tx = minX; tx < width; tx++) {
+			if (src[tx] != _chars[c].transparency) {
+				if (src[tx] == 1) {
 					dst[tx] = color;
+				} else {
+					dst[tx] = src[tx];
 				}
-			} else {
-				if (src[tx] != 0) {
-					dst[tx] = color;
-				}
 			}
 		}
 		src += srcPitch;

Modified: scummvm/trunk/engines/scumm/nut_renderer.h
===================================================================
--- scummvm/trunk/engines/scumm/nut_renderer.h	2007-02-03 01:21:49 UTC (rev 25344)
+++ scummvm/trunk/engines/scumm/nut_renderer.h	2007-02-03 05:50:35 UTC (rev 25345)
@@ -31,31 +31,41 @@
 
 class NutRenderer {
 protected:
+
+	enum {
+		kDefaultTransparentColor = 0,
+		kSmush44TransparentColor = 2
+	};
+
 	ScummEngine *_vm;
-	bool _bitmapFont;
 	int _numChars;
+	int _maxCharSize;
+	byte *_charBuffer;
 	byte *_decodedData;
+	byte *_paletteMap;
+	byte _bpp;
+	byte _palette[16];
 	struct {
 		uint16 width;
 		uint16 height;
 		byte *src;
+		byte transparency;
 	} _chars[256];
 
 	void codec1(byte *dst, const byte *src, int width, int height, int pitch);
 	void codec21(byte *dst, const byte *src, int width, int height, int pitch);
 
-	void drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color);
-	void draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color);
-
 	void loadFont(const char *filename);
+	byte *unpackChar(byte c);
 
 public:
-	NutRenderer(ScummEngine *vm, const char *filename, bool bitmap);
+	NutRenderer(ScummEngine *vm, const char *filename);
 	virtual ~NutRenderer();
 	int getNumChars() const { return _numChars; }
 
 	void drawFrame(byte *dst, int c, int x, int y);
-	void drawShadowChar(const Graphics::Surface &s, int c, int x, int y, byte color, bool showShadow);
+	void drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color);
+	void draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color);
 
 	int getCharWidth(byte c) const;
 	int getCharHeight(byte c) const;

Modified: scummvm/trunk/engines/scumm/smush/smush_font.cpp
===================================================================
--- scummvm/trunk/engines/scumm/smush/smush_font.cpp	2007-02-03 01:21:49 UTC (rev 25344)
+++ scummvm/trunk/engines/scumm/smush/smush_font.cpp	2007-02-03 05:50:35 UTC (rev 25345)
@@ -30,7 +30,7 @@
 namespace Scumm {
 
 SmushFont::SmushFont(ScummEngine *vm, const char *filename, bool use_original_colors, bool new_colors) :
-	NutRenderer(vm, filename, false),
+	NutRenderer(vm, filename),
 	_color(-1),
 	_new_colors(new_colors),
 	_original(use_original_colors) {
@@ -65,7 +65,7 @@
 int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) {
 	int w = _chars[chr].width;
 	int h = _chars[chr].height;
-	const byte *src = _chars[chr].src;
+	const byte *src = unpackChar(chr);
 	byte *dst = buffer + dst_width * y + x;
 
 	assert(dst_width == _vm->_screenWidth);
@@ -74,7 +74,7 @@
 		for (int j = 0; j < h; j++) {
 			for (int i = 0; i < w; i++) {
 				int8 value = *src++;
-				if (value)
+				if (value != _chars[chr].transparency)
 					dst[i] = value;
 			}
 			dst += dst_width;
@@ -89,7 +89,7 @@
 						dst[i] = 0xFF;
 					} else if (value == -31) {
 						dst[i] = 0;
-					} else if (value) {
+					} else if (value != _chars[chr].transparency) {
 						dst[i] = value;
 					}
 				}
@@ -101,7 +101,7 @@
 					int8 value = *src++;
 					if (value == 1) {
 						dst[i] = color;
-					} else if (value) {
+					} else if (value != _chars[chr].transparency) {
 						dst[i] = 0;
 					}
 				}


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list