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