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