aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/nut_renderer.cpp
diff options
context:
space:
mode:
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];
}
}
}