SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLTexFont.cpp
Go to the documentation of this file.
1 /**
2  * \file sl/SLTexFont.cpp
3  * \date July 2014
4  * \authors Marcus Hudritsch, original author is Philippe Decaudin
5  * \copyright http://opensource.org/licenses/GPL-3.0
6  * \remarks Please use clangformat to format the code. See more code style on
7  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
8 */
9 
10 #include <SLScene.h>
11 #include <SLTexFont.h>
12 #include <SLGLProgram.h>
13 #include <Utils.h>
14 #include <SLFileStorage.h>
15 
16 #include <utility>
17 
18 //-----------------------------------------------------------------------------
19 SLTexFont::SLTexFont(SLstring fontFilename, SLGLProgram* fontTexProgram)
20 {
21  assert(fontTexProgram);
23  _deleteProgram = false;
24 
25  // Init texture members
26  _texType = TT_font;
27  _wrap_s = GL_CLAMP_TO_EDGE;
28  _wrap_t = GL_CLAMP_TO_EDGE;
29  _min_filter = GL_NEAREST;
30  _mag_filter = GL_NEAREST;
31 
32  for (auto& i : chars)
33  {
34  i.width = 0;
35  i.tx1 = 0;
36  i.tx2 = 0;
37  i.ty1 = 0;
38  i.ty2 = 0;
39  }
40  charsHeight = 0;
41 
42  create(std::move(fontFilename));
43 }
44 //-----------------------------------------------------------------------------
45 SLTexFont::SLTexFont(SLstring fontFilename, SLstring shaderDir)
46 {
47  _fontTexProgram = new SLGLProgramGeneric(nullptr, shaderDir + "FontTex.vert", shaderDir + "FontTex.frag");
48  _deleteProgram = true;
49 
50  // Init texture members
51  _texType = TT_font;
52  _wrap_s = GL_CLAMP_TO_EDGE;
53  _wrap_t = GL_CLAMP_TO_EDGE;
54  _min_filter = GL_NEAREST;
55  _mag_filter = GL_NEAREST;
56 
57  for (auto& i : chars)
58  {
59  i.width = 0;
60  i.tx1 = 0;
61  i.tx2 = 0;
62  i.ty1 = 0;
63  i.ty2 = 0;
64  }
65  charsHeight = 0;
66 
67  create(std::move(fontFilename));
68 }
69 //-----------------------------------------------------------------------------
71 {
73  {
74  delete _fontTexProgram;
75  }
76 }
77 //-----------------------------------------------------------------------------
78 /*!
79 SLTexFont::create creates the inherited texture map with the passed image file.
80 The bitmap image is parsed for all 224 character positions to create the
81 according texture coordinate.
82 */
83 void SLTexFont::create(SLstring fontFilename)
84 {
85  // Check the font filename with path
86  if (!SLFileStorage::exists(fontFilename, IOK_font))
87  {
88  SLstring msg = "SLTexFont::create: File not found: " + fontFilename;
89  SL_EXIT_MSG(msg.c_str());
90  }
91 
92  CVImage img;
93  img.load(fontFilename, false);
94 
95  // find height of the font
96  SLint x, y;
97  SLint bmpW = img.cvMat().cols;
98  SLint bmpH = img.cvMat().rows;
99  SLuchar* bmp = img.cvMat().data;
100  SLint h = 0, hh = 0;
101  SLint r, NbRow = 0;
102 
103  for (y = 0; y < bmpH; ++y)
104  {
105  if (bmp[y * bmpW] == 0)
106  {
107  if ((hh <= 0 && h <= 0) || (h != hh && h > 0 && hh > 0))
108  SL_EXIT_MSG("Cannot determine font height (check first pixel column)");
109  else if (h <= 0)
110  h = hh;
111  else if (hh <= 0)
112  break;
113  hh = 0;
114  ++NbRow;
115  }
116  else
117  ++hh;
118  }
119 
120  // find width and position of each character
121  SLint x0[224], y0[224], x1[224], y1[224];
122  SLint ch = 32;
123  SLint start;
124  for (r = 0; r < NbRow; ++r)
125  {
126  start = 1;
127  for (x = 1; x < bmpW; ++x)
128  {
129  if (bmp[(r * (h + 1) + h) * bmpW + x] == 0 || x == bmpW - 1)
130  {
131  if (x == start) break; // next row
132  if (ch < 256)
133  {
134  x0[ch - 32] = start;
135  x1[ch - 32] = x;
136  y0[ch - 32] = r * (h + 1);
137  y1[ch - 32] = r * (h + 1) + h - 1;
138  start = x + 1;
139  }
140  ++ch;
141  }
142  }
143  }
144 
145  for (x = ch - 32; x < 224; ++x)
146  {
147  x0[x] = 0;
148  y0[x] = 0;
149  x1[x] = 0;
150  y1[x] = 0;
151  }
152 
153  // Repack: build 14 rows of 16 characters. First, find the largest row
154  SLint l, lmax = 1;
155  for (r = 0; r < 14; ++r)
156  {
157  l = 0;
158  for (x = 0; x < 16; ++x)
159  l += x1[x + r * 16] - x0[x + r * 16] + 1;
160  if (l > lmax) lmax = l;
161  }
162 
163  // A little empty margin is added between chars to avoid artefact when anti aliasing is on
164  const SLint MARGIN_X = 2;
165  const SLint MARGIN_Y = 2;
166  lmax += 16 * MARGIN_X;
167 
168  // 2) build the texture
169  charsHeight = h;
170  SLuint texWidth = Utils::nextPowerOf2((SLuint)lmax);
171  SLuint texHeight = Utils::nextPowerOf2(14 * (SLuint)(h + MARGIN_Y));
172 
173  // Fill up with 0
174  SLuchar* bits = new SLuchar[texWidth * texHeight];
175  memset(bits, 0, texWidth * texHeight);
176 
177  SLfloat du = 0.0f;
178  SLfloat dv = 0.0f;
179 
180  for (r = 0; r < 14; ++r)
181  {
182  for (SLint xx = 0, ch = r * 16; ch < (r + 1) * 16; ++ch)
183  {
184  if (y1[ch] - y0[ch] == h - 1)
185  {
186  for (y = 0; y < h; ++y)
187  {
188  for (x = x0[ch]; x <= x1[ch]; ++x)
189  {
190  SLfloat alpha = ((SLfloat)(bmp[x + (y0[ch] + y) * bmpW])) / 256.0f;
191  // alpha = alpha*sqrtf(alpha); // powf(alpha, 1.5f); // some gamma correction
192  bits[(SLuint)(xx + x - x0[ch]) +
193  (SLuint)(r * (h + MARGIN_Y) + y) * texWidth] = (SLuchar)(alpha * 256.0f);
194  }
195  }
196  chars[ch + 32].tx1 = ((SLfloat)xx + du) / (SLfloat)texWidth;
197  xx += x1[ch] - x0[ch] + 1;
198  chars[ch + 32].tx2 = ((SLfloat)xx + du) / (SLfloat)texWidth;
199  chars[ch + 32].ty1 = ((SLfloat)(r * (h + MARGIN_Y)) + dv) / (SLfloat)texHeight;
200  chars[ch + 32].ty2 = ((SLfloat)(r * (h + MARGIN_Y) + h) + dv) / (SLfloat)texHeight;
201  chars[ch + 32].width = (SLfloat)(x1[ch] - x0[ch] + 1);
202  xx += MARGIN_X;
203  }
204  }
205  }
206 
207  // Allocate memory for image pixels using only the alpha channel
208  _images.clear();
209  SLGLState* stateGL = SLGLState::instance();
211  _images.push_back(new CVImage((SLint)texWidth,
212  (SLint)texHeight,
213  format,
214  fontFilename));
215  _images[0]->load((SLint)texWidth,
216  (SLint)texHeight,
217  format,
218  format,
219  bits,
220  true,
221  false);
222  delete[] bits;
223  _width = _images[0]->width();
224  _height = _images[0]->height();
225  _depth = (SLint)_images.size();
226 
227  // Set characters below 32 to default
228  const SLuchar Undef = 127; // default character used as for undifined ones (having ascii codes from 0 to 31)
229  for (ch = 0; ch < 32; ++ch)
230  {
231  chars[ch].tx1 = chars[Undef].tx1;
232  chars[ch].tx2 = chars[Undef].tx2;
233  chars[ch].ty1 = chars[Undef].ty1;
234  chars[ch].ty2 = chars[Undef].ty2;
235  chars[ch].width = chars[Undef].width;
236  }
237 }
238 //-----------------------------------------------------------------------------
239 /*! Returns the size (width & height) of the full text in float pixels. If a
240 max. width is passed the text is first wrapped into multiple lines. For multiline
241 text the line height is calculate as the font height * lineHeightFactor.
242 */
244  SLfloat maxWidth,
245  SLfloat lineHeightFactor)
246 {
247  SLVec2f size(0, 0);
248 
249  if (maxWidth > 0.0f)
250  {
251  SLfloat maxX = FLT_MIN;
252  SLVstring lines = wrapTextToLines(text, maxWidth);
253  for (const auto& line : lines)
254  {
255  SLVec2f sizeLine = calcTextSize(line);
256  if (sizeLine.x > maxX) maxX = sizeLine.x;
257  }
258  size.x = maxX;
259  size.y = (SLfloat)(lines.size() - 1) * (SLfloat)charsHeight * lineHeightFactor;
260  size.y += (SLfloat)charsHeight;
261  }
262  else
263  { // Loop through each character of text
264  for (char c : text)
265  {
266  size.x += chars[(SLuint)c].width;
267  }
268  size.y = (SLfloat)charsHeight;
269  }
270  return size;
271 }
272 //-----------------------------------------------------------------------------
273 /*!
274 Returns a vector of strings of the text to be wrapped to a max. with of maxW.
275 The sum of all characters in lines must be equal to the length of the input text
276 */
278  SLfloat maxW) // max. width in pixels
279 {
280  SLVstring lines;
281  SLfloat curX = 0.0f;
282  SLfloat maxX = FLT_MIN;
283  SLfloat xBlank = 0.0f;
284  SLuint iBlank = 0;
285  SLuint iLineStart = 0;
286  SLuint len = (SLuint)text.length();
287 
288  // Loop through each character of text
289  for (SLuint i = 0; i < len; ++i)
290  {
291  SLchar c = text[i];
292 
293  if (c == '\\' && i < len - 1 && text[i + 1] == 'n')
294  {
295  i++;
296  if (curX > maxX) maxX = curX;
297  lines.push_back(text.substr(iLineStart, i - iLineStart - 1) + " ");
298  iLineStart = i + 1;
299  curX = 0.0f;
300  }
301  else // add next character
302  {
303  // keep last blank x
304  if (c == ' ')
305  {
306  xBlank = curX;
307  iBlank = i;
308  }
309 
310  curX += chars[(SLuint)c].width;
311 
312  // if width exceeded wrap at last blank position
313  if (curX > maxW)
314  { // wrap at last blank
315  if (xBlank > 0.0f)
316  {
317  // keep largest line width
318  if (xBlank > maxX) maxX = xBlank;
319  curX = curX - xBlank - chars[(SLuint)' '].width;
320  lines.push_back(text.substr(iLineStart, iBlank - iLineStart + 1));
321  iLineStart = iBlank + 1;
322  }
323  else // wrap in the word
324  {
325  if (curX - chars[(SLuint)c].width > maxX)
326  maxX = curX - chars[(SLuint)c].width;
327  lines.push_back(text.substr(iLineStart, i - iLineStart));
328  curX = chars[(SLuint)c].width;
329  iLineStart = i + 1;
330  }
331  }
332  }
333  }
334  SLstring newLine = text.substr(iLineStart, len - iLineStart);
335  lines.push_back(newLine);
336  return lines;
337 }
338 //-----------------------------------------------------------------------------
339 /*!
340 Builds the vertex array object with 2 texture mapped triangles per
341 character. The text width < maxWidth the text will be on one line. If it is
342 wider it will be split into multiple lines with a
343 height = font height * lineHeight.
344 */
345 void SLTexFont::buildTextBuffers(SLGLVertexArray& vao, //!< external vertex array object
346  const SLstring& text, //!< text to render
347  SLfloat maxWidth, //!< max. width for multi-line text
348  SLfloat lineHeight) //!< line height factor
349 {
350  SLVstring lines; // Vector of text lines
351  SLVVec2f sizes; // Sizes of text lines
352  size_t numP = 0; // No. of vertices
353  size_t numI = 0; // No. of indices (3 per triangle)
354  SLfloat x; // current lower-left x position
355  SLfloat y; // current lower-left y position
356  SLuint iV; // current vertex index
357  SLuint iI; // current vertex index index
358 
359  // Calculate number of vertices & indices
360  if (maxWidth > 0.0f)
361  {
362  // multiple text lines
363  lines = wrapTextToLines(text, maxWidth);
364  for (auto& line : lines)
365  {
366  numP += line.length();
367  sizes.push_back(calcTextSize(line));
368  }
369  numP *= 4;
370  numI = numP * 2 * 3;
371  }
372  else
373  {
374  // single text line
375  lines.push_back(text);
376  numP = text.length() * 4;
377  numI = text.length() * 2 * 3;
378  }
379 
380  SLVVec3f P;
381  P.resize(numP); // Vertex positions
382  SLVVec2f T;
383  T.resize(numP); // Vertex texture coords.
384  SLVushort I;
385  I.resize(numI); // Indexes
386 
387  iV = iI = 0;
388  y = ((SLfloat)lines.size() - 1) * (SLfloat)charsHeight * lineHeight;
389 
390  for (auto& line : lines)
391  {
392  x = 0;
393 
394  // Loop through characters
395  for (char c : line)
396  {
397  // Get width and height
398  SLfloat w = chars[(SLuint)c].width;
400 
401  // Specify texture coordinates
402  T[iV].set(chars[(SLuint)c].tx1, chars[(SLuint)c].ty2);
403  T[iV + 1].set(chars[(SLuint)c].tx2, chars[(SLuint)c].ty2);
404  T[iV + 2].set(chars[(SLuint)c].tx2, chars[(SLuint)c].ty1);
405  T[iV + 3].set(chars[(SLuint)c].tx1, chars[(SLuint)c].ty1);
406 
407  // vertices of the character quad
408  P[iV].set(x, y);
409  P[iV + 1].set(x + w, y);
410  P[iV + 2].set(x + w, y + h);
411  P[iV + 3].set(x, y + h);
412 
413  // triangle indices of the character quad
414  I[iI++] = (SLushort)iV;
415  I[iI++] = (SLushort)iV + 1;
416  I[iI++] = (SLushort)iV + 3;
417  I[iI++] = (SLushort)iV + 1;
418  I[iI++] = (SLushort)iV + 2;
419  I[iI++] = (SLushort)iV + 3;
420 
421  // Move to next character
422  iV += 4;
423  x += w;
424  }
425 
426  y -= (SLfloat)charsHeight * lineHeight;
427  }
428 
429  // create buffers on GPU
431  sp->useProgram();
433  vao.setAttrib(AT_uv1, AT_uv1, &T);
434  vao.setIndices(&I);
435  vao.generate((SLuint)numP);
436 }
437 //-----------------------------------------------------------------------------
CVPixelFormatGL
Pixel format according to OpenGL pixel format defines.
Definition: CVImage.h:24
@ PF_luminance
Definition: CVImage.h:28
@ PF_red
Definition: CVImage.h:34
float SLfloat
Definition: SL.h:173
unsigned int SLuint
Definition: SL.h:171
char SLchar
Definition: SL.h:162
unsigned char SLuchar
Definition: SL.h:163
unsigned short SLushort
Definition: SL.h:169
vector< SLstring > SLVstring
Definition: SL.h:201
#define SL_EXIT_MSG(message)
Definition: SL.h:240
vector< SLushort > SLVushort
Definition: SL.h:195
string SLstring
Definition: SL.h:158
int SLint
Definition: SL.h:170
@ IOK_font
Definition: SLFileStorage.h:43
@ AT_position
Vertex position as a 2, 3 or 4 component vectors.
Definition: SLGLEnums.h:58
@ AT_uv1
Vertex 1st texture coordinate as 2 component vector.
Definition: SLGLEnums.h:60
@ TT_font
Definition: SLGLTexture.h:88
vector< SLVec2f > SLVVec2f
Definition: SLVec2.h:143
vector< SLVec3f > SLVVec3f
Definition: SLVec3.h:325
OpenCV image class with the same interface as the former SLImage class.
Definition: CVImage.h:64
CVMat cvMat() const
Definition: CVImage.h:122
void load(const string &filename, bool flipVertical=true, bool loadGrayscaleIntoAlpha=false)
Loads the image with the appropriate image loader.
Definition: CVImage.cpp:379
Generic Shader Program class inherited from SLGLProgram.
Encapsulation of an OpenGL shader program object.
Definition: SLGLProgram.h:56
void useProgram()
Singleton class holding all OpenGL states.
Definition: SLGLState.h:71
static SLGLState * instance()
Public static instance getter for singleton pattern.
Definition: SLGLState.h:74
SLbool pixelFormatIsSupported(SLint pixelFormat)
Returns true if the according GL pixel format is valid in the GL context.
Definition: SLGLState.cpp:588
SLint _width
Texture image width in pixels (images exist either in _images or on the GPU or on both)
Definition: SLGLTexture.h:305
SLuint width()
Definition: SLGLTexture.h:218
SLint _wrap_t
Wrapping in t direction.
Definition: SLGLTexture.h:314
SLint _min_filter
Minification filter.
Definition: SLGLTexture.h:311
SLint _wrap_s
Wrapping in s direction.
Definition: SLGLTexture.h:313
SLint _height
Texture image height in pixels (images exist either in _images or on the GPU or on both)
Definition: SLGLTexture.h:306
SLTextureType _texType
See SLTextureType.
Definition: SLGLTexture.h:304
CVVImage _images
Vector of CVImage pointers.
Definition: SLGLTexture.h:302
SLint _depth
3D Texture image depth (images exist either in _images or on the GPU or on both)
Definition: SLGLTexture.h:307
SLint _mag_filter
Magnification filter.
Definition: SLGLTexture.h:312
SLGLVertexArray encapsulates the core OpenGL drawing.
void setAttrib(SLGLAttributeType type, SLint elementSize, SLint location, void *dataPointer, SLGLBufferType dataType=BT_float)
Adds a vertex attribute with data pointer and an element size.
void setIndices(SLuint numIndicesElements, SLGLBufferType indexDataType, void *indexDataElements, SLuint numIndicesEdges=0, void *indexDataEdges=nullptr)
Adds the index array for indexed element drawing.
void generate(SLuint numVertices, SLGLBufferUsage usage=BU_static, SLbool outputInterleaved=true, SLuint divisor=0)
Generates the VA & VB objects for a NO. of vertices.
bool _deleteProgram
Definition: SLTexFont.h:72
SLTexFontChar chars[256]
Definition: SLTexFont.h:68
SLint charsHeight
Definition: SLTexFont.h:69
void buildTextBuffers(SLGLVertexArray &vao, const SLstring &text, SLfloat maxWidth=0.0f, SLfloat lineHeight=1.5f)
Definition: SLTexFont.cpp:345
void create(SLstring fontFilename)
Definition: SLTexFont.cpp:83
SLGLProgram * _fontTexProgram
Definition: SLTexFont.h:71
SLGLProgram * fontTexProgram()
Definition: SLTexFont.h:56
SLVstring wrapTextToLines(SLstring text, SLfloat maxW)
Definition: SLTexFont.cpp:277
SLVec2f calcTextSize(const SLstring &text, SLfloat maxWidth=0.0f, SLfloat lineHeightFactor=1.5f)
Definition: SLTexFont.cpp:243
SLTexFont(SLstring fontFilename, SLGLProgram *fontTexProgram)
Definition: SLTexFont.cpp:19
T y
Definition: SLVec2.h:30
T x
Definition: SLVec2.h:30
bool exists(std::string path, SLIOStreamKind kind)
Checks whether a given file exists.
unsigned nextPowerOf2(unsigned num)
Returns the next power of 2 to a passed number.
Definition: Utils.cpp:1237
SLfloat width
Width of char. in tex-coord.
Definition: SLTexFont.h:61
SLfloat ty1
Max. Texture y-coord.
Definition: SLTexFont.h:63
SLfloat ty2
Min. Texture y-coord.
Definition: SLTexFont.h:65
SLfloat tx1
Min. Texture x-coord.
Definition: SLTexFont.h:62
SLfloat tx2
Max. Texture x-coord.
Definition: SLTexFont.h:64