MyGUI
3.0.1
|
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