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