MyGUI
3.2.1
|
00001 /* 00002 * This source file is part of MyGUI. For the latest info, see http://mygui.info/ 00003 * Distributed under the MIT License 00004 * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) 00005 */ 00006 00007 #include "MyGUI_Precompiled.h" 00008 #include "MyGUI_ResourceTrueTypeFont.h" 00009 #include "MyGUI_DataManager.h" 00010 #include "MyGUI_DataStreamHolder.h" 00011 #include "MyGUI_RenderManager.h" 00012 #include "MyGUI_Bitwise.h" 00013 00014 #ifdef MYGUI_USE_FREETYPE 00015 00016 # include FT_GLYPH_H 00017 # include FT_TRUETYPE_TABLES_H 00018 # include FT_BITMAP_H 00019 # include FT_WINFONTS_H 00020 00021 // The following macro enables a workaround for a bug in FreeType's bytecode interpreter that, when using certain fonts at 00022 // certain sizes, causes FreeType to start measuring and rendering some glyphs inconsistently after certain other glyphs have 00023 // been loaded. See FreeType bug #35374 for details: https://savannah.nongnu.org/bugs/?35374 00024 // 00025 // To reproduce the bug, first disable the workaround by defining MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX to 0. Then load the 00026 // DejaVu Sans font at 10 pt using default values for all other properties. Observe that the glyphs for the "0", 6", "8", and 00027 // "9" characters are now badly corrupted when rendered. 00028 // 00029 // 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 00030 // workaround should be disabled, as it causes fonts to take longer to load. 00031 // 00032 // The bug can currently also be suppressed by disabling FreeType's bytecode interpreter altogether. To do so, remove the 00033 // TT_CONFIG_OPTION_BYTECODE_INTERPRETER macro in the "ftoption.h" FreeType header file. Once this is done, this workaround can 00034 // be safely disabled. Note that disabling FreeType's bytecode interpreter will cause rendered text to look somewhat different. 00035 // Whether it looks better or worse is a matter of taste and may also depend on the font. 00036 # ifndef MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX 00037 # define MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX 1 00038 # endif 00039 00040 #endif // MYGUI_USE_FREETYPE 00041 00042 namespace MyGUI 00043 { 00044 00045 #ifndef MYGUI_USE_FREETYPE 00046 ResourceTrueTypeFont::ResourceTrueTypeFont() 00047 { 00048 } 00049 00050 ResourceTrueTypeFont::~ResourceTrueTypeFont() 00051 { 00052 } 00053 00054 void ResourceTrueTypeFont::deserialization(xml::ElementPtr _node, Version _version) 00055 { 00056 Base::deserialization(_node, _version); 00057 MYGUI_LOG(Error, "ResourceTrueTypeFont: TrueType font '" << getResourceName() << "' disabled. Define MYGUI_USE_FREETYE if you need TrueType fonts."); 00058 } 00059 00060 GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id) 00061 { 00062 return nullptr; 00063 } 00064 00065 ITexture* ResourceTrueTypeFont::getTextureFont() 00066 { 00067 return nullptr; 00068 } 00069 00070 int ResourceTrueTypeFont::getDefaultHeight() 00071 { 00072 return 0; 00073 } 00074 00075 std::vector<std::pair<Char, Char> > ResourceTrueTypeFont::getCodePointRanges() const 00076 { 00077 return std::vector<std::pair<Char, Char> >(); 00078 } 00079 00080 Char ResourceTrueTypeFont::getSubstituteCodePoint() const 00081 { 00082 return Char(); 00083 } 00084 00085 void ResourceTrueTypeFont::initialise() 00086 { 00087 } 00088 00089 void ResourceTrueTypeFont::setSource(const std::string& _value) 00090 { 00091 } 00092 00093 void ResourceTrueTypeFont::setSize(float _value) 00094 { 00095 } 00096 00097 void ResourceTrueTypeFont::setResolution(uint _value) 00098 { 00099 } 00100 00101 void ResourceTrueTypeFont::setHinting(const std::string& _value) 00102 { 00103 } 00104 00105 void ResourceTrueTypeFont::setAntialias(bool _value) 00106 { 00107 } 00108 00109 void ResourceTrueTypeFont::setTabWidth(float _value) 00110 { 00111 } 00112 00113 void ResourceTrueTypeFont::setOffsetHeight(int _value) 00114 { 00115 } 00116 00117 void ResourceTrueTypeFont::setSubstituteCode(int _value) 00118 { 00119 } 00120 00121 void ResourceTrueTypeFont::setDistance(int _value) 00122 { 00123 } 00124 00125 void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second) 00126 { 00127 } 00128 00129 void ResourceTrueTypeFont::removeCodePointRange(Char _first, Char _second) 00130 { 00131 } 00132 00133 #else // MYGUI_USE_FREETYPE 00134 namespace 00135 { 00136 00137 template<typename T> 00138 void setMax(T& _var, const T& _newValue) 00139 { 00140 if (_var < _newValue) 00141 _var = _newValue; 00142 } 00143 00144 std::pair<const Char, const uint8> charMaskData[] = 00145 { 00146 std::make_pair(FontCodeType::Selected, (const uint8)'\x88'), 00147 std::make_pair(FontCodeType::SelectedBack, (const uint8)'\x60'), 00148 std::make_pair(FontCodeType::Cursor, (const uint8)'\xFF'), 00149 std::make_pair(FontCodeType::Tab, (const uint8)'\x00') 00150 }; 00151 00152 const std::map<const Char, const uint8> charMask(charMaskData, charMaskData + sizeof charMaskData / sizeof(*charMaskData)); 00153 00154 const uint8 charMaskBlack = (const uint8)'\x00'; 00155 const uint8 charMaskWhite = (const uint8)'\xFF'; 00156 00157 template<bool LAMode> 00158 struct PixelBase 00159 { 00160 // Returns PixelFormat::R8G8B8A8 when LAMode is false, or PixelFormat::L8A8 when LAMode is true. 00161 static PixelFormat::Enum getFormat(); 00162 00163 // Returns 4 when LAMode is false, or 2 when LAMode is true. 00164 static size_t getNumBytes(); 00165 00166 protected: 00167 // Sets a pixel in _dest as 4 or 2 bytes: L8L8L8A8 if LAMode is false, or L8A8 if LAMode is true. 00168 // Automatically advances _dest just past the pixel written. 00169 static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha); 00170 }; 00171 00172 template<> 00173 struct PixelBase<false> 00174 { 00175 static size_t getNumBytes() 00176 { 00177 return 4; 00178 } 00179 00180 static PixelFormat::Enum getFormat() 00181 { 00182 return PixelFormat::R8G8B8A8; 00183 } 00184 00185 protected: 00186 static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha) 00187 { 00188 *_dest++ = _luminance; 00189 *_dest++ = _luminance; 00190 *_dest++ = _luminance; 00191 *_dest++ = _alpha; 00192 } 00193 }; 00194 00195 template<> 00196 struct PixelBase<true> 00197 { 00198 static size_t getNumBytes() 00199 { 00200 return 2; 00201 } 00202 00203 static PixelFormat::Enum getFormat() 00204 { 00205 return PixelFormat::L8A8; 00206 } 00207 00208 protected: 00209 static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha) 00210 { 00211 *_dest++ = _luminance; 00212 *_dest++ = _alpha; 00213 } 00214 }; 00215 00216 template<bool LAMode, bool FromSource = false, bool Antialias = false> 00217 struct Pixel : PixelBase<LAMode> 00218 { 00219 // Sets a pixel in _dest as 4 or 2 bytes: L8L8L8A8 if LAMode is false, or L8A8 if LAMode is true. 00220 // Sets luminance from _source if both FromSource and Antialias are true; otherwise, uses the specified value. 00221 // Sets alpha from _source if FromSource is true; otherwise, uses the specified value. 00222 // Automatically advances _source just past the pixel read if FromSource is true. 00223 // Automatically advances _dest just past the pixel written. 00224 static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha, uint8*& _source); 00225 }; 00226 00227 template<bool LAMode, bool Antialias> 00228 struct Pixel<LAMode, false, Antialias> : PixelBase<LAMode> 00229 { 00230 // Sets the destination pixel using the specified luminance and alpha. Source is ignored, since FromSource is false. 00231 static void set(uint8*& _dest, uint8 _luminance, uint8 _alpha, uint8* = nullptr) 00232 { 00233 PixelBase<LAMode>::set(_dest, _luminance, _alpha); 00234 } 00235 }; 00236 00237 template<bool LAMode> 00238 struct Pixel<LAMode, true, false> : PixelBase<LAMode> 00239 { 00240 // Sets the destination pixel using the specified _luminance and using the alpha from the specified source. 00241 static void set(uint8*& _dest, uint8 _luminance, uint8, uint8*& _source) 00242 { 00243 PixelBase<LAMode>::set(_dest, _luminance, *_source++); 00244 } 00245 }; 00246 00247 template<bool LAMode> 00248 struct Pixel<LAMode, true, true> : PixelBase<LAMode> 00249 { 00250 // Sets the destination pixel using both the luminance and alpha from the specified source, since Antialias is true. 00251 static void set(uint8*& _dest, uint8, uint8, uint8*& _source) 00252 { 00253 PixelBase<LAMode>::set(_dest, *_source, *_source); 00254 ++_source; 00255 } 00256 }; 00257 00258 } 00259 00260 const int ResourceTrueTypeFont::mDefaultGlyphSpacing = 1; 00261 const float ResourceTrueTypeFont::mDefaultTabWidth = 8.0f; 00262 const float ResourceTrueTypeFont::mSelectedWidth = 1.0f; 00263 const float ResourceTrueTypeFont::mCursorWidth = 2.0f; 00264 00265 ResourceTrueTypeFont::ResourceTrueTypeFont() : 00266 mSize(0), 00267 mResolution(96), 00268 mHinting(HintingUseNative), 00269 mAntialias(false), 00270 mSpaceWidth(0.0f), 00271 mGlyphSpacing(-1), 00272 mTabWidth(0.0f), 00273 mOffsetHeight(0), 00274 mSubstituteCodePoint(static_cast<Char>(FontCodeType::NotDefined)), 00275 mDefaultHeight(0), 00276 mSubstituteGlyphInfo(nullptr), 00277 mTexture(nullptr) 00278 { 00279 } 00280 00281 ResourceTrueTypeFont::~ResourceTrueTypeFont() 00282 { 00283 if (mTexture != nullptr) 00284 { 00285 RenderManager::getInstance().destroyTexture(mTexture); 00286 mTexture = nullptr; 00287 } 00288 } 00289 00290 void ResourceTrueTypeFont::deserialization(xml::ElementPtr _node, Version _version) 00291 { 00292 Base::deserialization(_node, _version); 00293 00294 xml::ElementEnumerator node = _node->getElementEnumerator(); 00295 while (node.next()) 00296 { 00297 if (node->getName() == "Property") 00298 { 00299 const std::string& key = node->findAttribute("key"); 00300 const std::string& value = node->findAttribute("value"); 00301 if (key == "Source") 00302 setSource(value); 00303 else if (key == "Size") 00304 setSize(utility::parseFloat(value)); 00305 else if (key == "Resolution") 00306 setResolution(utility::parseUInt(value)); 00307 else if (key == "Antialias") 00308 setAntialias(utility::parseBool(value)); 00309 else if (key == "TabWidth") 00310 setTabWidth(utility::parseFloat(value)); 00311 else if (key == "OffsetHeight") 00312 setOffsetHeight(utility::parseInt(value)); 00313 else if (key == "SubstituteCode") 00314 setSubstituteCode(utility::parseInt(value)); 00315 else if (key == "Distance") 00316 setDistance(utility::parseInt(value)); 00317 else if (key == "Hinting") 00318 setHinting(value); 00319 else if (key == "SpaceWidth") 00320 { 00321 mSpaceWidth = utility::parseFloat(value); 00322 MYGUI_LOG(Warning, _node->findAttribute("type") << ": Property '" << key << "' in font '" << _node->findAttribute("name") << "' is deprecated; remove it to use automatic calculation."); 00323 } 00324 else if (key == "CursorWidth") 00325 { 00326 MYGUI_LOG(Warning, _node->findAttribute("type") << ": Property '" << key << "' in font '" << _node->findAttribute("name") << "' is deprecated; value ignored."); 00327 } 00328 } 00329 else if (node->getName() == "Codes") 00330 { 00331 // Range of inclusions. 00332 xml::ElementEnumerator range = node->getElementEnumerator(); 00333 while (range.next("Code")) 00334 { 00335 std::string range_value; 00336 if (range->findAttribute("range", range_value)) 00337 { 00338 std::vector<std::string> parse_range = utility::split(range_value); 00339 if (!parse_range.empty()) 00340 { 00341 Char first = utility::parseUInt(parse_range[0]); 00342 Char last = parse_range.size() > 1 ? utility::parseUInt(parse_range[1]) : first; 00343 addCodePointRange(first, last); 00344 } 00345 } 00346 } 00347 00348 // If no code points have been included, include the Unicode Basic Multilingual Plane by default before processing 00349 // any exclusions. 00350 if (mCharMap.empty()) 00351 addCodePointRange(0, 0xFFFF); 00352 00353 // Range of exclusions. 00354 range = node->getElementEnumerator(); 00355 while (range.next("Code")) 00356 { 00357 std::string range_value; 00358 if (range->findAttribute("hide", range_value)) 00359 { 00360 std::vector<std::string> parse_range = utility::split(range_value); 00361 if (!parse_range.empty()) 00362 { 00363 Char first = utility::parseUInt(parse_range[0]); 00364 Char last = parse_range.size() > 1 ? utility::parseUInt(parse_range[1]) : first; 00365 removeCodePointRange(first, last); 00366 } 00367 } 00368 } 00369 } 00370 } 00371 00372 initialise(); 00373 } 00374 00375 GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id) 00376 { 00377 CharMap::const_iterator charIter = mCharMap.find(_id); 00378 00379 if (charIter != mCharMap.end()) 00380 { 00381 GlyphMap::iterator glyphIter = mGlyphMap.find(charIter->second); 00382 00383 if (glyphIter != mGlyphMap.end()) 00384 return &glyphIter->second; 00385 } 00386 00387 return mSubstituteGlyphInfo; 00388 } 00389 00390 ITexture* ResourceTrueTypeFont::getTextureFont() 00391 { 00392 return mTexture; 00393 } 00394 00395 int ResourceTrueTypeFont::getDefaultHeight() 00396 { 00397 return mDefaultHeight; 00398 } 00399 00400 std::vector<std::pair<Char, Char> > ResourceTrueTypeFont::getCodePointRanges() const 00401 { 00402 std::vector<std::pair<Char, Char> > result; 00403 00404 if (!mCharMap.empty()) 00405 { 00406 CharMap::const_iterator iter = mCharMap.begin(), endIter = mCharMap.end(); 00407 00408 // Start the first range with the first code point in the map. 00409 Char rangeBegin = iter->first, rangeEnd = rangeBegin; 00410 00411 // Loop over the rest of the map and find the contiguous ranges. 00412 for (++iter; iter != endIter; ++iter) 00413 { 00414 if (iter->first == rangeEnd + 1) 00415 { 00416 // Extend the current range. 00417 ++rangeEnd; 00418 } 00419 else 00420 { 00421 // Found the start of a new range. First, save the current range. 00422 result.push_back(std::make_pair(rangeBegin, rangeEnd)); 00423 00424 // Then start the new range. 00425 rangeBegin = rangeEnd = iter->first; 00426 } 00427 } 00428 00429 // Save the final range. 00430 result.push_back(std::make_pair(rangeBegin, rangeEnd)); 00431 } 00432 00433 return result; 00434 } 00435 00436 Char ResourceTrueTypeFont::getSubstituteCodePoint() const 00437 { 00438 return mSubstituteCodePoint; 00439 } 00440 00441 void ResourceTrueTypeFont::addCodePoint(Char _codePoint) 00442 { 00443 mCharMap.insert(CharMap::value_type(_codePoint, 0)); 00444 } 00445 00446 void ResourceTrueTypeFont::removeCodePoint(Char _codePoint) 00447 { 00448 mCharMap.erase(_codePoint); 00449 } 00450 00451 void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second) 00452 { 00453 CharMap::iterator positionHint = mCharMap.lower_bound(_first); 00454 00455 if (positionHint != mCharMap.begin()) 00456 --positionHint; 00457 00458 for (Char i = _first; i <= _second; ++i) 00459 positionHint = mCharMap.insert(positionHint, CharMap::value_type(i, 0)); 00460 } 00461 00462 void ResourceTrueTypeFont::removeCodePointRange(Char _first, Char _second) 00463 { 00464 mCharMap.erase(mCharMap.lower_bound(_first), mCharMap.upper_bound(_second)); 00465 } 00466 00467 void ResourceTrueTypeFont::clearCodePoints() 00468 { 00469 mCharMap.clear(); 00470 } 00471 00472 void ResourceTrueTypeFont::initialise() 00473 { 00474 if (mGlyphSpacing == -1) 00475 mGlyphSpacing = mDefaultGlyphSpacing; 00476 00477 // If L8A8 (2 bytes per pixel) is supported, use it; otherwise, use R8G8B8A8 (4 bytes per pixel) as L8L8L8A8. 00478 bool laMode = MyGUI::RenderManager::getInstance().isFormatSupported(Pixel<true>::getFormat(), TextureUsage::Static | TextureUsage::Write); 00479 00480 // Select and call an appropriate initialisation method. By making this decision up front, we avoid having to branch on 00481 // these variables many thousands of times inside tight nested loops later. From this point on, the various function 00482 // templates ensure that all of the necessary branching is done purely at compile time for all combinations. 00483 int init = (laMode ? 2 : 0) | (mAntialias ? 1 : 0); 00484 00485 switch (init) 00486 { 00487 case 0: 00488 ResourceTrueTypeFont::initialiseFreeType<false, false>(); 00489 break; 00490 case 1: 00491 ResourceTrueTypeFont::initialiseFreeType<false, true>(); 00492 break; 00493 case 2: 00494 ResourceTrueTypeFont::initialiseFreeType<true, false>(); 00495 break; 00496 case 3: 00497 ResourceTrueTypeFont::initialiseFreeType<true, true>(); 00498 break; 00499 } 00500 } 00501 00502 template<bool LAMode, bool Antialias> 00503 void ResourceTrueTypeFont::initialiseFreeType() 00504 { 00505 //-------------------------------------------------------------------// 00506 // Initialise FreeType and load the font. 00507 //-------------------------------------------------------------------// 00508 00509 FT_Library ftLibrary; 00510 00511 if (FT_Init_FreeType(&ftLibrary) != 0) 00512 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not init the FreeType library!"); 00513 00514 uint8* fontBuffer = nullptr; 00515 00516 FT_Face ftFace = loadFace(ftLibrary, fontBuffer); 00517 00518 if (ftFace == nullptr) 00519 { 00520 MYGUI_LOG(Error, "ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00521 return; 00522 } 00523 00524 //-------------------------------------------------------------------// 00525 // Calculate the font metrics. 00526 //-------------------------------------------------------------------// 00527 00528 // The font's overall ascent and descent are defined in three different places in a TrueType font, and with different 00529 // values in each place. The most reliable source for these metrics is usually the "usWinAscent" and "usWinDescent" pair of 00530 // values in the OS/2 header; however, some fonts contain inaccurate data there. To be safe, we use the highest of the set 00531 // of values contained in the face metrics and the two sets of values contained in the OS/2 header. 00532 int fontAscent = ftFace->size->metrics.ascender >> 6; 00533 int fontDescent = -ftFace->size->metrics.descender >> 6; 00534 00535 TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(ftFace, ft_sfnt_os2); 00536 00537 if (os2 != nullptr) 00538 { 00539 setMax(fontAscent, os2->usWinAscent * ftFace->size->metrics.y_ppem / ftFace->units_per_EM); 00540 setMax(fontDescent, os2->usWinDescent * ftFace->size->metrics.y_ppem / ftFace->units_per_EM); 00541 00542 setMax(fontAscent, os2->sTypoAscender * ftFace->size->metrics.y_ppem / ftFace->units_per_EM); 00543 setMax(fontDescent, -os2->sTypoDescender * ftFace->size->metrics.y_ppem / ftFace->units_per_EM); 00544 } 00545 00546 // The nominal font height is calculated as the sum of its ascent and descent as specified by the font designer. Previously 00547 // it was defined by MyGUI in terms of the maximum ascent and descent of the glyphs currently in use, but this caused the 00548 // font's line spacing to change whenever glyphs were added to or removed from the font definition. Doing it this way 00549 // instead prevents a lot of layout problems, and it is also more typographically correct and more aesthetically pleasing. 00550 mDefaultHeight = fontAscent + fontDescent; 00551 00552 // Set the load flags based on the specified type of hinting. 00553 FT_Int32 ftLoadFlags; 00554 00555 switch (mHinting) 00556 { 00557 case HintingForceAuto: 00558 ftLoadFlags = FT_LOAD_FORCE_AUTOHINT; 00559 break; 00560 case HintingDisableAuto: 00561 ftLoadFlags = FT_LOAD_NO_AUTOHINT; 00562 break; 00563 case HintingDisableAll: 00564 // When hinting is completely disabled, glyphs must always be rendered -- even during layout calculations -- due to 00565 // discrepancies between the glyph metrics and the actual rendered bitmap metrics. 00566 ftLoadFlags = FT_LOAD_NO_HINTING | FT_LOAD_RENDER; 00567 break; 00568 //case HintingUseNative: 00569 default: 00570 ftLoadFlags = FT_LOAD_DEFAULT; 00571 break; 00572 } 00573 00574 //-------------------------------------------------------------------// 00575 // Create the glyphs and calculate their metrics. 00576 //-------------------------------------------------------------------// 00577 00578 GlyphHeightMap glyphHeightMap; 00579 int texWidth = 0; 00580 00581 // Before creating the glyphs, add the "Space" code point to force that glyph to be created. For historical reasons, MyGUI 00582 // users are accustomed to omitting this code point in their font definitions. 00583 addCodePoint(FontCodeType::Space); 00584 00585 // Create the standard glyphs. 00586 for (CharMap::iterator iter = mCharMap.begin(); iter != mCharMap.end(); ) 00587 { 00588 const Char& codePoint = iter->first; 00589 FT_UInt glyphIndex = FT_Get_Char_Index(ftFace, codePoint); 00590 00591 texWidth += createFaceGlyph(glyphIndex, codePoint, fontAscent, ftFace, ftLoadFlags, glyphHeightMap); 00592 00593 // If the newly created glyph is the "Not Defined" glyph, it means that the code point is not supported by the font. 00594 // Remove it from the character map so that we can provide our own substitute instead of letting FreeType do it. 00595 if (iter->second != 0) 00596 ++iter; 00597 else 00598 mCharMap.erase(iter++); 00599 } 00600 00601 #if MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX 00602 00603 bool isBytecodeAvailable = (ftFace->face_flags & FT_FACE_FLAG_HINTER) != 0; 00604 bool isBytecodeUsedByLoadFlags = (ftLoadFlags & (FT_LOAD_FORCE_AUTOHINT | FT_LOAD_NO_HINTING)) == 0; 00605 00606 if (isBytecodeAvailable && isBytecodeUsedByLoadFlags) 00607 { 00608 for (GlyphMap::iterator iter = mGlyphMap.begin(); iter != mGlyphMap.end(); ++iter) 00609 { 00610 if (FT_Load_Glyph(ftFace, iter->first, ftLoadFlags) == 0) 00611 { 00612 GlyphInfo& info = iter->second; 00613 GlyphInfo newInfo = createFaceGlyphInfo(0, fontAscent, ftFace->glyph); 00614 00615 if (info.width != newInfo.width) 00616 { 00617 texWidth += (int)ceil(newInfo.width) - (int)ceil(info.width); 00618 info.width = newInfo.width; 00619 } 00620 00621 if (info.height != newInfo.height) 00622 { 00623 GlyphHeightMap::mapped_type oldHeightMap = glyphHeightMap[(FT_Pos)info.height]; 00624 GlyphHeightMap::mapped_type::iterator heightMapItem = oldHeightMap.find(iter->first); 00625 glyphHeightMap[(FT_Pos)newInfo.height].insert(*heightMapItem); 00626 oldHeightMap.erase(heightMapItem); 00627 info.height = newInfo.height; 00628 } 00629 00630 if (info.advance != newInfo.advance) 00631 info.advance = newInfo.advance; 00632 00633 if (info.bearingX != newInfo.bearingX) 00634 info.bearingX = newInfo.bearingX; 00635 00636 if (info.bearingY != newInfo.bearingY) 00637 info.bearingY = newInfo.bearingY; 00638 } 00639 else 00640 { 00641 MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot load glyph " << iter->first << " for character " << iter->second.codePoint << " in font '" << getResourceName() << "'."); 00642 } 00643 } 00644 } 00645 00646 #endif // MYGUI_USE_FREETYPE_BYTECODE_BUG_FIX 00647 00648 // Do some special handling for the "Space" and "Tab" glyphs. 00649 GlyphInfo* spaceGlyphInfo = getGlyphInfo(FontCodeType::Space); 00650 00651 if (spaceGlyphInfo != nullptr && spaceGlyphInfo->codePoint == FontCodeType::Space) 00652 { 00653 // Adjust the width of the "Space" glyph if it has been customized. 00654 if (mSpaceWidth != 0.0f) 00655 { 00656 texWidth += (int)ceil(mSpaceWidth) - (int)ceil(spaceGlyphInfo->width); 00657 spaceGlyphInfo->width = mSpaceWidth; 00658 spaceGlyphInfo->advance = mSpaceWidth; 00659 } 00660 00661 // If the width of the "Tab" glyph hasn't been customized, make it eight spaces wide. 00662 if (mTabWidth == 0.0f) 00663 mTabWidth = mDefaultTabWidth * spaceGlyphInfo->advance; 00664 } 00665 00666 // Create the special glyphs. They must be created after the standard glyphs so that they take precedence in case of a 00667 // collision. To make sure that the indices of the special glyphs don't collide with any glyph indices in the font, we must 00668 // use glyph indices higher than the highest glyph index in the font. 00669 FT_UInt nextGlyphIndex = (FT_UInt)ftFace->num_glyphs; 00670 00671 float height = (float)mDefaultHeight; 00672 00673 texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(static_cast<Char>(FontCodeType::Tab), 0.0f, 0.0f, mTabWidth, 0.0f, 0.0f), glyphHeightMap); 00674 texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(static_cast<Char>(FontCodeType::Selected), mSelectedWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap); 00675 texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(static_cast<Char>(FontCodeType::SelectedBack), mSelectedWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap); 00676 texWidth += createGlyph(nextGlyphIndex++, GlyphInfo(static_cast<Char>(FontCodeType::Cursor), mCursorWidth, height, 0.0f, 0.0f, 0.0f), glyphHeightMap); 00677 00678 // If a substitute code point has been specified, check to make sure that it exists in the character map. If it doesn't, 00679 // revert to the default "Not Defined" code point. This is not a real code point but rather an invalid Unicode value that 00680 // is guaranteed to cause the "Not Defined" special glyph to be created. 00681 if (mSubstituteCodePoint != FontCodeType::NotDefined && mCharMap.find(mSubstituteCodePoint) == mCharMap.end()) 00682 mSubstituteCodePoint = static_cast<Char>(FontCodeType::NotDefined); 00683 00684 // Create the "Not Defined" code point (and its corresponding glyph) if it's in use as the substitute code point. 00685 if (mSubstituteCodePoint == FontCodeType::NotDefined) 00686 texWidth += createFaceGlyph(0, static_cast<Char>(FontCodeType::NotDefined), fontAscent, ftFace, ftLoadFlags, glyphHeightMap); 00687 00688 // Cache a pointer to the substitute glyph info for fast lookup. 00689 mSubstituteGlyphInfo = &mGlyphMap.find(mCharMap.find(mSubstituteCodePoint)->second)->second; 00690 00691 // Calculate the average height of all of the glyphs that are in use. This value will be used for estimating how large the 00692 // texture needs to be. 00693 double averageGlyphHeight = 0.0; 00694 00695 for (GlyphHeightMap::const_iterator j = glyphHeightMap.begin(); j != glyphHeightMap.end(); ++j) 00696 averageGlyphHeight += j->first * j->second.size(); 00697 00698 averageGlyphHeight /= mGlyphMap.size(); 00699 00700 //-------------------------------------------------------------------// 00701 // Calculate the final texture size. 00702 //-------------------------------------------------------------------// 00703 00704 // Round the current texture width and height up to the nearest powers of two. 00705 texWidth = Bitwise::firstPO2From(texWidth); 00706 int texHeight = Bitwise::firstPO2From((int)ceil(averageGlyphHeight) + mGlyphSpacing); 00707 00708 // At this point, the texture is only one glyph high and is probably very wide. For efficiency reasons, we need to make the 00709 // texture as square as possible. If the texture cannot be made perfectly square, make it taller than it is wide, because 00710 // the height may decrease in the final layout due to height packing. 00711 while (texWidth > texHeight) 00712 { 00713 texWidth /= 2; 00714 texHeight *= 2; 00715 } 00716 00717 // Calculate the final layout of all of the glyphs in the texture, packing them tightly by first arranging them by height. 00718 // We assume that the texture width is fixed but that the texture height can be adjusted up or down depending on how much 00719 // space is actually needed. 00720 // In most cases, the final texture will end up square or almost square. In some rare cases, however, we can end up with a 00721 // texture that's more than twice as high as it is wide; when this happens, we double the width and try again. 00722 do 00723 { 00724 if (texHeight > texWidth * 2) 00725 texWidth *= 2; 00726 00727 int texX = 0, texY = 0; 00728 00729 for (GlyphHeightMap::const_iterator j = glyphHeightMap.begin(); j != glyphHeightMap.end(); ++j) 00730 { 00731 for (GlyphHeightMap::mapped_type::const_iterator i = j->second.begin(); i != j->second.end(); ++i) 00732 { 00733 GlyphInfo& info = *i->second; 00734 00735 int glyphWidth = (int)ceil(info.width); 00736 int glyphHeight = (int)ceil(info.height); 00737 00738 autoWrapGlyphPos(glyphWidth, texWidth, glyphHeight, texX, texY); 00739 00740 if (glyphWidth > 0) 00741 texX += mGlyphSpacing + glyphWidth; 00742 } 00743 } 00744 00745 texHeight = Bitwise::firstPO2From(texY + glyphHeightMap.rbegin()->first); 00746 } 00747 while (texHeight > texWidth * 2); 00748 00749 //-------------------------------------------------------------------// 00750 // Create the texture and render the glyphs onto it. 00751 //-------------------------------------------------------------------// 00752 00753 mTexture = RenderManager::getInstance().createTexture(MyGUI::utility::toString((size_t)this, "_TrueTypeFont")); 00754 00755 mTexture->createManual(texWidth, texHeight, TextureUsage::Static | TextureUsage::Write, Pixel<LAMode>::getFormat()); 00756 00757 uint8* texBuffer = static_cast<uint8*>(mTexture->lock(TextureUsage::Write)); 00758 00759 if (texBuffer != nullptr) 00760 { 00761 // Make the texture background transparent white. 00762 for (uint8* dest = texBuffer, * endDest = dest + texWidth * texHeight * Pixel<LAMode>::getNumBytes(); dest != endDest; ) 00763 Pixel<LAMode, false, false>::set(dest, charMaskWhite, charMaskBlack); 00764 00765 renderGlyphs<LAMode, Antialias>(glyphHeightMap, ftLibrary, ftFace, ftLoadFlags, texBuffer, texWidth, texHeight); 00766 00767 mTexture->unlock(); 00768 00769 MYGUI_LOG(Info, "ResourceTrueTypeFont: Font '" << getResourceName() << "' using texture size " << texWidth << " x " << texHeight << "."); 00770 MYGUI_LOG(Info, "ResourceTrueTypeFont: Font '" << getResourceName() << "' using real height " << mDefaultHeight << " pixels."); 00771 } 00772 else 00773 { 00774 MYGUI_LOG(Error, "ResourceTrueTypeFont: Error locking texture; pointer is nullptr."); 00775 } 00776 00777 FT_Done_Face(ftFace); 00778 FT_Done_FreeType(ftLibrary); 00779 00780 delete [] fontBuffer; 00781 } 00782 00783 FT_Face ResourceTrueTypeFont::loadFace(const FT_Library& _ftLibrary, uint8*& _fontBuffer) 00784 { 00785 FT_Face result = nullptr; 00786 00787 // Load the font file. 00788 IDataStream* datastream = DataManager::getInstance().getData(mSource); 00789 00790 if (datastream == nullptr) 00791 return result; 00792 00793 size_t fontBufferSize = datastream->size(); 00794 _fontBuffer = new uint8[fontBufferSize]; 00795 datastream->read(_fontBuffer, fontBufferSize); 00796 00797 DataManager::getInstance().freeData(datastream); 00798 datastream = nullptr; 00799 00800 // Determine how many faces the font contains. 00801 if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, -1, &result) != 0) 00802 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00803 00804 FT_Long numFaces = result->num_faces; 00805 FT_Long faceIndex = 0; 00806 00807 // Load the first face. 00808 if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0) 00809 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00810 00811 if (result->face_flags & FT_FACE_FLAG_SCALABLE) 00812 { 00813 // The font is scalable, so set the font size by first converting the requested size to FreeType's 26.6 fixed-point 00814 // format. 00815 FT_F26Dot6 ftSize = (FT_F26Dot6)(mSize * (1 << 6)); 00816 00817 if (FT_Set_Char_Size(result, ftSize, 0, mResolution, mResolution) != 0) 00818 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not set the font size for '" << getResourceName() << "'!"); 00819 00820 // If no code points have been specified, use the Unicode Basic Multilingual Plane by default. 00821 if (mCharMap.empty()) 00822 addCodePointRange(0, 0xFFFF); 00823 } 00824 else 00825 { 00826 // The font isn't scalable, so try to load it as a Windows FNT/FON file. 00827 FT_WinFNT_HeaderRec fnt; 00828 00829 // Enumerate all of the faces in the font and select the smallest one that's at least as large as the requested size 00830 // (after adjusting for resolution). If none of the faces are large enough, use the largest one. 00831 std::map<float, FT_Long> faceSizes; 00832 00833 do 00834 { 00835 if (FT_Get_WinFNT_Header(result, &fnt) != 0) 00836 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00837 00838 faceSizes.insert(std::make_pair((float)fnt.nominal_point_size * fnt.vertical_resolution / mResolution, faceIndex)); 00839 00840 FT_Done_Face(result); 00841 00842 if (++faceIndex < numFaces) 00843 if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0) 00844 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00845 } 00846 while (faceIndex < numFaces); 00847 00848 std::map<float, FT_Long>::const_iterator iter = faceSizes.lower_bound(mSize); 00849 00850 faceIndex = (iter != faceSizes.end()) ? iter->second : faceSizes.rbegin()->second; 00851 00852 if (FT_New_Memory_Face(_ftLibrary, _fontBuffer, (FT_Long)fontBufferSize, faceIndex, &result) != 0) 00853 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not load the font '" << getResourceName() << "'!"); 00854 00855 // Select the first bitmap strike available in the selected face. This needs to be done explicitly even though Windows 00856 // FNT/FON files contain only one bitmap strike per face. 00857 if (FT_Select_Size(result, 0) != 0) 00858 MYGUI_EXCEPT("ResourceTrueTypeFont: Could not set the font size for '" << getResourceName() << "'!"); 00859 00860 // Windows FNT/FON files do not support Unicode, so restrict the code-point range to either ISO-8859-1 or ASCII, 00861 // depending on the font's encoding. 00862 if (mCharMap.empty()) 00863 { 00864 // No code points have been specified, so add the printable ASCII range by default. 00865 addCodePointRange(0x20, 0x7E); 00866 00867 // Additionally, if the font's character set is CP-1252, add the range of non-ASCII 8-bit code points that are 00868 // common between CP-1252 and ISO-8859-1; i.e., everything but 0x80 through 0x9F. 00869 if (fnt.charset == FT_WinFNT_ID_CP1252) 00870 addCodePointRange(0xA0, 0xFF); 00871 } 00872 else 00873 { 00874 // Some code points have been specified, so remove anything in the non-printable ASCII range as well as anything 00875 // over 8 bits. 00876 removeCodePointRange(0, 0x1F); 00877 removeCodePointRange(0x100, std::numeric_limits<Char>::max()); 00878 00879 // Additionally, remove non-ASCII 8-bit code points (plus ASCII DEL, 0x7F). If the font's character set is CP-1252, 00880 // remove only the code points that differ between CP-1252 and ISO-8859-1; otherwise, remove all of them. 00881 if (fnt.charset == FT_WinFNT_ID_CP1252) 00882 removeCodePointRange(0x7F, 0x9F); 00883 else 00884 removeCodePointRange(0x7F, 0xFF); 00885 } 00886 } 00887 00888 return result; 00889 } 00890 00891 void ResourceTrueTypeFont::autoWrapGlyphPos(int _glyphWidth, int _texWidth, int _lineHeight, int& _texX, int& _texY) 00892 { 00893 if (_glyphWidth > 0 && _texX + mGlyphSpacing + _glyphWidth > _texWidth) 00894 { 00895 _texX = 0; 00896 _texY += mGlyphSpacing + _lineHeight; 00897 } 00898 } 00899 00900 GlyphInfo ResourceTrueTypeFont::createFaceGlyphInfo(Char _codePoint, int _fontAscent, FT_GlyphSlot _glyph) 00901 { 00902 float bearingX = _glyph->metrics.horiBearingX / 64.0f; 00903 00904 // The following calculations aren't currently needed but are kept here for future use. 00905 // float ascent = _glyph->metrics.horiBearingY / 64.0f; 00906 // float descent = (_glyph->metrics.height / 64.0f) - ascent; 00907 00908 return GlyphInfo( 00909 _codePoint, 00910 std::max((float)_glyph->bitmap.width, _glyph->metrics.width / 64.0f), 00911 std::max((float)_glyph->bitmap.rows, _glyph->metrics.height / 64.0f), 00912 (_glyph->advance.x / 64.0f) - bearingX, 00913 bearingX, 00914 floor(_fontAscent - (_glyph->metrics.horiBearingY / 64.0f) - mOffsetHeight)); 00915 } 00916 00917 int ResourceTrueTypeFont::createGlyph(FT_UInt _glyphIndex, const GlyphInfo& _glyphInfo, GlyphHeightMap& _glyphHeightMap) 00918 { 00919 int width = (int)ceil(_glyphInfo.width); 00920 int height = (int)ceil(_glyphInfo.height); 00921 00922 mCharMap[_glyphInfo.codePoint] = _glyphIndex; 00923 GlyphInfo& info = mGlyphMap.insert(GlyphMap::value_type(_glyphIndex, _glyphInfo)).first->second; 00924 _glyphHeightMap[(FT_Pos)height].insert(std::make_pair(_glyphIndex, &info)); 00925 00926 return (width > 0) ? mGlyphSpacing + width : 0; 00927 } 00928 00929 int ResourceTrueTypeFont::createFaceGlyph(FT_UInt _glyphIndex, Char _codePoint, int _fontAscent, const FT_Face& _ftFace, FT_Int32 _ftLoadFlags, GlyphHeightMap& _glyphHeightMap) 00930 { 00931 if (mGlyphMap.find(_glyphIndex) == mGlyphMap.end()) 00932 { 00933 if (FT_Load_Glyph(_ftFace, _glyphIndex, _ftLoadFlags) == 0) 00934 return createGlyph(_glyphIndex, createFaceGlyphInfo(_codePoint, _fontAscent, _ftFace->glyph), _glyphHeightMap); 00935 else 00936 MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot load glyph " << _glyphIndex << " for character " << _codePoint << " in font '" << getResourceName() << "'."); 00937 } 00938 else 00939 { 00940 mCharMap[_codePoint] = _glyphIndex; 00941 } 00942 00943 return 0; 00944 } 00945 00946 template<bool LAMode, bool Antialias> 00947 void ResourceTrueTypeFont::renderGlyphs(const GlyphHeightMap& _glyphHeightMap, const FT_Library& _ftLibrary, const FT_Face& _ftFace, FT_Int32 _ftLoadFlags, uint8* _texBuffer, int _texWidth, int _texHeight) 00948 { 00949 FT_Bitmap ftBitmap; 00950 FT_Bitmap_New(&ftBitmap); 00951 00952 int texX = 0, texY = 0; 00953 00954 for (GlyphHeightMap::const_iterator j = _glyphHeightMap.begin(); j != _glyphHeightMap.end(); ++j) 00955 { 00956 for (GlyphHeightMap::mapped_type::const_iterator i = j->second.begin(); i != j->second.end(); ++i) 00957 { 00958 GlyphInfo& info = *i->second; 00959 00960 switch (info.codePoint) 00961 { 00962 case FontCodeType::Selected: 00963 case FontCodeType::SelectedBack: 00964 { 00965 renderGlyph<LAMode, false, false>(info, charMaskWhite, charMaskBlack, charMask.find(info.codePoint)->second, j->first, _texBuffer, _texWidth, _texHeight, texX, texY); 00966 00967 // Manually adjust the glyph's width to zero. This prevents artifacts from appearing at the seams when 00968 // rendering multi-character selections. 00969 GlyphInfo* glyphInfo = getGlyphInfo(info.codePoint); 00970 glyphInfo->width = 0.0f; 00971 glyphInfo->uvRect.right = glyphInfo->uvRect.left; 00972 } 00973 break; 00974 00975 case FontCodeType::Cursor: 00976 case FontCodeType::Tab: 00977 renderGlyph<LAMode, false, false>(info, charMaskWhite, charMaskBlack, charMask.find(info.codePoint)->second, j->first, _texBuffer, _texWidth, _texHeight, texX, texY); 00978 break; 00979 00980 default: 00981 if (FT_Load_Glyph(_ftFace, i->first, _ftLoadFlags | FT_LOAD_RENDER) == 0) 00982 { 00983 if (_ftFace->glyph->bitmap.buffer != nullptr) 00984 { 00985 uint8* glyphBuffer = nullptr; 00986 00987 switch (_ftFace->glyph->bitmap.pixel_mode) 00988 { 00989 case FT_PIXEL_MODE_GRAY: 00990 glyphBuffer = _ftFace->glyph->bitmap.buffer; 00991 break; 00992 00993 case FT_PIXEL_MODE_MONO: 00994 // Convert the monochrome bitmap to 8-bit before rendering it. 00995 if (FT_Bitmap_Convert(_ftLibrary, &_ftFace->glyph->bitmap, &ftBitmap, 1) == 0) 00996 { 00997 // Go through the bitmap and convert all of the nonzero values to 0xFF (white). 00998 for (uint8* p = ftBitmap.buffer, * endP = p + ftBitmap.width * ftBitmap.rows; p != endP; ++p) 00999 *p ^= -*p ^ *p; 01000 01001 glyphBuffer = ftBitmap.buffer; 01002 } 01003 break; 01004 } 01005 01006 if (glyphBuffer != nullptr) 01007 renderGlyph<LAMode, true, Antialias>(info, charMaskWhite, charMaskWhite, charMaskWhite, j->first, _texBuffer, _texWidth, _texHeight, texX, texY, glyphBuffer); 01008 } 01009 } 01010 else 01011 { 01012 MYGUI_LOG(Warning, "ResourceTrueTypeFont: Cannot render glyph " << i->first << " for character " << info.codePoint << " in font '" << getResourceName() << "'."); 01013 } 01014 break; 01015 } 01016 } 01017 } 01018 01019 FT_Bitmap_Done(_ftLibrary, &ftBitmap); 01020 } 01021 01022 template<bool LAMode, bool UseBuffer, bool Antialias> 01023 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) 01024 { 01025 int width = (int)ceil(_info.width); 01026 int height = (int)ceil(_info.height); 01027 01028 autoWrapGlyphPos(width, _texWidth, _lineHeight, _texX, _texY); 01029 01030 uint8* dest = _texBuffer + (_texY * _texWidth + _texX) * Pixel<LAMode>::getNumBytes(); 01031 01032 // Calculate how much to advance the destination pointer after each row to get to the start of the next row. 01033 ptrdiff_t destNextRow = (_texWidth - width) * Pixel<LAMode>::getNumBytes(); 01034 01035 for (int j = height; j > 0; --j) 01036 { 01037 int i; 01038 for (i = width; i > 1; i -= 2) 01039 { 01040 Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance0, _alpha, _glyphBuffer); 01041 Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance1, _alpha, _glyphBuffer); 01042 } 01043 01044 if (i > 0) 01045 Pixel<LAMode, UseBuffer, Antialias>::set(dest, _luminance0, _alpha, _glyphBuffer); 01046 01047 dest += destNextRow; 01048 } 01049 01050 // Calculate and store the glyph's UV coordinates within the texture. 01051 _info.uvRect.left = (float)_texX / _texWidth; // u1 01052 _info.uvRect.top = (float)_texY / _texHeight; // v1 01053 _info.uvRect.right = (float)(_texX + _info.width) / _texWidth; // u2 01054 _info.uvRect.bottom = (float)(_texY + _info.height) / _texHeight; // v2 01055 01056 if (width > 0) 01057 _texX += mGlyphSpacing + width; 01058 } 01059 01060 void ResourceTrueTypeFont::setSource(const std::string& _value) 01061 { 01062 mSource = _value; 01063 } 01064 01065 void ResourceTrueTypeFont::setSize(float _value) 01066 { 01067 mSize = _value; 01068 } 01069 01070 void ResourceTrueTypeFont::setResolution(uint _value) 01071 { 01072 mResolution = _value; 01073 } 01074 01075 void ResourceTrueTypeFont::setHinting(const std::string& _value) 01076 { 01077 if (_value == "use_native") 01078 mHinting = HintingUseNative; 01079 else if (_value == "force_auto") 01080 mHinting = HintingForceAuto; 01081 else if (_value == "disable_auto") 01082 mHinting = HintingDisableAuto; 01083 else if (_value == "disable_all") 01084 mHinting = HintingDisableAll; 01085 else 01086 mHinting = HintingUseNative; 01087 } 01088 01089 void ResourceTrueTypeFont::setAntialias(bool _value) 01090 { 01091 mAntialias = _value; 01092 } 01093 01094 void ResourceTrueTypeFont::setTabWidth(float _value) 01095 { 01096 mTabWidth = _value; 01097 } 01098 01099 void ResourceTrueTypeFont::setOffsetHeight(int _value) 01100 { 01101 mOffsetHeight = _value; 01102 } 01103 01104 void ResourceTrueTypeFont::setSubstituteCode(int _value) 01105 { 01106 mSubstituteCodePoint = _value; 01107 } 01108 01109 void ResourceTrueTypeFont::setDistance(int _value) 01110 { 01111 mGlyphSpacing = _value; 01112 } 01113 01114 #endif // MYGUI_USE_FREETYPE 01115 01116 } // namespace MyGUI