MyGUI 3.2.0

- Main Page
- Related Pages
- Namespaces
- Data Structures
- Files
- Examples

- File List
- Globals

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"
23 #include "MyGUI_ResourceTrueTypeFont.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
189 ResourceTrueTypeFont::ResourceTrueTypeFont() :
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
203 ResourceTrueTypeFont::~ResourceTrueTypeFont()
204 {
205 if (mTexture != nullptr)
206 {
207 RenderManager::getInstance().destroyTexture(mTexture);
208 mTexture = nullptr;
209 }
210 }
211
212 void ResourceTrueTypeFont::deserialization(xml::ElementPtr _node, Version _version)
213 {
214 Base::deserialization(_node, _version);
215
216 xml::ElementEnumerator node = _node->getElementEnumerator();
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.
243 xml::ElementEnumerator range = node->getElementEnumerator();
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
288 GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id)
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
305 GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id)
306 {
307 return nullptr;
308 }
309
310 #endif // MYGUI_USE_FREETYPE
311
312 ITexture* ResourceTrueTypeFont::getTextureFont()
313 {
314 return mTexture;
315 }
316
317 int ResourceTrueTypeFont::getDefaultHeight()
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
358 Char ResourceTrueTypeFont::getSubstituteCodePoint() const
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.
403 bool laMode = MyGUI::RenderManager::getInstance().isFormatSupported(Pixel<true>::getFormat(), TextureUsage::Static | TextureUsage::Write);
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 {
864 case FontCodeType::Selected:
865 case FontCodeType::SelectedBack:
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
877 case FontCodeType::Cursor:
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
MyGUI::ResourceTrueTypeFont::getSubstituteCodePoint
Char getSubstituteCodePoint() const
Definition: MyGUI_ResourceTrueTypeFont.cpp:358
MyGUI::IResource::deserialization
virtual void deserialization(xml::ElementPtr _node, Version _version)
Definition: MyGUI_IResource.h:61
MyGUI::PixelFormat::Enum
Enum
Definition: MyGUI_RenderFormat.h:61
MyGUI::FontCodeType::SelectedBack
Definition: MyGUI_FontData.h:45
MyGUI::ITexture::createManual
virtual void createManual(int _width, int _height, TextureUsage _usage, PixelFormat _format)=0
MyGUI::utility::parseInt
int parseInt(const std::string &_value)
Definition: MyGUI_StringUtility.h:181
MyGUI_Precompiled.h
MyGUI_DataManager.h
MyGUI::Singleton< RenderManager >::getInstance
static RenderManager & getInstance()
Definition: MyGUI_Singleton.h:53
MyGUI::ResourceTrueTypeFont::getDefaultHeight
virtual int getDefaultHeight()
Definition: MyGUI_ResourceTrueTypeFont.cpp:317
MyGUI::TextureUsage::Write
Definition: MyGUI_RenderFormat.h:98
MyGUI::utility::parseUInt
unsigned int parseUInt(const std::string &_value)
Definition: MyGUI_StringUtility.h:186
MyGUI::xml::Element::findAttribute
bool findAttribute(const std::string &_name, std::string &_value)
Definition: MyGUI_XmlDocument.cpp:261
MyGUI::ITexture::lock
virtual void * lock(TextureUsage _access)=0
MyGUI::IResource::getResourceName
const std::string & getResourceName() const
Definition: MyGUI_IResource.h:47
MyGUI::FontCodeType::NotDefined
Definition: MyGUI_FontData.h:47
nullptr
#define nullptr
Definition: MyGUI_Prerequest.h:47
MyGUI::ITexture::unlock
virtual void unlock()=0
MyGUI::FontCodeType::Tab
Definition: MyGUI_FontData.h:35
MyGUI::ResourceTrueTypeFont::ResourceTrueTypeFont
ResourceTrueTypeFont()
Definition: MyGUI_ResourceTrueTypeFont.cpp:189
MyGUI::xml::ElementEnumerator
Definition: MyGUI_XmlDocument.h:124
MYGUI_LOG
#define MYGUI_LOG(level, text)
Definition: MyGUI_Diagnostic.h:38
MyGUI::ResourceTrueTypeFont::getTextureFont
virtual ITexture * getTextureFont()
Definition: MyGUI_ResourceTrueTypeFont.cpp:312
MyGUI::RenderManager::isFormatSupported
virtual bool isFormatSupported(PixelFormat _format, TextureUsage _usage)
Definition: MyGUI_RenderManager.cpp:33
MyGUI_RenderManager.h
MyGUI::utility::split
std::vector< std::string > split(const std::string &_source, const std::string &_delims="\t\n ")
Definition: MyGUI_StringUtility.h:306
MYGUI_EXCEPT
#define MYGUI_EXCEPT(dest)
Definition: MyGUI_Diagnostic.h:49
MyGUI::ResourceTrueTypeFont::deserialization
virtual void deserialization(xml::ElementPtr _node, Version _version)
Definition: MyGUI_ResourceTrueTypeFont.cpp:212
MyGUI::xml::ElementEnumerator::next
bool next()
Definition: MyGUI_XmlDocument.cpp:115
MyGUI::FontCodeType::Space
Definition: MyGUI_FontData.h:38
MyGUI::Bitwise::firstPO2From
static __inline Type firstPO2From(Type _value)
Definition: MyGUI_Bitwise.h:36
MyGUI::utility::toString
std::string toString(T p)
Definition: MyGUI_StringUtility.h:42
MyGUI::ITexture
Definition: MyGUI_ITexture.h:41
MyGUI::Char
unsigned int Char
Definition: MyGUI_Types.h:66
MyGUI::xml::Element
Definition: MyGUI_XmlDocument.h:168
MyGUI::DataManager::getData
virtual IDataStream * getData(const std::string &_name)=0
MyGUI::TextureUsage::Static
Definition: MyGUI_RenderFormat.h:94
MyGUI::utility::parseFloat
float parseFloat(const std::string &_value)
Definition: MyGUI_StringUtility.h:196
MyGUI::Version
Definition: MyGUI_Version.h:32
MyGUI::GlyphInfo
Definition: MyGUI_FontData.h:53
MyGUI::utility::parseBool
bool parseBool(const std::string &_value)
Definition: MyGUI_StringUtility.h:206
MyGUI::ResourceTrueTypeFont::~ResourceTrueTypeFont
virtual ~ResourceTrueTypeFont()
Definition: MyGUI_ResourceTrueTypeFont.cpp:203
MyGUI::xml::Element::getElementEnumerator
ElementEnumerator getElementEnumerator()
Definition: MyGUI_XmlDocument.cpp:367
MyGUI::ResourceTrueTypeFont::getGlyphInfo
virtual GlyphInfo * getGlyphInfo(Char _id)
Definition: MyGUI_ResourceTrueTypeFont.cpp:305
MyGUI::FontCodeType::Cursor
Definition: MyGUI_FontData.h:46
MyGUI::xml::Element::getName
const std::string & getName() const
Definition: MyGUI_XmlDocument.cpp:347
MyGUI::IDataStream::size
virtual size_t size()=0
MyGUI_Bitwise.h
MyGUI::PixelFormat::R8G8B8A8
Definition: MyGUI_RenderFormat.h:67
MyGUI::PixelFormat::L8A8
Definition: MyGUI_RenderFormat.h:65
MyGUI::uint8
unsigned char uint8
Definition: MyGUI_Types.h:61
MyGUI_ResourceTrueTypeFont.h
MyGUI::RenderManager::createTexture
virtual ITexture * createTexture(const std::string &_name)=0
MyGUI::ResourceTrueTypeFont::getCodePointRanges
std::vector< std::pair< Char, Char > > getCodePointRanges() const
Definition: MyGUI_ResourceTrueTypeFont.cpp:322
MyGUI::FontCodeType::Selected
Definition: MyGUI_FontData.h:44
MyGUI::RenderManager::destroyTexture
virtual void destroyTexture(ITexture *_texture)=0