TDME2  1.9.200
GUIFont.cpp
Go to the documentation of this file.
2 
3 #include <ft2build.h>
4 #include FT_FREETYPE_H
5 
6 #include <string>
7 #include <unordered_map>
8 #include <vector>
9 
10 #include <tdme/tdme.h>
11 #include <tdme/engine/Texture.h>
14 #include <tdme/engine/Engine.h>
17 #include <tdme/math/Math.h>
23 #include <tdme/utilities/Float.h>
24 #include <tdme/utilities/Integer.h>
29 
30 using std::string;
31 using std::unordered_map;
32 using std::vector;
33 
41 using tdme::math::Math;
53 
54 bool GUIFont::ftInitialized = false;
55 FT_Library GUIFont::ftLibrary;
56 
57 GUIFont::GUIFont(const string& pathName, const string& fileName, int size): textureAtlas("font:" + pathName + "/" + fileName + "@" + to_string(size))
58 {
59  // load ttf data
60  FileSystem::getInstance()->getContent(pathName, fileName, ttfData);
61 
62  // open face
63  ftPathName = pathName + "/" + fileName;
64  ftOpenArgs = {
65  .flags = FT_OPEN_MEMORY,
66  .memory_base = ttfData.data(),
67  .memory_size = (int)ttfData.size(),
68  .pathname = (char*)ftPathName.c_str(),
69  .stream = nullptr,
70  .driver = nullptr,
71  .num_params = 0,
72  .params = nullptr
73  };
74  if (FT_Open_Face(ftLibrary, &ftOpenArgs, 0, &ftFace) != 0) {
75  Console::println("GUIFont::parse(): Could not load font: " + pathName + "/" + fileName);
76  return;
77  }
78 
79  //
80  FT_Set_Pixel_Sizes(ftFace, 0, size);
81 
82  //
83  lineHeight = ftFace->size->metrics.height >> 6;
84  baseLine = ftFace->size->metrics.ascender >> 6;
85 }
86 
88 {
89  for (const auto& [charId, character]: chars) delete character;
90  FT_Done_Face(ftFace);
91 }
92 
93 GUIFont* GUIFont::parse(const string& pathName, const string& fileName, int size)
94 {
95  // init freetype library if not yet done
96  if (ftInitialized == false) {
97  if (FT_Init_FreeType(&ftLibrary) != 0) {
98  Console::println("GUIFont::parse(): Could not initialize freetype library");
99  return nullptr;
100  }
101  ftInitialized = true;
102  }
103 
104  // include standard characters in default atlas
105  auto font = new GUIFont(pathName, fileName, size);
106  font->addCharactersToFont(" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ^0123456789°!&quot;$%&/()=?+*-<>|#,;.:'\"");
107 
108  //
109  return font;
110 }
111 
113  //
114  if (FT_Load_Char(ftFace, charId, FT_LOAD_RENDER))
115  {
116  Console::println("GUIFont::addToTextureAtlas(): Could not load glyph: " + Character::toString(charId) + "(" + to_string(charId) + ")");
117  return nullptr;
118  }
119 
120  //
121  auto glyphBitmapWidth = ftFace->glyph->bitmap.width;
122  auto glyphBitmapHeight = ftFace->glyph->bitmap.rows;
123  auto glyphBitmapBuffer = ftFace->glyph->bitmap.buffer;
124  auto glyphByteBuffer = ByteBuffer(glyphBitmapWidth * glyphBitmapHeight * 4);
125  for (int y = glyphBitmapHeight - 1; y >= 0; y--) {
126  for (auto x = 0; x < glyphBitmapWidth; x++) {
127  auto v = glyphBitmapBuffer[y * glyphBitmapWidth + x];
128  glyphByteBuffer.put(v); // red
129  glyphByteBuffer.put(v); // green
130  glyphByteBuffer.put(v); // blue
131  glyphByteBuffer.put(v == 0?0:(v < 0xff / 2?v * 2:0xff)); // alpha
132  }
133  }
134 
135  //
136  auto glyphTexture = new Texture(
137  Character::toString(charId),
138  Texture::TEXTUREDEPTH_RGBA,
139  Texture::TEXTUREFORMAT_RGBA,
140  glyphBitmapWidth, glyphBitmapHeight,
141  glyphBitmapWidth, glyphBitmapHeight,
142  Texture::TEXTUREFORMAT_RGBA,
143  glyphByteBuffer
144  );
145  glyphTexture->acquireReference();
146 
147  //
148  auto glyphTextureSmoothed = TextureReader::smooth(glyphTexture, string());
149  glyphTexture->releaseReference();
150 
151  //
152  textureAtlas.addTexture(glyphTextureSmoothed);
153 
154  //
155  glyphTextureSmoothed->releaseReference();
156 
157  //
158  auto character = new GUICharacter(
159  charId,
160  -1,
161  -1,
162  glyphBitmapWidth,
163  glyphBitmapHeight,
164  ftFace->glyph->bitmap_left,
165  (lineHeight - ftFace->glyph->bitmap_top) - (lineHeight - baseLine),
166  ftFace->glyph->advance.x >> 6
167  );
168  chars[charId] = character;
169 
170  //
171  return character;
172 }
173 
176  auto renderer = Engine::getInstance()->getRenderer();
177  auto contextIdx = renderer->CONTEXTINDEX_DEFAULT;
178  if (textureAtlas.getAtlasTexture() != nullptr) {
179  if (textureId == renderer->ID_NONE) textureId = renderer->createTexture();
180  renderer->bindTexture(contextIdx, textureId);
181  renderer->uploadTexture(contextIdx, textureAtlas.getAtlasTexture());
182  } else
183  if (textureId != renderer->ID_NONE) {
184  renderer->disposeTexture(textureId);
185  textureId = renderer->ID_NONE;
186  }
187  for (auto i = 0;; i++) {
188  auto atlasTexture = textureAtlas.getAtlasTexture(i);
189  if (atlasTexture == nullptr) {
190  break;
191  }
192  //
193  UTF8CharacterIterator u8It(atlasTexture->texture->getId());
194  auto character = getCharacter(u8It.next());
195  if (character == nullptr) {
196  Console::println("GUIFont::updateCharacters(): Could not find character for font character '" + atlasTexture->texture->getId() + "'");
197  continue;
198  }
199  character->x = atlasTexture->left;
200  character->y = atlasTexture->top;
201  character->rotated = atlasTexture->orientation == TextureAtlas::AtlasTexture::ORIENTATION_ROTATED;
202  }
203 }
204 
206 {
207 }
208 
210 {
211 }
212 
213 int GUIFont::getTextIndexX(const MutableString& text, int offset, int length, int index)
214 {
215  auto u8It = text.getUTF8CharacterIterator();
216  u8It.seekCharacterPosition(offset);
217  if (length == 0) length = text.length();
218  auto x = 0;
219  auto i = offset;
220  for (; u8It.hasNext() == true && i < index && i < length; i++) {
221  auto characterId = u8It.next();
222  auto character = getCharacter(characterId);
223  if (character == nullptr) continue;
224  x += character->getXAdvance();
225  }
226  return x;
227 }
228 
229 int GUIFont::getTextIndexByX(const MutableString& text, int offset, int length, int textX)
230 {
231  auto u8It = text.getUTF8CharacterIterator();
232  u8It.seekCharacterPosition(offset);
233  if (length == 0) length = text.length();
234  auto x = 0;
235  auto index = offset;
236  for (; u8It.hasNext() == true && index < length; index++) {
237  auto characterId = u8It.next();
238  auto character = getCharacter(characterId);
239  if (character == nullptr) continue;
240  auto xAdvance = character->getXAdvance();
241  x += xAdvance;
242  if (x - xAdvance / 2 > textX) {
243  return index;
244  }
245  }
246  return index;
247 }
248 
250 {
251  auto u8It = text.getUTF8CharacterIterator();
252  auto width = 0;
253  for (auto i = 0; u8It.hasNext() == true; i++) {
254  auto characterId = u8It.next();
255  auto character = getCharacter(characterId);
256  if (character == nullptr) continue;
257  width += character->getXAdvance();
258  }
259  return width;
260 }
261 
262 int GUIFont::getTextIndexXAtWidth(const MutableString& text, int width) {
263  auto u8It = text.getUTF8CharacterIterator();
264  auto x = 0;
265  for (auto i = 0; u8It.hasNext() == true; i++) {
266  auto characterId = u8It.next();
267  auto character = getCharacter(characterId);
268  if (character == nullptr) continue;
269  x += character->getXAdvance();
270  if (x > width) return i;
271  }
272  return text.length() - 1;
273 }
274 
275 void GUIFont::drawCharacter(GUIRenderer* guiRenderer, GUICharacter* character, int x, int y, const GUIColor& color) {
276  //
277  updateFont();
278  //
279  float screenWidth = guiRenderer->getScreenNode()->getScreenWidth();
280  float screenHeight = guiRenderer->getScreenNode()->getScreenHeight();
281  float left = x + character->getXOffset();
282  float top = y + character->getYOffset();
283  float width = character->getWidth();
284  float height = character->getHeight();
285  float textureWidth = textureAtlas.getAtlasTexture()->getTextureWidth();
286  float textureHeight = textureAtlas.getAtlasTexture()->getTextureHeight();
287  float textureCharLeft = character->getX();
288  float textureCharTop = character->getY();
289  float textureCharWidth = character->rotated == true?character->getHeight():character->getWidth();
290  float textureCharHeight = character->rotated == true?character->getWidth():character->getHeight();
291  guiRenderer->addQuad(
292  ((left) / (screenWidth / 2.0f)) - 1.0f,
293  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
294  color[0], color[1], color[2], color[3],
295  textureCharLeft / textureWidth, textureCharTop / textureHeight,
296  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
297  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
298  color[0], color[1], color[2], color[3],
299  (textureCharLeft + textureCharWidth) / textureWidth, textureCharTop / textureHeight,
300  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
301  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
302  color[0], color[1], color[2], color[3],
303  (textureCharLeft + textureCharWidth) / textureWidth,
304  (textureCharTop + textureCharHeight) / textureHeight,
305  ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
306  color[0], color[1], color[2], color[3],
307  (textureCharLeft) / textureWidth,
308  (textureCharTop + textureCharHeight) / textureHeight,
309  false,
310  character->rotated
311  );
312 }
313 
314 void GUIFont::drawCharacterBackground(GUIRenderer* guiRenderer, GUICharacter* character, int x, int y, int lineHeight, const GUIColor& color) {
315  //
316  updateFont();
317  //
318  float screenWidth = guiRenderer->getScreenNode()->getScreenWidth();
319  float screenHeight = guiRenderer->getScreenNode()->getScreenHeight();
320  float left = x;
321  float top = y;
322  float width = character->getXAdvance();
323  float height = lineHeight;
324  guiRenderer->addQuad(
325  ((left) / (screenWidth / 2.0f)) - 1.0f,
326  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
327  color[0], color[1], color[2], color[3],
328  0.0f, 1.0f,
329  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
330  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
331  color[0], color[1], color[2], color[3],
332  1.0f, 1.0f,
333  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
334  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
335  color[0], color[1], color[2], color[3],
336  1.0f, 0.0f,
337  ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
338  color[0], color[1], color[2], color[3],
339  0.0f, 0.0f,
340  true
341  );
342 }
343 
344 void GUIFont::drawString(GUIRenderer* guiRenderer, int x, int y, const MutableString& text, int offset, int length, const GUIColor& color, int selectionStartIndex, int selectionEndIndex, const GUIColor& backgroundColor)
345 {
346  //
347  updateFont();
348  //
349  auto inSelection = false;
350  auto currentColor = color;
351  auto currentBackgroundColor = backgroundColor;
352  if (selectionStartIndex != -1 && selectionEndIndex != -1) {
353  auto currentX = x;
354  auto u8It = text.getUTF8CharacterIterator();
355  u8It.seekCharacterPosition(offset);
356  for (auto i = offset; u8It.hasNext() == true && (length == 0 || i < length); i++) {
357  auto characterId = u8It.next();
358  auto character = getCharacter(characterId);
359  if (character == nullptr) continue;
360  auto currentInSelection = i >= selectionStartIndex && i < selectionEndIndex;
361  if (currentInSelection != inSelection) {
362  guiRenderer->render();
363  inSelection = currentInSelection;
364  currentColor = inSelection == true?backgroundColor:color;
365  currentBackgroundColor = inSelection == true?color:backgroundColor;
366  }
367  drawCharacterBackground(guiRenderer, character, currentX, y, lineHeight, currentBackgroundColor);
368  currentX += character->getXAdvance();
369  }
370  guiRenderer->render();
371  }
372  guiRenderer->render();
373  guiRenderer->bindTexture(textureId);
374  auto currentX = x;
375  auto u8It = text.getUTF8CharacterIterator();
376  u8It.seekCharacterPosition(offset);
377  for (auto i = offset; u8It.hasNext() == true && (length == 0 || i < length); i++) {
378  auto characterId = u8It.next();
379  auto character = getCharacter(characterId);
380  if (character == nullptr) continue;
381  auto currentInSelection = i >= selectionStartIndex && i < selectionEndIndex;
382  if (currentInSelection != inSelection) {
383  guiRenderer->setFontColor(currentColor);
384  guiRenderer->render();
385  inSelection = currentInSelection;
386  currentColor = inSelection == true?backgroundColor:color;
387  currentBackgroundColor = inSelection == true?color:backgroundColor;
388  }
389  drawCharacter(guiRenderer, character, currentX, y);
390  currentX += character->getXAdvance();
391  }
392  guiRenderer->setFontColor(currentColor);
393  guiRenderer->render();
394  guiRenderer->bindTexture(0);
395 }
Engine main class.
Definition: Engine.h:131
Texture entity.
Definition: Texture.h:24
GUI font class.
Definition: GUIFont.h:41
void drawString(GUIRenderer *guiRenderer, int x, int y, const MutableString &text, int offset, int length, const GUIColor &color, int selectionStartIndex=-1, int selectionEndIndex=-1, const GUIColor &backgroundColor=GUIColor::GUICOLOR_TRANSPARENT)
Draw string.
Definition: GUIFont.cpp:344
GUICharacter * getCharacter(uint32_t charId)
Get character defintion.
Definition: GUIFont.h:140
unordered_map< uint32_t, GUICharacter * > chars
Definition: GUIFont.h:51
GUICharacter * addToTextureAtlas(uint32_t charId)
Add character with given id to texture atlas.
Definition: GUIFont.cpp:112
void drawCharacter(GUIRenderer *guiRenderer, GUICharacter *character, int x, int y, const GUIColor &color=GUIColor::GUICOLOR_WHITE)
Draw character.
Definition: GUIFont.cpp:275
void drawCharacterBackground(GUIRenderer *guiRenderer, GUICharacter *character, int x, int y, int lineHeight, const GUIColor &color)
Draw background.
Definition: GUIFont.cpp:314
vector< uint8_t > ttfData
Definition: GUIFont.h:46
int getTextIndexX(const MutableString &text, int offset, int length, int index)
Get text index X of given text and index.
Definition: GUIFont.cpp:213
int getTextIndexByX(const MutableString &text, int offset, int length, int textX)
Get text index by text and X in space of text.
Definition: GUIFont.cpp:229
void updateFontInternal()
Do the update work.
Definition: GUIFont.cpp:174
static bool ftInitialized
Definition: GUIFont.h:43
static FT_Library ftLibrary
Definition: GUIFont.h:44
TextureAtlas textureAtlas
Definition: GUIFont.h:49
FT_Open_Args ftOpenArgs
Definition: GUIFont.h:47
GUIFont(const string &pathName, const string &fileName, int size)
Public constructor.
Definition: GUIFont.cpp:57
static GUIFont * parse(const string &pathName, const string &fileName, int size)
Parse the font definition file.
Definition: GUIFont.cpp:93
void updateFont()
Update font texture atlas and character definitions.
Definition: GUIFont.h:94
int getTextIndexXAtWidth(const MutableString &text, int width)
Get text index X at width.
Definition: GUIFont.cpp:262
int getTextWidth(const MutableString &text)
Text width.
Definition: GUIFont.cpp:249
void bindTexture(int32_t textureId)
Bind texture.
GUIScreenNode * getScreenNode()
Definition: GUIRenderer.h:157
void addQuad(float x1, float y1, float colorR1, float colorG1, float colorB1, float colorA1, float tu1, float tv1, float x2, float y2, float colorR2, float colorG2, float colorB2, float colorA2, float tu2, float tv2, float x3, float y3, float colorR3, float colorG3, float colorB3, float colorA3, float tu3, float tv3, float x4, float y4, float colorR4, float colorG4, float colorB4, float colorA4, float tu4, float tv4, bool solidColor=false, bool rotated=false)
Add quad Note: quad vertices order 1 2 +-—+ | | | | +-—+ 4 3.
Definition: GUIRenderer.h:507
void setFontColor(const GUIColor &color)
Set effect color mul.
Definition: GUIRenderer.h:176
Standard math functions.
Definition: Math.h:19
File system singleton class.
Definition: FileSystem.h:17
Byte buffer class.
Definition: ByteBuffer.h:27
Character class.
Definition: Character.h:17
Float class.
Definition: Float.h:27
Integer class.
Definition: Integer.h:25
Mutable utf8 aware string class.
Definition: MutableString.h:23
const UTF8CharacterIterator getUTF8CharacterIterator() const
String tokenizer class.
String tools class.
Definition: StringTools.h:22
const AtlasTexture * getAtlasTexture(int textureIdx)
Returns specific atlas texture information within atlas.
Definition: TextureAtlas.h:73
int addTexture(Texture *texture)
Add texture.
void update()
Update texture atlas.
UTF8 string character iterator.
void seekCharacterPosition(int position) const
Seek character position.
std::exception Exception
Exception base class.
Definition: Exception.h:18