Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
Font.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include <vlGraphics/Font.hpp>
33 #include <vlGraphics/OpenGL.hpp>
35 #include <vlCore/Log.hpp>
36 #include <vlCore/Say.hpp>
37 #include <vlCore/FileSystem.hpp>
38 #include <vlCore/Image.hpp>
39 
40 #include <ft2build.h>
41 #include FT_FREETYPE_H
42 
43 using namespace vl;
44 
45 // FreeType error table construction start ------------------------------------------
46 // taken from "fterrors.h" example
47 #undef __FTERRORS_H__
48 #define FT_ERRORDEF( e, v, s ) { e, s },
49 #define FT_ERROR_START_LIST {
50 #define FT_ERROR_END_LIST { 0, 0 } };
51 
52 const struct
53 {
54  int err_code;
55  const char* err_msg;
56 } ft_errors[] =
57 
58 #include FT_ERRORS_H
59 // FreeType error table construction end ------------------------------------------
60 
61 const char* get_ft_error_message(int error)
62 {
63  int i=0;
64  while( ft_errors[i].err_msg && ft_errors[i].err_code != error )
65  ++i;
66  return ft_errors[i].err_msg;
67 }
68 
69 //-----------------------------------------------------------------------------
70 // Glyph
71 //-----------------------------------------------------------------------------
73 {
74  if (mTextureHandle)
75  {
76  glDeleteTextures(1, &mTextureHandle);
77  mTextureHandle = 0;
78  }
79 }
80 //-----------------------------------------------------------------------------
81 // Font
82 //-----------------------------------------------------------------------------
83 Font::Font(FontManager* fm)
84 {
85  VL_DEBUG_SET_OBJECT_NAME()
86  mFontManager = fm;
87  mHeight = 0;
88  mFT_Face = NULL;
89  mSmooth = false;
90  mFreeTypeLoadForceAutoHint = true;
91  setSize(14);
92 }
93 //-----------------------------------------------------------------------------
94 Font::Font(FontManager* fm, const String& font_file, int size)
95 {
96  VL_DEBUG_SET_OBJECT_NAME()
97  mFontManager = fm;
98  mHeight = 0;
99  mFT_Face = NULL;
100  mSmooth = false;
101  mFreeTypeLoadForceAutoHint = true;
102  loadFont(font_file);
103  setSize(size);
104 }
105 //-----------------------------------------------------------------------------
106 Font::~Font()
107 {
108  releaseFreeTypeData();
109 }
110 //-----------------------------------------------------------------------------
112 {
113  if (mFT_Face)
114  {
115  if (!mFontManager->freeTypeLibrary())
116  {
117  vl::Log::error("Font::releaseFreeTypeData(): mFontManager->freeTypeLibrary() is NULL!\n");
118  VL_TRAP()
119  }
120  else
121  {
122  FT_Done_Face(mFT_Face);
123  }
124  mFT_Face = NULL;
125  }
126 }
127 //-----------------------------------------------------------------------------
128 void Font::setSize(int size)
129 {
130  if(mSize != size)
131  {
132  mSize = size;
133  // removes all the cached glyphs
134  mGlyphMap.clear();
135  }
136 }
137 //-----------------------------------------------------------------------------
138 void Font::loadFont(const String& path)
139 {
140  if(path == mFilePath)
141  return;
142 
143  mFilePath = path;
144  // removes all the cached glyphs
145  mGlyphMap.clear();
146 
147  // remove FreeType font face object
148  if (mFT_Face)
149  {
150  FT_Done_Face(mFT_Face);
151  mFT_Face = NULL;
152  }
153 
154  FT_Error error = 0;
155 
156  ref<VirtualFile> font_file = defFileSystem()->locateFile( filePath() );
157 
158  if (!font_file)
159  Log::error( Say("Font::loadFont('%s'): font file not found.\n") << filePath() );
160 
161  if ( font_file && font_file->load(mMemoryFile) )
162  {
163  if ( (int)mMemoryFile.size() == font_file->size() )
164  {
165  error = FT_New_Memory_Face( (FT_Library)mFontManager->freeTypeLibrary(),
166  (FT_Byte*)&mMemoryFile[0],
167  (int)mMemoryFile.size(),
168  0,
169  &mFT_Face );
170  }
171  else
172  Log::error( Say("Font::loadFont('%s'): could not read file.\n") << filePath() );
173  }
174 
175  if (error)
176  {
177  Log::error(Say("FT_New_Face error (%s): %s\n") << filePath() << get_ft_error_message(error) );
178  VL_TRAP()
179  return;
180  }
181 }
182 //-----------------------------------------------------------------------------
183 Glyph* Font::glyph(int character)
184 {
185  ref<Glyph>& glyph = mGlyphMap[character];
186 
187  if (glyph.get() == NULL)
188  {
189  glyph = new Glyph;
190  glyph->setFont(this);
191 
192  // create the glyph
193 
194  FT_Error error = 0;
195 
196  error = FT_Set_Char_Size(
197  mFT_Face, /* handle to face object */
198  0, /* char_width in 1/64th of points */
199  mSize*64, /* char_height in 1/64th of points */
200  96, /* horizontal device resolution */
201  96 ); /* vertical device resolution */
202 
203  if(error)
204  {
205  // Log::error(Say("FT_Set_Char_Size error: %s\n") << get_ft_error_message(error) );
206  if ( (mFT_Face->face_flags & FT_FACE_FLAG_SCALABLE) == 0 && mFT_Face->num_fixed_sizes)
207  {
208  // look for the size which is less or equal to the given size
209 
210  int best_match_index = -1;
211  int best_match_size = 0;
212  for( int i=0; i < mFT_Face->num_fixed_sizes; ++i )
213  {
214  int size = mFT_Face->available_sizes[i].y_ppem/64;
215  // skip bigger characters
216  if (size <= mSize)
217  {
218  if (best_match_index == -1 || (mSize - size) < (mSize - best_match_size) )
219  {
220  best_match_index = i;
221  best_match_size = size;
222  }
223  }
224  }
225 
226  if (best_match_index == -1)
227  best_match_index = 0;
228 
229  error = FT_Select_Size(mFT_Face, best_match_index);
230  if (error)
231  Log::error(Say("FT_Select_Size error (%s): %s\n") << filePath() << get_ft_error_message(error) );
232  VL_CHECK(!error)
233  }
234  // else
235  {
236  Log::error(Say("FT_Set_Char_Size error (%s): %s\n") << filePath() << get_ft_error_message(error) );
237  VL_TRAP()
238  return glyph.get();
239  }
240  }
241 
242  mHeight = mFT_Face->size->metrics.height / 64.0f;
243 
244  // using FT_Load_Char instead of FT_Get_Char_Index + FT_Load_Glyph works better, probably
245  // FreeType performs some extra tricks internally to better support less reliable fonts...
246 
247  // FT_UInt glyph_index = FT_Load_Char( mFT_Face, character, FT_LOAD_DEFAULT );
248  // glyph->glyphIndex() = glyph_index;
249  //FT_UInt glyph_index = FT_Get_Char_Index( mFT_Face, character );
250  //glyph->glyphIndex() = glyph_index;
252  //FT_Int32 load_flags = FT_LOAD_DEFAULT;
253  //error = FT_Load_Glyph(
254  // mFT_Face, /* handle to face object */
255  // glyph_index, /* glyph index */
256  // load_flags ); /* load flags, see below */
257  //if(error)
258  //{
259  // Log::error(Say("FT_Load_Glyph error: %s") << get_ft_error_message(error) );
260  // VL_TRAP()
261  // return glyph;
262  //}
263 
264  // Note with FT 2.3.9 FT_LOAD_DEFAULT worked well, with FT 2.4 instead we ned FT_LOAD_FORCE_AUTOHINT
265  // This might work well with VL's font but it might be suboptimal for other fonts.
266 
267  error = FT_Load_Char( mFT_Face, character, freeTypeLoadForceAutoHint() ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_DEFAULT );
268 
269  if(error)
270  {
271  Log::error(Say("FT_Load_Char error (%s): %s\n") << filePath() << get_ft_error_message(error) );
272  VL_TRAP()
273  glyph = NULL;
274  return glyph.get();
275  }
276 
277  glyph->setGlyphIndex( FT_Get_Char_Index( mFT_Face, character ) );
278 
279  error = FT_Render_Glyph(
280  mFT_Face->glyph, /* glyph slot */
281  FT_RENDER_MODE_NORMAL ); /* render mode: FT_RENDER_MODE_MONO or FT_RENDER_MODE_NORMAL */
282 
283  // fonts like webdings.ttf generate an error when an unsupported char code is requested instead of
284  // reverting to char code 0, so we have to do it by hand...
285 
286  if(error)
287  {
288  // Log::error(Say("FT_Render_Glyph error: %s") << get_ft_error_message(error) );
289  // VL_TRAP()
290  error = FT_Load_Glyph(
291  mFT_Face,/* handle to face object */
292  0, /* glyph index */
293  FT_LOAD_DEFAULT ); /* load flags, see below */
294  glyph->setGlyphIndex(0);
295 
296  error = FT_Render_Glyph(
297  mFT_Face->glyph, /* glyph slot */
298  FT_RENDER_MODE_NORMAL ); /* render mode: FT_RENDER_MODE_MONO or FT_RENDER_MODE_NORMAL */
299  }
300 
301  if(error)
302  {
303  Log::error(Say("FT_Render_Glyph error (%s): %s\n") << filePath() << get_ft_error_message(error) );
304  VL_TRAP()
305  glyph = NULL;
306  return glyph.get();
307  }
308 
309  bool ok_format = mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO;
310  ok_format &= mFT_Face->glyph->bitmap.palette_mode == 0;
311  ok_format &= mFT_Face->glyph->bitmap.pitch > 0 || mFT_Face->glyph->bitmap.buffer == NULL;
312 
313  if (!ok_format)
314  {
315  Log::error( Say("Font::glyph() error (%s): glyph format not supported. Visualization Library currently supports only FT_PIXEL_MODE_GRAY and FT_PIXEL_MODE_MONO.\n") << filePath() );
316  VL_TRAP()
317  return glyph.get();
318  }
319 
320  if ( mFT_Face->glyph->bitmap.buffer )
321  {
322  if (mHeight == 0)
323  mHeight = (float)mFT_Face->glyph->bitmap.rows;
324 
325  glyph->setWidth ( mFT_Face->glyph->bitmap.width);
326  glyph->setHeight( mFT_Face->glyph->bitmap.rows);
327  glyph->setLeft ( mFT_Face->glyph->bitmap_left);
328  glyph->setTop ( mFT_Face->glyph->bitmap_top);
329 
330  VL_CHECK( mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO )
331  VL_CHECK( mFT_Face->glyph->bitmap.palette_mode == 0 )
332  VL_CHECK( mFT_Face->glyph->bitmap.pitch > 0 )
333 
334  unsigned int texhdl;
335  glGenTextures( 1, &texhdl );
336  glyph->setTextureHandle(texhdl);
337  VL_glActiveTexture(GL_TEXTURE0);
338  glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
339 
340  int texsize[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
341  int max_tex_size = 0;
342  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
343 
344  int w=0, h=0, margin = 1;
345 
346  for(int i=0; texsize[i]; ++i)
347  {
348  if ( (texsize[i] >= glyph->width() + margin*2 && texsize[i] >= glyph->height() + margin*2) || texsize[i+1] > max_tex_size )
349  {
350  w = texsize[i];
351  h = texsize[i];
352  break;
353  }
354  }
355  VL_CHECK(w)
356  VL_CHECK(h)
357 
358 #if(1)
359  // tex coords DO include the border
360  glyph->setS0( 0 );
361  glyph->setT0( 1 );
362  glyph->setS1((margin*2 + glyph->width() ) /(float)(w-1) );
363  glyph->setT1( 1 -(margin*2 + glyph->height() ) /(float)(h-1) );
364 
365  // tex coords DO NOT include the border
366  //glyph->setS0((float)margin /(float)(w-1));
367  //glyph->setT0( 1 -(float)margin /(float)(h-1));
368  //glyph->setS1(((float)margin + glyph->width() ) /(float)(w-1));
369  //glyph->setT1( 1 -((float)margin + glyph->height() ) /(float)(h-1));
370 #else
371  glyph->setS0((float)margin /(float)w);
372  glyph->setT0( 1 -(float)margin /(float)h);
373  glyph->setS1(((float)margin + glyph->width() ) /(float)w);
374  glyph->setT1( 1 -((float)margin + glyph->height() ) /(float)h);
375 #endif
376 
377  ref<Image> img = new Image;
378  img->allocate2D(w, h, 1, IF_RGBA, IT_UNSIGNED_BYTE);
379 
380  // init to all transparent white
381  for(unsigned char *px = img->pixels(), *end = px + img->requiredMemory(); px<end; px+=4)
382  {
383  px[0] = 0xFF;
384  px[1] = 0xFF;
385  px[2] = 0xFF;
386  px[3] = 0x0;
387  }
388 
389  // maps the glyph on the texture leaving a 1px margin
390 
391  for(int y=0; y<glyph->height(); y++)
392  {
393  for(int x=0; x<glyph->width(); x++)
394  {
395  int offset_1 = (x+margin) * 4 + (w-1-y-margin) * img->pitch();
396  int offset_2 = 0;
397  if (mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
398  offset_2 = x / 8 + y * ::abs(mFT_Face->glyph->bitmap.pitch);
399  else
400  offset_2 = x + y * mFT_Face->glyph->bitmap.pitch;
401 
402 #if (1)
403  if (mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
404  img->pixels()[ offset_1+3 ] = (mFT_Face->glyph->bitmap.buffer[ offset_2 ] >> (7-x%8)) & 0x1 ? 0xFF : 0x0;
405  else
406  img->pixels()[ offset_1+3 ] = mFT_Face->glyph->bitmap.buffer[ offset_2 ];
407 #else
408  // debug code
409  img->pixels()[ offset_1+0 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
410  img->pixels()[ offset_1+1 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
411  img->pixels()[ offset_1+2 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
412  img->pixels()[ offset_1+3 ] = 0xFF; // face->glyph->bitmap.buffer[ offset_2 ];
413 #endif
414  }
415  }
416 
417  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->width(), img->height(), 0, img->format(), img->type(), img->pixels() ); VL_CHECK_OGL();
418 
419  if ( smooth() )
420  {
421  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
422  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
423  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
424  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
425  }
426  else
427  {
428  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
429  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
430  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
431  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
432  }
433 
434  // sets anisotropy to the maximum supported
435  if (Has_GL_EXT_texture_filter_anisotropic)
436  {
437  float max_anisotropy;
438  glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
439  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
440  }
441 
442  VL_CHECK_OGL();
443  glBindTexture( GL_TEXTURE_2D, 0 );
444  }
445 
446  glyph->setAdvance( fvec2( (float)mFT_Face->glyph->advance.x / 64.0f, (float)mFT_Face->glyph->advance.y / 64.0f ) );
447  }
448 
449  return glyph.get();
450 }
451 //-----------------------------------------------------------------------------
452 void Font::setSmooth(bool smooth)
453 {
454  mSmooth = smooth;
455  std::map<int, ref<Glyph> >::iterator it = mGlyphMap.begin();
456  for(; it != mGlyphMap.end(); ++it )
457  {
458  const ref<Glyph>& glyph = it->second;
459  if (glyph->textureHandle() == 0)
460  continue;
461 
462  glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
463  if (smooth)
464  {
465  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
466  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
467  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
468  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
469  }
470  else
471  {
472  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
473  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
474  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
475  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
476  }
477  }
478  glBindTexture( GL_TEXTURE_2D, 0 );
479 }
480 //-----------------------------------------------------------------------------
void setS1(float s1)
Definition: Font.hpp:93
The FontManager class keeps a map associating a font path, size and smoothing flag to a Font object...
Definition: FontManager.hpp:45
VLCORE_EXPORT FileSystem * defFileSystem()
Returns the default FileSystem used by VisualizationLibrary.
Definition: pimpl.cpp:97
void setGlyphIndex(unsigned int glyph_index)
Definition: Font.hpp:102
const T * get() const
Definition: Object.hpp:128
A simple String formatting class.
Definition: Say.hpp:124
const unsigned char * pixels() const
Raw pointer to pixels.
Definition: Image.hpp:170
int width() const
Definition: Font.hpp:74
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
void loadFont(const String &path)
Loads a font using fileSystem()locateFile().
Glyph * glyph(int character)
Returns (and eventually creates) the Glyph* associated to the given character.
The Glyph associated to a character of a given Font.
Definition: Font.hpp:55
void setFont(Font *font)
Definition: Font.hpp:105
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
unsigned int textureHandle() const
Definition: Font.hpp:71
Glyph()
Definition: Font.hpp:67
void releaseFreeTypeData()
Releases the FreeType&#39;s FT_Face used by a Font.
virtual ref< VirtualFile > locateFile(const String &full_path, const String &alternate_path=String()) const
Looks for a VirtualFile on the disk and in the currently active FileSystem.
Definition: FileSystem.cpp:61
void setSize(int size)
The size of the font.
void setTop(int top)
Definition: Font.hpp:84
int requiredMemory() const
Returns the number of bytes requested to store the image.
Definition: Image.cpp:532
const char * err_msg
Definition: Font.cpp:55
int err_code
Definition: Font.cpp:54
Visualization Library main namespace.
Vector2< float > fvec2
A 2 components vector with float precision.
Definition: Vector2.hpp:282
void setT0(float t0)
Definition: Font.hpp:90
int height() const
Definition: Image.hpp:209
#define VL_TRAP()
Definition: checks.hpp:70
void setTextureHandle(unsigned int handle)
Definition: Font.hpp:72
void setS0(float s0)
Definition: Font.hpp:87
int height() const
Definition: Font.hpp:77
int width() const
Definition: Image.hpp:207
void setHeight(int height)
Definition: Font.hpp:78
T abs(T a)
Definition: glsl_math.hpp:646
void setSmooth(bool smooth)
Whether the font rendering should use linear filtering or not.
int pitch() const
Definition: Image.hpp:213
#define NULL
Definition: OpenGLDefs.hpp:81
unsigned int mTextureHandle
Definition: Font.hpp:115
virtual long long size() const =0
Returns the size of the file in bytes.
void setLeft(int left)
Definition: Font.hpp:81
#define VL_CHECK_OGL()
Definition: OpenGL.hpp:156
void setWidth(int width)
Definition: Font.hpp:75
void setT1(float t1)
Definition: Font.hpp:96
~Font()
Destructor.
EImageType type() const
Definition: Image.hpp:217
void setAdvance(const fvec2 &advance)
Definition: Font.hpp:99
int mHeight
Definition: Font.hpp:117
Implements a generic 1d, 2d, 3d and cubemap image that can have mipmaps.
Definition: Image.hpp:54
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
void allocate2D(int x, int y, int bytealign, EImageFormat format, EImageType type)
Definition: Image.cpp:608
long long load(std::vector< char > &data)
Loads the entire file in the specified vector.
#define VL_CHECK(expr)
Definition: checks.hpp:73
String & clear()
Clears the string.
Definition: String.hpp:142
EImageFormat format() const
Definition: Image.hpp:215
vl::Glyph ft_errors[]