/* 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. * */ #include "common/debug.h" #include "common/rect.h" #include "toon/font.h" namespace Toon { FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) { _currentFontColor[0] = 0; _currentFontColor[1] = 0xc8; _currentFontColor[2] = 0xcb; _currentFontColor[3] = 0xce; _currentFont = nullptr; } FontRenderer::~FontRenderer() { } // mapping extended characters required for foreign versions to font (animation) static const byte map_textToFont[0x80] = { '?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x '?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx '?', '?', '?', '?', 0x1d, '?', '?', 0x02, '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx '?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', 0x20, 0x1f, '?', '?', 0x19, // 0xDx 0x0d, 0x04, 0x0e, '?', 0x1a, '?', '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx 0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx }; byte FontRenderer::textToFont(byte c) { // No need to remap simple characters. if (c < 0x80) return c; // The Spanish version shows grave accent over the 'e' when it should // be acute. This happens both in the original interpreter and when // using the common map which works for other languages, so we add a // special case for it. if (_vm->_language == Common::ES_ESP && c == 0xe9) return 0x10; // Use the common map to convert the extended characters. return map_textToFont[c - 0x80]; } void FontRenderer::renderText(int16 x, int16 y, const Common::String &origText, int32 mode) { debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); int16 xx, yy; computeSize(origText, &xx, &yy); if (mode & 2) { y -= yy / 2; } else if (mode & 4) { y -= yy; } if (mode & 1) { x -= xx / 2; } _vm->addDirtyRect(x, y, x + xx, y + yy); int16 curX = x; int16 curY = y; int32 height = 0; const byte *text = (const byte *)origText.c_str(); while (*text) { byte curChar = *text; if (curChar == 13) { curY = curY + height; height = 0; curX = x; } else { curChar = textToFont(curChar); _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor); curX = curX + MAX(_currentFont->getFrameWidth(curChar) - 2, 0); height = MAX(height, _currentFont->getFrameHeight(curChar)); } text++; } } void FontRenderer::computeSize(const Common::String &origText, int16 *retX, int16 *retY) { debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str()); int16 lineWidth = 0; int16 lineHeight = 0; int16 totalHeight = 0; int16 totalWidth = 0; int16 lastLineHeight = 0; const byte *text = (const byte *)origText.c_str(); while (*text) { byte curChar = *text; if (curChar == 13) { totalWidth = MAX(totalWidth, lineWidth); totalHeight += lineHeight; lineHeight = 0; lineWidth = 0; lastLineHeight = 0; } else if (curChar < 32) { text++; continue; } else { curChar = textToFont(curChar); int16 charWidth = _currentFont->getFrameWidth(curChar) - 1; int16 charHeight = _currentFont->getFrameHeight(curChar); lineWidth += charWidth; lineHeight = MAX(lineHeight, charHeight); // The character may be offset, so the height doesn't // really tell how far it will stick out. For now, // assume we only need to take the lower bound into // consideration. //Common::Rect charRect = _currentFont->getFrameRect(curChar); lastLineHeight = MAX(lastLineHeight, _currentFont->getHeight()); } text++; } totalHeight += lastLineHeight; totalWidth = MAX(totalWidth, lineWidth); *retX = totalWidth; *retY = totalHeight; } void FontRenderer::setFont(Animation *font) { debugC(5, kDebugFont, "setFont(font)"); _currentFont = font; } void FontRenderer::setFontColorByCharacter(int32 characterId) { debugC(5, kDebugFont, "setFontColorByCharacter(%d)", characterId); // unfortunately this table was hardcoded in the original executable static const byte colorsByCharacters[] = { 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xe9, 0xde, 0xc8, 0xeb, 0xe8, 0xc8, 0xd1, 0xcf, 0xc8, 0xdb, 0xd5, 0xc8, 0xfb, 0xfa, 0xc8, 0xd9, 0xd7, 0xc8, 0xe8, 0xe4, 0xc8, 0xe9, 0xfa, 0xc8, 0xeb, 0xe4, 0xc8, 0xeb, 0xe4, 0xc8, 0xd2, 0xea, 0xc8, 0xd3, 0xd0, 0xc8, 0xe1, 0xdd, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd2, 0xcf, 0xc8, 0xd1, 0xcf, 0xc8, 0xd9, 0xd7, 0xc8, 0xe3, 0xdd, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xe6, 0xe4, 0xc8, 0xd9, 0xd7, 0xc8, 0xcd, 0xca, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xeb, 0xe8, 0xc8, 0xdb, 0xd5, 0xc8, 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xd3, 0xd0, 0xc8, 0xd1, 0xcf, 0xc8, 0xe6, 0xe4, 0xc8, 0xd1, 0xcf, 0xc8, 0xd2, 0xcf, 0xc8, 0xcc, 0xcb, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8 }; setFontColor(colorsByCharacters[characterId * 3 + 2], colorsByCharacters[characterId * 3 + 1], colorsByCharacters[characterId * 3 + 0]); } void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3) { debugC(5, kDebugFont, "setFontColor(%d, %d, %d)", fontColor1, fontColor2, fontColor3); _currentFontColor[0] = 0; _currentFontColor[1] = fontColor1; _currentFontColor[2] = fontColor2; _currentFontColor[3] = fontColor3; } void FontRenderer::renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode, Graphics::Surface &frame) { debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); // divide the text in several lines // based on number of characters or size of lines. byte text[1024]; Common::strlcpy((char *)text, origText.c_str(), 1024); byte *lines[16]; int32 lineSize[16]; int32 numLines = 0; byte *it = text; int16 maxWidth = 0; int16 curWidth = 0; while (true) { byte *lastLine = it; byte *lastSpace = it; int32 lastSpaceX = 0; int32 curLetterNr = 0; curWidth = 0; while (*it && curLetterNr < 50 && curWidth < 580) { byte curChar = *it; if (curChar == 32) { lastSpace = it; lastSpaceX = curWidth; } else curChar = textToFont(curChar); int width = _currentFont->getFrameWidth(curChar); curWidth += MAX(width - 2, 0); it++; curLetterNr++; } if (*lastLine == 0) break; lines[numLines] = lastLine; if (*it == 0) lineSize[numLines] = curWidth; else lineSize[numLines] = lastSpaceX; if (lineSize[numLines] > maxWidth) maxWidth = lineSize[numLines]; lastLine = lastSpace + 1; numLines++; if (*it == 0) break; it = lastLine; *lastSpace = 0; if (numLines >= 16) break; } if (curWidth > maxWidth) { maxWidth = curWidth; } //numLines++; // get font height (assumed to be constant) int16 height = _currentFont->getHeight(); int32 textSize = (height - 2) * numLines; y = y - textSize; if (y < 30) y = 30; if (y + textSize > 370) y = 370 - textSize; x -= _vm->state()->_currentScrollValue; // adapt x if (x - 30 - maxWidth / 2 < 0) x = maxWidth / 2 + 30; if (x + 30 + (maxWidth / 2) > TOON_SCREEN_WIDTH) x = TOON_SCREEN_WIDTH - (maxWidth / 2) - 30; // we have good coordinates now, we can render the multi line int16 curX = x; int16 curY = y; for (int32 i = 0; i < numLines; i++) { const byte *line = lines[i]; curX = x - lineSize[i] / 2; _vm->addDirtyRect(curX + _vm->state()->_currentScrollValue, curY, curX + lineSize[i] + _vm->state()->_currentScrollValue + 2, curY + height); while (*line) { byte curChar = textToFont(*line); if (curChar != 32) _currentFont->drawFontFrame(frame, curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor); curX = curX + MAX(_currentFont->getFrameWidth(curChar) - 2, 0); //height = MAX(height, _currentFont->getFrameHeight(curChar)); line++; } curY += height; } } } // End of namespace Toon