diff options
author | Torbjörn Andersson | 2007-02-03 05:50:35 +0000 |
---|---|---|
committer | Torbjörn Andersson | 2007-02-03 05:50:35 +0000 |
commit | ea4053567806ab80037fdd5060503731e6991362 (patch) | |
tree | 1405ea18dc820b2eca8bcc2f9a434e3d05e21a26 | |
parent | 5395c4a2f4463ec2f3f9fdcc42d6f0a37f085620 (diff) | |
download | scummvm-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
-rw-r--r-- | engines/scumm/charset.cpp | 19 | ||||
-rw-r--r-- | engines/scumm/insane/insane.cpp | 8 | ||||
-rw-r--r-- | engines/scumm/nut_renderer.cpp | 315 | ||||
-rw-r--r-- | engines/scumm/nut_renderer.h | 22 | ||||
-rw-r--r-- | engines/scumm/smush/smush_font.cpp | 10 |
5 files changed, 197 insertions, 177 deletions
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 6fb1949ede..654a7fd6c3 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -1626,7 +1626,7 @@ void CharsetRendererNut::setCurID(byte id) { 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,12 +1655,8 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) { if (chr == '@') return; - shadow.left = _left - 1; - shadow.top = _top - 1; - - // Note that the character is drawn with a shadow, so it is slightly - // larger than the advertised dimensions. See drawShadowChar() for - // details. + shadow.left = _left; + shadow.top = _top; if (_firstChar) { _str.left = (shadow.left >= 0) ? shadow.left : 0; @@ -1676,8 +1672,8 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) { 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 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) { 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) diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp index fbd30fd5f9..6209a96c0a 100644 --- a/engines/scumm/insane/insane.cpp +++ b/engines/scumm/insane/insane.cpp @@ -65,12 +65,12 @@ Insane::Insane(ScummEngine_v7 *scumm) { 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) { 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]; } } } diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h index 26a3f0a302..4fb01fc572 100644 --- a/engines/scumm/nut_renderer.h +++ b/engines/scumm/nut_renderer.h @@ -31,31 +31,41 @@ class ScummEngine; 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; diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp index de7fe9c460..0b194544d8 100644 --- a/engines/scumm/smush/smush_font.cpp +++ b/engines/scumm/smush/smush_font.cpp @@ -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::getStringHeight(const char *str) { 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 @@ int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) { 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 @@ int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) { 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 @@ int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) { int8 value = *src++; if (value == 1) { dst[i] = color; - } else if (value) { + } else if (value != _chars[chr].transparency) { dst[i] = 0; } } |