00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "MyGUI_Precompiled.h"
00024 #include "MyGUI_ResourceTrueTypeFont.h"
00025 #include "MyGUI_DataManager.h"
00026 #include "MyGUI_RenderManager.h"
00027
00028 #ifdef MYGUI_USE_FREETYPE
00029 #include <ft2build.h>
00030 #include FT_FREETYPE_H
00031 #include FT_GLYPH_H
00032 #endif // MYGUI_USE_FREETYPE
00033
00034 namespace MyGUI
00035 {
00036
00037 const unsigned char FONT_MASK_SELECT = 0x88;
00038 const unsigned char FONT_MASK_SELECT_DEACTIVE = 0x60;
00039 const unsigned char FONT_MASK_SPACE = 0x00;
00040 const unsigned char FONT_MASK_CHAR = 0xFF;
00041 const size_t MIN_FONT_TEXTURE_WIDTH = 256;
00042
00043 ResourceTrueTypeFont::ResourceTrueTypeFont() :
00044 mTtfSize(0),
00045 mTtfResolution(0),
00046 mAntialiasColour(false),
00047 mDistance(0),
00048 mSpaceWidth(0),
00049 mTabWidth(0),
00050 mCursorWidth(2),
00051 mSelectionWidth(2),
00052 mOffsetHeight(0),
00053 mHeightPix(0),
00054 mTexture(nullptr)
00055 {
00056 }
00057
00058 ResourceTrueTypeFont::~ResourceTrueTypeFont()
00059 {
00060 if (mTexture != nullptr)
00061 {
00062 RenderManager::getInstance().destroyTexture(mTexture);
00063 mTexture = nullptr;
00064 }
00065 }
00066
00067 GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id)
00068 {
00069 for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
00070 {
00071 GlyphInfo* info = iter->getInfo(_id);
00072 if (info == nullptr) continue;
00073 return info;
00074 }
00075
00076 return &mSpaceGlyphInfo;
00077 }
00078
00079 void ResourceTrueTypeFont::addGlyph(GlyphInfo * _info, Char _index, int _left, int _top, int _right, int _bottom, int _finalw, int _finalh, float _aspect, int _addHeight)
00080 {
00081 _info->codePoint = _index;
00082 _info->uvRect.left = (float)_left / (float)_finalw;
00083 _info->uvRect.top = (float)(_top + _addHeight) / (float)_finalh;
00084 _info->uvRect.right = (float)( _right ) / (float)_finalw;
00085 _info->uvRect.bottom = ( _bottom + _addHeight ) / (float)_finalh;
00086 _info->width = _right - _left;
00087 }
00088
00089 uint8* ResourceTrueTypeFont::writeData(uint8* _pDest, unsigned char _luminance, unsigned char _alpha, bool _rgba)
00090 {
00091 if (_rgba)
00092 {
00093 *_pDest++ = _luminance;
00094 *_pDest++ = _luminance;
00095 *_pDest++ = _luminance;
00096 *_pDest++ = _alpha;
00097 }
00098 else
00099 {
00100 *_pDest++ = _luminance;
00101 *_pDest++ = _alpha;
00102 }
00103 return _pDest;
00104 }
00105
00106 void ResourceTrueTypeFont::initialise()
00107 {
00108
00109 #ifndef MYGUI_USE_FREETYPE
00110
00111 MYGUI_LOG(Error, "ResourceTrueTypeFont '" << getResourceName() << "' - Ttf font disabled. Define MYGUI_USE_FREETYE if you need ttf fonts.");
00112
00113 #else // MYGUI_USE_FREETYPE
00114
00115 mTexture = RenderManager::getInstance().createTexture(MyGUI::utility::toString((size_t)this, "_TrueTypeFont"));
00116
00117
00118 FT_Library ftLibrary;
00119
00120 if ( FT_Init_FreeType( &ftLibrary ) ) MYGUI_EXCEPT("Could not init FreeType library!");
00121
00122
00123 FT_Face face;
00124
00125
00126 IDataStream* datastream = DataManager::getInstance().getData(mSource);
00127 MYGUI_ASSERT(datastream, "Could not open font face!");
00128
00129 size_t datasize = datastream->size();
00130 uint8* data = new uint8[datasize];
00131 datastream->read(data, datasize);
00132 delete datastream;
00133
00134 if ( FT_New_Memory_Face( ftLibrary, data, (FT_Long)datasize, 0, &face ) )
00135 MYGUI_EXCEPT("Could not open font face!");
00136
00137
00138 FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
00139 if ( FT_Set_Char_Size( face, ftSize, 0, mTtfResolution, mTtfResolution ) )
00140 MYGUI_EXCEPT("Could not set char size!");
00141
00142 int max_height = 0, max_bear = 0;
00143
00144 int spec_len = mCursorWidth + mSelectionWidth + mSelectionWidth + mSpaceWidth + mTabWidth + (mDistance * 5);
00145 int len = mDistance + spec_len;
00146 int height = 0;
00147
00148 size_t finalWidth = MIN_FONT_TEXTURE_WIDTH;
00149
00150 while (mTtfSize*mTtfResolution > finalWidth*6) finalWidth *= 2;
00151
00152 for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
00153 {
00154 for (Char index=iter->first; index<=iter->last; ++index)
00155 {
00156
00157
00158 if (checkHidePointCode(index)) continue;
00159
00160 if (FT_Load_Char( face, index, FT_LOAD_RENDER )) continue;
00161 if (nullptr == face->glyph->bitmap.buffer) continue;
00162 FT_Int advance = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
00163
00164 if ( ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY ) > max_height )
00165 max_height = ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY );
00166
00167 if ( face->glyph->metrics.horiBearingY > max_bear )
00168 max_bear = face->glyph->metrics.horiBearingY;
00169
00170 len += (advance + mDistance);
00171 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height ++; len = mDistance;}
00172
00173 }
00174 }
00175
00176 max_height >>= 6;
00177 max_bear >>= 6;
00178
00179 size_t finalHeight = (height+1) * (max_height + mDistance) + mDistance;
00180
00181
00182 while (finalHeight > finalWidth)
00183 {
00184 finalHeight /= 2;
00185 finalWidth *= 2;
00186 }
00187
00188
00189 size_t needHeight = 1;
00190 while (needHeight < finalHeight) needHeight <<= 1;
00191 finalHeight = needHeight;
00192
00193 float textureAspect = (float)finalWidth / (float)finalHeight;
00194
00195
00196
00197 bool rgbaMode = ! MyGUI::RenderManager::getInstance().isFormatSupported(PixelFormat::L8A8, TextureUsage::Static | TextureUsage::Write);
00198
00199 const size_t pixel_bytes = rgbaMode ? 4 : 2;
00200 size_t data_width = finalWidth * pixel_bytes;
00201 size_t data_size = finalWidth * finalHeight * pixel_bytes;
00202
00203 MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using texture size " << finalWidth << " x " << finalHeight);
00204 MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using real height " << max_height << " pixels");
00205 mHeightPix = max_height;
00206
00207 uint8* imageData = new uint8[data_size];
00208
00209 uint8* dest = imageData;
00210
00211 for (size_t i = 0; i < data_size; i += pixel_bytes)
00212 {
00213 dest = writeData(dest, 0xFF, 0x00, rgbaMode);
00214 }
00215
00216
00217 len = mDistance;
00218 height = mDistance;
00219 FT_Int advance = 0;
00220
00221
00222
00223
00224 advance = mSpaceWidth;
00225
00226
00227 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00228
00229 for (int j = 0; j < max_height; j++ )
00230 {
00231 int row = j + (int)height;
00232 uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00233 for (int k = 0; k < advance; k++ )
00234 {
00235 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
00236 }
00237 }
00238
00239 addGlyph(&mSpaceGlyphInfo, FontCodeType::Space, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00240 len += (advance + mDistance);
00241
00242
00243
00244
00245 advance = mTabWidth;
00246
00247
00248 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00249
00250 for (int j = 0; j < max_height; j++ )
00251 {
00252 int row = j + (int)height;
00253 uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00254 for (int k = 0; k < advance; k++ )
00255 {
00256 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
00257 }
00258 }
00259
00260 addGlyph(&mTabGlyphInfo, FontCodeType::Tab, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00261 len += (advance + mDistance);
00262
00263
00264
00265
00266 advance = mSelectionWidth;
00267 for (int j = 0; j < max_height; j++ )
00268 {
00269 int row = j + (int)height;
00270 uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00271 for (int k = 0; k < advance; k++ )
00272 {
00273 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT, rgbaMode);
00274 }
00275 }
00276
00277
00278 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00279
00280 addGlyph(&mSelectGlyphInfo, FontCodeType::Selected, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00281 len += (advance + mDistance);
00282
00283
00284
00285
00286 advance = mSelectionWidth;
00287
00288
00289 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00290
00291 for (int j = 0; j < max_height; j++ )
00292 {
00293 int row = j + (int)height;
00294 uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00295 for (int k = 0; k < advance; k++ )
00296 {
00297 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT_DEACTIVE, rgbaMode);
00298 }
00299 }
00300
00301 addGlyph(&mSelectDeactiveGlyphInfo, FontCodeType::SelectedBack, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00302 len += (advance + mDistance);
00303
00304
00305
00306
00307 advance = mCursorWidth;
00308
00309
00310 if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00311
00312 for (int j = 0; j < max_height; j++ )
00313 {
00314 int row = j + (int)height;
00315 uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00316 for (int k = 0; k < advance; k++ )
00317 {
00318 pDest = writeData(pDest, (k&1) ? 0 : 0xFF, FONT_MASK_CHAR, rgbaMode);
00319 }
00320 }
00321
00322 addGlyph(&mCursorGlyphInfo, FontCodeType::Cursor, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00323 len += (advance + mDistance);
00324
00325
00326
00327
00328 FT_Error ftResult;
00329 for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
00330 {
00331 size_t pos = 0;
00332 for (Char index=iter->first; index<=iter->last; ++index, ++pos)
00333 {
00334
00335 if (checkHidePointCode(index)) continue;
00336
00337 GlyphInfo& info = iter->range.at(pos);
00338
00339 ftResult = FT_Load_Char( face, index, FT_LOAD_RENDER );
00340 if (ftResult)
00341 {
00342
00343 MYGUI_LOG(Warning, "cannot load character " << index << " in font " << getResourceName());
00344 continue;
00345 }
00346
00347 FT_Int glyph_advance = (face->glyph->advance.x >> 6 );
00348 unsigned char* buffer = face->glyph->bitmap.buffer;
00349
00350 if (nullptr == buffer)
00351 {
00352
00353 MYGUI_LOG(Warning, "Freetype returned nullptr for character " << index << " in font " << getResourceName());
00354 continue;
00355 }
00356
00357 int y_bearnig = max_bear - ( face->glyph->metrics.horiBearingY >> 6 );
00358
00359
00360 if ( int(finalWidth - 1) < (len + face->glyph->bitmap.width + mDistance) ) { height += max_height + mDistance; len = mDistance; }
00361
00362 for (int j = 0; j < face->glyph->bitmap.rows; j++ )
00363 {
00364 int row = j + (int)height + y_bearnig;
00365 uint8* pDest = &imageData[(row * data_width) + (len + ( face->glyph->metrics.horiBearingX >> 6 )) * pixel_bytes];
00366 for (int k = 0; k < face->glyph->bitmap.width; k++ )
00367 {
00368 if (mAntialiasColour) pDest = writeData(pDest, *buffer, *buffer, rgbaMode);
00369 else pDest = writeData(pDest, FONT_MASK_CHAR, *buffer, rgbaMode);
00370 buffer++;
00371 }
00372 }
00373
00374 addGlyph(&info, index, len, height, len + glyph_advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00375 len += (glyph_advance + mDistance);
00376
00377 }
00378 }
00379
00380
00381
00382 RangeInfo info(FontCodeType::Selected, FontCodeType::Tab);
00383 info.setInfo(FontCodeType::Selected, &mSelectGlyphInfo);
00384 info.setInfo(FontCodeType::SelectedBack, &mSelectDeactiveGlyphInfo);
00385 info.setInfo(FontCodeType::Cursor, &mCursorGlyphInfo);
00386 info.setInfo(FontCodeType::Tab, &mTabGlyphInfo);
00387
00388 mVectorRangeInfo.push_back(info);
00389
00390
00391 mTexture->createManual(finalWidth, finalHeight, TextureUsage::Static | TextureUsage::Write, rgbaMode ? PixelFormat::R8G8B8A8 : PixelFormat::L8A8);
00392
00393 void* buffer_ptr = mTexture->lock(TextureUsage::Write);
00394 memcpy(buffer_ptr, imageData, data_size);
00395 mTexture->unlock();
00396
00397 delete [] imageData;
00398 delete [] data;
00399
00400 FT_Done_FreeType(ftLibrary);
00401
00402 #endif // MYGUI_USE_FREETYPE
00403
00404 }
00405
00406 void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second)
00407 {
00408 mVectorRangeInfo.push_back(RangeInfo(_first, _second));
00409 }
00410
00411 void ResourceTrueTypeFont::addHideCodePointRange(Char _first, Char _second)
00412 {
00413 mVectorHideCodePoint.push_back(PairCodePoint(_first, _second));
00414 }
00415
00416
00417 bool ResourceTrueTypeFont::checkHidePointCode(Char _id)
00418 {
00419 for (VectorPairCodePoint::iterator iter=mVectorHideCodePoint.begin(); iter!=mVectorHideCodePoint.end(); ++iter)
00420 {
00421 if (iter->isExist(_id)) return true;
00422 }
00423 return false;
00424 }
00425
00426 void ResourceTrueTypeFont::clearCodePointRanges()
00427 {
00428 mVectorRangeInfo.clear();
00429 mVectorHideCodePoint.clear();
00430 }
00431
00432 void ResourceTrueTypeFont::deserialization(xml::ElementPtr _node, Version _version)
00433 {
00434 Base::deserialization(_node, _version);
00435
00436 xml::ElementEnumerator node = _node->getElementEnumerator();
00437 while (node.next())
00438 {
00439 if (node->getName() == "Property")
00440 {
00441 const std::string& key = node->findAttribute("key");
00442 const std::string& value = node->findAttribute("value");
00443 if (key == "Source") mSource = value;
00444 else if (key == "Size") mTtfSize = utility::parseFloat(value);
00445 else if (key == "Resolution") mTtfResolution = utility::parseUInt(value);
00446 else if (key == "Antialias") mAntialiasColour = utility::parseBool(value);
00447 else if (key == "SpaceWidth") mSpaceWidth = utility::parseInt(value);
00448 else if (key == "TabWidth") mTabWidth = utility::parseInt(value);
00449
00450 else if (key == "Distance") mDistance = utility::parseInt(value);
00451 else if (key == "OffsetHeight") mOffsetHeight = utility::parseInt(value);
00452 }
00453 else if (node->getName() == "Codes")
00454 {
00455 xml::ElementEnumerator range = node->getElementEnumerator();
00456 while (range.next("Code"))
00457 {
00458 std::string range_value;
00459 std::vector<std::string> parse_range;
00460
00461 if (range->findAttribute("range", range_value))
00462 {
00463 parse_range = utility::split(range_value);
00464 if (!parse_range.empty())
00465 {
00466 int first = utility::parseInt(parse_range[0]);
00467 int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
00468 addCodePointRange(first, last);
00469 }
00470 }
00471
00472 else if (range->findAttribute("hide", range_value))
00473 {
00474 parse_range = utility::split(range_value);
00475 if (!parse_range.empty())
00476 {
00477 int first = utility::parseInt(parse_range[0]);
00478 int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
00479 addHideCodePointRange(first, last);
00480 }
00481 }
00482 }
00483 }
00484 }
00485
00486
00487 initialise();
00488 }
00489
00490 }