aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/nut_renderer.cpp
diff options
context:
space:
mode:
authorTorbjörn Andersson2007-02-03 05:50:35 +0000
committerTorbjörn Andersson2007-02-03 05:50:35 +0000
commitea4053567806ab80037fdd5060503731e6991362 (patch)
tree1405ea18dc820b2eca8bcc2f9a434e3d05e21a26 /engines/scumm/nut_renderer.cpp
parent5395c4a2f4463ec2f3f9fdcc42d6f0a37f085620 (diff)
downloadscummvm-rg350-ea4053567806ab80037fdd5060503731e6991362.tar.gz
scummvm-rg350-ea4053567806ab80037fdd5060503731e6991362.tar.bz2
scummvm-rg350-ea4053567806ab80037fdd5060503731e6991362.zip
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. svn-id: r25345
Diffstat (limited to 'engines/scumm/nut_renderer.cpp')
-rw-r--r--engines/scumm/nut_renderer.cpp315
1 files changed, 163 insertions, 152 deletions
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index 57dc8ce645..8dc3ee3ddf 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -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 NutRenderer::codec1(byte *dst, const byte *src, int width, int height, int pitch) {
- byte val, code;
- int32 length;
- int h = height, size_line;
+void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
- 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;
- }
+void NutRenderer::codec1(byte *dst, const byte *src, int width, int height, int pitch) {
+ 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 @@ void NutRenderer::codec21(byte *dst, const byte *src, int width, int height, int
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 @@ void NutRenderer::codec21(byte *dst, const byte *src, int width, int height, int
// 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 @@ void NutRenderer::loadFont(const char *filename) {
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 @@ void NutRenderer::loadFont(const char *filename) {
_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 @@ int NutRenderer::getCharHeight(byte c) const {
return _chars[c].height;
}
-void NutRenderer::drawShadowChar(const Graphics::Surface &s, int c, int x, int y, byte color, bool showShadow) {
-
- // 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.
-
- 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;
-
- if (!showShadow)
- i = 6;
-
- for (; i < 7; i++) {
- x += offsetX[i];
- y += offsetY[i];
- color = cTable[i];
-
- if (c >= 256 && _vm->_useCJKMode)
- draw2byte(s, c, x, y, color);
- else
- drawChar(s, (byte)c, x, y, color);
-
- x -= offsetX[i];
- y -= offsetY[i];
+byte *NutRenderer::unpackChar(byte c) {
+ if (_bpp == 8)
+ return _chars[c].src;
+
+ byte *src = _chars[c].src;
+ int pitch = (_bpp * _chars[c].width + 7) / 8;
+
+ for (int ty = 0; ty < _chars[c].height; ty++) {
+ for (int tx = 0; tx < _chars[c].width; tx++) {
+ byte val;
+ int offset;
+ byte bit;
+
+ 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;
+ }
+
+ val = 0;
+
+ for (int i = 0; i < _bpp; i++) {
+ if (src[offset] & (bit >> i))
+ val |= (1 << 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,14 +360,8 @@ void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byt
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;
-
- if (_bitmapFont) {
- srcPitch = (_chars[c].width + 7) / 8;
- } else {
- srcPitch = _chars[c].width;
- }
+ const byte *src = unpackChar(c);
+ int srcPitch = _chars[c].width;
const int minX = x < 0 ? -x : 0;
const int minY = y < 0 ? -y : 0;
@@ -361,16 +376,12 @@ void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byt
}
for (int ty = minY; ty < height; ty++) {
- int tx;
-
- for (tx = minX; tx < width; tx++) {
- if (_bitmapFont) {
- if (src[tx / 8] & (0x80 >> (tx % 8))) {
- dst[tx] = color;
- }
- } else {
- if (src[tx] != 0) {
+ 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];
}
}
}