MyGUI  3.2.0
MyGUI_ResourceTrueTypeFont.cpp
Go to the documentation of this file.
1 
6 /*
7  This file is part of MyGUI.
8 
9  MyGUI is free software: you can redistribute it and/or modify
10  it under the terms of the GNU Lesser General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  MyGUI is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU Lesser General Public License for more details.
18 
19  You should have received a copy of the GNU Lesser General Public License
20  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
21 */
22 #include "MyGUI_Precompiled.h"
24 #include "MyGUI_DataManager.h"
25 #include "MyGUI_RenderManager.h"
26 #include "MyGUI_Bitwise.h"
27 
28 #ifdef MYGUI_USE_FREETYPE
29 
30  #include FT_GLYPH_H
31  #include FT_TRUETYPE_TABLES_H
32  #include FT_BITMAP_H
33  #include FT_WINFONTS_H
34 
35  // The following macro enables a workaround for a bug in FreeType's bytecode interpreter that, when using certain fonts at
36  // certain sizes, causes FreeType to start measuring and rendering some glyphs inconsistently after certain other glyphs have
37  // been loaded. See FreeType bug #35374 for details: https://savannah.nongnu.org/bugs/?35374
38  //
39  // To reproduce the bug, first disable the workaround by defining MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX to 0. Then load the
40  // DejaVu Sans font at 10 pt using default values for all other properties. Observe that the glyphs for the "0", 6", "8", and
41  // "9" characters are now badly corrupted when rendered.
42  //
43  // This bug still exists as of FreeType 2.4.8 and there are currently no plans to fix it. If the bug is ever fixed, this
44  // workaround should be disabled, as it causes fonts to take longer to load.
45  //
46  // The bug can currently also be suppressed by disabling FreeType's bytecode interpreter altogether. To do so, remove the
47  // TT_CONFIG_OPTION_BYTECODE_INTERPRETER macro in the "ftoption.h" FreeType header file. Once this is done, this workaround can
48  // be safely disabled. Note that disabling FreeType's bytecode interpreter will cause rendered text to look somewhat different.
49  // Whether it looks better or worse is a matter of taste and may also depend on the font.
50  #ifndef MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX
51  #define MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX 1
52  #endif
53 
54 #endif // MYGUI_USE_FREETYPE
55 
56 namespace MyGUI
57 {
58 
59  namespace
60  {
61 
62  template<typename T>
63  void setMax(T& _var, const T& _newValue)
64  {
65  if (_var < _newValue)
66  _var = _newValue;
67  }
68 
69  std::pair<const Char, const uint8> charMaskData[] =
70  {
71  std::make_pair(FontCodeType::Selected, (const uint8)'\x88'),
72  std::make_pair(FontCodeType::SelectedBack, (const uint8)'\x60'),
73  std::make_pair(FontCodeType::Cursor, (const uint8)'\xFF'),
74  std::make_pair(FontCodeType::Tab, (const uint8)'\x00')
75  };
76 
77  const std::map<const Char, const uint8> charMask(charMaskData, charMaskData + sizeof charMaskData / sizeof *charMaskData);
78 
79  const uint8 charMaskBlack = (const uint8)'\x00';
80  const uint8 charMaskWhite = (const uint8)'\xFF';
81 
82  template<bool LAMode>
83  struct PixelBase
84  {
85  // Returns PixelFormat::R8G8B8A8 when LAMode is false, or PixelFormat::L8A8 when LAMode is true.
86  static PixelFormat::Enum getFormat();
87 
88  // Returns 4 when LAMode is false, or 2 when LAMode is true.
89  static size_t getNumBytes();
90 
91  protected:
92  // Sets a pixel in _dest as 4 or 2 bytes: L8L8L8A8 if LAMode is false, or L8A8 if LAMode is true.
93  // Automatically advances _dest just past the pixel written.
94  static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha);
95  };
96 
97  template<>
98  struct PixelBase<false>
99  {
100  static size_t getNumBytes()
101  {
102  return 4;
103  }
104 
105  static PixelFormat::Enum getFormat()
106  {
107  return PixelFormat::R8G8B8A8;
108  }
109 
110  protected:
111  static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha)
112  {
113  *_dest++ = _luminance;
114  *_dest++ = _luminance;
115  *_dest++ = _luminance;
116  *_dest++ = _alpha;
117  }
118  };
119 
120  template<>
121  struct PixelBase<true>
122  {
123  static size_t getNumBytes()
124  {
125  return 2;
126  }
127 
128  static PixelFormat::Enum getFormat()
129  {
130  return PixelFormat::L8A8;
131  }
132 
133  protected:
134  static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha)
135  {
136  *_dest++ = _luminance;
137  *_dest++ = _alpha;
138  }
139  };
140 
141  template<bool LAMode, bool FromSource = false, bool Antialias = false>
142  struct Pixel : PixelBase<LAMode>
143  {
144  // Sets a pixel in _dest as 4 or 2 bytes: L8L8L8A8 if LAMode is false, or L8A8 if LAMode is true.
145  // Sets luminance from _source if both FromSource and Antialias are true; otherwise, uses the specified value.
146  // Sets alpha from _source if FromSource is true; otherwise, uses the specified value.
147  // Automatically advances _source just past the pixel read if FromSource is true.
148  // Automatically advances _dest just past the pixel written.
149  static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha, uint8*& _source);
150  };
151 
152  template<bool LAMode, bool Antialias>
153  struct Pixel<LAMode, false, Antialias> : PixelBase<LAMode>
154  {
155  // Sets the destination pixel using the specified luminance and alpha. Source is ignored, since FromSource is false.
156  static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha, uint8* = nullptr)
157  {
158  PixelBase<LAMode>::set(_dest, _luminance, _alpha);
159  }
160  };
161 
162  template<bool LAMode>
163  struct Pixel<LAMode, true, false> : PixelBase<LAMode>
164  {
165  // Sets the destination pixel using the specified _luminance and using the alpha from the specified source.
166  static void set(uint8*& _dest, uint8 _luminance, uint8, uint8*& _source)
167  {
168  PixelBase<LAMode>::set(_dest, _luminance, *_source++);
169  }
170  };
171 
172  template<bool LAMode>
173  struct Pixel<LAMode, true, true> : PixelBase<LAMode>
174  {
175  // Sets the destination pixel using both the luminance and alpha from the specified source, since Antialias is true.
176  static void set(uint8*& _dest, uint8, uint8, uint8*& _source)
177  {
178  PixelBase<LAMode>::set(_dest, *_source, *_source);
179  ++_source;
180  }
181  };
182 
183  }
184 
185  const int ResourceTrueTypeFont::mGlyphSpacing = 1;
186  const float ResourceTrueTypeFont::mSelectedWidth = 1.0f;
187  const float ResourceTrueTypeFont::mCursorWidth = 2.0f;
188 
190  mSize(0),
191  mResolution(96),
192  mAntialias(false),
193  mSpaceWidth(0.0f),
194  mTabWidth(0.0f),
195  mOffsetHeight(0),
196  mSubstituteCodePoint(FontCodeType::NotDefined),
197  mDefaultHeight(0),
198  mSubstituteGlyphInfo(nullptr),
199  mTexture(nullptr)
200  {
201  }
202 
204  {
205  if (mTexture != nullptr)
206  {
208  mTexture = nullptr;
209  }
210  }
211 
213  {
214  Base::deserialization(_node, _version);
215 
217  while (node.next())
218  {
219  if (node->getName() == "Property")
220  {
221  const std::string& key = node->findAttribute("key");
222  const std::string& value = node->findAttribute("value");
223  if (key == "Source") mSource = value;
224  else if (key == "Size") mSize = utility::parseFloat(value);
225  else if (key == "Resolution") mResolution = utility::parseUInt(value);
226  else if (key == "Antialias") mAntialias = utility::parseBool(value);
227  else if (key == "SpaceWidth")
228  {
229  mSpaceWidth = utility::parseFloat(value);
230  MYGUI_LOG(Warning, _node->findAttribute("type") << ": Property '" << key << "' in font '" << _node->findAttribute("name") << "' is deprecated; remove it to use automatic calculation.");
231  }
232  else if (key == "TabWidth") mTabWidth = utility::parseFloat(value);
233  else if (key == "OffsetHeight") mOffsetHeight = utility::parseInt(value);
234  else if (key == "SubstituteCode") mSubstituteCodePoint = utility::parseInt(value);
235  else if (key == "CursorWidth" || key == "Distance")
236  {
237  MYGUI_LOG(Warning, _node->findAttribute("type") << ": Property '" << key << "' in font '" << _node->findAttribute("name") << "' is deprecated; value ignored.");
238  }
239  }
240  else if (node->getName() == "Codes")
241  {
242  // Range of inclusions.
244  while (range.next("Code"))
245  {
246  std::string range_value;
247  if (range->findAttribute("range", range_value))
248  {
249  std::vector<std::string> parse_range = utility::split(range_value);
250  if (!parse_range.empty())
251  {
252  Char first = utility::parseUInt(parse_range[0]);
253  Char last = parse_range.size() > 1 ? utility::parseUInt(parse_range[1]) : first;
254  addCodePointRange(first, last);
255  }
256  }
257  }
258 
259  // If no code points have been included, include the Unicode Basic Multilingual Plane by default before processing
260  // any exclusions.
261  if (mCharMap.empty())
262  addCodePointRange(0, 0xFFFF);
263 
264  // Range of exclusions.
265  range = node->getElementEnumerator();
266  while (range.next("Code"))
267  {
268  std::string range_value;
269  if (range->findAttribute("hide", range_value))
270  {
271  std::vector<std::string> parse_range = utility::split(range_value);
272  if (!parse_range.empty())
273  {
274  Char first = utility::parseUInt(parse_range[0]);
275  Char last = parse_range.size() > 1 ? utility::parseUInt(parse_range[1]) : first;
276  removeCodePointRange(first, last);
277  }
278  }
279  }
280  }
281  }
282 
283  initialise();
284  }
285 
286 #ifdef MYGUI_USE_FREETYPE
287 
289  {
290  CharMap::const_iterator charIter = mCharMap.find(_id);
291 
292  if (charIter != mCharMap.end())
293  {
294  GlyphMap::iterator glyphIter = mGlyphMap.find(charIter->second);
295 
296  if (glyphIter != mGlyphMap.end())
297  return &glyphIter->second;
298  }
299 
300  return mSubstituteGlyphInfo;
301  }
302 
303 #else
304 
306  {
307  return nullptr;
308  }
309 
310 #endif // MYGUI_USE_FREETYPE
311 
313  {
314  return mTexture;
315  }
316 
318  {
319  return mDefaultHeight;
320  }
321 
322  std::vector<std::pair<Char, Char> > ResourceTrueTypeFont::getCodePointRanges() const
323  {
324  std::vector<std::pair<Char, Char> > result;
325 
326  if (!mCharMap.empty())
327  {
328  CharMap::const_iterator iter = mCharMap.begin(), endIter = mCharMap.end();
329 
330  // Start the first range with the first code point in the map.
331  Char rangeBegin = iter->first, rangeEnd = rangeBegin;
332 
333  // Loop over the rest of the map and find the contiguous ranges.
334  for (++iter; iter != endIter; ++iter)
335  {
336  if (iter->first == rangeEnd + 1)
337  {
338  // Extend the current range.
339  ++rangeEnd;
340  }
341  else
342  {
343  // Found the start of a new range. First, save the current range.
344  result.push_back(std::make_pair(rangeBegin, rangeEnd));
345 
346  // Then start the new range.
347  rangeBegin = rangeEnd = iter->first;
348  }
349  }
350 
351  // Save the final range.
352  result.push_back(std::make_pair(rangeBegin, rangeEnd));
353  }
354 
355  return result;
356  }
357 
359  {
360  return mSubstituteCodePoint;
361  }
362 
363  void ResourceTrueTypeFont::addCodePoint(Char _codePoint)
364  {
365  mCharMap.insert(CharMap::value_type(_codePoint, 0));
366  }
367 
368  void ResourceTrueTypeFont::removeCodePoint(Char _codePoint)
369  {
370  mCharMap.erase(_codePoint);
371  }
372 
373  void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second)
374  {
375  CharMap::iterator positionHint = mCharMap.lower_bound(_first);
376 
377  if (positionHint != mCharMap.begin())
378  --positionHint;
379 
380  for (Char i = _first; i <= _second; ++i)
381  positionHint = mCharMap.insert(positionHint, CharMap::value_type(i, 0));
382  }
383 
384  void ResourceTrueTypeFont::removeCodePointRange(Char _first, Char _second)
385  {
386  mCharMap.erase(mCharMap.lower_bound(_first), mCharMap.upper_bound(_second));
387  }
388 
389  void ResourceTrueTypeFont::clearCodePoints()
390  {
391  mCharMap.clear();
392  }
393 
394  void ResourceTrueTypeFont::initialise()
395  {
396 #ifndef MYGUI_USE_FREETYPE
397 
398  MYGUI_LOG(Error, "ResourceTrueTypeFont: TrueType font '" << getResourceName() << "' disabled. Define MYGUI_USE_FREETYE if you need TrueType fonts.");
399 
400 #else // MYGUI_USE_FREETYPE
401 
402  // If L8A8 (2 bytes per pixel) is supported, use it; otherwise, use R8G8B8A8 (4 bytes per pixel) as L8L8L8A8.
404 
405  // Select and call an appropriate initialisation method. By making this decision up front, we avoid having to branch on
406  // these variables many thousands of times inside tight nested loops later. From this point on, the various function
407  // templates ensure that all of the necessary branching is done purely at compile time for all combinations.
408  int init = (laMode ? 2 : 0) | (mAntialias ? 1 : 0);
409 
410  switch (init)
411  {
412  case 0:
413  ResourceTrueTypeFont::initialiseFreeType<false, false>();
414  break;
415  case 1:
416  ResourceTrueTypeFont::initialiseFreeType<false, true>();
417  break;
418  case 2:
419  ResourceTrueTypeFont::initialiseFreeType<true, false>();
420  break;
421  case 3:
422  ResourceTrueTypeFont::initialiseFreeType<true, true>();
423  break;
424  }
425 
426 #endif // MYGUI_USE_FREETYPE
427  }
428 
429 #ifdef MYGUI_USE_FREETYPE
430 
431  template<bool LAMode, bool Antialias>
432  void ResourceTrueTypeFont::initialiseFreeType()
433  {
434  //-------------------------------------------------------------------//
435  // Initialise FreeType and load the font.
436  //-------------------------------------------------------------------//
437 
438  FT_Library ftLibrary;
439 
440  if (FT_Init_FreeType(&ftLibrary) != 0)
441  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not init the FreeType library!");
442 
443  uint8* fontBuffer = nullptr;
444 
445  FT_Face face = loadFace(ftLibrary, fontBuffer);
446 
447  if (face == nullptr)
448  {
449  MYGUI_LOG(Error, "ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
450  return;
451  }
452 
453  //-------------------------------------------------------------------//
454  // Calculate the font metrics.
455  //-------------------------------------------------------------------//
456 
457  // The font's overall ascent and descent are defined in three different places in a TrueType font, and with different
458  // values in each place. The most reliable source for these metrics is usually the "usWinAscent" and "usWinDescent" pair of
459  // values in the OS/2 header; however, some fonts contain inaccurate data there. To be safe, we use the highest of the set
460  // of values contained in the face metrics and the two sets of values contained in the OS/2 header.
461  int fontAscent = face->size->metrics.ascender >> 6;
462  int fontDescent = -face->size->metrics.descender >> 6;
463 
464  TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
465 
466  if (os2 != nullptr)
467  {
468  setMax(fontAscent, os2->usWinAscent * face->size->metrics.y_ppem / face->units_per_EM);
469  setMax(fontDescent, os2->usWinDescent * face->size->metrics.y_ppem / face->units_per_EM);
470 
471  setMax(fontAscent, os2->sTypoAscender * face->size->metrics.y_ppem / face->units_per_EM);
472  setMax(fontDescent, -os2->sTypoDescender * face->size->metrics.y_ppem / face->units_per_EM);
473  }
474 
475  // The nominal font height is calculated as the sum of its ascent and descent as specified by the font designer. Previously
476  // it was defined by MyGUI in terms of the maximum ascent and descent of the glyphs currently in use, but this caused the
477  // font's line spacing to change whenever glyphs were added to or removed from the font definition. Doing it this way
478  // instead prevents a lot of layout problems, and it is also more typographically correct and more aesthetically pleasing.
479  mDefaultHeight = fontAscent + fontDescent;
480 
481  //-------------------------------------------------------------------//
482  // Create the glyphs and calculate their metrics.
483  //-------------------------------------------------------------------//
484 
485  GlyphHeightMap glyphHeightMap;
486  int texWidth = 0;
487 
488  // Before creating the glyphs, add the "Space" code point to force that glyph to be created. For historical reasons, MyGUI
489  // users are accustomed to omitting this code point in their font definitions.
490  addCodePoint(FontCodeType::Space);
491 
492  // Create the standard glyphs.
493  for (CharMap::iterator iter = mCharMap.begin(); iter != mCharMap.end(); )
494  {
495  const Char& codePoint = iter->first;
496  FT_UInt glyphIndex = FT_Get_Char_Index(face, codePoint);
497 
498  texWidth += createFaceGlyph(glyphIndex, codePoint, fontAscent, face, glyphHeightMap);
499 
500  // If the newly created glyph is the "Not Defined" glyph, it means that the code point is not supported by the font.
501  // Remove it from the character map so that we can provide our own substitute instead of letting FreeType do it.
502  if (iter->second != 0)
503  ++iter;
504  else
505  mCharMap.erase(iter++);
506  }
507 
508 #if MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX
509 
510  for (GlyphMap::iterator iter = mGlyphMap.begin(); iter != mGlyphMap.end(); ++iter)
511  {
512  if (FT_Load_Glyph(face, iter->first, FT_LOAD_DEFAULT) == 0)
513  {
514  GlyphInfo& info = iter->second;
515  GlyphInfo newInfo = createFaceGlyphInfo(0, fontAscent, face->glyph);
516 
517  if (info.width != newInfo.width)
518  {
519  texWidth += (int)ceil(newInfo.width) - (int)ceil(info.width);
520  info.width = newInfo.width;
521  }
522 
523  if (info.height != newInfo.height)
524  {
525  GlyphHeightMap::mapped_type oldHeightMap = glyphHeightMap[(FT_Pos)info.height];
526  GlyphHeightMap::mapped_type::iterator heightMapItem = oldHeightMap.find(iter->first);
527  glyphHeightMap[(FT_Pos)newInfo.height].insert(*heightMapItem);
528  oldHeightMap.erase(heightMapItem);
529  info.height = newInfo.height;
530  }
531 
532  if (info.advance != newInfo.advance)
533  info.advance = newInfo.advance;
534 
535  if (info.bearingX != newInfo.bearingX)
536  info.bearingX = newInfo.bearingX;
537 
538  if (info.bearingY != newInfo.bearingY)
539  info.bearingY = newInfo.bearingY;
540  }
541  else
542  {
543  MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot load glyph " << iter->first << " for character " << iter->second.codePoint << " in font '" << getResourceName() << "'.");
544  }
545  }
546 
547 #endif // MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX
548 
549  // Do some special handling for the "Space" and "Tab" glyphs.
550  GlyphInfo* spaceGlyphInfo = getGlyphInfo(FontCodeType::Space);
551 
552  if (spaceGlyphInfo != nullptr && spaceGlyphInfo->codePoint == FontCodeType::Space)
553  {
554  // Adjust the width of the "Space" glyph if it has been customized.
555  if (mSpaceWidth != 0.0f)
556  {
557  texWidth += (int)ceil(mSpaceWidth) - (int)ceil(spaceGlyphInfo->width);
558  spaceGlyphInfo->width = mSpaceWidth;
559  spaceGlyphInfo->advance = mSpaceWidth;
560  }
561 
562  // If the width of the "Tab" glyph hasn't been customized, make it eight spaces wide.
563  if (mTabWidth == 0.0f)
564  mTabWidth = 8.0f * spaceGlyphInfo->advance;
565  }
566 
567  // Create the special glyphs. They must be created after the standard glyphs so that they take precedence in case of a
568  // collision. To make sure that the indices of the special glyphs don't collide with any glyph indices in the font, we must
569  // use glyph indices higher than the highest glyph index in the font.
570  FT_UInt nextGlyphIndex = (FT_UInt)face->num_glyphs;
571 
572  float height = (float)mDefaultHeight;
573 
574  texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(FontCodeType::Tab, 0.0f, 0.0f, mTabWidth, 0.0f, 0.0f), glyphHeightMap);
575  texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(FontCodeType::Selected, mSelectedWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap);
576  texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(FontCodeType::SelectedBack, mSelectedWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap);
577  texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(FontCodeType::Cursor, mCursorWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap);
578 
579  // If a substitute code point has been specified, check to make sure that it exists in the character map. If it doesn't,
580  // revert to the default "Not Defined" code point. This is not a real code point but rather an invalid Unicode value that
581  // is guaranteed to cause the "Not Defined" special glyph to be created.
582  if (mSubstituteCodePoint != FontCodeType::NotDefined && mCharMap.find(mSubstituteCodePoint) == mCharMap.end())
583  {
584  mSubstituteCodePoint = FontCodeType::NotDefined;
585  }
586 
587  // Create the "Not Defined" code point (and its corresponding glyph) if it's in use as the substitute code point.
588  if (mSubstituteCodePoint == FontCodeType::NotDefined)
589  {
590  texWidth += createFaceGlyph(0, FontCodeType::NotDefined, fontAscent, face, glyphHeightMap);
591  }
592 
593  // Cache a pointer to the substitute glyph info for fast lookup.
594  mSubstituteGlyphInfo = &mGlyphMap.find(mCharMap.find(mSubstituteCodePoint)->second)->second;
595 
596  // Calculate the average height of all of the glyphs that are in use. This value will be used for estimating how large the
597  // texture needs to be.
598  double averageGlyphHeight = 0.0;
599 
600  for (GlyphHeightMap::const_iterator j = glyphHeightMap.begin(); j != glyphHeightMap.end(); ++j)
601  averageGlyphHeight += j->first * j->second.size();
602 
603  averageGlyphHeight /= mGlyphMap.size();
604 
605  //-------------------------------------------------------------------//
606  // Calculate the final texture size.
607  //-------------------------------------------------------------------//
608 
609  // Round the current texture width and height up to the nearest powers of two.
610  texWidth = Bitwise::firstPO2From(texWidth);
611  int texHeight = Bitwise::firstPO2From((int)ceil(averageGlyphHeight) + mGlyphSpacing);
612 
613  // At this point, the texture is only one glyph high and is probably very wide. For efficiency reasons, we need to make the
614  // texture as square as possible. If the texture cannot be made perfectly square, make it taller than it is wide, because
615  // the height may decrease in the final layout due to height packing.
616  while (texWidth > texHeight)
617  {
618  texWidth /= 2;
619  texHeight *= 2;
620  }
621 
622  // Calculate the final layout of all of the glyphs in the texture, packing them tightly by first arranging them by height.
623  // We assume that the texture width is fixed but that the texture height can be adjusted up or down depending on how much
624  // space is actually needed.
625  // In most cases, the final texture will end up square or almost square. In some rare cases, however, we can end up with a
626  // texture that's more than twice as high as it is wide; when this happens, we double the width and try again.
627  do
628  {
629  if (texHeight > texWidth * 2)
630  texWidth *= 2;
631 
632  int texX = 0, texY = 0;
633 
634  for (GlyphHeightMap::const_iterator j = glyphHeightMap.begin(); j != glyphHeightMap.end(); ++j)
635  {
636  for (GlyphHeightMap::mapped_type::const_iterator i = j->second.begin(); i != j->second.end(); ++i)
637  {
638  GlyphInfo& info = *i->second;
639 
640  int glyphWidth = (int)ceil(info.width);
641  int glyphHeight = (int)ceil(info.height);
642 
643  autoWrapGlyphPos(glyphWidth, texWidth, glyphHeight, texX, texY);
644 
645  if (glyphWidth > 0)
646  texX += mGlyphSpacing + glyphWidth;
647  }
648  }
649 
650  texHeight = Bitwise::firstPO2From(texY + glyphHeightMap.rbegin()->first);
651  }
652  while (texHeight > texWidth * 2);
653 
654  //-------------------------------------------------------------------//
655  // Create the texture and render the glyphs onto it.
656  //-------------------------------------------------------------------//
657 
658  mTexture = RenderManager::getInstance().createTexture(MyGUI::utility::toString((size_t)this, "_TrueTypeFont"));
659 
660  mTexture->createManual(texWidth, texHeight, TextureUsage::Static | TextureUsage::Write, Pixel<LAMode>::getFormat());
661 
662  uint8* texBuffer = static_cast<uint8*>(mTexture->lock(TextureUsage::Write));
663 
664  if (texBuffer != nullptr)
665  {
666  // Make the texture background transparent white.
667  for (uint8* dest = texBuffer, * endDest = dest + texWidth * texHeight * Pixel<LAMode>::getNumBytes(); dest != endDest; )
668  Pixel<LAMode, false, false>::set(dest, charMaskWhite, charMaskBlack);
669 
670  renderGlyphs<LAMode, Antialias>(glyphHeightMap, ftLibrary, face, texBuffer, texWidth, texHeight);
671 
672  mTexture->unlock();
673 
674  MYGUI_LOG(Info, "ResourceTrueTypeFont: Font '" << getResourceName() << "' using texture size " << texWidth << " x " << texHeight << ".");
675  MYGUI_LOG(Info, "ResourceTrueTypeFont: Font '" << getResourceName() << "' using real height " << mDefaultHeight << " pixels.");
676  }
677  else
678  {
679  MYGUI_LOG(Error, "ResourceTrueTypeFont: Error locking texture; pointer is nullptr.");
680  }
681 
682  FT_Done_Face(face);
683  FT_Done_FreeType(ftLibrary);
684 
685  delete [] fontBuffer;
686  }
687 
688  FT_Face ResourceTrueTypeFont::loadFace(const FT_Library& _ftLibrary, uint8*& _fontBuffer)
689  {
690  FT_Face result = nullptr;
691 
692  // Load the font file.
693  IDataStream* datastream = DataManager::getInstance().getData(mSource);
694 
695  if (datastream == nullptr)
696  return result;
697 
698  size_t fontBufferSize = datastream->size();
699  _fontBuffer = new uint8[fontBufferSize];
700  datastream->read(_fontBuffer, fontBufferSize);
701  delete datastream;
702 
703  // Determine how many faces the font contains.
704  if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, -1, &result) != 0)
705  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
706 
707  FT_Long numFaces = result->num_faces;
708  FT_Long faceIndex = 0;
709 
710  // Load the first face.
711  if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0)
712  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
713 
714  if (result->face_flags & FT_FACE_FLAG_SCALABLE)
715  {
716  // The font is scalable, so set the font size by first converting the requested size to FreeType's 26.6 fixed-point
717  // format.
718  FT_F26Dot6 ftSize = (FT_F26Dot6)(mSize * (1 << 6));
719 
720  if (FT_Set_Char_Size(result, ftSize, 0, mResolution, mResolution) != 0)
721  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not set the font size for '" << getResourceName() << "'!");
722 
723  // If no code points have been specified, use the Unicode Basic Multilingual Plane by default.
724  if (mCharMap.empty())
725  addCodePointRange(0, 0xFFFF);
726  }
727  else
728  {
729  // The font isn't scalable, so try to load it as a Windows FNT/FON file.
730  FT_WinFNT_HeaderRec fnt;
731 
732  // Enumerate all of the faces in the font and select the smallest one that's at least as large as the requested size
733  // (after adjusting for resolution). If none of the faces are large enough, use the largest one.
734  std::map<float, FT_Long> faceSizes;
735 
736  do
737  {
738  if (FT_Get_WinFNT_Header(result, &fnt) != 0)
739  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
740 
741  faceSizes.insert(std::make_pair((float)fnt.nominal_point_size * fnt.vertical_resolution / mResolution, faceIndex));
742 
743  FT_Done_Face(result);
744 
745  if (++faceIndex < numFaces)
746  if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0)
747  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
748  }
749  while (faceIndex < numFaces);
750 
751  std::map<float, FT_Long>::const_iterator iter = faceSizes.lower_bound(mSize);
752 
753  faceIndex = (iter != faceSizes.end()) ? iter->second : faceSizes.rbegin()->second;
754 
755  if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0)
756  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!");
757 
758  // Select the first bitmap strike available in the selected face. This needs to be done explicitly even though Windows
759  // FNT/FON files contain only one bitmap strike per face.
760  if (FT_Select_Size(result, 0) != 0)
761  MYGUI_EXCEPT("ResourceTrueTypeFont: Could not set the font size for '" << getResourceName() << "'!");
762 
763  // Windows FNT/FON files do not support Unicode, so restrict the code-point range to either ISO-8859-1 or ASCII,
764  // depending on the font's encoding.
765  if (mCharMap.empty())
766  {
767  // No code points have been specified, so add the printable ASCII range by default.
768  addCodePointRange(0x20, 0x7E);
769 
770  // Additionally, if the font's character set is CP-1252, add the range of non-ASCII 8-bit code points that are
771  // common between CP-1252 and ISO-8859-1; i.e., everything but 0x80 through 0x9F.
772  if (fnt.charset == FT_WinFNT_ID_CP1252)
773  addCodePointRange(0xA0, 0xFF);
774  }
775  else
776  {
777  // Some code points have been specified, so remove anything in the non-printable ASCII range as well as anything
778  // over 8 bits.
779  removeCodePointRange(0, 0x1F);
780  removeCodePointRange(0x100, std::numeric_limits<Char>::max());
781 
782  // Additionally, remove non-ASCII 8-bit code points (plus ASCII DEL, 0x7F). If the font's character set is CP-1252,
783  // remove only the code points that differ between CP-1252 and ISO-8859-1; otherwise, remove all of them.
784  if (fnt.charset == FT_WinFNT_ID_CP1252)
785  removeCodePointRange(0x7F, 0x9F);
786  else
787  removeCodePointRange(0x7F, 0xFF);
788  }
789  }
790 
791  return result;
792  }
793 
794  void ResourceTrueTypeFont::autoWrapGlyphPos(int _glyphWidth, int _texWidth, int _lineHeight, int& _texX, int& _texY)
795  {
796  if (_glyphWidth > 0 && _texX + mGlyphSpacing + _glyphWidth > _texWidth)
797  {
798  _texX = 0;
799  _texY += mGlyphSpacing + _lineHeight;
800  }
801  }
802 
803  GlyphInfo ResourceTrueTypeFont::createFaceGlyphInfo(Char _codePoint, int _fontAscent, FT_GlyphSlot _glyph)
804  {
805  float bearingX = _glyph->metrics.horiBearingX / 64.0f;
806 
807  // The following calculations aren't currently needed but are kept here for future use.
808  // float ascent = _glyph->metrics.horiBearingY / 64.0f;
809  // float descent = (_glyph->metrics.height / 64.0f) - ascent;
810 
811  return GlyphInfo(
812  _codePoint,
813  _glyph->metrics.width / 64.0f,
814  _glyph->metrics.height / 64.0f,
815  (_glyph->advance.x / 64.0f) - bearingX,
816  bearingX,
817  _fontAscent - (_glyph->metrics.horiBearingY / 64.0f) - mOffsetHeight);
818  }
819 
820  int ResourceTrueTypeFont::createGlyph(FT_UInt _glyphIndex, const GlyphInfo& _glyphInfo, GlyphHeightMap& _glyphHeightMap)
821  {
822  mCharMap[_glyphInfo.codePoint] = _glyphIndex;
823  GlyphInfo& info = mGlyphMap.insert(GlyphMap::value_type(_glyphIndex, _glyphInfo)).first->second;
824  _glyphHeightMap[(FT_Pos)_glyphInfo.height].insert(std::make_pair(_glyphIndex, &info));
825 
826  int width = (int)ceil(_glyphInfo.width);
827 
828  return (width > 0) ? mGlyphSpacing + width : 0;
829  }
830 
831  int ResourceTrueTypeFont::createFaceGlyph(FT_UInt _glyphIndex, Char _codePoint, int _fontAscent, const FT_Face& _face, GlyphHeightMap& _glyphHeightMap)
832  {
833  if (mGlyphMap.find(_glyphIndex) == mGlyphMap.end())
834  {
835  if (FT_Load_Glyph(_face, _glyphIndex, FT_LOAD_DEFAULT) == 0)
836  return createGlyph(_glyphIndex, createFaceGlyphInfo(_codePoint, _fontAscent, _face->glyph), _glyphHeightMap);
837  else
838  MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot load glyph " << _glyphIndex << " for character " << _codePoint << " in font '" << getResourceName() << "'.");
839  }
840  else
841  {
842  mCharMap[_codePoint] = _glyphIndex;
843  }
844 
845  return 0;
846  }
847 
848  template<bool LAMode, bool Antialias>
849  void ResourceTrueTypeFont::renderGlyphs(const GlyphHeightMap& _glyphHeightMap, const FT_Library& _ftLibrary, const FT_Face& _face, uint8* _texBuffer, int _texWidth, int _texHeight)
850  {
851  FT_Bitmap ftBitmap;
852  FT_Bitmap_New(&ftBitmap);
853 
854  int texX = 0, texY = 0;
855 
856  for (GlyphHeightMap::const_iterator j = _glyphHeightMap.begin(); j != _glyphHeightMap.end(); ++j)
857  {
858  for (GlyphHeightMap::mapped_type::const_iterator i = j->second.begin(); i != j->second.end(); ++i)
859  {
860  GlyphInfo& info = *i->second;
861 
862  switch (info.codePoint)
863  {
866  {
867  renderGlyph<LAMode, false, false>(info, charMaskWhite, charMaskBlack, charMask.find(info.codePoint)->second, j->first, _texBuffer, _texWidth, _texHeight, texX, texY);
868 
869  // Manually adjust the glyph's width to zero. This prevents artifacts from appearing at the seams when
870  // rendering multi-character selections.
871  GlyphInfo* glyphInfo = getGlyphInfo(info.codePoint);
872  glyphInfo->width = 0.0f;
873  glyphInfo->uvRect.right = glyphInfo->uvRect.left;
874  }
875  break;
876 
878  case FontCodeType::Tab:
879  renderGlyph<LAMode, false, false>(info, charMaskWhite, charMaskBlack, charMask.find(info.codePoint)->second, j->first, _texBuffer, _texWidth, _texHeight, texX, texY);
880  break;
881 
882  default:
883  if (FT_Load_Glyph(_face, i->first, FT_LOAD_RENDER) == 0)
884  {
885  if (_face->glyph->bitmap.buffer != nullptr)
886  {
887  uint8* glyphBuffer = nullptr;
888 
889  switch (_face->glyph->bitmap.pixel_mode)
890  {
891  case FT_PIXEL_MODE_GRAY:
892  glyphBuffer = _face->glyph->bitmap.buffer;
893  break;
894 
895  case FT_PIXEL_MODE_MONO:
896  // Convert the monochrome bitmap to 8-bit before rendering it.
897  if (FT_Bitmap_Convert(_ftLibrary, &_face->glyph->bitmap, &ftBitmap, 1) == 0)
898  {
899  // Go through the bitmap and convert all of the nonzero values to 0xFF (white).
900  for (uint8* p = ftBitmap.buffer, * endP = p + ftBitmap.width * ftBitmap.rows; p != endP; ++p)
901  *p ^= -*p ^ *p;
902 
903  glyphBuffer = ftBitmap.buffer;
904  }
905  break;
906  }
907 
908  if (glyphBuffer != nullptr)
909  renderGlyph<LAMode, true, Antialias>(info, charMaskWhite, charMaskWhite, charMaskWhite, j->first, _texBuffer, _texWidth, _texHeight, texX, texY, glyphBuffer);
910  }
911  }
912  else
913  {
914  MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot render glyph " << i->first << " for character " << info.codePoint << " in font '" << getResourceName() << "'.");
915  }
916  break;
917  }
918  }
919  }
920 
921  FT_Bitmap_Done(_ftLibrary, &ftBitmap);
922  }
923 
924  template<bool LAMode, bool UseBuffer, bool Antialias>
925  void ResourceTrueTypeFont::renderGlyph(GlyphInfo& _info, uint8 _luminance0, uint8 _luminance1, uint8 _alpha, int _lineHeight, uint8* _texBuffer, int _texWidth, int _texHeight, int& _texX, int& _texY, uint8* _glyphBuffer)
926  {
927  int width = (int)ceil(_info.width);
928  int height = (int)ceil(_info.height);
929 
930  autoWrapGlyphPos(width, _texWidth, _lineHeight, _texX, _texY);
931 
932  uint8* dest = _texBuffer + (_texY * _texWidth + _texX) * Pixel<LAMode>::getNumBytes();
933 
934  // Calculate how much to advance the destination pointer after each row to get to the start of the next row.
935  ptrdiff_t destNextRow = (_texWidth - width) * Pixel<LAMode>::getNumBytes();
936 
937  for (int j = height; j > 0; --j)
938  {
939  int i;
940  for (i = width; i > 1; i -= 2)
941  {
942  Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance0, _alpha, _glyphBuffer);
943  Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance1, _alpha, _glyphBuffer);
944  }
945 
946  if (i > 0)
947  Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance0, _alpha, _glyphBuffer);
948 
949  dest += destNextRow;
950  }
951 
952  // Calculate and store the glyph's UV coordinates within the texture.
953  _info.uvRect.left = (float)_texX / _texWidth; // u1
954  _info.uvRect.top = (float)_texY / _texHeight; // v1
955  _info.uvRect.right = (float)(_texX + _info.width) / _texWidth; // u2
956  _info.uvRect.bottom = (float)(_texY + _info.height) / _texHeight; // v2
957 
958  if (width > 0)
959  _texX += mGlyphSpacing + width;
960  }
961 
962 #endif // MYGUI_USE_FREETYPE
963 
964 } // namespace MyGUI
virtual void deserialization(xml::ElementPtr _node, Version _version)
virtual void createManual(int _width, int _height, TextureUsage _usage, PixelFormat _format)=0
int parseInt(const std::string &_value)
static RenderManager & getInstance()
unsigned int parseUInt(const std::string &_value)
bool findAttribute(const std::string &_name, std::string &_value)
virtual void * lock(TextureUsage _access)=0
const std::string & getResourceName() const
#define nullptr
virtual void unlock()=0
#define MYGUI_LOG(level, text)
virtual bool isFormatSupported(PixelFormat _format, TextureUsage _usage)
std::vector< std::string > split(const std::string &_source, const std::string &_delims="\t\n ")
#define MYGUI_EXCEPT(dest)
virtual void deserialization(xml::ElementPtr _node, Version _version)
static __inline Type firstPO2From(Type _value)
Definition: MyGUI_Bitwise.h:36
std::string toString(T p)
unsigned int Char
Definition: MyGUI_Types.h:66
virtual IDataStream * getData(const std::string &_name)=0
float parseFloat(const std::string &_value)
bool parseBool(const std::string &_value)
ElementEnumerator getElementEnumerator()
virtual GlyphInfo * getGlyphInfo(Char _id)
const std::string & getName() const
virtual size_t size()=0
unsigned char uint8
Definition: MyGUI_Types.h:61
virtual ITexture * createTexture(const std::string &_name)=0
std::vector< std::pair< Char, Char > > getCodePointRanges() const
virtual void destroyTexture(ITexture *_texture)=0