MyGUI  3.0.1
MyGUI_ResourceTrueTypeFont.cpp
Go to the documentation of this file.
1 
7 /*
8  This file is part of MyGUI.
9 
10  MyGUI is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  MyGUI is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
22 */
23 #include "MyGUI_Precompiled.h"
25 #include "MyGUI_DataManager.h"
26 #include "MyGUI_RenderManager.h"
27 
28 #ifdef MYGUI_USE_FREETYPE
29  #include <ft2build.h>
30  #include FT_FREETYPE_H
31  #include FT_GLYPH_H
32 #endif // MYGUI_USE_FREETYPE
33 
34 namespace MyGUI
35 {
36 
37  const unsigned char FONT_MASK_SELECT = 0x88;
38  const unsigned char FONT_MASK_SELECT_DEACTIVE = 0x60;
39  const unsigned char FONT_MASK_SPACE = 0x00;
40  const unsigned char FONT_MASK_CHAR = 0xFF;
41  const size_t MIN_FONT_TEXTURE_WIDTH = 256;
42 
44  mTtfSize(0),
45  mTtfResolution(0),
46  mAntialiasColour(false),
47  mDistance(0),
48  mSpaceWidth(0),
49  mTabWidth(0),
50  mCursorWidth(2),
51  mSelectionWidth(2),
52  mOffsetHeight(0),
53  mHeightPix(0),
54  mTexture(nullptr)
55  {
56  }
57 
59  {
60  if (mTexture != nullptr)
61  {
63  mTexture = nullptr;
64  }
65  }
66 
68  {
69  for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
70  {
71  GlyphInfo* info = iter->getInfo(_id);
72  if (info == nullptr) continue;
73  return info;
74  }
75  // при ошибках возвращаем пробел
76  return &mSpaceGlyphInfo;
77  }
78 
79  void ResourceTrueTypeFont::addGlyph(GlyphInfo * _info, Char _index, int _left, int _top, int _right, int _bottom, int _finalw, int _finalh, float _aspect, int _addHeight)
80  {
81  _info->codePoint = _index;
82  _info->uvRect.left = (float)_left / (float)_finalw; // u1
83  _info->uvRect.top = (float)(_top + _addHeight) / (float)_finalh; // v1
84  _info->uvRect.right = (float)( _right ) / (float)_finalw; // u2
85  _info->uvRect.bottom = ( _bottom + _addHeight ) / (float)_finalh; // v2
86  _info->width = _right - _left;
87  }
88 
89  uint8* ResourceTrueTypeFont::writeData(uint8* _pDest, unsigned char _luminance, unsigned char _alpha, bool _rgba)
90  {
91  if (_rgba)
92  {
93  *_pDest++ = _luminance; // luminance
94  *_pDest++ = _luminance; // luminance
95  *_pDest++ = _luminance; // luminance
96  *_pDest++ = _alpha; // alpha
97  }
98  else
99  {
100  *_pDest++ = _luminance; // luminance
101  *_pDest++ = _alpha; // alpha
102  }
103  return _pDest;
104  }
105 
106  void ResourceTrueTypeFont::initialise()
107  {
108 
109 #ifndef MYGUI_USE_FREETYPE
110 
111  MYGUI_LOG(Error, "ResourceTrueTypeFont '" << getResourceName() << "' - Ttf font disabled. Define MYGUI_USE_FREETYE if you need ttf fonts.");
112 
113 #else // MYGUI_USE_FREETYPE
114 
115  mTexture = RenderManager::getInstance().createTexture(MyGUI::utility::toString((size_t)this, "_TrueTypeFont"));
116 
117  // ManualResourceLoader implementation - load the texture
118  FT_Library ftLibrary;
119  // Init freetype
120  if ( FT_Init_FreeType( &ftLibrary ) ) MYGUI_EXCEPT("Could not init FreeType library!");
121 
122  // Load font
123  FT_Face face;
124 
125  //FIXME научить работать без шрифтов
126  IDataStream* datastream = DataManager::getInstance().getData(mSource);
127  MYGUI_ASSERT(datastream, "Could not open font face!");
128 
129  size_t datasize = datastream->size();
130  uint8* data = new uint8[datasize];
131  datastream->read(data, datasize);
132  delete datastream;
133 
134  if ( FT_New_Memory_Face( ftLibrary, data, (FT_Long)datasize, 0, &face ) )
135  MYGUI_EXCEPT("Could not open font face!");
136 
137  // Convert our point size to freetype 26.6 fixed point format
138  FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
139  if ( FT_Set_Char_Size( face, ftSize, 0, mTtfResolution, mTtfResolution ) )
140  MYGUI_EXCEPT("Could not set char size!");
141 
142  int max_height = 0, max_bear = 0;
143 
144  int spec_len = mCursorWidth + mSelectionWidth + mSelectionWidth + mSpaceWidth + mTabWidth + (mDistance * 5);
145  int len = mDistance + spec_len;
146  int height = 0; // здесь используется как колличество строк
147 
148  size_t finalWidth = MIN_FONT_TEXTURE_WIDTH;
149  // trying to guess necessary width for texture
150  while (mTtfSize*mTtfResolution > finalWidth*6) finalWidth *= 2;
151 
152  for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
153  {
154  for (Char index=iter->first; index<=iter->last; ++index)
155  {
156 
157  // символ рисовать ненужно
158  if (checkHidePointCode(index)) continue;
159 
160  if (FT_Load_Char( face, index, FT_LOAD_RENDER )) continue;
161  if (nullptr == face->glyph->bitmap.buffer) continue;
162  FT_Int advance = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
163 
164  if ( ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY ) > max_height )
165  max_height = ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY );
166 
167  if ( face->glyph->metrics.horiBearingY > max_bear )
168  max_bear = face->glyph->metrics.horiBearingY;
169 
170  len += (advance + mDistance);
171  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height ++; len = mDistance;}
172 
173  }
174  }
175 
176  max_height >>= 6;
177  max_bear >>= 6;
178 
179  size_t finalHeight = (height+1) * (max_height + mDistance) + mDistance;
180 
181  //make it more squared
182  while (finalHeight > finalWidth)
183  {
184  finalHeight /= 2;
185  finalWidth *= 2;
186  }
187 
188  // вычисляем ближайшую кратную 2
189  size_t needHeight = 1;
190  while (needHeight < finalHeight) needHeight <<= 1;
191  finalHeight = needHeight;
192 
193  float textureAspect = (float)finalWidth / (float)finalHeight;
194 
195  // if L8A8 (2 bytes per pixel) not supported use 4 bytes per pixel R8G8B8A8
196  // where R == G == B == L
198 
199  const size_t pixel_bytes = rgbaMode ? 4 : 2;
200  size_t data_width = finalWidth * pixel_bytes;
201  size_t data_size = finalWidth * finalHeight * pixel_bytes;
202 
203  MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using texture size " << finalWidth << " x " << finalHeight);
204  MYGUI_LOG(Info, "ResourceTrueTypeFont '" << getResourceName() << "' using real height " << max_height << " pixels");
205  mHeightPix = max_height;
206 
207  uint8* imageData = new uint8[data_size];
208 
209  uint8* dest = imageData;
210  // Reset content (White, transparent)
211  for (size_t i = 0; i < data_size; i += pixel_bytes)
212  {
213  dest = writeData(dest, 0xFF, 0x00, rgbaMode);
214  }
215 
216  // текущее положение в текстуре
217  len = mDistance;
218  height = mDistance; // здесь используется как текущее положение высоты
219  FT_Int advance = 0;
220 
221  //------------------------------------------------------------------
222  // создаем символ пробела
223  //------------------------------------------------------------------
224  advance = mSpaceWidth;
225 
226  // перевод на новую строку
227  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
228 
229  for (int j = 0; j < max_height; j++ )
230  {
231  int row = j + (int)height;
232  uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
233  for (int k = 0; k < advance; k++ )
234  {
235  pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
236  }
237  }
238 
239  addGlyph(&mSpaceGlyphInfo, FontCodeType::Space, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
240  len += (advance + mDistance);
241 
242  //------------------------------------------------------------------
243  // создаем табуляцию
244  //------------------------------------------------------------------
245  advance = mTabWidth;
246 
247  // перевод на новую строку
248  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
249 
250  for (int j = 0; j < max_height; j++ )
251  {
252  int row = j + (int)height;
253  uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
254  for (int k = 0; k < advance; k++ )
255  {
256  pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SPACE, rgbaMode);
257  }
258  }
259 
260  addGlyph(&mTabGlyphInfo, FontCodeType::Tab, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
261  len += (advance + mDistance);
262 
263  //------------------------------------------------------------------
264  // создаем выделение
265  //------------------------------------------------------------------
266  advance = mSelectionWidth;
267  for (int j = 0; j < max_height; j++ )
268  {
269  int row = j + (int)height;
270  uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
271  for (int k = 0; k < advance; k++ )
272  {
273  pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT, rgbaMode);
274  }
275  }
276 
277  // перевод на новую строку
278  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
279 
280  addGlyph(&mSelectGlyphInfo, FontCodeType::Selected, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
281  len += (advance + mDistance);
282 
283  //------------------------------------------------------------------
284  // создаем неактивное выделение
285  //------------------------------------------------------------------
286  advance = mSelectionWidth;
287 
288  // перевод на новую строку
289  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
290 
291  for (int j = 0; j < max_height; j++ )
292  {
293  int row = j + (int)height;
294  uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
295  for (int k = 0; k < advance; k++ )
296  {
297  pDest = writeData(pDest, FONT_MASK_CHAR, FONT_MASK_SELECT_DEACTIVE, rgbaMode);
298  }
299  }
300 
301  addGlyph(&mSelectDeactiveGlyphInfo, FontCodeType::SelectedBack, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
302  len += (advance + mDistance);
303 
304  //------------------------------------------------------------------
305  // создаем курсор
306  //------------------------------------------------------------------
307  advance = mCursorWidth;
308 
309  // перевод на новую строку
310  if ( int(finalWidth - 1) < (len + advance + mDistance) ) { height += max_height + mDistance; len = mDistance; }
311 
312  for (int j = 0; j < max_height; j++ )
313  {
314  int row = j + (int)height;
315  uint8* pDest = &imageData[(row * data_width) + len * pixel_bytes];
316  for (int k = 0; k < advance; k++ )
317  {
318  pDest = writeData(pDest, (k&1) ? 0 : 0xFF, FONT_MASK_CHAR, rgbaMode);
319  }
320  }
321 
322  addGlyph(&mCursorGlyphInfo, FontCodeType::Cursor, len, height, len + advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
323  len += (advance + mDistance);
324 
325  //------------------------------------------------------------------
326  // создаем все остальные символы
327  //------------------------------------------------------------------
328  FT_Error ftResult;
329  for (VectorRangeInfo::iterator iter=mVectorRangeInfo.begin(); iter!=mVectorRangeInfo.end(); ++iter)
330  {
331  size_t pos = 0;
332  for (Char index=iter->first; index<=iter->last; ++index, ++pos)
333  {
334  // сомвол рисовать не нада
335  if (checkHidePointCode(index)) continue;
336 
337  GlyphInfo& info = iter->range.at(pos);
338 
339  ftResult = FT_Load_Char( face, index, FT_LOAD_RENDER );
340  if (ftResult)
341  {
342  // problem loading this glyph, continue
343  MYGUI_LOG(Warning, "cannot load character " << index << " in font " << getResourceName());
344  continue;
345  }
346 
347  FT_Int glyph_advance = (face->glyph->advance.x >> 6 );
348  unsigned char* buffer = face->glyph->bitmap.buffer;
349 
350  if (nullptr == buffer)
351  {
352  // Yuck, FT didn't detect this but generated a nullptr pointer!
353  MYGUI_LOG(Warning, "Freetype returned nullptr for character " << index << " in font " << getResourceName());
354  continue;
355  }
356 
357  int y_bearnig = max_bear - ( face->glyph->metrics.horiBearingY >> 6 );
358 
359  // перевод на новую строку
360  if ( int(finalWidth - 1) < (len + face->glyph->bitmap.width + mDistance) ) { height += max_height + mDistance; len = mDistance; }
361 
362  for (int j = 0; j < face->glyph->bitmap.rows; j++ )
363  {
364  int row = j + (int)height + y_bearnig;
365  uint8* pDest = &imageData[(row * data_width) + (len + ( face->glyph->metrics.horiBearingX >> 6 )) * pixel_bytes];
366  for (int k = 0; k < face->glyph->bitmap.width; k++ )
367  {
368  if (mAntialiasColour) pDest = writeData(pDest, *buffer, *buffer, rgbaMode);
369  else pDest = writeData(pDest, FONT_MASK_CHAR, *buffer, rgbaMode);
370  buffer++;
371  }
372  }
373 
374  addGlyph(&info, index, len, height, len + glyph_advance, height + max_height, finalWidth, finalHeight, textureAspect, mOffsetHeight);
375  len += (glyph_advance + mDistance);
376 
377  }
378  }
379 
380  // Добавляем спец символы в основной список
381  // пробел можно не добавлять, его вернет по ошибке
382  RangeInfo info(FontCodeType::Selected, FontCodeType::Tab);
383  info.setInfo(FontCodeType::Selected, &mSelectGlyphInfo);
384  info.setInfo(FontCodeType::SelectedBack, &mSelectDeactiveGlyphInfo);
385  info.setInfo(FontCodeType::Cursor, &mCursorGlyphInfo);
386  info.setInfo(FontCodeType::Tab, &mTabGlyphInfo);
387 
388  mVectorRangeInfo.push_back(info);
389 
390 
391  mTexture->createManual(finalWidth, finalHeight, TextureUsage::Static | TextureUsage::Write, rgbaMode ? PixelFormat::R8G8B8A8 : PixelFormat::L8A8);
392 
393  void* buffer_ptr = mTexture->lock(TextureUsage::Write);
394  memcpy(buffer_ptr, imageData, data_size);
395  mTexture->unlock();
396 
397  delete [] imageData;
398  delete [] data;
399 
400  FT_Done_FreeType(ftLibrary);
401 
402 #endif // MYGUI_USE_FREETYPE
403 
404  }
405 
406  void ResourceTrueTypeFont::addCodePointRange(Char _first, Char _second)
407  {
408  mVectorRangeInfo.push_back(RangeInfo(_first, _second));
409  }
410 
411  void ResourceTrueTypeFont::addHideCodePointRange(Char _first, Char _second)
412  {
413  mVectorHideCodePoint.push_back(PairCodePoint(_first, _second));
414  }
415 
416  // проверяет, входит ли символ в зоны ненужных символов
417  bool ResourceTrueTypeFont::checkHidePointCode(Char _id)
418  {
419  for (VectorPairCodePoint::iterator iter=mVectorHideCodePoint.begin(); iter!=mVectorHideCodePoint.end(); ++iter)
420  {
421  if (iter->isExist(_id)) return true;
422  }
423  return false;
424  }
425 
426  void ResourceTrueTypeFont::clearCodePointRanges()
427  {
428  mVectorRangeInfo.clear();
429  mVectorHideCodePoint.clear();
430  }
431 
433  {
434  Base::deserialization(_node, _version);
435 
437  while (node.next())
438  {
439  if (node->getName() == "Property")
440  {
441  const std::string& key = node->findAttribute("key");
442  const std::string& value = node->findAttribute("value");
443  if (key == "Source") mSource = value;
444  else if (key == "Size") mTtfSize = utility::parseFloat(value);
445  else if (key == "Resolution") mTtfResolution = utility::parseUInt(value);
446  else if (key == "Antialias") mAntialiasColour = utility::parseBool(value);
447  else if (key == "SpaceWidth") mSpaceWidth = utility::parseInt(value);
448  else if (key == "TabWidth") mTabWidth = utility::parseInt(value);
449  //else if (key == "CursorWidth") mCursorWidth = utility::parseInt(value);
450  else if (key == "Distance") mDistance = utility::parseInt(value);
451  else if (key == "OffsetHeight") mOffsetHeight = utility::parseInt(value);
452  }
453  else if (node->getName() == "Codes")
454  {
456  while (range.next("Code"))
457  {
458  std::string range_value;
459  std::vector<std::string> parse_range;
460  // диапазон включений
461  if (range->findAttribute("range", range_value))
462  {
463  parse_range = utility::split(range_value);
464  if (!parse_range.empty())
465  {
466  int first = utility::parseInt(parse_range[0]);
467  int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
468  addCodePointRange(first, last);
469  }
470  }
471  // диапазон исключений
472  else if (range->findAttribute("hide", range_value))
473  {
474  parse_range = utility::split(range_value);
475  if (!parse_range.empty())
476  {
477  int first = utility::parseInt(parse_range[0]);
478  int last = parse_range.size() > 1 ? utility::parseInt(parse_range[1]) : first;
479  addHideCodePointRange(first, last);
480  }
481  }
482  }
483  }
484  }
485 
486  // инициализируем
487  initialise();
488  }
489 
490 } // namespace MyGUI