MyGUI  3.2.0
MyGUI_ResourceTrueTypeFont.cpp
Go to the documentation of this file.
00001 
00006 /*
00007     This file is part of MyGUI.
00008 
00009     MyGUI is free software: you can redistribute it and/or modify
00010     it under the terms of the GNU Lesser General Public License as published by
00011     the Free Software Foundation, either version 3 of the License, or
00012     (at your option) any later version.
00013 
00014     MyGUI is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU Lesser General Public License for more details.
00018 
00019     You should have received a copy of the GNU Lesser General Public License
00020     along with MyGUI.  If not, see <http://www.gnu.org/licenses/>.
00021 */
00022 #include "MyGUI_Precompiled.h"
00023 #include "MyGUI_ResourceTrueTypeFont.h"
00024 #include "MyGUI_DataManager.h"
00025 #include "MyGUI_RenderManager.h"
00026 
00027 #ifdef MYGUI_USE_FREETYPE
00028     #include <ft2build.h>
00029     #include FT_FREETYPE_H
00030     #include FT_GLYPH_H
00031 #endif // MYGUI_USE_FREETYPE
00032 
00033 namespace MyGUI
00034 {
00035 
00036     const unsigned char FONT_MASK_SELECT = 0x88;
00037     const unsigned char FONT_MASK_SELECT_DEACTIVE = 0x60;
00038     const unsigned char FONT_MASK_SPACE = 0x00;
00039     const unsigned char FONT_MASK_CHAR = 0xFF;
00040     const int MIN_FONT_TEXTURE_WIDTH = 256;
00041 
00042     ResourceTrueTypeFont::ResourceTrueTypeFont() :
00043         mTtfSize(0),
00044         mTtfResolution(0),
00045         mAntialiasColour(false),
00046         mDistance(0),
00047         mSpaceWidth(0),
00048         mTabWidth(0),
00049         mCursorWidth(2),
00050         mSelectionWidth(2),
00051         mOffsetHeight(0),
00052         mHeightPix(0),
00053         mTexture(nullptr)
00054     {
00055     }
00056 
00057     ResourceTrueTypeFont::~ResourceTrueTypeFont()
00058     {
00059         if (mTexture != nullptr)
00060         {
00061             RenderManager::getInstance().destroyTexture(mTexture);
00062             mTexture = nullptr;
00063         }
00064     }
00065 
00066     GlyphInfo* ResourceTrueTypeFont::getGlyphInfo(Char _id)
00067     {
00068         for (VectorRangeInfo::iterator iter = mVectorRangeInfo.begin(); iter != mVectorRangeInfo.end(); ++iter)
00069         {
00070             GlyphInfo* info = iter->getInfo(_id);
00071             if (info == nullptr) continue;
00072             return info;
00073         }
00074         // при ошибках возвращаем пробел
00075         return &mSpaceGlyphInfo;
00076     }
00077 
00078     void ResourceTrueTypeFont::addGlyph(GlyphInfo* _info, Char _index, int _left, int _top, int _right, int _bottom, int _finalw, int _finalh, float _aspect, int _addHeight) const
00079     {
00080         _info->codePoint = _index;
00081         _info->uvRect.left = (float)_left / (float)_finalw;  // u1
00082         _info->uvRect.top = (float)(_top + _addHeight) / (float)_finalh;  // v1
00083         _info->uvRect.right = (float)( _right ) / (float)_finalw; // u2
00084         _info->uvRect.bottom = ( _bottom + _addHeight ) / (float)_finalh; // v2
00085         _info->width = _right - _left;
00086     }
00087 
00088     uint8* ResourceTrueTypeFont::writeData(uint8* _pDest, unsigned char _luminance, unsigned char _alpha, bool _rgba)
00089     {
00090         if (_rgba)
00091         {
00092             *_pDest++ = _luminance; // luminance
00093             *_pDest++ = _luminance; // luminance
00094             *_pDest++ = _luminance; // luminance
00095             *_pDest++ = _alpha; // alpha
00096         }
00097         else
00098         {
00099             *_pDest++ = _luminance; // luminance
00100             *_pDest++ = _alpha; // alpha
00101         }
00102         return _pDest;
00103     }
00104 
00105     void ResourceTrueTypeFont::initialise()
00106     {
00107 
00108 #ifndef MYGUI_USE_FREETYPE
00109 
00110         MYGUI_LOG(Error, "ResourceTrueTypeFont '" << getResourceName() << "' - Ttf font disabled. Define MYGUI_USE_FREETYE if you need ttf fonts.");
00111 
00112 #else // MYGUI_USE_FREETYPE
00113 
00114         IDataStream* datastream = DataManager::getInstance().getData(mSource);
00115         if (!datastream)
00116         {
00117             MYGUI_LOG(Error, "Could not open font face!");
00118             return;
00119         }
00120 
00121         mTexture = RenderManager::getInstance().createTexture(MyGUI::utility::toString((size_t)this, "_TrueTypeFont"));
00122 
00123         // ManualResourceLoader implementation - load the texture
00124         FT_Library ftLibrary;
00125         // Init freetype
00126         if ( FT_Init_FreeType( &ftLibrary ) ) MYGUI_EXCEPT("Could not init FreeType library!");
00127 
00128         // Load font
00129         FT_Face face;
00130 
00131         size_t datasize = datastream->size();
00132         uint8* data = new uint8[datasize];
00133         datastream->read(data, datasize);
00134         delete datastream;
00135 
00136         if ( FT_New_Memory_Face( ftLibrary, data, (FT_Long)datasize, 0, &face ) )
00137             MYGUI_EXCEPT("Could not open font face!");
00138 
00139         // Convert our point size to freetype 26.6 fixed point format
00140         FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
00141         if ( FT_Set_Char_Size( face, ftSize, 0, mTtfResolution, mTtfResolution ) )
00142             MYGUI_EXCEPT("Could not set char size!");
00143 
00144         int max_height = 0, max_bear = 0;
00145 
00146         int spec_len = mCursorWidth + mSelectionWidth + mSelectionWidth + mSpaceWidth + mTabWidth + (mDistance * 5);
00147         int len = mDistance + spec_len;
00148         int height = 0; // здесь используется как колличество строк
00149 
00150         int finalWidth = MIN_FONT_TEXTURE_WIDTH;
00151         // trying to guess necessary width for texture
00152         while (mTtfSize * mTtfResolution > finalWidth * 6) finalWidth *= 2;
00153 
00154         for (VectorRangeInfo::iterator iter = mVectorRangeInfo.begin(); iter != mVectorRangeInfo.end(); ++iter)
00155         {
00156             for (Char index = iter->first; index <= iter->last; ++index)
00157             {
00158 
00159                 // символ рисовать ненужно
00160                 if (checkHidePointCode(index)) continue;
00161 
00162                 if (FT_Load_Char( face, index, FT_LOAD_RENDER )) continue;
00163                 if (nullptr == face->glyph->bitmap.buffer) continue;
00164                 FT_Int advance = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
00165 
00166                 if ( ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY ) > max_height )
00167                     max_height = ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY );
00168 
00169                 if ( face->glyph->metrics.horiBearingY > max_bear )
00170                     max_bear = face->glyph->metrics.horiBearingY;
00171 
00172                 len += (advance + mDistance);
00173                 if ( finalWidth - 1 < (len + advance + mDistance) )
00174                 {
00175                     height ++;
00176                     len = mDistance;
00177                 }
00178 
00179             }
00180         }
00181 
00182         max_height >>= 6;
00183         max_bear >>= 6;
00184 
00185         int finalHeight = (height + 1) * (max_height + mDistance) + mDistance;
00186 
00187         //make it more squared
00188         while (finalHeight > finalWidth)
00189         {
00190             finalHeight /= 2;
00191             finalWidth *= 2;
00192         }
00193 
00194         // вычисляем ближайшую кратную 2
00195         int needHeight = 1;
00196         while (needHeight < finalHeight) needHeight <<= 1;
00197         finalHeight = needHeight;
00198 
00199         float textureAspect = (float)finalWidth / (float)finalHeight;
00200 
00201         // if L8A8 (2 bytes per pixel) not supported use 4 bytes per pixel R8G8B8A8
00202         // where R == G == B == L
00203         bool rgbaMode = ! MyGUI::RenderManager::getInstance().isFormatSupported(PixelFormat::L8A8, TextureUsage::Static | TextureUsage::Write);
00204 
00205         const size_t pixel_bytes = rgbaMode ? 4 : 2;
00206         size_t data_width = finalWidth * pixel_bytes;
00207         size_t data_size = finalWidth * finalHeight * pixel_bytes;
00208 
00209         MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using texture size " << finalWidth << " x " << finalHeight);
00210         MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using real height " << max_height << " pixels");
00211         mHeightPix = max_height;
00212 
00213         uint8* imageData = new uint8[data_size];
00214 
00215         uint8* dest = imageData;
00216         // Reset content (White, transparent)
00217         for (size_t i = 0; i < data_size; i += pixel_bytes)
00218         {
00219             dest = writeData(dest, 0xFF, 0x00, rgbaMode);
00220         }
00221 
00222         // текущее положение в текстуре
00223         len = mDistance;
00224         height = mDistance; // здесь используется как текущее положение высоты
00225         FT_Int advance = 0;
00226 
00227         //------------------------------------------------------------------
00228         // создаем символ пробела
00229         //------------------------------------------------------------------
00230         advance = mSpaceWidth;
00231 
00232         // перевод на новую строку
00233         if ( finalWidth - 1 < (len + advance + mDistance) )
00234         {
00235             height += max_height + mDistance;
00236             len = mDistance;
00237         }
00238 
00239         for (int j = 0; j < max_height; j++ )
00240         {
00241             int row = j + (int)height;
00242             uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00243             for (int k = 0; k < advance; k++ )
00244             {
00245                 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
00246             }
00247         }
00248 
00249         addGlyph(&mSpaceGlyphInfo, FontCodeType::Space, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00250         len += (advance + mDistance);
00251 
00252         //------------------------------------------------------------------
00253         // создаем табуляцию
00254         //------------------------------------------------------------------
00255         advance = mTabWidth;
00256 
00257         // перевод на новую строку
00258         if ( finalWidth - 1 < (len + advance + mDistance) )
00259         {
00260             height += max_height + mDistance;
00261             len = mDistance;
00262         }
00263 
00264         for (int j = 0; j < max_height; j++ )
00265         {
00266             int row = j + (int)height;
00267             uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00268             for (int k = 0; k < advance; k++ )
00269             {
00270                 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
00271             }
00272         }
00273 
00274         addGlyph(&mTabGlyphInfo, FontCodeType::Tab, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00275         len += (advance + mDistance);
00276 
00277         //------------------------------------------------------------------
00278         // создаем выделение
00279         //------------------------------------------------------------------
00280         advance = mSelectionWidth;
00281         for (int j = 0; j < max_height; j++ )
00282         {
00283             int row = j + (int)height;
00284             uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00285             for (int k = 0; k < advance; k++ )
00286             {
00287                 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT, rgbaMode);
00288             }
00289         }
00290 
00291         // перевод на новую строку
00292         if ( finalWidth - 1 < (len + advance + mDistance) )
00293         {
00294             height += max_height + mDistance;
00295             len = mDistance;
00296         }
00297 
00298         addGlyph(&mSelectGlyphInfo, FontCodeType::Selected, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00299         len += (advance + mDistance);
00300 
00301         //------------------------------------------------------------------
00302         // создаем неактивное выделение
00303         //------------------------------------------------------------------
00304         advance = mSelectionWidth;
00305 
00306         // перевод на новую строку
00307         if ( finalWidth - 1 < (len + advance + mDistance) )
00308         {
00309             height += max_height + mDistance;
00310             len = mDistance;
00311         }
00312 
00313         for (int j = 0; j < max_height; j++ )
00314         {
00315             int row = j + (int)height;
00316             uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00317             for (int k = 0; k < advance; k++ )
00318             {
00319                 pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT_DEACTIVE, rgbaMode);
00320             }
00321         }
00322 
00323         addGlyph(&mSelectDeactiveGlyphInfo, FontCodeType::SelectedBack, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00324         len += (advance + mDistance);
00325 
00326         //------------------------------------------------------------------
00327         // создаем курсор
00328         //------------------------------------------------------------------
00329         advance = mCursorWidth;
00330 
00331         // перевод на новую строку
00332         if ( finalWidth - 1 < (len + advance + mDistance) )
00333         {
00334             height += max_height + mDistance;
00335             len = mDistance;
00336         }
00337 
00338         for (int j = 0; j < max_height; j++ )
00339         {
00340             int row = j + (int)height;
00341             uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
00342             for (int k = 0; k < advance; k++ )
00343             {
00344                 pDest = writeData(pDest, (k & 1) ? 0 : 0xFF, FONT_MASK_CHAR, rgbaMode);
00345             }
00346         }
00347 
00348         addGlyph(&mCursorGlyphInfo, FontCodeType::Cursor, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00349         len += (advance + mDistance);
00350 
00351         //------------------------------------------------------------------
00352         // создаем все остальные символы
00353         //------------------------------------------------------------------
00354         FT_Error ftResult;
00355         for (VectorRangeInfo::iterator iter = mVectorRangeInfo.begin(); iter != mVectorRangeInfo.end(); ++iter)
00356         {
00357             size_t pos = 0;
00358             for (Char index = iter->first; index <= iter->last; ++index, ++pos)
00359             {
00360                 // сомвол рисовать не нада
00361                 if (checkHidePointCode(index)) continue;
00362 
00363                 GlyphInfo& info = iter->range.at(pos);
00364 
00365                 ftResult = FT_Load_Char( face, index, FT_LOAD_RENDER );
00366                 if (ftResult)
00367                 {
00368                     // problem loading this glyph, continue
00369                     MYGUI_LOG(Warning, "cannot load character " << index << " in font " << getResourceName());
00370                     continue;
00371                 }
00372 
00373                 FT_Int glyph_advance = (face->glyph->advance.x >> 6 );
00374                 unsigned char* buffer = face->glyph->bitmap.buffer;
00375 
00376                 if (nullptr == buffer)
00377                 {
00378                     // Yuck, FT didn't detect this but generated a nullptr pointer!
00379                     MYGUI_LOG(Warning, "Freetype returned nullptr for character " << index << " in font " << getResourceName());
00380                     continue;
00381                 }
00382 
00383                 int y_bearnig = max_bear - ( face->glyph->metrics.horiBearingY >> 6 );
00384 
00385                 // перевод на новую строку
00386                 if ( finalWidth - 1 < (len + face->glyph->bitmap.width + mDistance) )
00387                 {
00388                     height += max_height + mDistance;
00389                     len = mDistance;
00390                 }
00391 
00392                 for (int j = 0; j < face->glyph->bitmap.rows; j++ )
00393                 {
00394                     int row = j + (int)height + y_bearnig;
00395                     uint8* pDest = &imageData[(row * data_width) + (len + ( face->glyph->metrics.horiBearingX >> 6 )) * pixel_bytes];
00396                     for (int k = 0; k < face->glyph->bitmap.width; k++ )
00397                     {
00398                         if (mAntialiasColour) pDest = writeData(pDest, *buffer, *buffer, rgbaMode);
00399                         else pDest = writeData(pDest, FONT_MASK_CHAR, *buffer, rgbaMode);
00400                         buffer++;
00401                     }
00402                 }
00403 
00404                 addGlyph(&info, index, len, height, len + glyph_advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
00405                 len += (glyph_advance + mDistance);
00406 
00407             }
00408         }
00409 
00410         // Добавляем спец символы в основной список
00411         // пробел можно не добавлять, его вернет по ошибке
00412         RangeInfo info(FontCodeType::Selected, FontCodeType::Tab);
00413         info.setInfo(FontCodeType::Selected, &mSelectGlyphInfo);
00414         info.setInfo(FontCodeType::SelectedBack, &mSelectDeactiveGlyphInfo);
00415         info.setInfo(FontCodeType::Cursor, &mCursorGlyphInfo);
00416         info.setInfo(FontCodeType::Tab, &mTabGlyphInfo);
00417 
00418         mVectorRangeInfo.push_back(info);
00419 
00420 
00421         mTexture->createManual(finalWidth, finalHeight, TextureUsage::Static | TextureUsage::Write, rgbaMode ? PixelFormat::R8G8B8A8 : PixelFormat::L8A8);
00422 
00423         void* buffer_ptr = mTexture->lock(TextureUsage::Write);
00424         if (buffer_ptr != nullptr)
00425         {
00426             memcpy(buffer_ptr, imageData, data_size);
00427         }
00428         else
00429         {
00430             MYGUI_LOG(Error, "ResourceTrueTypeFont, error lock texture, pointer is nullptr");
00431         }
00432         mTexture->unlock();
00433 
00434         delete [] imageData;
00435         delete [] data;
00436 
00437         FT_Done_FreeType(ftLibrary);
00438 
00439 #endif // MYGUI_USE_FREETYPE
00440 
00441     }
00442 
00443     void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second)
00444     {
00445         mVectorRangeInfo.push_back(RangeInfo(_first, _second));
00446     }
00447 
00448     void ResourceTrueTypeFont::addHideCodePointRange(Char _first, Char _second)
00449     {
00450         mVectorHideCodePoint.push_back(PairCodePoint(_first, _second));
00451     }
00452 
00453     // проверяет, входит ли символ в зоны ненужных символов
00454     bool ResourceTrueTypeFont::checkHidePointCode(Char _id)
00455     {
00456         for (VectorPairCodePoint::iterator iter = mVectorHideCodePoint.begin(); iter != mVectorHideCodePoint.end(); ++iter)
00457         {
00458             if (iter->isExist(_id)) return true;
00459         }
00460         return false;
00461     }
00462 
00463     void ResourceTrueTypeFont::clearCodePointRanges()
00464     {
00465         mVectorRangeInfo.clear();
00466         mVectorHideCodePoint.clear();
00467     }
00468 
00469     void ResourceTrueTypeFont::deserialization(xml::ElementPtr _node, Version _version)
00470     {
00471         Base::deserialization(_node, _version);
00472 
00473         xml::ElementEnumerator node = _node->getElementEnumerator();
00474         while (node.next())
00475         {
00476             if (node->getName() == "Property")
00477             {
00478                 const std::string& key = node->findAttribute("key");
00479                 const std::string& value = node->findAttribute("value");
00480                 if (key == "Source") mSource = value;
00481                 else if (key == "Size") mTtfSize = utility::parseFloat(value);
00482                 else if (key == "Resolution") mTtfResolution = utility::parseUInt(value);
00483                 else if (key == "Antialias") mAntialiasColour = utility::parseBool(value);
00484                 else if (key == "SpaceWidth") mSpaceWidth = utility::parseInt(value);
00485                 else if (key == "TabWidth") mTabWidth = utility::parseInt(value);
00486                 //else if (key == "CursorWidth") mCursorWidth = utility::parseInt(value);
00487                 else if (key == "Distance") mDistance = utility::parseInt(value);
00488                 else if (key == "OffsetHeight") mOffsetHeight = utility::parseInt(value);
00489             }
00490             else if (node->getName() == "Codes")
00491             {
00492                 xml::ElementEnumerator range = node->getElementEnumerator();
00493                 while (range.next("Code"))
00494                 {
00495                     std::string range_value;
00496                     std::vector<std::string> parse_range;
00497                     // диапазон включений
00498                     if (range->findAttribute("range", range_value))
00499                     {
00500                         parse_range = utility::split(range_value);
00501                         if (!parse_range.empty())
00502                         {
00503                             int first = utility::parseInt(parse_range[0]);
00504                             int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
00505                             addCodePointRange(first, last);
00506                         }
00507                     }
00508                     // диапазон исключений
00509                     else if (range->findAttribute("hide", range_value))
00510                     {
00511                         parse_range = utility::split(range_value);
00512                         if (!parse_range.empty())
00513                         {
00514                             int first = utility::parseInt(parse_range[0]);
00515                             int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
00516                             addHideCodePointRange(first, last);
00517                         }
00518                     }
00519                 }
00520             }
00521         }
00522 
00523         // инициализируем
00524         initialise();
00525     }
00526 
00527     ITexture* ResourceTrueTypeFont::getTextureFont()
00528     {
00529         return mTexture;
00530     }
00531 
00532     // получившаяся высота при генерации в пикселях
00533     int ResourceTrueTypeFont::getDefaultHeight()
00534     {
00535         return mHeightPix;
00536     }
00537 
00538 } // namespace MyGUI