MyGUI 3.0.1
MyGUI_ResourceTrueTypeFont.cpp
Go to the documentation of this file.
00001 
00007 /*
00008     This file is part of MyGUI.
00009 
00010     MyGUI is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU Lesser General Public License as published by
00012     the Free Software Foundation, either version 3 of the License, or
00013     (at your option) any later version.
00014 
00015     MyGUI is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU Lesser General Public License for more details.
00019 
00020     You should have received a copy of the GNU Lesser General Public License
00021     along with MyGUI.  If not, see <http://www.gnu.org/licenses/>.
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;  // u1
00083         _info->uvRect.top = (float)(_top + _addHeight) / (float)_finalh;  // v1
00084         _info->uvRect.right = (float)( _right ) / (float)_finalw; // u2
00085         _info->uvRect.bottom = ( _bottom + _addHeight ) / (float)_finalh; // v2
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; // luminance
00094             *_pDest++ = _luminance; // luminance
00095             *_pDest++ = _luminance; // luminance
00096             *_pDest++ = _alpha; // alpha
00097         }
00098         else
00099         {
00100             *_pDest++ = _luminance; // luminance
00101             *_pDest++ = _alpha; // 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         // ManualResourceLoader implementation - load the texture
00118         FT_Library ftLibrary;
00119         // Init freetype
00120         if ( FT_Init_FreeType( &ftLibrary ) ) MYGUI_EXCEPT("Could not init FreeType library!");
00121 
00122         // Load font
00123         FT_Face face;
00124 
00125         //FIXME научить работать без шрифтов
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         // Convert our point size to freetype 26.6 fixed point format
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         // trying to guess necessary width for texture
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         //make it more squared
00182         while (finalHeight > finalWidth)
00183         {
00184             finalHeight /= 2;
00185             finalWidth *= 2;
00186         }
00187 
00188         // вычисляем ближайшую кратную 2
00189         size_t needHeight = 1;
00190         while (needHeight < finalHeight) needHeight <<= 1;
00191         finalHeight = needHeight;
00192 
00193         float textureAspect = (float)finalWidth / (float)finalHeight;
00194 
00195         // if L8A8 (2 bytes per pixel) not supported use 4 bytes per pixel R8G8B8A8
00196         // where R == G == B == L
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         // Reset content (White, transparent)
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                     // problem loading this glyph, continue
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                     // Yuck, FT didn't detect this but generated a nullptr pointer!
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                 //else if (key == "CursorWidth") mCursorWidth = utility::parseInt(value);
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 } // namespace MyGUI