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 "graphics/fonts/bdf.h"
24 
25 #include "common/file.h"
26 #include "common/endian.h"
27 #include "common/textconsole.h"
28 
29 #include "graphics/surface.h"
30 
31 namespace Graphics {
32 
BdfFont(const BdfFontData & data,DisposeAfterUse::Flag dispose)33 BdfFont::BdfFont(const BdfFontData &data, DisposeAfterUse::Flag dispose)
34 	: _data(data), _dispose(dispose) {
35 }
36 
~BdfFont()37 BdfFont::~BdfFont() {
38 	if (_dispose == DisposeAfterUse::YES) {
39 		for (int i = 0; i < _data.numCharacters; ++i)
40 			delete[] _data.bitmaps[i];
41 		delete[] _data.bitmaps;
42 		delete[] _data.advances;
43 		delete[] _data.boxes;
44 		delete[] _data.familyName;
45 		delete[] _data.slant;
46 	}
47 }
48 
getFamilyName() const49 const char *BdfFont::getFamilyName() const {
50 	return _data.familyName;
51 }
52 
getFontSlant() const53 const char *BdfFont::getFontSlant() const {
54 	return _data.slant;
55 }
56 
getFontHeight() const57 int BdfFont::getFontHeight() const {
58 	return _data.height;
59 }
60 
getFontAscent() const61 int BdfFont::getFontAscent() const {
62 	return _data.ascent;
63 }
64 
getFontSize() const65 int BdfFont::getFontSize() const {
66 	return _data.size;
67 }
68 
getMaxCharWidth() const69 int BdfFont::getMaxCharWidth() const {
70 	return _data.maxAdvance;
71 }
72 
getCharWidth(uint32 chr) const73 int BdfFont::getCharWidth(uint32 chr) const {
74 	// In case all font have the same advance value, we use the maximum.
75 	if (!_data.advances)
76 		return _data.maxAdvance;
77 
78 	const int ch = mapToIndex(chr);
79 	// In case no mapping exists, we use the maximum advance.
80 	if (ch < 0)
81 		return _data.maxAdvance;
82 	else
83 		return _data.advances[ch];
84 }
85 
86 
87 template<typename PixelType>
drawCharIntern(byte * ptr,uint pitch,const byte * src,int h,int width,int minX,int maxX,const PixelType color)88 void drawCharIntern(byte *ptr, uint pitch, const byte *src, int h, int width, int minX, int maxX, const PixelType color) {
89 	byte data = 0;
90 	while (h--) {
91 		PixelType *dst = (PixelType *)ptr;
92 
93 		for (int x = 0; x < width; ++x) {
94 			if (!(x % 8))
95 				data = *src++;
96 
97 			if (x >= minX && x <= maxX && (data & 0x80))
98 				dst[x] = color;
99 
100 			data <<= 1;
101 		}
102 
103 		ptr += pitch;
104 	}
105 }
106 
mapToIndex(uint32 ch) const107 int BdfFont::mapToIndex(uint32 ch) const {
108 	// Check whether the character is included
109 	if (_data.firstCharacter <= (int)ch && (int)ch <= _data.firstCharacter + _data.numCharacters) {
110 		if (_data.bitmaps[ch - _data.firstCharacter])
111 			return ch - _data.firstCharacter;
112 	}
113 
114 	return _data.defaultCharacter - _data.firstCharacter;
115 }
116 
drawChar(Surface * dst,uint32 chr,const int tx,const int ty,const uint32 color) const117 void BdfFont::drawChar(Surface *dst, uint32 chr, const int tx, const int ty, const uint32 color) const {
118 	assert(dst != 0);
119 
120 	// TODO: Where is the relation between the max advance being smaller or
121 	// equal to 50 and the decision of the theme designer?
122 	// asserting _data.maxAdvance <= 50: let the theme designer decide what looks best
123 	//assert(_data.maxAdvance <= 50);
124 	assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
125 
126 	const int idx = mapToIndex(chr);
127 	if (idx < 0)
128 		return;
129 
130 	int width, height, xOffset, yOffset;
131 
132 	// Get the bounding box of the character
133 	if (!_data.boxes) {
134 		width = _data.defaultBox.width;
135 		height = _data.defaultBox.height;
136 		xOffset = _data.defaultBox.xOffset;
137 		yOffset = _data.defaultBox.yOffset;
138 	} else {
139 		width = _data.boxes[idx].width;
140 		height = _data.boxes[idx].height;
141 		xOffset = _data.boxes[idx].xOffset;
142 		yOffset = _data.boxes[idx].yOffset;
143 	}
144 
145 	int y = ty + _data.ascent - yOffset - height;
146 	int x = tx + xOffset;
147 
148 	const byte *src = _data.bitmaps[idx];
149 
150 	const int bytesPerRow = (width + 7) / 8;
151 	const int originalWidth = width;
152 
153 	// Make sure we do not draw outside the surface
154 	if (y < 0) {
155 		src -= y * bytesPerRow;
156 		height += y;
157 		y = 0;
158 	}
159 
160 	if (y + height > dst->h)
161 		height = dst->h - y;
162 
163 	if (height <= 0)
164 		return;
165 
166 	int xStart = 0;
167 	if (x < 0) {
168 		xStart = -x;
169 		width += x;
170 		x = 0;
171 	}
172 
173 	if (x + width > dst->w)
174 		width = dst->w - x;
175 
176 	if (width <= 0)
177 		return;
178 
179 	const int xEnd = xStart + width - 1;
180 
181 	byte *ptr = (byte *)dst->getBasePtr(x, y);
182 
183 	if (dst->format.bytesPerPixel == 1)
184 		drawCharIntern<byte>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
185 	else if (dst->format.bytesPerPixel == 2)
186 		drawCharIntern<uint16>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
187 	else if (dst->format.bytesPerPixel == 4)
188 		drawCharIntern<uint32>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
189 }
190 
191 namespace {
192 
hexToInt(char c)193 inline byte hexToInt(char c) {
194 	if (c >= '0' && c <= '9')
195 		return c - '0';
196 	else if (c >= 'A' && c <= 'F')
197 		return c - 'A' + 10;
198 	else if (c >= 'a' && c <= 'f')
199 		return c - 'a' + 10;
200 	else
201 		return 0;
202 }
203 
loadCharacter(Common::SeekableReadStream & stream,int & encoding,int & advance,BdfBoundingBox & box)204 byte *loadCharacter(Common::SeekableReadStream &stream, int &encoding, int &advance, BdfBoundingBox &box) {
205 	Common::String line;
206 	byte *bitmap = 0;
207 
208 	while (true) {
209 		line = stream.readLine();
210 		line.trim(); 	// BDF files created from unifont tools (make hex)
211 						// have a rogue space character after the "BITMAP" label
212 
213 		if (stream.err() || stream.eos()) {
214 			warning("BdfFont::loadCharacter: Premature end of file");
215 			delete[] bitmap;
216 			return 0;
217 		}
218 
219 		if (line.hasPrefix("ENCODING ")) {
220 			if (sscanf(line.c_str(), "ENCODING %d", &encoding) != 1) {
221 				warning("BdfFont::loadCharacter: Invalid ENCODING");
222 				delete[] bitmap;
223 				return 0;
224 			}
225 		} else if (line.hasPrefix("DWIDTH ")) {
226 			int yAdvance;
227 			if (sscanf(line.c_str(), "DWIDTH %d %d", &advance, &yAdvance) != 2) {
228 				warning("BdfFont::loadCharacter: Invalid DWIDTH");
229 				delete[] bitmap;
230 				return 0;
231 			}
232 
233 			if (yAdvance != 0) {
234 				warning("BdfFont::loadCharacter: Character %d has an y advance of %d", encoding, yAdvance);
235 				delete[] bitmap;
236 				return 0;
237 			}
238 
239 			if (advance < 0) {
240 				warning("BdfFont::loadCharacter: Character %d has an x advance of %d", encoding, advance);
241 				delete[] bitmap;
242 				return 0;
243 			}
244 		} else if (line.hasPrefix("BBX ")) {
245 			int width, height, xOffset, yOffset;
246 			if (sscanf(line.c_str(), "BBX %d %d %d %d",
247 			           &width, &height, &xOffset, &yOffset) != 4) {
248 				warning("BdfFont::loadCharacter: Invalid BBX");
249 				delete[] bitmap;
250 				return 0;
251 			}
252 
253 			box.width = width;
254 			box.height = height;
255 			box.xOffset = xOffset;
256 			box.yOffset = yOffset;
257 		} else if (line == "BITMAP") {
258 			const uint bytesPerRow = (box.width + 7) / 8;
259 
260 			if (bitmap) {
261 				warning("Bdf::loadCharacter(): Double BITMAP definitions");
262 				delete[] bitmap;
263 			}
264 
265 			byte *dst = bitmap = new byte[box.height * bytesPerRow];
266 
267 			for (int y = 0; y < box.height; ++y) {
268 				line = stream.readLine();
269 				if (stream.err() || stream.eos()) {
270 					warning("BdfFont::loadCharacter: Premature end of file");
271 					delete[] bitmap;
272 					return 0;
273 				}
274 
275 				if (line.size() != 2 * bytesPerRow) {
276 					warning("BdfFont::loadCharacter: Pixel line has wrong size");
277 					delete[] bitmap;
278 					return 0;
279 				}
280 
281 				for (uint x = 0; x < bytesPerRow; ++x) {
282 					char nibble1 = line[x * 2 + 0];
283 					char nibble2 = line[x * 2 + 1];
284 					*dst++ = (hexToInt(nibble1) << 4) | hexToInt(nibble2);
285 				}
286 			}
287 		} else if (line == "ENDCHAR") {
288 			return bitmap;
289 		}
290 	}
291 }
292 
freeBitmaps(byte ** bitmaps,int size)293 void freeBitmaps(byte **bitmaps, int size) {
294 	for (int i = 0; i < size; ++i)
295 		delete[] bitmaps[i];
296 }
297 
298 } // End of anonymous namespace
299 
loadFont(Common::SeekableReadStream & stream)300 BdfFont *BdfFont::loadFont(Common::SeekableReadStream &stream) {
301 	BdfFontData font;
302 	memset(&font, 0, sizeof(font));
303 	font.ascent = -1;
304 	font.defaultCharacter = -1;
305 
306 	// We only load the first 256 characters
307 	font.numCharacters = 256;
308 	byte **bitmaps = new byte *[font.numCharacters];
309 	memset(bitmaps, 0, sizeof(byte *) * font.numCharacters);
310 	byte *advances = new byte[font.numCharacters];
311 	BdfBoundingBox *boxes = new BdfBoundingBox[font.numCharacters];
312 	char *familyName = nullptr;
313 	char *slant = nullptr;
314 
315 	int descent = -1;
316 
317 	Common::String line;
318 	while (true) {
319 		line = stream.readLine();
320 		if (stream.err() || stream.eos()) {
321 			warning("BdfFont::loadFont: Premature end of file");
322 			freeBitmaps(bitmaps, font.numCharacters);
323 			delete[] bitmaps;
324 			delete[] advances;
325 			delete[] boxes;
326 			delete[] familyName;
327 			delete[] slant;
328 			return 0;
329 		}
330 
331 		// Only parse and handle declarations we actually need
332 		if (line.hasPrefix("FONTBOUNDINGBOX ")) {
333 			int width, height, xOffset, yOffset;
334 			if (sscanf(line.c_str(), "FONTBOUNDINGBOX %d %d %d %d",
335 			           &width, &height, &xOffset, &yOffset) != 4) {
336 				warning("BdfFont::loadFont: Invalid FONTBOUNDINGBOX");
337 				freeBitmaps(bitmaps, font.numCharacters);
338 				delete[] bitmaps;
339 				delete[] advances;
340 				delete[] boxes;
341 				delete[] familyName;
342 				delete[] slant;
343 				return 0;
344 			}
345 
346 			font.defaultBox.width = width;
347 			font.defaultBox.height = height;
348 			font.defaultBox.xOffset = xOffset;
349 			font.defaultBox.yOffset = yOffset;
350 		} else if (line.hasPrefix("PIXEL_SIZE ")) {
351 			if (sscanf(line.c_str(), "PIXEL_SIZE %d", &font.size) != 1) {
352 				warning("BdfFont::loadFont: Invalid PIXEL_SIZE");
353 				freeBitmaps(bitmaps, font.numCharacters);
354 				delete[] bitmaps;
355 				delete[] advances;
356 				delete[] boxes;
357 				delete[] familyName;
358 				delete[] slant;
359 				return 0;
360 			}
361 		} else if (line.hasPrefix("FONT_ASCENT ")) {
362 			if (sscanf(line.c_str(), "FONT_ASCENT %d", &font.ascent) != 1) {
363 				warning("BdfFont::loadFont: Invalid FONT_ASCENT");
364 				freeBitmaps(bitmaps, font.numCharacters);
365 				delete[] bitmaps;
366 				delete[] advances;
367 				delete[] boxes;
368 				delete[] familyName;
369 				delete[] slant;
370 				return 0;
371 			}
372 		} else if (line.hasPrefix("FONT_DESCENT ")) {
373 			if (sscanf(line.c_str(), "FONT_DESCENT %d", &descent) != 1) {
374 				warning("BdfFont::loadFont: Invalid FONT_DESCENT");
375 				freeBitmaps(bitmaps, font.numCharacters);
376 				delete[] bitmaps;
377 				delete[] advances;
378 				delete[] boxes;
379 				delete[] familyName;
380 				delete[] slant;
381 				return 0;
382 			}
383 		} else if (line.hasPrefix("DEFAULT_CHAR ")) {
384 			if (sscanf(line.c_str(), "DEFAULT_CHAR %d", &font.defaultCharacter) != 1) {
385 				warning("BdfFont::loadFont: Invalid DEFAULT_CHAR");
386 				freeBitmaps(bitmaps, font.numCharacters);
387 				delete[] bitmaps;
388 				delete[] advances;
389 				delete[] boxes;
390 				delete[] familyName;
391 				delete[] slant;
392 				return 0;
393 			}
394 		} else if (line.hasPrefix("STARTCHAR ")) {
395 			BdfBoundingBox box = font.defaultBox;
396 			int encoding = -1;
397 			int advance = -1;
398 			byte *bitmap = loadCharacter(stream, encoding, advance, box);
399 
400 			// Ignore all characters above 255.
401 			if (encoding < -1 || encoding >= font.numCharacters) {
402 				delete[] bitmap;
403 				encoding = -1;
404 			}
405 
406 			// Calculate the max advance
407 			if (encoding != -1 && advance > font.maxAdvance)
408 				font.maxAdvance = advance;
409 
410 			if (!bitmap && encoding != -1) {
411 				warning("BdfFont::loadFont: Character %d invalid", encoding);
412 				freeBitmaps(bitmaps, font.numCharacters);
413 				delete[] bitmaps;
414 				delete[] advances;
415 				delete[] boxes;
416 				delete[] familyName;
417 				delete[] slant;
418 				return 0;
419 			}
420 
421 			if (encoding != -1) {
422 				bitmaps[encoding] = bitmap;
423 				advances[encoding] = advance;
424 				boxes[encoding] = box;
425 			}
426 		} else if (line.hasPrefix("FAMILY_NAME \"")) {
427 			if (familyName != nullptr) {
428 				warning("BdfFont::loadFont: Duplicated FAMILY_NAME");
429 				delete[] familyName;
430 			}
431 			familyName = new char[line.size()];
432 			Common::strlcpy(familyName, line.c_str() + 13, line.size() - 12);	// strlcpy() copies at most size-1 characters and then add a '\0'
433 			char *p = &familyName[strlen(familyName)];
434 			while (p != familyName && *p != '"')
435 				p--;
436 			if (p == familyName) {
437 				warning("BdfFont::loadFont: Invalid FAMILY_NAME");
438 				freeBitmaps(bitmaps, font.numCharacters);
439 				delete[] bitmaps;
440 				delete[] advances;
441 				delete[] boxes;
442 				delete[] familyName;
443 				delete[] slant;
444 				return 0;
445 			}
446 			*p = '\0'; // Remove last quote
447 		} else if (line.hasPrefix("SLANT \"")) {
448 			if (slant != nullptr) {
449 				warning("BdfFont::loadFont: Duplicated SLANT");
450 				delete[] slant;
451 			}
452 			slant = new char[line.size()];
453 			Common::strlcpy(slant, line.c_str() + 7, line.size() - 6);  // strlcpy() copies at most size-1 characters and then add a '\0'
454 			char *p = &slant[strlen(slant)];
455 			while (p != slant && *p != '"')
456 				p--;
457 			if (p == slant) {
458 				warning("BdfFont::loadFont: Invalid SLANT");
459 				freeBitmaps(bitmaps, font.numCharacters);
460 				delete[] bitmaps;
461 				delete[] advances;
462 				delete[] boxes;
463 				delete[] familyName;
464 				delete[] slant;
465 				return 0;
466 			}
467 			*p = '\0'; // Remove last quote
468 		} else if (line == "ENDFONT") {
469 			break;
470 		}
471 	}
472 
473 	if (font.ascent < 0 || descent < 0) {
474 		warning("BdfFont::loadFont: Invalid ascent or descent");
475 		freeBitmaps(bitmaps, font.numCharacters);
476 		delete[] bitmaps;
477 		delete[] advances;
478 		delete[] boxes;
479 		delete[] familyName;
480 		delete[] slant;
481 		return 0;
482 	}
483 
484 	font.height = font.ascent + descent;
485 
486 	font.bitmaps = bitmaps;
487 	font.advances = advances;
488 	font.boxes = boxes;
489 	font.familyName = familyName;
490 	font.slant = slant;
491 
492 	int firstCharacter = font.numCharacters;
493 	int lastCharacter = -1;
494 	bool hasFixedBBox = true;
495 	bool hasFixedAdvance = true;
496 
497 	for (int i = 0; i < font.numCharacters; ++i) {
498 		if (!font.bitmaps[i])
499 			continue;
500 
501 		if (i < firstCharacter)
502 			firstCharacter = i;
503 
504 		if (i > lastCharacter)
505 			lastCharacter = i;
506 
507 		if (font.advances[i] != font.maxAdvance)
508 			hasFixedAdvance = false;
509 
510 		const BdfBoundingBox &bbox = font.boxes[i];
511 		if (bbox.width != font.defaultBox.width
512 		    || bbox.height != font.defaultBox.height
513 		    || bbox.xOffset != font.defaultBox.xOffset
514 		    || bbox.yOffset != font.defaultBox.yOffset)
515 			hasFixedBBox = false;
516 	}
517 
518 	if (lastCharacter == -1) {
519 		warning("BdfFont::loadFont: No glyphs found");
520 		delete[] font.bitmaps;
521 		delete[] font.advances;
522 		delete[] font.boxes;
523 		delete[] familyName;
524 		delete[] slant;
525 		return 0;
526 	}
527 
528 	// Free the advance table, in case all glyphs use the same advance
529 	if (hasFixedAdvance) {
530 		delete[] font.advances;
531 		font.advances = 0;
532 	}
533 
534 	// Free the box table, in case all glyphs use the same box
535 	if (hasFixedBBox) {
536 		delete[] font.boxes;
537 		font.boxes = 0;
538 	}
539 
540 	// Adapt for the fact that we never use encoding 0.
541 	if (font.defaultCharacter < firstCharacter
542 	    || font.defaultCharacter > lastCharacter)
543 		font.defaultCharacter = -1;
544 
545 	font.firstCharacter = firstCharacter;
546 
547 	const int charsAvailable = lastCharacter - firstCharacter + 1;
548 	// Try to compact the tables
549 	if (charsAvailable < font.numCharacters) {
550 		byte **newBitmaps = new byte *[charsAvailable];
551 		boxes = 0;
552 		advances = 0;
553 		if (!hasFixedBBox)
554 			boxes = new BdfBoundingBox[charsAvailable];
555 		if (!hasFixedAdvance)
556 			advances = new byte[charsAvailable];
557 
558 		for (int i = 0; i < charsAvailable; ++i) {
559 			const int encoding = i + firstCharacter;
560 			if (font.bitmaps[encoding]) {
561 				newBitmaps[i] = bitmaps[encoding];
562 
563 				if (!hasFixedBBox)
564 					boxes[i] = font.boxes[encoding];
565 				if (!hasFixedAdvance)
566 					advances[i] = font.advances[encoding];
567 			} else {
568 				newBitmaps[i] = 0;
569 			}
570 		}
571 
572 		delete[] font.bitmaps;
573 		font.bitmaps = newBitmaps;
574 		delete[] font.advances;
575 		font.advances = advances;
576 		delete[] font.boxes;
577 		font.boxes = boxes;
578 
579 		font.numCharacters = charsAvailable;
580 	}
581 
582 	return new BdfFont(font, DisposeAfterUse::YES);
583 }
584 
585 #define BDF_FONTCACHE_TAG MKTAG('S', 'V', 'F', 'C')
586 #define BDF_FONTCACHE_VERSION 1
587 
cacheFontData(const BdfFont & font,const Common::String & filename)588 bool BdfFont::cacheFontData(const BdfFont &font, const Common::String &filename) {
589 	Common::DumpFile cacheFile;
590 	if (!cacheFile.open(filename)) {
591 		warning("BdfFont::cacheFontData: Couldn't open file '%s' for writing", filename.c_str());
592 		return false;
593 	}
594 
595 	const BdfFontData &data = font._data;
596 
597 	cacheFile.writeUint32BE(BDF_FONTCACHE_TAG);
598 	cacheFile.writeUint32BE(BDF_FONTCACHE_VERSION);
599 	cacheFile.writeUint16BE(data.maxAdvance);
600 	cacheFile.writeByte(data.height);
601 	cacheFile.writeByte(data.defaultBox.width);
602 	cacheFile.writeByte(data.defaultBox.height);
603 	cacheFile.writeSByte(data.defaultBox.xOffset);
604 	cacheFile.writeSByte(data.defaultBox.yOffset);
605 	cacheFile.writeByte(data.ascent);
606 	cacheFile.writeUint16BE(data.firstCharacter);
607 	cacheFile.writeSint16BE(data.defaultCharacter);
608 	cacheFile.writeUint16BE(data.numCharacters);
609 
610 	for (int i = 0; i < data.numCharacters; ++i) {
611 		const BdfBoundingBox &box = data.boxes ? data.boxes[i] : data.defaultBox;
612 		if (data.bitmaps[i]) {
613 			const int bytes = ((box.width + 7) / 8) * box.height;
614 			cacheFile.writeUint32BE(bytes);
615 			cacheFile.write(data.bitmaps[i], bytes);
616 		} else {
617 			cacheFile.writeUint32BE(0);
618 		}
619 	}
620 
621 	if (data.advances) {
622 		cacheFile.writeByte(0xFF);
623 		cacheFile.write(data.advances, data.numCharacters);
624 	} else {
625 		cacheFile.writeByte(0x00);
626 	}
627 
628 	if (data.boxes) {
629 		cacheFile.writeByte(0xFF);
630 
631 		for (int i = 0; i < data.numCharacters; ++i) {
632 			const BdfBoundingBox &box = data.boxes[i];
633 			cacheFile.writeByte(box.width);
634 			cacheFile.writeByte(box.height);
635 			cacheFile.writeSByte(box.xOffset);
636 			cacheFile.writeSByte(box.yOffset);
637 		}
638 	} else {
639 		cacheFile.writeByte(0x00);
640 	}
641 
642 	return !cacheFile.err();
643 }
644 
loadFromCache(Common::SeekableReadStream & stream)645 BdfFont *BdfFont::loadFromCache(Common::SeekableReadStream &stream) {
646 	const uint32 magic = stream.readUint32BE();
647 	if (magic != BDF_FONTCACHE_TAG)
648 		return nullptr;
649 
650 	const uint32 version = stream.readUint32BE();
651 	if (version != BDF_FONTCACHE_VERSION)
652 		return nullptr;
653 
654 	BdfFontData data;
655 
656 	data.maxAdvance = stream.readUint16BE();
657 	data.height = stream.readByte();
658 	data.defaultBox.width = stream.readByte();
659 	data.defaultBox.height = stream.readByte();
660 	data.defaultBox.xOffset = stream.readSByte();
661 	data.defaultBox.yOffset = stream.readSByte();
662 	data.ascent = stream.readByte();
663 	data.firstCharacter = stream.readUint16BE();
664 	data.defaultCharacter = stream.readSint16BE();
665 	data.numCharacters = stream.readUint16BE();
666 
667 	if (stream.err() || stream.eos())
668 		return nullptr;
669 
670 	if (data.numCharacters == 0) {
671 		warning("BdfFont::loadFromCache(): Requested to load 0 characters font");
672 		return nullptr;
673 	}
674 
675 	byte **bitmaps = new byte *[data.numCharacters];
676 	byte *advances = 0;
677 	BdfBoundingBox *boxes = 0;
678 	for (int i = 0; i < data.numCharacters; ++i) {
679 		uint32 size = stream.readUint32BE();
680 
681 		if (stream.err() || stream.eos()) {
682 			for (int j = 0; j < i; ++j)
683 				delete[] bitmaps[i];
684 			delete[] bitmaps;
685 			return nullptr;
686 		}
687 
688 		if (size) {
689 			bitmaps[i] = new byte[size];
690 			stream.read(bitmaps[i], size);
691 		} else {
692 			bitmaps[i] = 0;
693 		}
694 	}
695 
696 
697 	if (stream.readByte() == 0xFF) {
698 		advances = new byte[data.numCharacters];
699 		stream.read(advances, data.numCharacters);
700 	}
701 
702 	if (stream.readByte() == 0xFF) {
703 		boxes = new BdfBoundingBox[data.numCharacters];
704 		for (int i = 0; i < data.numCharacters; ++i) {
705 			boxes[i].width = stream.readByte();
706 			boxes[i].height = stream.readByte();
707 			boxes[i].xOffset = stream.readSByte();
708 			boxes[i].yOffset = stream.readSByte();
709 		}
710 	}
711 
712 	if (stream.eos() || stream.err()) {
713 		for (int i = 0; i < data.numCharacters; ++i)
714 			delete[] bitmaps[i];
715 		delete[] bitmaps;
716 		delete[] advances;
717 		delete[] boxes;
718 		return nullptr;
719 	}
720 
721 	data.bitmaps = bitmaps;
722 	data.advances = advances;
723 	data.boxes = boxes;
724 	data.familyName = nullptr;
725 	data.slant = nullptr;
726 	data.size = data.height;
727 	return new BdfFont(data, DisposeAfterUse::YES);
728 }
729 
scaleFont(BdfFont * src,int newSize)730 BdfFont *BdfFont::scaleFont(BdfFont *src, int newSize) {
731 	if (!src) {
732 		warning("BdfFont::scaleFont(): Empty font reference in scale font");
733 		return nullptr;
734 	}
735 
736 	if (src->getFontSize() == 0) {
737 		warning("BdfFont::scaleFont(): Requested to scale 0 size font");
738 		return nullptr;
739 	}
740 
741 	if (src->_data.numCharacters == 0) {
742 		warning("BdfFont::scaleFont(): Requested to scale 0 characters font");
743 		return nullptr;
744 	}
745 
746 	float scale = (float)newSize / (float)src->getFontSize();
747 
748 	BdfFontData data;
749 
750 	data.maxAdvance = (int)((float)src->_data.maxAdvance * scale);
751 	data.height = (int)((float)src->_data.height * scale);
752 	data.size = (int)((float)src->_data.size * scale);
753 	data.defaultBox.width = (int)((float)src->_data.defaultBox.width * scale);
754 	data.defaultBox.height = (int)((float)src->_data.defaultBox.height * scale);
755 	data.defaultBox.xOffset = (int)((float)src->_data.defaultBox.xOffset * scale);
756 	data.defaultBox.yOffset = (int)((float)src->_data.defaultBox.yOffset * scale);
757 	data.ascent = (int)((float)src->_data.ascent * scale);
758 	data.firstCharacter = src->_data.firstCharacter;
759 	data.defaultCharacter = src->_data.defaultCharacter;
760 	data.numCharacters = src->_data.numCharacters;
761 	char *familyName = new char[1 + strlen(src->_data.familyName)];
762 	strcpy(familyName, src->_data.familyName);
763 	data.familyName = familyName;
764 	char *slant = new char[1 + strlen(src->_data.slant)];
765 	strcpy(slant, src->_data.slant);
766 	data.slant = slant;
767 
768 	BdfBoundingBox *boxes = new BdfBoundingBox[data.numCharacters];
769 	for (int i = 0; i < data.numCharacters; ++i) {
770 		boxes[i].width = (int)((float)src->_data.boxes[i].width * scale);
771 		boxes[i].height = (int)((float)src->_data.boxes[i].height * scale);
772 		boxes[i].xOffset = (int)((float)src->_data.boxes[i].xOffset * scale);
773 		boxes[i].yOffset = (int)((float)src->_data.boxes[i].yOffset * scale);
774 	}
775 	data.boxes = boxes;
776 
777 	byte *advances = new byte[data.numCharacters];
778 	for (int i = 0; i < data.numCharacters; ++i) {
779 		advances[i] = (int)((float)src->_data.advances[i] * scale);
780 	}
781 	data.advances = advances;
782 
783 	byte **bitmaps = new byte *[data.numCharacters];
784 
785 	for (int i = 0; i < data.numCharacters; i++) {
786 		const BdfBoundingBox &box = data.boxes ? data.boxes[i] : data.defaultBox;
787 		const BdfBoundingBox &srcBox = data.boxes ? src->_data.boxes[i] : src->_data.defaultBox;
788 
789 		if (src->_data.bitmaps[i]) {
790 			const int bytes = ((box.width + 7) / 8) * box.height; // Dimensions have been already corrected
791 			bitmaps[i] = new byte[bytes];
792 
793 			int srcPitch = (srcBox.width + 7) / 8;
794 			int dstPitch = (box.width + 7) / 8;
795 
796 			byte *ptr = bitmaps[i];
797 
798 			for (int y = 0; y < box.height; y++) {
799 				const byte *srcd = (const byte *)&src->_data.bitmaps[i][((int)((float)y / scale)) * srcPitch];
800 				byte *dst = ptr;
801 				byte b = 0;
802 
803 				for (int x = 0; x < box.width; x++) {
804 					b <<= 1;
805 
806 					int sx = (int)((float)x / scale);
807 
808 					if (srcd[sx / 8] & (0x80 >> (sx % 8)))
809 						b |= 1;
810 
811 					if (x % 8 == 7) {
812 						*dst++ = b;
813 						b = 0;
814 					}
815 				}
816 
817 				if (((box.width - 1) % 8)) {
818 					b <<= 7 - ((box.width - 1) % 8);
819 					*dst = b;
820 				}
821 
822 				ptr += dstPitch;
823 			}
824 
825 		} else {
826 			bitmaps[i] = 0;
827 		}
828 	}
829 
830 	data.bitmaps = bitmaps;
831 
832 	return new BdfFont(data, DisposeAfterUse::YES);
833 }
834 
835 } // End of namespace Graphics
836