1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/debug.h"
24 #include "common/rect.h"
25 
26 #include "toon/font.h"
27 
28 namespace Toon {
29 
FontRenderer(ToonEngine * vm)30 FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) {
31 	_currentFontColor[0] = 0;
32 	_currentFontColor[1] = 0xc8;
33 	_currentFontColor[2] = 0xcb;
34 	_currentFontColor[3] = 0xce;
35 
36 	_currentFont = nullptr;
37 }
38 
~FontRenderer()39 FontRenderer::~FontRenderer() {
40 }
41 
42 // mapping extended characters required for foreign versions to font (animation)
43 static const byte map_textToFont[0x80] = {
44 	 '?',  '?',  '?',  '?', 0x03,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0x8x
45 	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0x9x
46 	 '?', 0x09,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0xAx
47 	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0x0a, // 0xBx
48 	 '?',  '?',  '?',  '?', 0x1d,  '?',  '?', 0x02,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0xCx
49 	 '?', 0x0b,  '?',  '?',  '?',  '?', 0x1e,  '?',  '?',  '?',  '?', 0x20, 0x1f,  '?',  '?', 0x19, // 0xDx
50 	0x0d, 0x04, 0x0e,  '?', 0x1a,  '?',  '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx
51 	0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23  // 0xFx
52 };
53 
textToFont(byte c)54 byte FontRenderer::textToFont(byte c) {
55 	// No need to remap simple characters.
56 	if (c < 0x80)
57 		return c;
58 
59 	// The Spanish version shows grave accent over the 'e' when it should
60 	// be acute. This happens both in the original interpreter and when
61 	// using the common map which works for other languages, so we add a
62 	// special case for it.
63 	if (_vm->_language == Common::ES_ESP && c == 0xe9)
64 		return 0x10;
65 
66 	// Use the common map to convert the extended characters.
67 	return map_textToFont[c - 0x80];
68 }
69 
renderText(int16 x,int16 y,const Common::String & origText,int32 mode)70 void FontRenderer::renderText(int16 x, int16 y, const Common::String &origText, int32 mode) {
71 	debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
72 
73 	int16 xx, yy;
74 	computeSize(origText, &xx, &yy);
75 
76 	if (mode & 2) {
77 		y -= yy / 2;
78 	} else if (mode & 4) {
79 		y -= yy;
80 	}
81 
82 	if (mode & 1) {
83 		x -= xx / 2;
84 	}
85 
86 	_vm->addDirtyRect(x, y, x + xx, y + yy);
87 
88 	int16 curX = x;
89 	int16 curY = y;
90 	int32 height = 0;
91 
92 	const byte *text = (const byte *)origText.c_str();
93 	while (*text) {
94 		byte curChar = *text;
95 		if (curChar == 13) {
96 			curY = curY + height;
97 			height = 0;
98 			curX = x;
99 		} else {
100 			curChar = textToFont(curChar);
101 			_currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
102 			curX = curX + MAX<int32>(_currentFont->getFrameWidth(curChar) - 2, 0);
103 			height = MAX<int32>(height, _currentFont->getFrameHeight(curChar));
104 		}
105 		text++;
106 	}
107 }
108 
computeSize(const Common::String & origText,int16 * retX,int16 * retY)109 void FontRenderer::computeSize(const Common::String &origText, int16 *retX, int16 *retY) {
110 	debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str());
111 
112 	int16 lineWidth = 0;
113 	int16 lineHeight = 0;
114 	int16 totalHeight = 0;
115 	int16 totalWidth = 0;
116 	int16 lastLineHeight = 0;
117 
118 	const byte *text = (const byte *)origText.c_str();
119 	while (*text) {
120 		byte curChar = *text;
121 		if (curChar == 13) {
122 			totalWidth = MAX(totalWidth, lineWidth);
123 			totalHeight += lineHeight;
124 			lineHeight = 0;
125 			lineWidth = 0;
126 			lastLineHeight = 0;
127 		} else if (curChar < 32) {
128 			text++;
129 			continue;
130 		} else {
131 			curChar = textToFont(curChar);
132 			int16 charWidth = _currentFont->getFrameWidth(curChar) - 1;
133 			int16 charHeight = _currentFont->getFrameHeight(curChar);
134 			lineWidth += charWidth;
135 			lineHeight = MAX(lineHeight, charHeight);
136 
137 			// The character may be offset, so the height doesn't
138 			// really tell how far it will stick out. For now,
139 			// assume we only need to take the lower bound into
140 			// consideration.
141 			//Common::Rect charRect = _currentFont->getFrameRect(curChar);
142 			lastLineHeight = MAX(lastLineHeight, _currentFont->getHeight());
143 		}
144 		text++;
145 	}
146 
147 	totalHeight += lastLineHeight;
148 	totalWidth = MAX(totalWidth, lineWidth);
149 
150 	*retX = totalWidth;
151 	*retY = totalHeight;
152 }
153 
setFont(Animation * font)154 void FontRenderer::setFont(Animation *font) {
155 	debugC(5, kDebugFont, "setFont(font)");
156 
157 	_currentFont = font;
158 }
159 
setFontColorByCharacter(int32 characterId)160 void FontRenderer::setFontColorByCharacter(int32 characterId) {
161 	debugC(5, kDebugFont, "setFontColorByCharacter(%d)", characterId);
162 
163 	// unfortunately this table was hardcoded in the original executable
164 	static const byte colorsByCharacters[] = {
165 		0xe0, 0xdc, 0xc8,   0xd6, 0xc1, 0xc8,   0xe9, 0xde, 0xc8,   0xeb, 0xe8, 0xc8,
166 		0xd1, 0xcf, 0xc8,   0xdb, 0xd5, 0xc8,   0xfb, 0xfa, 0xc8,   0xd9, 0xd7, 0xc8,
167 		0xe8, 0xe4, 0xc8,   0xe9, 0xfa, 0xc8,   0xeb, 0xe4, 0xc8,   0xeb, 0xe4, 0xc8,
168 		0xd2, 0xea, 0xc8,   0xd3, 0xd0, 0xc8,   0xe1, 0xdd, 0xc8,   0xd9, 0xd7, 0xc8,
169 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,
170 		0xd2, 0xcf, 0xc8,   0xd1, 0xcf, 0xc8,   0xd9, 0xd7, 0xc8,   0xe3, 0xdd, 0xc8,
171 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,
172 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,
173 		0xd9, 0xd7, 0xc8,   0xe6, 0xe4, 0xc8,   0xd9, 0xd7, 0xc8,   0xcd, 0xca, 0xc8,
174 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xeb, 0xe8, 0xc8,   0xdb, 0xd5, 0xc8,
175 		0xe0, 0xdc, 0xc8,   0xd6, 0xc1, 0xc8,   0xd3, 0xd0, 0xc8,   0xd1, 0xcf, 0xc8,
176 		0xe6, 0xe4, 0xc8,   0xd1, 0xcf, 0xc8,   0xd2, 0xcf, 0xc8,   0xcc, 0xcb, 0xc8,
177 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,
178 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,
179 		0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8,   0xd9, 0xd7, 0xc8
180 	};
181 
182 	setFontColor(colorsByCharacters[characterId * 3 + 2], colorsByCharacters[characterId * 3 + 1], colorsByCharacters[characterId * 3 + 0]);
183 }
184 
setFontColor(int32 fontColor1,int32 fontColor2,int32 fontColor3)185 void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3) {
186 	debugC(5, kDebugFont, "setFontColor(%d, %d, %d)", fontColor1, fontColor2, fontColor3);
187 
188 	_currentFontColor[0] = 0;
189 	_currentFontColor[1] = fontColor1;
190 	_currentFontColor[2] = fontColor2;
191 	_currentFontColor[3] = fontColor3;
192 }
193 
renderMultiLineText(int16 x,int16 y,const Common::String & origText,int32 mode)194 void FontRenderer::renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode) {
195 	debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
196 
197 	// divide the text in several lines
198 	// based on number of characters or size of lines.
199 	byte text[1024];
200 	Common::strlcpy((char *)text, origText.c_str(), 1024);
201 
202 	byte *lines[16];
203 	int32 lineSize[16];
204 	int32 numLines = 0;
205 
206 	byte *it = text;
207 
208 	int16 maxWidth = 0;
209 	int16 curWidth = 0;
210 
211 	while (true) {
212 		byte *lastLine = it;
213 		byte *lastSpace = it;
214 		int32 lastSpaceX = 0;
215 		int32 curLetterNr = 0;
216 		curWidth = 0;
217 
218 		while (*it && curLetterNr < 50 && curWidth < 580) {
219 			byte curChar = *it;
220 			if (curChar == 32) {
221 				lastSpace = it;
222 				lastSpaceX = curWidth;
223 			} else
224 				curChar = textToFont(curChar);
225 
226 			int width = _currentFont->getFrameWidth(curChar);
227 			curWidth += MAX(width - 2, 0);
228 			it++;
229 			curLetterNr++;
230 		}
231 
232 		if (*lastLine == 0)
233 			break;
234 
235 		lines[numLines] = lastLine;
236 
237 		if (*it == 0)
238 			lineSize[numLines] = curWidth;
239 		else
240 			lineSize[numLines] = lastSpaceX;
241 
242 		if (lineSize[numLines] > maxWidth)
243 			maxWidth = lineSize[numLines];
244 
245 		lastLine = lastSpace + 1;
246 		numLines++;
247 
248 		if (*it == 0)
249 			break;
250 
251 		it = lastLine;
252 		*lastSpace = 0;
253 
254 		if (numLines >= 16)
255 			break;
256 	}
257 
258 	if (curWidth > maxWidth) {
259 		maxWidth = curWidth;
260 	}
261 	//numLines++;
262 
263 	// get font height (assumed to be constant)
264 	int16 height = _currentFont->getHeight();
265 	int32 textSize = (height - 2) * numLines;
266 	y = y - textSize;
267 	if (y < 30)
268 		y = 30;
269 	if (y + textSize > 370)
270 		y = 370 - textSize;
271 
272 	x -= _vm->state()->_currentScrollValue;
273 
274 	// adapt x
275 	if (x - 30 - maxWidth / 2 < 0)
276 		x = maxWidth / 2 + 30;
277 
278 	if (x + 30 + (maxWidth / 2) > TOON_SCREEN_WIDTH)
279 		x = TOON_SCREEN_WIDTH - (maxWidth / 2) - 30;
280 
281 	// we have good coordinates now, we can render the multi line
282 	int16 curX = x;
283 	int16 curY = y;
284 
285 	for (int32 i = 0; i < numLines; i++) {
286 		const byte *line = lines[i];
287 		curX = x - lineSize[i] / 2;
288 		_vm->addDirtyRect(curX + _vm->state()->_currentScrollValue, curY, curX + lineSize[i] + _vm->state()->_currentScrollValue + 2, curY + height);
289 
290 		while (*line) {
291 			byte curChar = textToFont(*line);
292 			if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor);
293 			curX = curX + MAX<int32>(_currentFont->getFrameWidth(curChar) - 2, 0);
294 			//height = MAX(height, _currentFont->getFrameHeight(curChar));
295 			line++;
296 		}
297 		curY += height;
298 	}
299 }
300 
301 } // End of namespace Toon
302