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