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