FontMesh.cpp
Go to the documentation of this file.
1 #include "FontMesh.hpp"
2 
3 #include "../Defs.hpp"
4 #include "../Rendering/ColorTransform.hpp"
5 
6 #include <utf8.h>
7 
8 namespace iv
9 {
10 
12  cm( inst, this, "FontMesh" ),
13  inst( inst ),
14  shader( inst ),
15  font( inst ),
16  variant_texture( nullptr )
17 {
18  this->cm.owns( this->shader.cm, this->font.cm );
19 }
20 
22 {
23  return this->inst;
24 }
25 
26 void FontMesh::font_path( ResourcePath const & path )
27 {
28  this->font.path( path );
29 }
30 
32 {
33  return this->shader.get() ? this->shader->program_id() : 0;
34 }
35 
37 {
38  if( !this->font.get() )
39  return 0;
40 
41  auto const & variant = this->font->SelectVariantMsdf();
42  if( !variant.texture )
43  return 0;
44 
45  return variant.texture->gl_texture()->texture_id();
46 }
47 
48 FontMesh::Geometry FontMesh::ComputeGeometry( std::string const & text, float font_size, Location const & location )
49 {
50  if( !this->font.get() )
51  return Geometry();
52 
53  // font data
54  Font::Info const & info = this->font->GetInfo();
55  float my_ascender = info.ascender * font_size;
56  float my_descender = info.descender * font_size;
57 
58  // surface data
59  Geometry geo;
60  LineState state = location.line_state;
61 
62  // loop data
63  unsigned skip = location.skip_characters;
64  char const * pos = text.c_str();
65  char const * const end = pos + text.size();
66  while( pos < end )
67  {
68  uint32_t c = ::utf8::next( pos, end );
69 
70  if( skip > 0 )
71  {
72  skip--;
73  }
74  else
75  {
76  if( c == uint32_t( '\n' ) )
77  {
78  // alone in line, but can't fit anyway
79  if( !state.baseline_fixed )
80  {
81  geo.fits = false;
82  break;
83  }
84 
85  // next line
86  geo.max_width = std::max( geo.max_width, state.basepoint.x );
87  state.basepoint.y = state.basepoint.y - state.descender + location.line_spacing;
88  state.basepoint.x = 0.0f;
89  state.preadvance = 0.0f;
90  state.baseline_fixed = false;
91 
92  //
93  geo.characters++;
94  continue;
95  }
96 
97  auto it_adv = info.advances.find( c );
98  if( it_adv == info.advances.end() )
99  {
100  geo.characters++;
101  continue;
102  }
103 
104  auto it_width = info.widths.find( c );
105  if( it_width == info.widths.end() )
106  {
107  geo.characters++;
108  continue;
109  }
110 
111  float adv = it_adv->second * font_size;
112  float glyph_width = it_width->second * font_size;
113 
114  // check width, consider newline
115  if( state.basepoint.x + state.preadvance + glyph_width > location.size.x )
116  {
117  // alone in line, but can't fit anyway
118  if( !state.baseline_fixed )
119  {
120  geo.fits = false;
121  break;
122  }
123 
124  // next line
125  geo.max_width = std::max( geo.max_width, state.basepoint.x );
126  state.basepoint.y = state.basepoint.y - state.descender + location.line_spacing;
127  state.basepoint.x = 0.0f;
128  state.preadvance = 0.0f;
129  state.baseline_fixed = false;
130  }
131 
132  // fix baseline
133  if( !state.baseline_fixed )
134  {
135  state.ascender = my_ascender;
136  state.descender = my_descender;
137  state.basepoint.y += state.ascender;
138  state.baseline_fixed = true;
139  }
140 
141  // check vertical space
142  if( state.basepoint.y - state.descender > location.size.y )
143  {
144  geo.fits = false;
145  break;
146  }
147 
148  // advance by one glyph
149  state.basepoint.x += state.preadvance + glyph_width;
150  state.preadvance = adv - glyph_width;
151  }
152 
153  geo.characters++;
154  }
155 
156  geo.max_width = std::max( geo.max_width, state.basepoint.x );
157  geo.line_state = state;
158 
159  return geo;
160 }
161 
162 void FontMesh::GenerateMesh( std::string const & text, float font_size, Location const & location )
163 {
164  // reset meshes
165  this->mesh.CreateMesh( &this->cm );
166  this->variant_texture = nullptr;
167 
168  //
169  if( !this->font.get() )
170  return;
171 
172  // font data
173  Font::Info const & info = this->font->GetInfo();
174  float my_ascender = info.ascender * font_size;
175  float my_descender = info.descender * font_size;
176  Font::Variant const & variant = this->font->SelectVariantMsdf();
177  float variant_size = variant.size;
178  float2 tex_size = variant.texture->metadata().size;
179 
180  this->variant_texture = variant.texture;
181  this->variant_texcoordDensity = float2( 1.0f / tex_size.x, 1.0f / tex_size.y ) * variant_size / font_size;
182 
183  // surface data
184  LineState state = location.line_state;
185 
186  // loop data
187  GlMeshData data;
188  unsigned skip = location.skip_characters;
189 
190  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "GenerateMesh, location.size: ", location.size, " location.line_state.basepoint: ", location.line_state.basepoint );
191 
192  unsigned next_index = 0;
193 
194  char const * pos = text.c_str();
195  char const * const end = pos + text.size();
196  while( pos < end )
197  {
198  uint32_t c = ::utf8::next( pos, end );
199 
200  if( skip > 0 )
201  {
202  skip--;
203  }
204  else
205  {
206  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Glyph ", c, " '", char(c),"'." );
207 
208  if( c == uint32_t( '\n' ) )
209  {
210  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Newline because of newline character." );
211 
212  // alone in line, but can't fit anyway
213  if( !state.baseline_fixed )
214  {
215  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Not enough horizontal space, abort." );
216  break;
217  }
218 
219  // next line
220  state.basepoint.y = state.basepoint.y - state.descender + location.line_spacing;
221  state.basepoint.x = 0.0f;
222  state.preadvance = 0.0f;
223  state.baseline_fixed = false;
224 
225  //
226  continue;
227  }
228 
229  auto glyph_it = variant.glyphs.find( c );
230  if( glyph_it == variant.glyphs.end() )
231  {
232  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Variant not found." );
233  continue;
234  }
235 
236  auto it_adv = info.advances.find( c );
237  if( it_adv == info.advances.end() )
238  {
239  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Advance not found." );
240  continue;
241  }
242 
243  auto it_width = info.widths.find( c );
244  if( it_width == info.widths.end() )
245  {
246  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Glyph width not found." );
247  continue;
248  }
249 
250  float adv = it_adv->second * font_size;
251  float glyph_width = it_width->second * font_size;
252  Font::Glyph const & glyph = glyph_it->second;
253 
254  // check width, consider newline
255  if( state.basepoint.x + state.preadvance + glyph_width > location.size.x )
256  {
257  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Newline because basepoint ", state.basepoint.x, " remainder advance ", state.preadvance, " and glyph width ", glyph_width, " exceed available size ", location.size.x, "." );
258 
259  // alone in line, but can't fit anyway
260  if( !state.baseline_fixed )
261  {
262  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Not enough horizontal space, abort." );
263  break;
264  }
265 
266  // next line
267  state.basepoint.y = state.basepoint.y - state.descender + location.line_spacing;
268  state.basepoint.x = 0.0f;
269  state.preadvance = 0.0f;
270  state.baseline_fixed = false;
271  }
272 
273  // fix baseline
274  if( !state.baseline_fixed )
275  {
276  state.ascender = my_ascender;
277  state.descender = my_descender;
278  state.basepoint.y += state.ascender;
279  state.baseline_fixed = true;
280  }
281 
282  // check vertical space
283  if( state.basepoint.y - state.descender > location.size.y )
284  {
285  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Not enough vertical space, abort." );
286  break;
287  }
288 
289  // add glyph to mesh
290  float pos_left = state.basepoint.x + state.preadvance + glyph.bearing_x * font_size / variant_size;
291  float pos_top = state.basepoint.y - glyph.bearing_y * font_size / variant_size;
292  float pos_right = pos_left + float( glyph.width ) * font_size / variant_size;
293  float pos_bottom = pos_top + float( glyph.height ) * font_size / variant_size;
294 
295  float tex_left = float( glyph.pos_x ) / tex_size.x;
296  float tex_top = float( glyph.pos_y ) / tex_size.y;
297  float tex_right = tex_left + float( glyph.width ) / tex_size.x;
298  float tex_bottom = tex_top + float( glyph.height ) / tex_size.y;
299 
300  tex_top = 1.0f - tex_top;
301  tex_bottom = 1.0f - tex_bottom;
302 
303  // ( 0, 0 )
304  data.positions.push_back( pos_left );
305  data.positions.push_back( pos_top );
306  data.positions.push_back( 0 );
307 
308  data.texcoords.push_back( tex_left );
309  data.texcoords.push_back( tex_top );
310 
311  data.indices.push_back( next_index++ );
312 
313  // ( 0, 1 )
314  data.positions.push_back( pos_left );
315  data.positions.push_back( pos_bottom );
316  data.positions.push_back( 0 );
317 
318  data.texcoords.push_back( tex_left );
319  data.texcoords.push_back( tex_bottom );
320 
321  data.indices.push_back( next_index++ );
322 
323  // ( 1, 0 )
324  data.positions.push_back( pos_right );
325  data.positions.push_back( pos_top );
326  data.positions.push_back( 0 );
327 
328  data.texcoords.push_back( tex_right );
329  data.texcoords.push_back( tex_top );
330 
331  data.indices.push_back( next_index++ );
332 
333  // ( 1, 1 )
334  data.positions.push_back( pos_right );
335  data.positions.push_back( pos_bottom );
336  data.positions.push_back( 0 );
337 
338  data.texcoords.push_back( tex_right );
339  data.texcoords.push_back( tex_bottom );
340 
341  data.indices.push_back( next_index++ );
342 
343  // primitive restart
344  data.indices.push_back( GlMesh::PrimitiveRestart );
345 
346  //
347  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Glyph generated on ( ", pos_left, ", ", pos_top, " ) - ( ", pos_right, ", ", pos_bottom, " )", Context::Endl(),
348  "with texcoords ( ", tex_left, ", ", tex_top, " ) - ( ", tex_right, ", ", tex_bottom, " )." );
349 
350  // advance
351  this->cm.log( SRC_INFO, Defs::Log::Text_Verbose, "Advance by remainder ", state.preadvance, " and glyph width ", glyph_width, "." );
352  state.basepoint.x += state.preadvance + glyph_width;
353  state.preadvance = adv - glyph_width;
354  }
355  }
356 
357  data.draw_mode = GL_TRIANGLE_STRIP;
358  this->mesh.Load_All( &this->cm, data );
359 }
360 
361 void FontMesh::Render( CameraState const & camera, float4x4 const & model_transform, float4 color, float4 preblend, bool translucent, std::optional< float > depth_override, ShaderScissor const & scissor )
362 {
363  FlatShader::Params params( &this->cm );
365  params.alpha.Set( color.a );
366  params.colorTransform.Set( ColorTransform::Shift( float4( color.r, color.g, color.b, 1.0f ) ) * ColorTransform::Zero() );
367 
368  this->shader->Render(
369  this->cm,
370 
371  camera,
372  depth_override,
373 
374  model_transform,
375  scissor,
376 
377  preblend,
378  translucent,
379 
380  &this->mesh,
381  float3( 1, 1, 1 ),
382  this->variant_texcoordDensity,
383 
384  this->variant_texture->gl_texture(),
385  1.0f,
386  this->variant_texture->metadata().msdf_pixelRange,
387 
388  params
389  );
390 }
391 
392 }