aboutsummaryrefslogtreecommitdiff
path: root/engines/draci/font.cpp
blob: 2a6e5989f095e23d39ea59a8e3fb2da5572992e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/* 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.
 *
 * $URL$
 * $Id$
 *
 */

#include "common/file.h"

#include "draci/draci.h"
#include "draci/font.h"

namespace Draci {

const Common::String kFontSmall("Small.fon");
const Common::String kFontBig("Big.fon"); 

Font::Font(const Common::String &filename) { 

	_fontHeight = 0;
	_maxCharWidth = 0;
	_charWidths = NULL;
	_charData = NULL;

	loadFont(filename);

	_currentFontColour = kFontColour1;
}

Font::~Font() {
	 freeFont(); 
}

/**
 * @brief Sets the varying font colour
 * @param colour The new font colour
 */

void Font::setColour(uint8 colour) {
	_currentFontColour = colour;
}

/**
 * @brief Loads fonts from a file
 * @param path Path to font file
 * @return true if the font was loaded successfully, false otherwise
 *
 * Loads fonts from a file into a Font instance. The original game uses two
 * fonts (located inside files "Small.fon" and "Big.fon"). The characters in the
 * font are indexed from the space character so an appropriate offset must be 
 * added to convert them to equivalent char values, i.e. kDraciIndexOffset.
 * Characters in the higher range are non-ASCII and vary between different
 * language versions of the game.
 *
 * font format: [1 byte] maximum character width
 *				[1 byte] font height
 *				[138 bytes] character widths of all 138 characters in the font
 *				[138 * fontHeight * maxWidth bytes] character data, stored row-wise 
 */

bool Font::loadFont(const Common::String &filename) {
	
	// Free previously loaded font (if any)
	freeFont();

	Common::File f;

	f.open(filename);
	if (f.isOpen()) {
		debugC(6, kDraciGeneralDebugLevel, "Opened font file %s", 
			filename.c_str());
	} else {
		debugC(6, kDraciGeneralDebugLevel, "Error opening font file %s", 
			filename.c_str());
		return false;
	}

	_maxCharWidth = f.readByte();
	_fontHeight = f.readByte();

	// Read in the widths of the glyphs	
	_charWidths = new uint8[kCharNum];
	for (unsigned int i = 0; i < kCharNum; ++i) {
		_charWidths[i] = f.readByte();
	}

	// Calculate size of font data
	unsigned int fontDataSize = kCharNum * _maxCharWidth * _fontHeight;

	// Read in all glyphs
	_charData = new byte[fontDataSize];
	f.read(_charData, fontDataSize);

	debugC(5, kDraciGeneralDebugLevel, "Font %s loaded", filename.c_str()); 

	return true;
}

void Font::freeFont() {
	delete[] _charWidths;
	delete[] _charData;
}

uint8 Font::getCharWidth(uint8 chr) const {
	return _charWidths[chr - kCharIndexOffset];
}

/**
 * @brief Draw a char to a Draci::Surface
 *
 * @param dst 	Pointer to the destination surface
 * @param chr 	Character to draw
 * @param tx  	Horizontal offset on the surface
 * @param ty  	Vertical offset on the surface
 */

void Font::drawChar(Surface *dst, uint8 chr, int tx, int ty, bool markDirty) const {
	assert(dst != NULL);
	assert(tx >= 0);
	assert(ty >= 0);

	byte *ptr = (byte *)dst->getBasePtr(tx, ty);
	uint8 charIndex = chr - kCharIndexOffset;
	int charOffset = charIndex * _fontHeight * _maxCharWidth;
	uint8 currentWidth = _charWidths[charIndex];

	// Determine how many pixels to draw horizontally (to prevent overflow)
	int xSpaceLeft = dst->w - tx - 1;	
	int xPixelsToDraw = (currentWidth < xSpaceLeft) ? currentWidth : xSpaceLeft;

	// Determine how many pixels to draw vertically
	int ySpaceLeft = dst->h - ty - 1;	
	int yPixelsToDraw = (_fontHeight < ySpaceLeft) ? _fontHeight : ySpaceLeft;

	int _transparent = dst->getTransparentColour();

	for (int y = 0; y < yPixelsToDraw; ++y) {
		for (int x = 0; x <= xPixelsToDraw; ++x) {

			int curr = y * _maxCharWidth + x;
			int colour = _charData[charOffset + curr];

			// Replace colour with font colours
			switch (colour) {

			case 254:
				colour = _currentFontColour;
				break;

			case 253:
				colour = kFontColour2;
				break;

			case 252:
				colour = kFontColour3;
				break;

			case 251:
				colour = kFontColour4;
				break;
			}
			
			// Paint pixel (if not transparent)
			if (colour != _transparent)			
				ptr[x] = colour;
		}

		// Advance to next row
		ptr += dst->pitch;	
	}

	if (markDirty) {
		Common::Rect r(tx, ty, tx + xPixelsToDraw + 1, ty + yPixelsToDraw);
		dst->markDirtyRect(r);
	}
}

/**
 * @brief Draw a string to a Draci::Surface
 *
 * @param dst 		Pointer to the destination surface
 * @param str 		Buffer containing string data
 * @param len		Length of the data
 * @param x  		Horizontal offset on the surface
 * @param y  		Vertical offset on the surface
 * @param spacing 	Space to leave between individual characters. Defaults to 0. 
 */

void Font::drawString(Surface *dst, const byte *str, uint len, 
							int x, int y, int spacing, bool markDirty) const {
	assert(dst != NULL);
	assert(x >= 0);
	assert(y >= 0);

	int curx = x;
	int cury = y;

	for (unsigned int i = 0; i < len; ++i) {

		// If we encounter the '|' char (newline and end of string marker),
		// skip it and go to the start of the next line
		if (str[i] == '|') {
			cury += getFontHeight();
			curx = x;
			continue;
		}
		
		// Break early if there's no more space on the screen	
		if (curx >= dst->w - 1 || cury >= dst->h - 1) {
			break;
		}		
		
		drawChar(dst, str[i], curx, cury, markDirty);
		curx += getCharWidth(str[i]) + spacing;
	}
}

/**
 * @brief Draw a string to a Draci::Surface
 *
 * @param dst 		Pointer to the destination surface
 * @param str 		String to draw
 * @param x  		Horizontal offset on the surface
 * @param y  		Vertical offset on the surface
 * @param spacing 	Space to leave between individual characters. Defaults to 0. 
 */

void Font::drawString(Surface *dst, const Common::String &str, 
							int x, int y, int spacing, bool markDirty) const {

	drawString(dst, (byte *) str.c_str(), str.size(), x, y, spacing, markDirty);
}

/**
 * @brief Calculate the width of a string when drawn in the current font
 *
 * @param str 		String to draw
 * @param spacing	Space to leave between individual characters. Defaults to 0. 
 *
 * @return The calculated width of the string 
 */

int Font::getStringWidth(const Common::String &str, int spacing) const {
	unsigned int width = 0;	

	// Real length, including '|' separators
	uint len = str.size();

	for (unsigned int i = 0, tmp = 0; i < len; ++i) {

		// Newline char encountered, skip it and store the new length if it is greater
		if (str[i] == '|') {
			if (tmp > width) {
				width = tmp;
			}
			continue;
		}

		uint8 charIndex = str[i] - kCharIndexOffset;
		tmp += _charWidths[charIndex];
		tmp += spacing;
	}

	return width + 1;
}

/**
 * @brief Calculate the height of a string by counting the number of '|' chars (which
 * 		  are used as newline characters and end-of-string markers)
 *
 * @param str 		String to draw
 * @param spacing	Space to leave between individual characters. Defaults to 0. 
 *
 * @return The calculated height of the string 
 */


int Font::getStringHeight(const Common::String &str) const {
	uint len = str.size();
	int separators = 0;

	for (unsigned int i = 0; i < len; ++i) {
		if (str[i] == '|') {
			++separators;
		}
	}
	
	return separators * getFontHeight();
}

} // End of namespace Draci