• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

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

Generated on Sun Jan 30 2011 for MyGUI by  doxygen 1.7.1