1 /*
2 Copyright (c) 2010 Peter "Corsix" Cawley
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23 #include "th_gfx_font.h"
24
25 #include "config.h"
26 #ifdef CORSIX_TH_USE_FREETYPE2
27 #include FT_GLYPH_H
28 #include <map>
29 #include <vector>
30 #endif
31 #include <algorithm>
32 #include <cstdio>
33 #include <cstring>
34 #include <cwctype>
35 #include <stdexcept>
36
37 namespace {
38
39 constexpr unsigned int invalid_char_codepoint = 0xFFFD;
40
discard_leading_set_bits(uint8_t & byte)41 size_t discard_leading_set_bits(uint8_t& byte) {
42 size_t count = 0;
43 while ((byte & 0x80) != 0) {
44 count++;
45 byte = byte << 1;
46 }
47 byte = byte >> count;
48 return count;
49 }
50
next_utf8_codepoint(const char * & sString,const char * end)51 unsigned int next_utf8_codepoint(const char*& sString, const char* end) {
52 if (sString >= end) {
53 throw std::out_of_range("pointer is outside of string");
54 }
55
56 uint8_t cur_byte = *reinterpret_cast<const uint8_t*>(sString++);
57 size_t leading_bit_count = discard_leading_set_bits(cur_byte);
58
59 if (leading_bit_count == 1 || leading_bit_count > 4) {
60 // A single leading bit is a continuation character. A utf-8 character
61 // can be at most 4 bytes long.
62 return invalid_char_codepoint;
63 }
64
65 unsigned int codepoint = cur_byte;
66 for (size_t i = 1; i < leading_bit_count; ++i) {
67 if (sString == end) {
68 return invalid_char_codepoint;
69 }
70
71 cur_byte = *reinterpret_cast<const uint8_t*>(sString++);
72 size_t continue_leading_bits = discard_leading_set_bits(cur_byte);
73
74 if (continue_leading_bits != 1) {
75 // Not enough continuation characters
76 return invalid_char_codepoint;
77 }
78 codepoint = (codepoint << 6) | cur_byte;
79 }
80 return codepoint;
81 }
82
83 constexpr uint16_t unicode_to_cp437_table[0x60] = {
84 0xFF, 0xAD, 0x9B, 0x9C, 0x3F, 0x9D, 0x3F, 0x3F, 0x3F, 0x3F, 0xA6, 0xAE,
85 0xAA, 0x3F, 0x3F, 0x3F, 0xF8, 0xF1, 0xFD, 0x3F, 0x3F, 0x3F, 0x3F, 0xFA,
86 0x3F, 0x3F, 0xA7, 0xAF, 0xAC, 0xAB, 0x3F, 0xA8, 0x3F, 0x3F, 0x3F, 0x3F,
87 0x8E, 0x8F, 0x3F, 0x80, 0x3F, 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
88 0x3F, 0xA5, 0x3F, 0x3F, 0x3F, 0x3F, 0x99, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
89 0x9A, 0x3F, 0x3F, 0xE1, 0x85, 0xA0, 0x83, 0x3F, 0x84, 0x86, 0x91, 0x87,
90 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, 0x3F, 0xA4, 0x95, 0xA2,
91 0x93, 0x3F, 0x94, 0xF6, 0x3F, 0x97, 0xA3, 0x96, 0x81, 0x3F, 0x3F, 0x98};
92
unicode_to_codepage_437(unsigned int iCodePoint)93 unsigned int unicode_to_codepage_437(unsigned int iCodePoint) {
94 if (iCodePoint < 0x80) return iCodePoint;
95 if (iCodePoint < 0xA0) return '?';
96 if (iCodePoint < 0x100) return unicode_to_cp437_table[iCodePoint - 0xA0];
97 switch (iCodePoint) {
98 case 0x0192:
99 return 0x9F;
100 case 0x0393:
101 return 0xE2;
102 case 0x0398:
103 return 0xE9;
104 case 0x03A3:
105 return 0xE4;
106 case 0x03A6:
107 return 0xE8;
108 case 0x03A9:
109 return 0xEA;
110 case 0x03B1:
111 return 0xE0;
112 case 0x03B4:
113 return 0xEB;
114 case 0x03B5:
115 return 0xEE;
116 case 0x03BC:
117 return 0xE6;
118 case 0x03C0:
119 return 0xE3;
120 case 0x03C3:
121 return 0xE5;
122 case 0x03C4:
123 return 0xE7;
124 case 0x03C6:
125 return 0xED;
126 case 0x207F:
127 return 0xFC;
128 case 0x20A7:
129 return 0x9E;
130 case 0x2219:
131 return 0xF9;
132 case 0x221A:
133 return 0xFB;
134 case 0x221E:
135 return 0xEC;
136 case 0x2229:
137 return 0xEF;
138 case 0x2248:
139 return 0xF7;
140 case 0x2261:
141 return 0xF0;
142 case 0x2264:
143 return 0xF3;
144 case 0x2265:
145 return 0xF2;
146 case 0x2310:
147 return 0xA9;
148 case 0x2320:
149 return 0xF4;
150 case 0x2321:
151 return 0xF5;
152 case 0x25A0:
153 return 0xFE;
154 }
155 return 0x3F;
156 }
157
158 #ifdef CORSIX_TH_USE_FREETYPE2
159 // Since these functions are only used when we use freetype2, this silences
160 // warnings about defined and not used.
161
decode_utf8(const char * sString,const char * end)162 unsigned int decode_utf8(const char* sString, const char* end) {
163 return next_utf8_codepoint(sString, end);
164 }
165
previous_utf8_codepoint(const char * sString)166 const char* previous_utf8_codepoint(const char* sString) {
167 do {
168 --sString;
169 } while (((*sString) & 0xC0) == 0x80);
170 return sString;
171 }
172 #endif
173
174 } // namespace
175
bitmap_font()176 bitmap_font::bitmap_font() {
177 sheet = nullptr;
178 letter_spacing = 0;
179 line_spacing = 0;
180 }
181
set_sprite_sheet(sprite_sheet * pSpriteSheet)182 void bitmap_font::set_sprite_sheet(sprite_sheet* pSpriteSheet) {
183 sheet = pSpriteSheet;
184 }
185
set_separation(int iCharSep,int iLineSep)186 void bitmap_font::set_separation(int iCharSep, int iLineSep) {
187 letter_spacing = iCharSep;
188 line_spacing = iLineSep;
189 }
190
get_text_dimensions(const char * sMessage,size_t iMessageLength,int iMaxWidth) const191 text_layout bitmap_font::get_text_dimensions(const char* sMessage,
192 size_t iMessageLength,
193 int iMaxWidth) const {
194 return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth,
195 INT_MAX, 0);
196 }
197
draw_text(render_target * pCanvas,const char * sMessage,size_t iMessageLength,int iX,int iY) const198 void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage,
199 size_t iMessageLength, int iX, int iY) const {
200 pCanvas->start_nonoverlapping_draws();
201 if (iMessageLength != 0 && sheet != nullptr) {
202 const unsigned int iFirstASCII = 31;
203 unsigned int iLastASCII =
204 static_cast<unsigned int>(sheet->get_sprite_count()) + iFirstASCII;
205 const char* sMessageEnd = sMessage + iMessageLength;
206
207 while (sMessage != sMessageEnd) {
208 unsigned int iChar =
209 unicode_to_codepage_437(next_utf8_codepoint(sMessage, sMessageEnd));
210 if (iFirstASCII <= iChar && iChar <= iLastASCII) {
211 iChar -= iFirstASCII;
212 int iWidth;
213 int iHeight;
214 sheet->draw_sprite(pCanvas, iChar, iX, iY, 0);
215 sheet->get_sprite_size_unchecked(iChar, &iWidth, &iHeight);
216 iX += iWidth + letter_spacing;
217 }
218 }
219 }
220 pCanvas->finish_nonoverlapping_draws();
221 }
222
draw_text_wrapped(render_target * pCanvas,const char * sMessage,size_t iMessageLength,int iX,int iY,int iWidth,int iMaxRows,int iSkipRows,text_alignment eAlign) const223 text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas,
224 const char* sMessage,
225 size_t iMessageLength, int iX,
226 int iY, int iWidth, int iMaxRows,
227 int iSkipRows,
228 text_alignment eAlign) const {
229 text_layout oDrawArea = {};
230 int iSkippedRows = 0;
231 if (iMessageLength != 0 && sheet != nullptr) {
232 const unsigned int iFirstASCII = 31;
233 unsigned int iLastASCII =
234 static_cast<unsigned int>(sheet->get_sprite_count()) + iFirstASCII;
235 const char* sMessageEnd = sMessage + iMessageLength;
236
237 while (sMessage != sMessageEnd && oDrawArea.row_count < iMaxRows) {
238 const char* sBreakPosition = sMessageEnd;
239 const char* sLastGoodBreakPosition = sBreakPosition;
240 int iMsgWidth = -letter_spacing;
241 int iMsgBreakWidth = iMsgWidth;
242 int iTallest = 0;
243 const char* s;
244 bool foundNewLine = false;
245 unsigned int iNextChar = 0;
246
247 for (s = sMessage; s != sMessageEnd;) {
248 const char* sOld = s;
249 unsigned int iChar =
250 unicode_to_codepage_437(next_utf8_codepoint(s, sMessageEnd));
251 iNextChar = unicode_to_codepage_437(static_cast<unsigned char>(*s));
252 if ((iChar == '\n' && iNextChar == '\n') ||
253 (iChar == '/' && iNextChar == '/')) {
254 foundNewLine = true;
255 iMsgBreakWidth = iMsgWidth;
256 sBreakPosition = sOld;
257 break;
258 }
259 int iCharWidth = 0;
260 int iCharHeight = 0;
261 if (iFirstASCII <= iChar && iChar <= iLastASCII) {
262 sheet->get_sprite_size_unchecked(iChar - iFirstASCII, &iCharWidth,
263 &iCharHeight);
264 }
265 iMsgWidth += letter_spacing + iCharWidth;
266 if (iChar == ' ') {
267 sLastGoodBreakPosition = sOld;
268 iMsgBreakWidth = iMsgWidth - iCharWidth;
269 }
270
271 if (iMsgWidth > iWidth) {
272 sBreakPosition = sLastGoodBreakPosition;
273 break;
274 }
275 if (iCharHeight > iTallest) iTallest = iCharHeight;
276 }
277
278 if (s == sMessageEnd) iMsgBreakWidth = iMsgWidth;
279 if (iMsgBreakWidth > oDrawArea.width) oDrawArea.width = iMsgBreakWidth;
280
281 if (iSkippedRows >= iSkipRows) {
282 if (pCanvas) {
283 int iXOffset = 0;
284 if (iMsgBreakWidth < iWidth)
285 iXOffset = (iWidth - iMsgBreakWidth) * static_cast<int>(eAlign) / 2;
286 draw_text(pCanvas, sMessage, sBreakPosition - sMessage, iX + iXOffset,
287 iY);
288 }
289 iY += static_cast<int>(iTallest) + line_spacing;
290 oDrawArea.end_x = iMsgWidth;
291 oDrawArea.row_count++;
292 if (foundNewLine) {
293 iY += static_cast<int>(iTallest) + line_spacing;
294 oDrawArea.row_count++;
295 }
296 } else {
297 iSkippedRows++;
298 if (foundNewLine) {
299 if (iSkippedRows == iSkipRows) {
300 iY += static_cast<int>(iTallest) + line_spacing;
301 oDrawArea.row_count++;
302 }
303 iSkippedRows++;
304 }
305 }
306 sMessage = sBreakPosition;
307 if (sMessage != sMessageEnd) {
308 next_utf8_codepoint(sMessage, sMessageEnd);
309 if (foundNewLine) {
310 next_utf8_codepoint(sMessage, sMessageEnd);
311 }
312 }
313 }
314 }
315 oDrawArea.end_x = iX + oDrawArea.end_x;
316 oDrawArea.end_y = iY;
317 return oDrawArea;
318 }
319
320 #ifdef CORSIX_TH_USE_FREETYPE2
321 FT_Library freetype_font::freetype_library = nullptr;
322 int freetype_font::freetype_init_count = 0;
323
freetype_font()324 freetype_font::freetype_font() {
325 font_face = nullptr;
326 is_done_freetype_init = false;
327 for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2);
328 ++pEntry) {
329 pEntry->message = nullptr;
330 pEntry->message_length = 0;
331 pEntry->message_buffer_length = 0;
332 pEntry->alignment = text_alignment::left;
333 pEntry->width = 0;
334 pEntry->height = 0;
335 pEntry->widest_line_width = 0;
336 pEntry->last_x = 0;
337 pEntry->data = nullptr;
338 pEntry->is_valid = false;
339 pEntry->texture = nullptr;
340 }
341 }
342
~freetype_font()343 freetype_font::~freetype_font() {
344 for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2);
345 ++pEntry) {
346 delete[] pEntry->message;
347 delete[] pEntry->data;
348 free_texture(pEntry);
349 }
350 if (font_face != nullptr) FT_Done_Face(font_face);
351 if (is_done_freetype_init) {
352 if (--freetype_init_count == 0) {
353 FT_Done_FreeType(freetype_library);
354 freetype_library = nullptr;
355 }
356 }
357 }
358
get_copyright_notice()359 const char* freetype_font::get_copyright_notice() {
360 return "Portions of this software are copyright \xC2\xA9 2010 "
361 "The FreeType Project (www.freetype.org). All rights reserved.";
362 }
363
initialise()364 FT_Error freetype_font::initialise() {
365 if (is_done_freetype_init) return FT_Err_Ok;
366 if (freetype_init_count == 0) {
367 int iError = FT_Init_FreeType(&freetype_library);
368 if (iError != FT_Err_Ok) return iError;
369 }
370 ++freetype_init_count;
371 is_done_freetype_init = true;
372 return FT_Err_Ok;
373 }
374
clear_cache()375 void freetype_font::clear_cache() {
376 for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2);
377 ++pEntry) {
378 pEntry->is_valid = false;
379 free_texture(pEntry);
380 }
381 }
382
set_face(const uint8_t * pData,size_t iLength)383 FT_Error freetype_font::set_face(const uint8_t* pData, size_t iLength) {
384 int iError;
385 if (freetype_library == nullptr) {
386 iError = initialise();
387 if (iError != FT_Err_Ok) return iError;
388 }
389 if (font_face) {
390 iError = FT_Done_Face(font_face);
391 if (iError != FT_Err_Ok) return iError;
392 font_face = nullptr;
393 }
394 iError = FT_New_Memory_Face(freetype_library, pData,
395 static_cast<FT_Long>(iLength), 0, &font_face);
396 return iError;
397 }
398
match_bitmap_font(sprite_sheet * pBitmapFontSpriteSheet)399 FT_Error freetype_font::match_bitmap_font(
400 sprite_sheet* pBitmapFontSpriteSheet) {
401 if (pBitmapFontSpriteSheet == nullptr) return FT_Err_Invalid_Argument;
402
403 // Try to take the size and colour of a standard character (em is generally
404 // the standard font character, but for fonts which only have numbers, zero
405 // seems like the next best choice).
406 for (const char* sCharToTry = "M0"; *sCharToTry; ++sCharToTry) {
407 int iWidth;
408 int iHeight;
409 unsigned int iSprite = *sCharToTry - 31;
410 if (pBitmapFontSpriteSheet->get_sprite_size(iSprite, &iWidth, &iHeight) &&
411 pBitmapFontSpriteSheet->get_sprite_average_colour(iSprite, &colour) &&
412 iWidth > 1 && iHeight > 1) {
413 return set_ideal_character_size(iWidth, iHeight);
414 }
415 }
416
417 // Take the average size of all characters, and the colour of one of them.
418 int iWidthSum = 0;
419 int iHeightSum = 0;
420 int iAverageNum = 0;
421 for (size_t i = 0; i < pBitmapFontSpriteSheet->get_sprite_count(); ++i) {
422 int iWidth;
423 int iHeight;
424 pBitmapFontSpriteSheet->get_sprite_size_unchecked(i, &iWidth, &iHeight);
425 if (iWidth <= 1 || iHeight <= 1) continue;
426 if (!pBitmapFontSpriteSheet->get_sprite_average_colour(i, &colour))
427 continue;
428 iWidthSum += iWidth;
429 iHeightSum += iHeight;
430 ++iAverageNum;
431 }
432 if (iAverageNum == 0) return FT_Err_Divide_By_Zero;
433
434 return set_ideal_character_size((iWidthSum + iAverageNum / 2) / iAverageNum,
435 (iHeightSum + iAverageNum / 2) / iAverageNum);
436 }
437
set_ideal_character_size(int iWidth,int iHeight)438 FT_Error freetype_font::set_ideal_character_size(int iWidth, int iHeight) {
439 if (font_face == nullptr) return FT_Err_Invalid_Face_Handle;
440
441 if (is_monochrome() || iHeight <= 14 || iWidth <= 9) {
442 // Look for a bitmap strike of a similar size
443 int iBestBitmapScore = 50;
444 FT_Int iBestBitmapIndex = -1;
445 for (FT_Int i = 0; i < font_face->num_fixed_sizes; ++i) {
446 if (font_face->available_sizes[i].height > iHeight) continue;
447 int iDeltaH = iHeight - font_face->available_sizes[i].height;
448 int iDeltaW = font_face->available_sizes[i].width - iWidth;
449 int iScore = iDeltaH * iDeltaH * 3 + iDeltaW * iDeltaW;
450 if (iScore < iBestBitmapScore) {
451 iBestBitmapScore = iScore;
452 iBestBitmapIndex = i;
453 }
454 }
455
456 // Select the bitmap strike, if there was one
457 if (iBestBitmapIndex != -1)
458 return FT_Select_Size(font_face, iBestBitmapIndex);
459 }
460
461 // Go with the original size request if there was no bitmap strike, unless
462 // the size was very small, in which case scale things up, as vector fonts
463 // look rather poor at small sizes.
464 if (iHeight < 14) {
465 iWidth = iWidth * 14 / iHeight;
466 iHeight = 14;
467 }
468 if (iWidth < 9) {
469 iHeight = iHeight * 9 / iWidth;
470 iWidth = 9;
471 }
472 return FT_Set_Pixel_Sizes(font_face, iWidth, iHeight);
473 }
474
get_text_dimensions(const char * sMessage,size_t iMessageLength,int iMaxWidth) const475 text_layout freetype_font::get_text_dimensions(const char* sMessage,
476 size_t iMessageLength,
477 int iMaxWidth) const {
478 return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth,
479 INT_MAX, 0);
480 }
481
draw_text(render_target * pCanvas,const char * sMessage,size_t iMessageLength,int iX,int iY) const482 void freetype_font::draw_text(render_target* pCanvas, const char* sMessage,
483 size_t iMessageLength, int iX, int iY) const {
484 draw_text_wrapped(pCanvas, sMessage, iMessageLength, iX, iY, INT_MAX);
485 }
486
487 namespace {
488
489 struct codepoint_glyph {
490 FT_Glyph_Metrics metrics;
491 FT_Glyph glyph;
492 FT_UInt index;
493 };
494
495 enum class CJK_breakable { nonbreakable = 0, break_after, break_before };
496
497 // Determine if the character code is a suitable Chinese/Japanese/Korean
498 // character for a line break.
isCjkBreakCharacter(unsigned int charcode)499 CJK_breakable isCjkBreakCharacter(unsigned int charcode) {
500 if (charcode == 0x3000 || // Ideographic space
501 charcode == 0x3001 || // Ideographic comma
502 charcode == 0x3002 || // Ideographic full stop
503 charcode == 0x301e || // Double prime quotation mark
504 charcode == 0xff09 || // Fullwidth right parenthesis
505 charcode == 0xff0c || // Fullwidth comma
506 charcode == 0xff0d || // Fullwidth hyphen-minus
507 charcode == 0xff1a || // Fullwidth Colon
508 charcode == 0xff1b || // Fullwidth semicolon
509 charcode == 0xff1f) // Fullwidth question mark
510 return CJK_breakable::break_after;
511 if (charcode == 0x301d || // Reversed double prime quotation mark
512 charcode == 0xff08) // Fullwidth left parenthesis
513 return CJK_breakable::break_before;
514 return CJK_breakable::nonbreakable;
515 }
516
pixel_align(FT_Pos position)517 FT_Pos pixel_align(FT_Pos position) { return ((position + 63) >> 6) << 6; }
518
519 } // namespace
520
draw_text_wrapped(render_target * pCanvas,const char * sMessage,size_t iMessageLength,int iX,int iY,int iWidth,int iMaxRows,int iSkipRows,text_alignment eAlign) const521 text_layout freetype_font::draw_text_wrapped(render_target* pCanvas,
522 const char* sMessage,
523 size_t iMessageLength, int iX,
524 int iY, int iWidth, int iMaxRows,
525 int iSkipRows,
526 text_alignment eAlign) const {
527 text_layout oDrawArea = {};
528 int iNumRows = 0;
529 int iHandledRows = 0;
530
531 // Calculate an index into the cache to use for this piece of text.
532 size_t iHash = iMessageLength +
533 (static_cast<size_t>(iMaxRows) << (cache_size_log2 / 8)) +
534 (static_cast<size_t>(iSkipRows) << (cache_size_log2 / 4)) +
535 (static_cast<size_t>(iWidth) << (cache_size_log2 / 2)) +
536 (static_cast<size_t>(eAlign) << cache_size_log2);
537 for (size_t i = 0; i < iMessageLength; ++i)
538 iHash ^= (iHash << 5) + (iHash >> 2) + static_cast<size_t>(sMessage[i]);
539 iHash &= (1 << cache_size_log2) - 1;
540
541 cached_text* pEntry = cache + iHash;
542 if (pEntry->message_length != iMessageLength || pEntry->width > iWidth ||
543 (iWidth != INT_MAX && pEntry->width < iWidth) ||
544 pEntry->alignment != eAlign || !pEntry->is_valid ||
545 std::memcmp(pEntry->message, sMessage, iMessageLength) != 0) {
546 // Cache entry does not match the message being drawn, so discard the
547 // cache entry.
548 free_texture(pEntry);
549 delete[] pEntry->data;
550 pEntry->data = nullptr;
551 pEntry->is_valid = false;
552
553 // Set the entry metadata to that of the new message.
554 if (iMessageLength > pEntry->message_buffer_length) {
555 delete[] pEntry->message;
556 pEntry->message = new char[iMessageLength];
557 pEntry->message_buffer_length = iMessageLength;
558 }
559 std::memcpy(pEntry->message, sMessage, iMessageLength);
560 pEntry->message_length = iMessageLength;
561 pEntry->width = iWidth;
562 pEntry->alignment = eAlign;
563
564 // Split the message into lines, and determine the position within the
565 // line for each character.
566 std::vector<std::pair<const char*, const char*> > vLines;
567 std::vector<FT_Vector> vCharPositions(iMessageLength);
568 std::map<unsigned int, codepoint_glyph> mapGlyphs;
569 vLines.reserve(2);
570
571 FT_Vector ftvPen = {0, 0};
572 FT_Bool bUseKerning = FT_HAS_KERNING(font_face);
573 FT_UInt iPreviousGlyphIndex = 0;
574
575 const char* sMessageStart = sMessage;
576 const char* sMessageEnd = sMessage + iMessageLength;
577 const char* sLineStart = sMessageStart;
578 const char* sLineBreakPosition = sLineStart;
579
580 while (sMessage != sMessageEnd) {
581 const char* sOldMessage = sMessage;
582 unsigned int iCode = next_utf8_codepoint(sMessage, sMessageEnd);
583 unsigned int iNextCode =
584 *reinterpret_cast<const unsigned char*>(sMessage);
585 bool bIsNewLine = (iCode == '\n' && iNextCode == '\n') ||
586 (iCode == '/' && iNextCode == '/');
587 // Just replace single line breaks with space.
588 if (!bIsNewLine && iCode == '\n') {
589 iCode = ' ';
590 }
591
592 codepoint_glyph& oGlyph = mapGlyphs[iCode];
593 if (oGlyph.glyph == nullptr) {
594 oGlyph.index = FT_Get_Char_Index(font_face, iCode);
595
596 /* FT_Error iError = */
597 FT_Load_Glyph(font_face, oGlyph.index, FT_LOAD_DEFAULT);
598 // TODO: iError != FT_Err_Ok
599
600 /* iError = */
601 FT_Get_Glyph(font_face->glyph, &oGlyph.glyph);
602 // TODO: iError != FT_Err_Ok
603
604 oGlyph.metrics = font_face->glyph->metrics;
605 }
606
607 // Apply kerning
608 if (bUseKerning && iPreviousGlyphIndex && oGlyph.index) {
609 FT_Vector ftvKerning;
610 FT_Get_Kerning(font_face, iPreviousGlyphIndex, oGlyph.index,
611 FT_KERNING_DEFAULT, &ftvKerning);
612 ftvPen.x += ftvKerning.x;
613 ftvPen.y += ftvKerning.y;
614 }
615
616 // Make an automatic line break if one is needed.
617 long line_width_with_glyph =
618 (ftvPen.x + oGlyph.metrics.horiBearingX + oGlyph.metrics.width + 63) /
619 64;
620 if (line_width_with_glyph >= iWidth || bIsNewLine) {
621 if (bIsNewLine) {
622 sLineBreakPosition = sOldMessage;
623 }
624 ftvPen.x = ftvPen.y = 0;
625 iPreviousGlyphIndex = 0;
626 if (sLineStart != sLineBreakPosition) {
627 // Only really save if we have skipped enough lines
628 if (iHandledRows >= iSkipRows) {
629 vLines.push_back(std::make_pair(sLineStart, sLineBreakPosition));
630 }
631 if (bIsNewLine) {
632 if (iHandledRows + 1 >= iSkipRows) {
633 vLines.push_back(
634 std::make_pair(sLineBreakPosition, sLineBreakPosition));
635 }
636 next_utf8_codepoint(sLineBreakPosition, sMessageEnd);
637 iHandledRows++;
638 }
639 sMessage = sLineBreakPosition;
640 // Skip one char only when it's .. (maybe a CJK character)
641 if (std::iswspace(iCode) || // Whitespace characters
642 iCode == 0x3000) { // Ideographic space
643 next_utf8_codepoint(sMessage, sMessageEnd);
644 }
645 sLineStart = sMessage;
646 } else {
647 if (iHandledRows >= iSkipRows) {
648 vLines.push_back(std::make_pair(sLineStart, sOldMessage));
649 }
650 if (bIsNewLine) {
651 next_utf8_codepoint(sMessage, sMessageEnd);
652 sLineStart = sLineBreakPosition = sMessage;
653 } else {
654 sMessage = sLineStart = sLineBreakPosition = sOldMessage;
655 }
656 }
657 iHandledRows++;
658 continue;
659 }
660
661 // Determine if a line can be broken at the current position.
662 if (iCode == ' ') {
663 sLineBreakPosition = sOldMessage;
664 } else {
665 switch (isCjkBreakCharacter(iCode)) {
666 case CJK_breakable::break_after:
667 // break after this codepoint (cjk codepoints are 3 bytes in
668 // utf-8)
669 sLineBreakPosition = sOldMessage + 3;
670 break;
671 case CJK_breakable::break_before:
672 // break before this codepoint
673 sLineBreakPosition = sOldMessage;
674 break;
675 default:
676 break;
677 }
678 }
679
680 // Save (unless we are skipping lines) and advance the pen.
681 if (iHandledRows >= iSkipRows) {
682 vCharPositions[sOldMessage - sMessageStart] = ftvPen;
683 }
684
685 iPreviousGlyphIndex = oGlyph.index;
686 ftvPen.x += oGlyph.metrics.horiAdvance;
687 }
688 if (sLineStart != sMessageEnd)
689 vLines.push_back(std::make_pair(sLineStart, sMessageEnd));
690 sMessage = sMessageStart;
691
692 // Finalise the position of each character (alignment might change X,
693 // and baseline / lines will change Y), and calculate overall height
694 // and widest line.
695 FT_Pos iPriorLinesHeight = 0;
696 FT_Pos iLineWidth = 0, iAlignDelta = 0, iWidestLine = 0;
697 const FT_Pos iLineSpacing = 2 << 6;
698 codepoint_glyph& oGlyph = mapGlyphs['l'];
699 FT_Pos iBearingY = oGlyph.metrics.horiBearingY;
700 FT_Pos iNormalLineHeight = oGlyph.metrics.height - iBearingY;
701
702 iBearingY = pixel_align(iBearingY);
703 iNormalLineHeight += iBearingY;
704 iNormalLineHeight += iLineSpacing;
705
706 iNormalLineHeight = pixel_align(iNormalLineHeight);
707
708 for (auto itr = vLines.begin(); itr != vLines.end() && iNumRows < iMaxRows;
709 ++itr) {
710 // Calculate the X change resulting from alignment.
711 const char* sLastChar = previous_utf8_codepoint(itr->second);
712 codepoint_glyph& oLastGlyph =
713 mapGlyphs[decode_utf8(sLastChar, sMessageEnd)];
714 iLineWidth = vCharPositions[sLastChar - sMessage].x +
715 oLastGlyph.metrics.horiBearingX + oLastGlyph.metrics.width;
716 if ((iLineWidth >> 6) < iWidth) {
717 iAlignDelta =
718 ((iWidth * 64 - iLineWidth) * static_cast<int>(eAlign)) / 2;
719 }
720 if (iLineWidth > iWidestLine) iWidestLine = iLineWidth;
721
722 // Calculate the line height and baseline position.
723 FT_Pos iLineHeight = 0;
724 FT_Pos iBaselinePos = 0;
725 for (const char* s = itr->first; s < itr->second;) {
726 codepoint_glyph& oGlyph =
727 mapGlyphs[next_utf8_codepoint(s, sMessageEnd)];
728 FT_Pos iBearingY = oGlyph.metrics.horiBearingY;
729 FT_Pos iCoBearingY = oGlyph.metrics.height - iBearingY;
730 if (iBearingY > iBaselinePos) iBaselinePos = iBearingY;
731 if (iCoBearingY > iLineHeight) iLineHeight = iCoBearingY;
732 }
733
734 iBaselinePos = pixel_align(iBaselinePos);
735 iLineHeight += iBaselinePos;
736 iLineHeight += iLineSpacing;
737 iLineHeight = pixel_align(iLineHeight);
738
739 iNormalLineHeight = std::max(iNormalLineHeight, iLineHeight);
740
741 // Apply the character position changes.
742 for (const char* s = itr->first; s < itr->second;
743 next_utf8_codepoint(s, sMessageEnd)) {
744 FT_Vector& ftvPos = vCharPositions[s - sMessage];
745 ftvPos.x += iAlignDelta;
746 ftvPos.y += iBaselinePos + iPriorLinesHeight;
747 }
748 // Empty lines is a special case
749 if (itr->first == itr->second) {
750 iPriorLinesHeight += iNormalLineHeight;
751 } else {
752 iPriorLinesHeight += iLineHeight;
753 }
754 iNumRows++;
755 }
756 if (iPriorLinesHeight > 0) iPriorLinesHeight -= iLineSpacing;
757 pEntry->height = static_cast<int>(1 + (iPriorLinesHeight >> 6));
758 pEntry->widest_line_width = static_cast<int>(1 + (iWidestLine >> 6));
759 pEntry->row_count = iNumRows;
760 if (iWidth == INT_MAX) pEntry->width = pEntry->widest_line_width;
761 pEntry->last_x = 1 + (static_cast<int>(iLineWidth + iAlignDelta) >> 6);
762
763 // Get a bitmap for each glyph.
764 bool bIsMonochrome = is_monochrome();
765 FT_Render_Mode eRenderMode =
766 bIsMonochrome ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL;
767 for (auto itr = mapGlyphs.begin(), itrEnd = mapGlyphs.end(); itr != itrEnd;
768 ++itr) {
769 FT_Glyph_To_Bitmap(&itr->second.glyph, eRenderMode, nullptr, 1);
770 }
771
772 // Prepare a canvas for rendering.
773 pEntry->data = new uint8_t[pEntry->width * pEntry->height];
774 std::memset(pEntry->data, 0, pEntry->width * pEntry->height);
775
776 int iDrawnLines = 0;
777 // Render each character to the canvas.
778 for (auto itr = vLines.begin();
779 itr != vLines.end() && iDrawnLines < iMaxRows + iSkipRows; ++itr) {
780 iDrawnLines++;
781 for (const char* s = itr->first; s < itr->second;) {
782 FT_Vector& ftvPos = vCharPositions[s - sMessage];
783 unsigned int iCode = next_utf8_codepoint(s, sMessageEnd);
784 if (iCode == '\n') {
785 iCode = ' ';
786 }
787 FT_BitmapGlyph pGlyph =
788 reinterpret_cast<FT_BitmapGlyph>(mapGlyphs[iCode].glyph);
789 FT_Pos x = pGlyph->left + (ftvPos.x >> 6);
790 FT_Pos y = (ftvPos.y >> 6) - pGlyph->top;
791 // We may have asked for grayscale but been given monochrome,
792 // hence use the bitmap's pixel_mode rather than bIsMonochrome.
793 switch (pGlyph->bitmap.pixel_mode) {
794 case FT_PIXEL_MODE_GRAY:
795 render_gray(pEntry, &pGlyph->bitmap, x, y);
796 break;
797 case FT_PIXEL_MODE_MONO:
798 render_mono(pEntry, &pGlyph->bitmap, x, y);
799 break;
800 }
801 }
802 }
803
804 // Free all glyphs.
805 for (auto itr = mapGlyphs.begin(); itr != mapGlyphs.end(); ++itr) {
806 FT_Done_Glyph(itr->second.glyph);
807 }
808
809 pEntry->is_valid = true;
810 }
811
812 if (pCanvas != nullptr) {
813 if (pEntry->texture == nullptr) {
814 make_texture(pCanvas, pEntry);
815 }
816 draw_texture(pCanvas, pEntry, iX, iY);
817 }
818 oDrawArea.width = pEntry->widest_line_width;
819 oDrawArea.end_x = iX + pEntry->last_x;
820 oDrawArea.end_y = iY + pEntry->height;
821 oDrawArea.row_count = pEntry->row_count;
822 return oDrawArea;
823 }
824
825 // In theory, the renderers should only be invoked with coordinates which end
826 // up within the canvas being rendered to. In practice, this might not happen,
827 // at which point the following line can be removed.
828 // #define TRUST_RENDER_COORDS
829
render_mono(cached_text * pCacheEntry,FT_Bitmap * pBitmap,FT_Pos x,FT_Pos y) const830 void freetype_font::render_mono(cached_text* pCacheEntry, FT_Bitmap* pBitmap,
831 FT_Pos x, FT_Pos y) const {
832 uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x;
833 uint8_t* pInRow = pBitmap->buffer;
834 int rows = static_cast<int>(pBitmap->rows);
835 int width = static_cast<int>(pBitmap->width);
836 for (int iY = 0; iY < rows;
837 ++iY, pOutRow += pCacheEntry->width, pInRow += pBitmap->pitch) {
838 #ifndef TRUST_RENDER_COORDS
839 if (y + iY < 0) continue;
840 if (y + iY >= pCacheEntry->height) break;
841 #endif
842 uint8_t *pIn = pInRow, *pOut = pOutRow;
843 uint8_t iMask = 0x80;
844 for (int iX = 0; iX < width; ++iX, ++pOut) {
845 #ifndef TRUST_RENDER_COORDS
846 if (x + iX < 0) continue;
847 if (x + iX >= pCacheEntry->width) break;
848 #endif
849 if (*pIn & iMask) *pOut = 0xFF;
850 iMask = static_cast<uint8_t>(iMask / 2);
851 if (iMask == 0) {
852 iMask = 0x80;
853 ++pIn;
854 }
855 }
856 }
857 }
858
render_gray(cached_text * pCacheEntry,FT_Bitmap * pBitmap,FT_Pos x,FT_Pos y) const859 void freetype_font::render_gray(cached_text* pCacheEntry, FT_Bitmap* pBitmap,
860 FT_Pos x, FT_Pos y) const {
861 uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x;
862 uint8_t* pInRow = pBitmap->buffer;
863 int rows = static_cast<int>(pBitmap->rows);
864 int width = static_cast<int>(pBitmap->width);
865 for (int iY = 0; iY < rows;
866 ++iY, pOutRow += pCacheEntry->width, pInRow += pBitmap->pitch) {
867 #ifndef TRUST_RENDER_COORDS
868 if (y + iY < 0) continue;
869 if (y + iY >= pCacheEntry->height) break;
870 #endif
871 uint8_t *pIn = pInRow, *pOut = pOutRow;
872 for (int iX = 0; iX < width; ++iX, ++pIn, ++pOut) {
873 #ifndef TRUST_RENDER_COORDS
874 if (x + iX < 0) continue;
875 if (x + iX >= pCacheEntry->width) break;
876 #endif
877 unsigned int iIn = *pIn;
878 unsigned int iOut = *pOut;
879 uint8_t cMerged = static_cast<uint8_t>(iIn + iOut - (iIn * iOut) / 255);
880 *pOut = cMerged;
881 }
882 }
883 }
884
885 #endif // CORSIX_TH_USE_FREETYPE2
886