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