naev 0.12.5
font.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
16#include <ft2build.h>
17#include FT_FREETYPE_H
18#include FT_GLYPH_H
19#include FT_MODULE_H
20#include "linebreak.h"
21#include "linebreakdef.h"
22#include <wctype.h>
23
24#include "naev.h"
26
27#include "font.h"
28
29#include "array.h"
30#include "distance_field.h"
31#include "log.h"
32#include "ndata.h"
33#include "ntracing.h"
34#include "utf8.h"
35
36#define MAX_EFFECT_RADIUS \
37 4
38#define FONT_DISTANCE_FIELD_SIZE 55
39#define HASH_LUT_SIZE 512
40#define DEFAULT_TEXTURE_SIZE \
41 1024
42#define MAX_ROWS 64
43
49static FT_Library font_library = NULL;
50static FT_UInt
53
57typedef struct glFontRow_s {
58 int x;
59 int y;
60 int h;
61} glFontRow;
62
66typedef struct glFontTex_s {
67 GLuint id;
69} glFontTex;
70
74typedef struct glFontGlyph_s {
75 uint32_t codepoint;
76 GLfloat adv_x;
77 GLfloat m;
80 GLushort vbo_id;
81 int next;
83
87typedef struct font_char_s {
88 GLubyte *data;
89 GLfloat *dataf;
90 int w;
91 int h;
93 int off_x;
94 int off_y;
95 GLfloat adv_x;
96 GLfloat m;
97 int tx;
98 int ty;
99 int tw;
100 int th;
102
107typedef struct glFontFile_s {
108 char *name;
110 FT_Byte *data;
111 size_t datasize;
112} glFontFile;
113
117typedef struct glFontStashFreetype_s {
119 FT_Face face;
121
125typedef struct glFontStash_s {
126 /* Core values (determine font). */
127 char *fname;
128
129 /* Generated values. */
130 GLint magfilter;
131 GLint minfilter;
132 int h;
133 int tw;
134 int th;
136 gl_vbo *vbo_tex;
137 gl_vbo *vbo_vert;
138 GLfloat *vbo_tex_data;
139 GLshort *vbo_vert_data;
140 int nvbo;
141 int mvbo;
144
145 /* Freetype stuff. */
147
150
155 NULL;
156
157/* default font */
161
162/* Last used colour. */
163static const glColour *font_lastCol =
164 NULL;
165static int font_restoreLast = 0;
166
167/*
168 * prototypes
169 */
170static int gl_fontstashAddFallback( glFontStash *stsh, const char *fname,
171 unsigned int h );
172static size_t font_limitSize( glFontStash *stsh, int *width, const char *text,
173 const int max );
174static const glColour *gl_fontGetColour( uint32_t ch );
175static uint32_t font_nextChar( const char *s, size_t *i );
176/* Get unicode glyphs from cache. */
177static glFontGlyph *gl_fontGetGlyph( glFontStash *stsh, uint32_t ch );
178/* Render.
179 * TODO this should be changed to be more like font-stash
180 * (https://github.com/akrinke/Font-Stash) In particular, instead of writing
181 * char by char, they should be batched up by textures and rendered when
182 * gl_fontRenderEnd() is called, saving lots of opengl calls.
183 */
184static void gl_fontRenderStart( const glFontStash *stsh, double x, double y,
185 const glColour *c, double outlineR );
186static void gl_fontRenderStartH( const glFontStash *stsh, const mat4 *H,
187 const glColour *c, double outlineR );
188static int gl_fontRenderGlyph( glFontStash *stsh, uint32_t ch,
189 const glColour *c, int state );
190static void gl_fontRenderEnd( void );
191/* Fussy layout concerns. */
192static void gl_fontKernStart( void );
193static int gl_fontKernGlyph( glFontStash *stsh, uint32_t ch,
194 glFontGlyph *glyph );
196
200static glFontStash *gl_fontGetStash( const glFont *font )
201{
202 return &avail_fonts[font->id];
203}
204
209 glFontGlyph *glyph )
210{
211 int n;
212 glFontRow *gr, *lr;
213 glFontTex *tex;
214 GLfloat *vbo_tex;
215 GLshort *vbo_vert;
216 GLfloat tx, ty, txw, tyh;
217 GLfloat fw, fh;
218 GLshort vx, vy, vw, vh;
219 double mx, my;
220
221 /* Find free row. */
222 tex = NULL;
223 gr = NULL;
224 for ( int i = 0; i < array_size( stsh->tex ); i++ ) {
225 for ( int j = 0; j < MAX_ROWS; j++ ) {
226 glFontRow *r = &stsh->tex->rows[j];
227 /* Not empty row and doesn't fit. */
228 if ( ( r->h != 0 ) && ( r->h != ch->h ) )
229 continue;
230 if ( r->h == ch->h ) {
231 /* Fits in current row, so use that. */
232 if ( r->x + ch->w <= stsh->tw ) {
233 tex = &stsh->tex[i];
234 gr = r;
235 break;
236 } else
237 continue; /* Didn't fit so continue looking. */
238 }
239 if ( r->h != 0 )
240 continue;
241 /* First row. */
242 if ( j == 0 ) {
243 assert( ch->h <=
244 stsh->th ); /* Would be ridiculously large character... */
245 r->h = ch->h;
246 tex = &stsh->tex[i];
247 gr = r;
248 break;
249 }
250 /* See if height fits to create a new row. */
251 lr = &stsh->tex->rows[j - 1];
252 if ( lr->y + lr->h + ch->h <= stsh->th ) {
253 r->h = ch->h;
254 r->y = lr->y + lr->h;
255 tex = &stsh->tex[i];
256 gr = r;
257 }
258 break; /* Have to break here because either we added a new row or
259 texture is full. */
260 }
261 if ( gr != NULL )
262 break;
263 }
264
265 /* Didn't fit so allocate new texture. */
266 if ( gr == NULL ) {
267 tex = &array_grow( &stsh->tex );
268 memset( tex, 0, sizeof( glFontTex ) );
269
270 /* Create new texture. */
271 glGenTextures( 1, &tex->id );
272 glBindTexture( GL_TEXTURE_2D, tex->id );
273
274 /* Set a sane default minification and magnification filter. */
275 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, stsh->magfilter );
276 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, stsh->minfilter );
277
278 /* Clamp texture .*/
279 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
280 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
281
282 /* Initialize size. */
283 glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, stsh->tw, stsh->th, 0, GL_RED,
284 GL_UNSIGNED_BYTE, NULL );
285
286 /* Check for errors. */
287 gl_checkErr();
288
289 /* Create a new entry at the beginning of the first row with our target
290 * height. */
291 gr = &tex->rows[0];
292 gr->h = ch->h;
293 }
294
295 /* Upload data. */
296 glBindTexture( GL_TEXTURE_2D, tex->id );
297 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
298 if ( ch->dataf != NULL )
299 glTexSubImage2D( GL_TEXTURE_2D, 0, gr->x, gr->y, ch->w, ch->h, GL_RED,
300 GL_FLOAT, ch->dataf );
301 else
302 glTexSubImage2D( GL_TEXTURE_2D, 0, gr->x, gr->y, ch->w, ch->h, GL_RED,
303 GL_UNSIGNED_BYTE, ch->data );
304 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
305
306 /* Check for error. */
307 gl_checkErr();
308
309 /* Update VBOs. */
310 stsh->nvbo++;
311 if ( stsh->nvbo > stsh->mvbo ) {
312 stsh->mvbo *= 2;
313 stsh->vbo_tex_data =
314 realloc( stsh->vbo_tex_data, 8 * stsh->mvbo * sizeof( GLfloat ) );
315 stsh->vbo_vert_data =
316 realloc( stsh->vbo_vert_data, 8 * stsh->mvbo * sizeof( GLshort ) );
317 }
318 n = 8 * stsh->nvbo;
319 vbo_tex = &stsh->vbo_tex_data[n - 8];
320 vbo_vert = &stsh->vbo_vert_data[n - 8];
321 /* We do something like the following for vertex coordinates.
322 *
323 *
324 * +----------------- top reference \ <------- font->h
325 * | |
326 * | | --- off_y
327 * +----------------- glyph top /
328 * |
329 * |
330 * +----------------- glyph bottom
331 * |
332 * v y
333 *
334 *
335 * +----+-------------> x
336 * | |
337 * | glyph start
338 * |
339 * side reference
340 *
341 * \----/
342 * off_x
343 */
344 /* Temporary variables. */
345 fw = (GLfloat)stsh->tw;
346 fh = (GLfloat)stsh->th;
347 mx = 1. / fw;
348 my = 1. / fh;
349 tx = (GLfloat)( gr->x + 1. ) / fw;
350 ty = (GLfloat)( gr->y + 1. ) / fh;
351 txw = (GLfloat)( gr->x + ch->w - 1. ) / fw;
352 tyh = (GLfloat)( gr->y + ch->h - 1. ) / fh;
353 vx = ch->off_x + mx;
354 vy = ch->off_y - ch->h + mx;
355 vw = ch->w - mx;
356 vh = ch->h - my;
357 /* Texture coords. */
358 vbo_tex[0] = tx; /* Top left. */
359 vbo_tex[1] = ty;
360 vbo_tex[2] = txw; /* Top right. */
361 vbo_tex[3] = ty;
362 vbo_tex[4] = tx; /* Bottom left. */
363 vbo_tex[5] = tyh;
364 vbo_tex[6] = txw; /* Bottom right. */
365 vbo_tex[7] = tyh;
366 /* Vertex coords. */
367 vbo_vert[0] = vx; /* Top left. */
368 vbo_vert[1] = vy + vh;
369 vbo_vert[2] = vx + vw; /* Top right. */
370 vbo_vert[3] = vy + vh;
371 vbo_vert[4] = vx; /* Bottom left. */
372 vbo_vert[5] = vy;
373 vbo_vert[6] = vx + vw; /* Bottom right. */
374 vbo_vert[7] = vy;
375 /* Update vbos. */
376 gl_vboData( stsh->vbo_tex, sizeof( GLfloat ) * n, stsh->vbo_tex_data );
377 gl_vboData( stsh->vbo_vert, sizeof( GLshort ) * n, stsh->vbo_vert_data );
378
379 /* Add space for the new character. */
380 gr->x += ch->w;
381
382 /* Save glyph data. */
383 glyph->vbo_id = ( n - 8 ) / 2;
384 glyph->tex_index = tex - stsh->tex;
385
386 /* Since the VBOs have possibly changed, we have to reset the data. */
387 gl_vboActivateAttribOffset( stsh->vbo_vert, shaders.font.vertex, 0, 2,
388 GL_SHORT, 0 );
389 gl_vboActivateAttribOffset( stsh->vbo_tex, shaders.font.tex_coord, 0, 2,
390 GL_FLOAT, 0 );
391
392 return 0;
393}
394
399{
400 font_lastCol = NULL;
401}
402
407{
408 if ( font_lastCol != NULL )
410}
411
417{
418 memset( restore, 0, sizeof( glFontRestore ) );
419}
420
425void gl_printRestore( const glFontRestore *restore )
426{
427 if ( restore->col != NULL ) {
428 font_lastCol = restore->col;
430 }
431}
432
440void gl_printStoreMax( glFontRestore *restore, const char *text, int max )
441{
442 const glColour *col = restore->col; /* Use whatever is there. */
443 for ( int i = 0; ( text[i] != '\0' ) && ( i <= max ); i++ ) {
444 /* Only want escape sequences. */
445 if ( text[i] != FONT_COLOUR_CODE )
446 continue;
447
448 /* Get colour. */
449 if ( ( i + 1 <= max ) && ( text[i + 1] != '\0' ) ) {
450 col = gl_fontGetColour( text[i + 1] );
451 i += 1;
452 }
453 }
454
455 restore->col = col;
456}
457
463void gl_printStore( glFontRestore *restore, const char *text )
464{
465 gl_printStoreMax( restore, text, INT_MAX );
466}
467
477static size_t font_limitSize( glFontStash *stsh, int *width, const char *text,
478 const int max )
479{
480 GLfloat n;
481 size_t i;
482 uint32_t ch;
483
484 /* Avoid segfaults. */
485 if ( ( text == NULL ) || ( text[0] == '\0' ) )
486 return 0;
487
488 /* limit size */
490 i = 0;
491 n = 0.;
492 while ( ( ch = u8_nextchar( text, &i ) ) ) {
493 int adv_x;
494
495 /* Ignore escape sequence. */
496 if ( ch == FONT_COLOUR_CODE ) {
497 if ( text[i] != '\0' )
498 i++;
499 continue;
500 }
501
502 /* Count length. */
503 glFontGlyph *glyph = gl_fontGetGlyph( stsh, ch );
504 adv_x = gl_fontKernGlyph( stsh, ch, glyph ) + glyph->adv_x;
505
506 /* See if enough room. */
507 n += adv_x;
508 if ( (int)round( n ) > max ) {
509 u8_dec( text, &i );
510 n -= adv_x; /* actual size */
511 break;
512 }
513 }
514
515 if ( width != NULL )
516 ( *width ) = (int)round( n );
517 return i;
518}
519
529 const char *text, int width )
530{
531 memset( iter, 0, sizeof( glPrintLineIterator ) );
532 iter->text = text;
533 iter->ft_font = ( ft_font == NULL ? &gl_defFont : ft_font );
534 iter->width = width;
535}
536
537typedef struct _linepos_t_ {
538 size_t i;
539 uint32_t ch;
540 GLfloat
542} _linepos_t;
543
551{
552 glFontStash *stsh = gl_fontGetStash( iter->ft_font );
553 int brk, can_break, can_fit, any_char_fit = 0, any_word_fit;
554 size_t char_end = iter->l_next;
555 struct LineBreakContext lbc;
556
557 if ( iter->dead )
558 return 0;
559
560 /* limit size per line */
562
563 /* Initialize line break stuff. */
564 iter->l_begin = iter->l_next;
565 iter->l_end = iter->l_begin;
566 _linepos_t pos = { .i = char_end, .w = 0. };
567 pos.ch = font_nextChar( iter->text, &char_end );
568 lb_init_break_context( &lbc, pos.ch, gettext_getLanguage() );
569
570 while ( pos.ch != '\0' ) {
571 glFontGlyph *glyph = gl_fontGetGlyph( stsh, pos.ch );
572 GLfloat glyph_w =
573 glyph == NULL ? 0
574 : gl_fontKernGlyph( stsh, pos.ch, glyph ) + glyph->adv_x;
575 _linepos_t nextpos = { .i = char_end, .w = pos.w + glyph_w };
576 nextpos.ch = font_nextChar( iter->text, &char_end );
577 brk = lb_process_next_char( &lbc, nextpos.ch );
578 can_break = ( brk == LINEBREAK_ALLOWBREAK && !iter->no_soft_breaks ) ||
579 brk == LINEBREAK_MUSTBREAK;
580 can_fit = ( iter->width >= (int)round( nextpos.w ) );
581 any_word_fit = ( iter->l_end != iter->l_begin );
582 /* Emergency situations: */
583 can_break |= !can_fit && !any_word_fit;
584 can_fit |= !any_char_fit;
585
586 if ( can_break && iswspace( pos.ch ) ) {
587 iter->l_width = (int)round( pos.w );
588 /* IMPORTANT: when eating a space, we can't backtrack to a previous
589 * position, because there might be a skipped font markup sequence in
590 * between. */
591 iter->l_end = iter->l_next = nextpos.i;
592 u8_dec( iter->text, &iter->l_end );
593 } else if ( can_break && can_fit ) {
594 iter->l_width = (int)round( nextpos.w );
595 iter->l_end = iter->l_next = nextpos.i;
596 } else if ( !can_fit && !any_word_fit ) {
597 iter->l_width = (int)round( pos.w );
598 iter->l_end = iter->l_next = pos.i;
599 }
600
601 if ( !can_fit || brk == LINEBREAK_MUSTBREAK )
602 return 1;
603
604 any_char_fit = 1;
605 pos = nextpos;
606 }
607
608 /* Ran out of text. */
609 iter->l_width = (int)round( pos.w );
610 iter->l_end = iter->l_next = char_end;
611 iter->dead = 1;
612 return 1;
613}
614
619static uint32_t font_nextChar( const char *s, size_t *i )
620{
621 uint32_t ch = s[*i]; /* To be corrected: the character starting at byte *i.
622 Whether it's zero or not is already correct. */
623 while ( ch != 0 ) {
624 ch = u8_nextchar( s, i );
625 if ( ch != FONT_COLOUR_CODE )
626 return ch;
627 ch = u8_nextchar( s, i ); /* Skip the operand and try again. */
628 if ( ch == FONT_COLOUR_CODE )
629 return ch; /* Doubled escape char represents the escape char itself. */
630 }
631 return 0;
632}
633
646void gl_printRaw( const glFont *ft_font, double x, double y, const glColour *c,
647 double outlineR, const char *text )
648{
649 int s;
650 size_t i;
651 uint32_t ch;
652 NTracingZone( _ctx, 1 );
653
654 if ( ft_font == NULL )
655 ft_font = &gl_defFont;
656 glFontStash *stsh = gl_fontGetStash( ft_font );
657
658 /* Render it. */
659 s = 0;
660 i = 0;
661 gl_fontRenderStart( stsh, x, y, c, outlineR );
662 while ( ( ch = u8_nextchar( text, &i ) ) )
663 s = gl_fontRenderGlyph( stsh, ch, c, s );
665
666 NTracingZoneEnd( _ctx );
667}
668
680void gl_printRawH( const glFont *ft_font, const mat4 *H, const glColour *c,
681 const double outlineR, const char *text )
682{
683 int s;
684 size_t i;
685 uint32_t ch;
686 NTracingZone( _ctx, 1 );
687
688 if ( ft_font == NULL )
689 ft_font = &gl_defFont;
690 glFontStash *stsh = gl_fontGetStash( ft_font );
691
692 /* Render it. */
693 s = 0;
694 i = 0;
695 gl_fontRenderStartH( stsh, H, c, outlineR );
696 while ( ( ch = u8_nextchar( text, &i ) ) )
697 s = gl_fontRenderGlyph( stsh, ch, c, s );
699
700 NTracingZoneEnd( _ctx );
701}
702
708void gl_printMarkerRaw( const glFont *ft_font, double x, double y,
709 const glColour *c, const char *text )
710{
711 gl_printRaw( ft_font, x, y, c, 1, text );
712}
713
725void gl_print( const glFont *ft_font, const double x, const double y,
726 const glColour *c, const char *fmt, ... )
727{
728 if ( fmt == NULL )
729 return;
730
731 char text[STRMAX_SHORT]; /* holds the string */
732 va_list ap;
733 va_start( ap, fmt );
734 vsnprintf( text, sizeof( text ), fmt, ap );
735 va_end( ap );
736
737 gl_printRaw( ft_font, x, y, c, -1., text );
738}
739
753int gl_printMaxRaw( const glFont *ft_font, const int max, double x, double y,
754 const glColour *c, double outlineR, const char *text )
755{
756 int s;
757 size_t ret, i;
758 uint32_t ch;
759 NTracingZone( _ctx, 1 );
760
761 if ( ft_font == NULL )
762 ft_font = &gl_defFont;
763 glFontStash *stsh = gl_fontGetStash( ft_font );
764
765 /* Limit size. */
766 ret = font_limitSize( stsh, NULL, text, max );
767
768 /* Render it. */
769 s = 0;
770 gl_fontRenderStart( stsh, x, y, c, outlineR );
771 i = 0;
772 while ( ( ch = u8_nextchar( text, &i ) ) && ( i <= ret ) )
773 s = gl_fontRenderGlyph( stsh, ch, c, s );
775
776 NTracingZoneEnd( _ctx );
777 return ret;
778}
779
792int gl_printMax( const glFont *ft_font, const int max, double x, double y,
793 const glColour *c, const char *fmt, ... )
794{
795 if ( fmt == NULL )
796 return -1;
797
798 char text[STRMAX_SHORT]; /* holds the string */
799 va_list ap;
800 va_start( ap, fmt );
801 vsnprintf( text, sizeof( text ), fmt, ap );
802 va_end( ap );
803
804 return gl_printMaxRaw( ft_font, max, x, y, c, -1., text );
805}
806
821int gl_printMidRaw( const glFont *ft_font, int width, double x, double y,
822 const glColour *c, double outlineR, const char *text )
823{
824 int n, s;
825 size_t ret, i;
826 uint32_t ch;
827 NTracingZone( _ctx, 1 );
828
829 if ( ft_font == NULL )
830 ft_font = &gl_defFont;
831 glFontStash *stsh = gl_fontGetStash( ft_font );
832
833 /* limit size */
834 n = 0;
835 ret = font_limitSize( stsh, &n, text, width );
836 x += (double)( width - n ) / 2.;
837
838 /* Render it. */
839 s = 0;
840 gl_fontRenderStart( stsh, x, y, c, outlineR );
841 i = 0;
842 while ( ( ch = u8_nextchar( text, &i ) ) && ( i <= ret ) )
843 s = gl_fontRenderGlyph( stsh, ch, c, s );
845
846 NTracingZoneEnd( _ctx );
847 return ret;
848}
849
863int gl_printMid( const glFont *ft_font, const int width, double x, double y,
864 const glColour *c, const char *fmt, ... )
865{
866 if ( fmt == NULL )
867 return -1;
868
869 char text[STRMAX_SHORT]; /* holds the string */
870 va_list ap;
871 va_start( ap, fmt );
872 vsnprintf( text, sizeof( text ), fmt, ap );
873 va_end( ap );
874
875 return gl_printMidRaw( ft_font, width, x, y, c, -1., text );
876}
877
895int gl_printTextRaw( const glFont *ft_font, const int width, const int height,
896 double bx, double by, int line_height, const glColour *c,
897 double outlineR, const char *text )
898{
900 int s;
901 double x, y;
902 uint32_t ch;
903 NTracingZone( _ctx, 1 );
904
905 if ( ft_font == NULL )
906 ft_font = &gl_defFont;
907 glFontStash *stsh = gl_fontGetStash( ft_font );
908
909 x = bx;
910 y = by + height - (double)ft_font->h; /* y is top left corner */
911
912 /* Default to 1.5 line height. */
913 if ( line_height == 0 )
914 line_height = 1.5 * (double)ft_font->h;
915
916 /* Clears restoration. */
918
919 s = 0;
920 gl_printLineIteratorInit( &iter, ft_font, text, width );
921 while ( ( y - by > -DOUBLE_TOL ) && gl_printLineIteratorNext( &iter ) ) {
922 /* Must restore stuff. */
924
925 /* Render it. */
926 gl_fontRenderStart( stsh, x, y, c, outlineR );
927 for ( size_t i = iter.l_begin; i < iter.l_end; ) {
928 ch = u8_nextchar( text, &i );
929 s = gl_fontRenderGlyph( stsh, ch, c, s );
930 }
932
933 y -= line_height; /* move position down */
934 }
935
936 NTracingZoneEnd( _ctx );
937 return 0;
938}
939
956int gl_printText( const glFont *ft_font, const int width, const int height,
957 double bx, double by, int line_height, const glColour *c,
958 const char *fmt, ... )
959{
960 char text[STRMAX]; /* holds the string */
961
962 if ( fmt == NULL )
963 return -1;
964 else { /* convert the symbols to text */
965 va_list ap;
966 va_start( ap, fmt );
967 vsnprintf( text, sizeof( text ), fmt, ap );
968 va_end( ap );
969 }
970
971 return gl_printTextRaw( ft_font, width, height, bx, by, line_height, c, -1.,
972 text );
973}
974
984int gl_printWidthRaw( const glFont *ft_font, const char *text )
985{
986 GLfloat n, nmax;
987 size_t i;
988 uint32_t ch;
989 NTracingZone( _ctx, 1 );
990
991 if ( ft_font == NULL )
992 ft_font = &gl_defFont;
993 glFontStash *stsh = gl_fontGetStash( ft_font );
994
996 nmax = n = 0.;
997 i = 0;
998 while ( ( ch = font_nextChar( text, &i ) ) ) {
999 /* Newline. */
1000 if ( ch == '\n' ) {
1002 nmax = MAX( nmax, n );
1003 n = 0.;
1004 continue;
1005 }
1006
1007 /* Increment width. */
1008 glFontGlyph *glyph = gl_fontGetGlyph( stsh, ch );
1009 n += gl_fontKernGlyph( stsh, ch, glyph ) + glyph->adv_x;
1010 }
1011 nmax = MAX( nmax, n );
1012
1013 NTracingZoneEnd( _ctx );
1014 return (int)round( nmax );
1015}
1016
1026int gl_printWidth( const glFont *ft_font, const char *fmt, ... )
1027{
1028 if ( fmt == NULL )
1029 return 0;
1030
1031 char text[STRMAX_SHORT]; /* holds the string */
1032 va_list ap;
1033 va_start( ap, fmt );
1034 vsnprintf( text, sizeof( text ), fmt, ap );
1035 va_end( ap );
1036
1037 return gl_printWidthRaw( ft_font, text );
1038}
1039
1050int gl_printHeightRaw( const glFont *ft_font, const int width,
1051 const char *text )
1052{
1054 int line_height;
1055 double y = 0.;
1056
1057 /* Check 0 length strings. */
1058 if ( text[0] == '\0' )
1059 return 0;
1060
1061 if ( ft_font == NULL )
1062 ft_font = &gl_defFont;
1063
1064 line_height = 1.5 * (double)ft_font->h;
1065 gl_printLineIteratorInit( &iter, ft_font, text, width );
1066 while ( gl_printLineIteratorNext( &iter ) )
1067 y += line_height;
1068
1069 return (int)y - line_height + ft_font->h + 1;
1070}
1071
1082int gl_printHeight( const glFont *ft_font, const int width, const char *fmt,
1083 ... )
1084{
1085 if ( fmt == NULL )
1086 return -1;
1087
1088 char text[STRMAX_SHORT]; /* holds the string */
1089 va_list ap;
1090 va_start( ap, fmt );
1091 vsnprintf( text, sizeof( text ), fmt, ap );
1092 va_end( ap );
1093
1094 return gl_printHeightRaw( ft_font, width, text );
1095}
1096
1107int gl_printEndRaw( int *xo, int *yo, const glFont *ft_font, int width,
1108 const char *text )
1109{
1111 int s;
1112 double x, y;
1113 uint32_t ch;
1114 NTracingZone( _ctx, 1 );
1115
1116 if ( ft_font == NULL )
1117 ft_font = &gl_defFont;
1118 glFontStash *stsh = gl_fontGetStash( ft_font );
1119
1120 /* Default to 1.5 line height. */
1121 int line_height = 1.5 * (double)ft_font->h;
1122 y = -line_height;
1123
1124 /* Clears restoration. */
1126
1127 x = 0;
1128 s = 0;
1129 gl_printLineIteratorInit( &iter, ft_font, text, width );
1130 while ( gl_printLineIteratorNext( &iter ) ) {
1131 /* Must restore stuff. */
1133
1134 /* Start counting. */
1135 x = 0;
1137 for ( size_t i = iter.l_begin; i < iter.l_end; ) {
1138 ch = u8_nextchar( text, &i );
1139
1140 /* Handle escape sequences. */
1141 if ( ( ch == FONT_COLOUR_CODE ) && ( s == 0 ) ) { /* Start sequence. */
1142 s = 1;
1143 continue;
1144 }
1145 if ( ( s == 1 ) && ( ch != FONT_COLOUR_CODE ) ) {
1146 font_lastCol = gl_fontGetColour( ch ); /* Need non-NULL value. */
1147 s = 0;
1148 continue;
1149 }
1150
1151 glFontGlyph *glyph = gl_fontGetGlyph( stsh, ch );
1152 int kern_adv_x = gl_fontKernGlyph( stsh, ch, glyph );
1153
1154 x += glyph->adv_x + kern_adv_x;
1155 }
1156
1157 y += line_height; /* move position down */
1158 }
1159
1160 *xo = round( x );
1161 *yo = round( MAX( y, 0 ) );
1162
1163 NTracingZoneEnd( _ctx );
1164 return 0;
1165}
1166
1177PRINTF_FORMAT( 5, 6 )
1178int gl_printEnd( int *x, int *y, const glFont *ft_font, int width,
1179 const char *fmt, ... )
1180{
1181 if ( fmt == NULL )
1182 return -1;
1183
1184 char text[STRMAX_SHORT]; /* holds the string */
1185 va_list ap;
1186 va_start( ap, fmt );
1187 vsnprintf( text, sizeof( text ), fmt, ap );
1188 va_end( ap );
1189
1190 return gl_printEndRaw( x, y, ft_font, width, text );
1191}
1192
1203int gl_printLinesRaw( const glFont *ft_font, const int width, const char *text )
1204{
1206 int n = 0;
1207
1208 /* Check 0 length strings. */
1209 if ( text[0] == '\0' )
1210 return 0;
1211
1212 if ( ft_font == NULL )
1213 ft_font = &gl_defFont;
1214
1215 gl_printLineIteratorInit( &iter, ft_font, text, width );
1216 while ( gl_printLineIteratorNext( &iter ) )
1217 n++;
1218
1219 return n;
1220}
1221
1232int gl_printLines( const glFont *ft_font, const int width, const char *fmt,
1233 ... )
1234{
1235 if ( fmt == NULL )
1236 return -1;
1237
1238 char text[STRMAX_SHORT]; /* holds the string */
1239 va_list ap;
1240 va_start( ap, fmt );
1241 vsnprintf( text, sizeof( text ), fmt, ap );
1242 va_end( ap );
1243
1244 return gl_printLinesRaw( ft_font, width, text );
1245}
1246
1247/*
1248 *
1249 * G L _ F O N T
1250 *
1251 */
1254static int font_makeChar( glFontStash *stsh, font_char_t *c, uint32_t ch )
1255{
1256 int len = array_size( stsh->ft );
1257 for ( int i = 0; i < len; i++ ) {
1258 FT_UInt glyph_index;
1259 int w, h, rw, rh, b;
1260 double vmax;
1261 FT_Bitmap bitmap;
1262 FT_GlyphSlot slot;
1263 glFontStashFreetype *ft = &stsh->ft[i];
1264
1265 /* Get glyph index. */
1266 glyph_index = FT_Get_Char_Index( ft->face, ch );
1267 /* Skip missing unless last font. */
1268 if ( glyph_index == 0 ) {
1269 if ( i < len - 1 )
1270 continue;
1271 else {
1272 WARN( _( "Font '%s' unicode character '%#x' not found in font! "
1273 "Using missing glyph." ),
1274 ft->file->name, ch );
1275 ft = &stsh->ft[0]; /* Fallback to first font. */
1276 }
1277 }
1278
1279 /* Load the glyph. */
1280 if ( FT_Load_Glyph( ft->face, glyph_index,
1281 FT_LOAD_RENDER | FT_LOAD_NO_BITMAP |
1282 FT_LOAD_TARGET_NORMAL ) ) {
1283 WARN( _( "FT_Load_Glyph failed." ) );
1284 return -1;
1285 }
1286
1287 slot = ft->face->glyph; /* Small shortcut. */
1288 bitmap = slot->bitmap; /* to simplify */
1289 if ( bitmap.pixel_mode != FT_PIXEL_MODE_GRAY )
1290 WARN( _( "Font '%s' not using FT_PIXEL_MODE_GRAY!" ), ft->file->name );
1291
1292 w = bitmap.width;
1293 h = bitmap.rows;
1294
1295 /* Store data. */
1296 c->data = NULL;
1297 c->dataf = NULL;
1298 if ( bitmap.buffer == NULL ) {
1299 /* Space characters tend to have no buffer. */
1300 b = 0;
1301 rw = w;
1302 rh = h;
1303 c->data = malloc( sizeof( GLubyte ) * w * h );
1304 memset( c->data, 0, sizeof( GLubyte ) * w * h );
1305 vmax = 1.0; /* arbitrary */
1306 } else {
1307 GLubyte *buffer;
1308 /* Create a larger image using an extra border and center glyph. */
1309 b = 1 + ( ( MAX_EFFECT_RADIUS + 1 ) * FONT_DISTANCE_FIELD_SIZE - 1 ) /
1310 stsh->h;
1311 rw = w + b * 2;
1312 rh = h + b * 2;
1313 buffer = calloc( rw * rh, sizeof( GLubyte ) );
1314 for ( int v = 0; v < h; v++ )
1315 for ( int u = 0; u < w; u++ )
1316 buffer[( b + v ) * rw + ( b + u )] = bitmap.buffer[v * w + u];
1317 /* Compute signed fdistance field with buffered glyph. */
1318 c->dataf = make_distance_mapbf( buffer, rw, rh, &vmax );
1319 free( buffer );
1320 }
1321 c->w = rw;
1322 c->h = rh;
1323 c->m = ( 2. * vmax * stsh->h ) / FONT_DISTANCE_FIELD_SIZE;
1324 c->off_x = slot->bitmap_left - b;
1325 c->off_y = slot->bitmap_top + b;
1326 c->adv_x = (GLfloat)slot->metrics.horiAdvance / 64.;
1327 c->ft_index = i;
1328
1329 return 0;
1330 }
1331 WARN( _( "Unable to load character '%#x'!" ), ch );
1332 return -1;
1333}
1334
1338static void gl_fontRenderStart( const glFontStash *stsh, double x, double y,
1339 const glColour *c, double outlineR )
1340{
1341 /* OpenGL has pixel centers at 0.5 offset. */
1342 mat4 H = gl_view_matrix;
1343 mat4_translate_xy( &H, x + 0.5 * gl_screen.wscale,
1344 y + 0.5 * gl_screen.hscale );
1345 gl_fontRenderStartH( stsh, &H, c, outlineR );
1346}
1347static void gl_fontRenderStartH( const glFontStash *stsh, const mat4 *H,
1348 const glColour *c, double outlineR )
1349{
1350 double a, scale;
1351 const glColour *col;
1352
1353 outlineR = ( outlineR == -1 ) ? 1 : MAX( outlineR, 0 );
1354
1355 /* Handle colour. */
1356 a = ( c == NULL ) ? 1. : c->a;
1357 if ( font_restoreLast )
1358 col = ( font_lastCol == NULL ) ? &cWhite : font_lastCol;
1359 else if ( c == NULL )
1360 col = &cWhite;
1361 else
1362 col = c;
1363
1364 glUseProgram( shaders.font.program );
1365 gl_uniformAColour( shaders.font.colour, col, a );
1366 if ( outlineR == 0. )
1367 gl_uniformAColour( shaders.font.outline_colour, col, 0. );
1368 else
1369 gl_uniformAColour( shaders.font.outline_colour, &cGrey10, a );
1370
1371 scale = (double)stsh->h / FONT_DISTANCE_FIELD_SIZE;
1373 mat4_scale( &font_projection_mat, scale, scale, 1 );
1374
1375 font_restoreLast = 0;
1377
1378 /* Activate the appropriate VBOs. */
1379 glEnableVertexAttribArray( shaders.font.vertex );
1380 gl_vboActivateAttribOffset( stsh->vbo_vert, shaders.font.vertex, 0, 2,
1381 GL_SHORT, 0 );
1382 glEnableVertexAttribArray( shaders.font.tex_coord );
1383 gl_vboActivateAttribOffset( stsh->vbo_tex, shaders.font.tex_coord, 0, 2,
1384 GL_FLOAT, 0 );
1385
1386 /* Depth testing is used to draw the outline under the glyph. */
1387 if ( outlineR > 0. )
1388 glEnable( GL_DEPTH_TEST );
1389}
1390
1394static const glColour *gl_fontGetColour( uint32_t ch )
1395{
1396 const glColour *col;
1397 switch ( ch ) {
1398 /* TOP SECRET COLOUR CONVENTION
1399 * FOR YOUR EYES ONLY
1400 *
1401 * Lowercase characters represent base colours.
1402 * Uppercase characters reperesent fancy game related colours.
1403 * Digits represent states.
1404 */
1405 /* Colours. */
1406 case 'r':
1407 col = &cFontRed;
1408 break;
1409 case 'g':
1410 col = &cFontGreen;
1411 break;
1412 case 'b':
1413 col = &cFontBlue;
1414 break;
1415 case 'o':
1416 col = &cFontOrange;
1417 break;
1418 case 'y':
1419 col = &cFontYellow;
1420 break;
1421 case 'w':
1422 col = &cFontWhite;
1423 break;
1424 case 'p':
1425 col = &cFontPurple;
1426 break;
1427 case 'n':
1428 col = &cFontGrey;
1429 break;
1430 /* Fancy states. */
1431 case 'F':
1432 col = &cFriend;
1433 break;
1434 case 'H':
1435 col = &cHostile;
1436 break;
1437 case 'N':
1438 col = &cNeutral;
1439 break;
1440 case 'I':
1441 col = &cInert;
1442 break;
1443 case 'R':
1444 col = &cRestricted;
1445 break;
1446 case 'C':
1447 col = &cFontGreen;
1448 break;
1449 case '0':
1450 col = NULL;
1451 break;
1452 default:
1453 WARN( "Unknown font escape code '%c'", ch );
1454 col = NULL;
1455 break;
1456 }
1457 return col;
1458}
1459
1466static uint32_t hashint( uint32_t a )
1467{
1468 a += ~( a << 15 );
1469 a ^= ( a >> 10 );
1470 a += ( a << 3 );
1471 a ^= ( a >> 6 );
1472 a += ~( a << 11 );
1473 a ^= ( a >> 16 );
1474 return a;
1475}
1476
1480static glFontGlyph *gl_fontGetGlyph( glFontStash *stsh, uint32_t ch )
1481{
1482 int i;
1483 unsigned int h;
1484
1485 /* Use hash table and linked lists to find the glyph. */
1486 h = hashint( ch ) & ( HASH_LUT_SIZE - 1 );
1487 i = stsh->lut[h];
1488 while ( i != -1 ) {
1489 if ( stsh->glyphs[i].codepoint == ch )
1490 return &stsh->glyphs[i];
1491 i = stsh->glyphs[i].next;
1492 }
1493
1494 /* Glyph not found, have to generate. */
1495 glFontGlyph *glyph;
1496 font_char_t ft_char;
1497 int idx;
1498
1499 /* Load data from freetype. */
1500 if ( font_makeChar( stsh, &ft_char, ch ) )
1501 return NULL;
1502
1503 /* Create new character. */
1504 glyph = &array_grow( &stsh->glyphs );
1505 glyph->codepoint = ch;
1506 glyph->adv_x = ft_char.adv_x;
1507 glyph->m = ft_char.m;
1508 glyph->ft_index = ft_char.ft_index;
1509 glyph->next = -1;
1510 idx = glyph - stsh->glyphs;
1511
1512 /* Insert in linked list. */
1513 i = stsh->lut[h];
1514 if ( i == -1 ) {
1515 stsh->lut[h] = idx;
1516 } else {
1517 while ( i != -1 ) {
1518 if ( stsh->glyphs[i].next == -1 ) {
1519 stsh->glyphs[i].next = idx;
1520 break;
1521 }
1522 i = stsh->glyphs[i].next;
1523 }
1524 }
1525
1526 /* Find empty texture and render char. */
1527 gl_fontAddGlyphTex( stsh, &ft_char, glyph );
1528
1529 free( ft_char.data );
1530 free( ft_char.dataf );
1531
1532 return glyph;
1533}
1534
1538static void gl_fontKernStart( void )
1539{
1540 prev_glyph_index = 0;
1541}
1542
1547static int gl_fontKernGlyph( glFontStash *stsh, uint32_t ch,
1548 glFontGlyph *glyph )
1549{
1550 FT_Face ft_face;
1551 FT_UInt ft_glyph_index;
1552 int kern_adv_x = 0;
1553
1554 ft_face = stsh->ft[glyph->ft_index].face;
1555 ft_glyph_index = FT_Get_Char_Index( ft_face, ch );
1556 if ( prev_glyph_index && prev_glyph_ft_index == glyph->ft_index ) {
1557 FT_Vector kerning;
1558 FT_Get_Kerning( ft_face, prev_glyph_index, ft_glyph_index,
1559 FT_KERNING_DEFAULT, &kerning );
1560 kern_adv_x = kerning.x / 64;
1561 }
1562 prev_glyph_index = ft_glyph_index;
1564 return kern_adv_x;
1565}
1566
1570static int gl_fontRenderGlyph( glFontStash *stsh, uint32_t ch,
1571 const glColour *c, int state )
1572{
1573 double scale;
1574 int kern_adv_x;
1575 glFontGlyph *glyph;
1576
1577 /* Handle escape sequences. */
1578 if ( ( ch == FONT_COLOUR_CODE ) && ( state == 0 ) ) { /* Start sequence. */
1579 return 1;
1580 }
1581 if ( ( state == 1 ) && ( ch != FONT_COLOUR_CODE ) ) {
1582 const glColour *col = gl_fontGetColour( ch );
1583 double a = ( c == NULL ) ? 1. : c->a;
1584 if ( col != NULL )
1585 gl_uniformAColour( shaders.font.colour, col, a );
1586 else if ( c == NULL )
1587 gl_uniformColour( shaders.font.colour, &cWhite );
1588 else
1589 gl_uniformColour( shaders.font.colour, c );
1590 font_lastCol = col;
1591 return 0;
1592 }
1593
1594 /* Unicode goes here.
1595 * First try to find the glyph. */
1596 glyph = gl_fontGetGlyph( stsh, ch );
1597 if ( glyph == NULL ) {
1598 WARN( _( "Unable to find glyph '%d'!" ), ch );
1599 return -1;
1600 }
1601
1602 /* Kern if possible. */
1603 scale = (double)stsh->h / FONT_DISTANCE_FIELD_SIZE;
1604 kern_adv_x = gl_fontKernGlyph( stsh, ch, glyph );
1605 if ( kern_adv_x )
1606 mat4_translate_x( &font_projection_mat, kern_adv_x / scale );
1607
1608 /* Activate texture. */
1609 glBindTexture( GL_TEXTURE_2D, stsh->tex[glyph->tex_index].id );
1610
1611 glUniform1f( shaders.font.m, glyph->m );
1612 gl_uniformMat4( shaders.font.projection, &font_projection_mat );
1613
1614 /* Draw the element. */
1615 glDrawArrays( GL_TRIANGLE_STRIP, glyph->vbo_id, 4 );
1616
1617 /* Translate matrix. */
1618 mat4_translate_x( &font_projection_mat, glyph->adv_x / scale );
1619
1620 return 0;
1621}
1622
1626static void gl_fontRenderEnd( void )
1627{
1628 glDisableVertexAttribArray( shaders.font.vertex );
1629 glDisableVertexAttribArray( shaders.font.tex_coord );
1630 glUseProgram( 0 );
1631
1632 glDisable( GL_DEPTH_TEST );
1633
1634 /* Check for errors. */
1635 gl_checkErr();
1636}
1637
1645void gl_fontSetFilter( const glFont *ft_font, GLint min, GLint mag )
1646{
1647 glFontStash *stsh = gl_fontGetStash( ft_font );
1648 stsh->minfilter = min;
1649 stsh->magfilter = mag;
1650
1651 for ( int i = 0; i < array_size( stsh->tex ); i++ ) {
1652 glBindTexture( GL_TEXTURE_2D, stsh->tex[i].id );
1653 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, stsh->magfilter );
1654 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, stsh->minfilter );
1655 }
1656
1657 gl_checkErr();
1658}
1659
1670int gl_fontInit( glFont *font, const char *fname, const unsigned int h,
1671 const char *prefix, unsigned int flags )
1672{
1673 size_t len, plen;
1674 glFontStash *stsh, *reusable_stsh_slot;
1675 int ch;
1676 char fullname[PATH_MAX];
1677
1678 /* Initialize FreeType. */
1679 if ( font_library == NULL ) {
1680 if ( FT_Init_FreeType( &font_library ) ) {
1681 WARN( _( "FT_Init_FreeType failed with font %s." ), fname );
1682 return -1;
1683 }
1684 }
1685
1686 /* Replace name if NULL. */
1687 if ( fname == NULL )
1688 fname = FONT_DEFAULT_PATH;
1689
1690 /* Get font stash. */
1691 if ( avail_fonts == NULL )
1693
1694 /* Check if available. */
1695 reusable_stsh_slot = NULL;
1696 if ( !( flags & FONT_FLAG_DONTREUSE ) ) {
1697 for ( int i = 0; i < array_size( avail_fonts ); i++ ) {
1698 stsh = &avail_fonts[i];
1699 if ( stsh->fname == NULL ) {
1700 /* This glFontStash must have been zeroed by gl_freeFont after its
1701 * refcount dropped to zero. */
1702 reusable_stsh_slot = stsh;
1703 continue;
1704 }
1705 if ( strcmp( stsh->fname, fname ) != 0 || stsh->h != (int)h )
1706 continue;
1707 /* Found a match! */
1708 stsh->refcount++;
1709 font->id = stsh - avail_fonts;
1710 font->h = h;
1711 return 0;
1712 }
1713 }
1714
1715 /* Create new font. */
1716 if ( reusable_stsh_slot != NULL )
1717 stsh = reusable_stsh_slot;
1718 else
1719 stsh = &array_grow( &avail_fonts );
1720 memset( stsh, 0, sizeof( glFontStash ) );
1721 stsh->refcount = 1; /* Initialize refcount. */
1722 stsh->fname = strdup( fname );
1723 font->id = stsh - avail_fonts;
1724 font->h = h;
1725
1726 /* Default stuff. */
1727 stsh->magfilter = GL_LINEAR;
1728 stsh->minfilter = GL_LINEAR;
1729 stsh->tw = DEFAULT_TEXTURE_SIZE;
1730 stsh->th = DEFAULT_TEXTURE_SIZE;
1731 stsh->h = h;
1732
1733 /* Set up font stuff for next glyphs. */
1734 stsh->ft = array_create( glFontStashFreetype );
1735 ch = 0;
1736 len = strlen( fname );
1737 plen = strlen( prefix );
1738 for ( size_t i = 0; i <= len; i++ ) {
1739 if ( ( fname[i] == '\0' ) || ( fname[i] == ',' ) ) {
1740 strncpy( fullname, prefix, sizeof( fullname ) - 1 );
1741 strncat( fullname, &fname[ch],
1742 MIN( sizeof( fullname ) - 1 - plen, i - ch ) );
1743 gl_fontstashAddFallback( stsh, fullname, h );
1744 ch = i;
1745 if ( fname[i] == ',' )
1746 ch++;
1747 }
1748 }
1749
1750 /* Initialize the unicode support. */
1751 for ( int i = 0; i < HASH_LUT_SIZE; i++ )
1752 stsh->lut[i] = -1;
1753 stsh->glyphs = array_create( glFontGlyph );
1754 stsh->tex = array_create( glFontTex );
1755
1756 /* Set up VBOs. */
1757 stsh->mvbo = 256;
1758 stsh->vbo_tex_data = calloc( 8 * stsh->mvbo, sizeof( GLfloat ) );
1759 stsh->vbo_vert_data = calloc( 8 * stsh->mvbo, sizeof( GLshort ) );
1760 stsh->vbo_tex = gl_vboCreateStatic( sizeof( GLfloat ) * 8 * stsh->mvbo,
1761 stsh->vbo_tex_data );
1762 stsh->vbo_vert = gl_vboCreateStatic( sizeof( GLshort ) * 8 * stsh->mvbo,
1763 stsh->vbo_vert_data );
1764
1765 return 0;
1766}
1767
1776int gl_fontAddFallback( glFont *font, const char *fname, const char *prefix )
1777{
1778 size_t len, plen;
1779 int ch, ret;
1780 glFontStash *stsh = gl_fontGetStash( font );
1781
1782 ret = 0;
1783 ch = 0;
1784 len = strlen( fname );
1785 plen = strlen( prefix );
1786 for ( size_t i = 0; i <= len; i++ ) {
1787 if ( ( fname[i] == '\0' ) || ( fname[i] == ',' ) ) {
1788 char fullname[PATH_MAX];
1789 strncpy( fullname, prefix, sizeof( fullname ) - 1 );
1790 strncat( fullname, &fname[ch],
1791 MIN( sizeof( fullname ) - 1 - plen, i - ch ) );
1792 ret |= gl_fontstashAddFallback( stsh, fullname, font->h );
1793 ch = i;
1794 if ( fname[i] == ',' )
1795 ch++;
1796 }
1797 }
1798
1799 return ret;
1800}
1801
1810static int gl_fontstashAddFallback( glFontStash *stsh, const char *fname,
1811 unsigned int h )
1812{
1813 glFontStashFreetype ft = { .file = NULL, .face = NULL };
1814
1815 /* Set up file data. Reference a loaded copy if we have one. */
1816 for ( int i = 0; i < array_size( avail_fonts ); i++ ) {
1817 if ( avail_fonts[i].ft == NULL )
1818 continue;
1819 for ( int j = 0; j < array_size( avail_fonts[i].ft ); j++ )
1820 if ( !strcmp( fname, avail_fonts[i].ft[j].file->name ) )
1821 ft.file = avail_fonts[i].ft[j].file;
1822 if ( ft.file != NULL ) {
1823 ft.file->refcount++;
1824 break;
1825 }
1826 }
1827
1828 if ( ft.file == NULL ) {
1829 /* Read font file. */
1830 ft.file = malloc( sizeof( glFontFile ) );
1831 ft.file->name = strdup( fname );
1832 ft.file->refcount = 1;
1833 ft.file->data = (FT_Byte *)ndata_read( fname, &ft.file->datasize );
1834 if ( ft.file->data == NULL ) {
1835 WARN( _( "Unable to read font: %s" ), fname );
1836 gl_fontstashftDestroy( &ft );
1837 return -1;
1838 }
1839 }
1840
1841 /* Object which freetype uses to store font info. */
1842 if ( FT_New_Memory_Face( font_library, ft.file->data, ft.file->datasize, 0,
1843 &ft.face ) ) {
1844 WARN( _( "FT_New_Memory_Face failed loading library from %s" ), fname );
1845 gl_fontstashftDestroy( &ft );
1846 return -1;
1847 }
1848
1849 /* Try to resize. */
1850 if ( FT_IS_SCALABLE( ft.face ) ) {
1851 FT_Matrix scale;
1852 if ( FT_Set_Char_Size( ft.face, 0, /* Same as width. */
1853 h * 64, 96, /* Create at 96 DPI */
1854 96 ) ) /* Create at 96 DPI */
1855 WARN( _( "FT_Set_Char_Size failed." ) );
1856 scale.xx = scale.yy = (FT_Fixed)FONT_DISTANCE_FIELD_SIZE * 0x10000 / h;
1857 scale.xy = scale.yx = 0;
1858 FT_Set_Transform( ft.face, &scale, NULL );
1859 } else
1860 WARN( _( "Font isn't resizable!" ) );
1861
1862 /* Select the character map. */
1863 if ( FT_Select_Charmap( ft.face, FT_ENCODING_UNICODE ) )
1864 WARN( _( "FT_Select_Charmap failed to change character mapping." ) );
1865
1866 /* Save stuff. */
1867 array_push_back( &stsh->ft, ft );
1868
1869 /* Success. */
1870 return 0;
1871}
1872
1881void gl_freeFont( glFont *font )
1882{
1883 if ( font == NULL )
1884 font = &gl_defFont;
1885 glFontStash *stsh = gl_fontGetStash( font );
1886
1887 /* Check references. */
1888 stsh->refcount--;
1889 if ( stsh->refcount > 0 )
1890 return;
1891 /* Not references and must eliminate. */
1892
1893 for ( int i = 0; i < array_size( stsh->ft ); i++ )
1894 gl_fontstashftDestroy( &stsh->ft[i] );
1895 array_free( stsh->ft );
1896
1897 free( stsh->fname );
1898 for ( int i = 0; i < array_size( stsh->tex ); i++ )
1899 glDeleteTextures( 1, &stsh->tex->id );
1900 array_free( stsh->tex );
1901
1902 array_free( stsh->glyphs );
1903 gl_vboDestroy( stsh->vbo_tex );
1904 gl_vboDestroy( stsh->vbo_vert );
1905 free( stsh->vbo_tex_data );
1906 free( stsh->vbo_vert_data );
1907
1908 memset( stsh, 0, sizeof( glFontStash ) );
1909 /* Font stash will get reused when possible, and we can't erase because IDs
1910 * will get messed up. */
1911}
1912
1917{
1918 if ( --ft->file->refcount == 0 ) {
1919 free( ft->file->name );
1920 free( ft->file->data );
1921 free( ft->file );
1922 }
1923 FT_Done_Face( ft->face );
1924}
1925
1930void gl_fontExit( void )
1931{
1932 FT_Done_FreeType( font_library );
1933 font_library = NULL;
1935 avail_fonts = NULL;
1936}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
float * make_distance_mapbf(unsigned char *img, unsigned int width, unsigned int height, double *vmax)
Perform a Euclidean Distance Transform on the input and normalize to [0,1], with a value of 0....
static uint32_t hashint(uint32_t a)
Hash function for integers.
Definition font.c:1466
static void gl_fontstashftDestroy(glFontStashFreetype *ft)
Frees resources referenced by a glFontStashFreetype struct.
Definition font.c:1916
static uint32_t font_nextChar(const char *s, size_t *i)
Reads the next utf-8 sequence out of a string, updating an index. Skips font markup directives.
Definition font.c:619
int gl_printHeightRaw(const glFont *ft_font, const int width, const char *text)
Gets the height of a non-formatted string.
Definition font.c:1050
static FT_UInt prev_glyph_index
Definition font.c:51
static void gl_fontRenderStart(const glFontStash *stsh, double x, double y, const glColour *c, double outlineR)
Starts the rendering engine.
Definition font.c:1338
int gl_printLines(const glFont *ft_font, const int width, const char *fmt,...)
Gets the number of lines of the text if it were printed.
Definition font.c:1232
void gl_printRestoreClear(void)
Clears the restoration.
Definition font.c:398
void gl_printRestoreInit(glFontRestore *restore)
Initializes a restore structure.
Definition font.c:416
#define MAX_ROWS
Definition font.c:42
int gl_printLineIteratorNext(glPrintLineIterator *iter)
Updates iter with the next line's information.
Definition font.c:550
static int gl_fontRenderGlyph(glFontStash *stsh, uint32_t ch, const glColour *c, int state)
Renders a character.
Definition font.c:1570
glFont gl_smallFont
Definition font.c:159
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition font.c:646
static int font_restoreLast
Definition font.c:165
int gl_printHeight(const glFont *ft_font, const int width, const char *fmt,...)
Gets the height of the text if it were printed.
Definition font.c:1082
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:984
static int gl_fontKernGlyph(glFontStash *stsh, uint32_t ch, glFontGlyph *glyph)
Return the signed advance (same units as adv_x) ahead of the current char.
Definition font.c:1547
static int prev_glyph_ft_index
Definition font.c:52
glFont gl_defFont
Definition font.c:158
#define DEFAULT_TEXTURE_SIZE
Definition font.c:40
int gl_printMidRaw(const glFont *ft_font, int width, double x, double y, const glColour *c, double outlineR, const char *text)
Displays text centered in position and width.
Definition font.c:821
void gl_print(const glFont *ft_font, const double x, const double y, const glColour *c, const char *fmt,...)
Prints text on screen like printf.
Definition font.c:725
int gl_fontAddFallback(glFont *font, const char *fname, const char *prefix)
Adds a fallback font to a font.
Definition font.c:1776
int gl_printTextRaw(const glFont *ft_font, const int width, const int height, double bx, double by, int line_height, const glColour *c, double outlineR, const char *text)
Prints a block of text that fits in the dimensions given.
Definition font.c:895
void gl_freeFont(glFont *font)
Frees a loaded font. Caution: its glFontStash still has a slot in avail_fonts. At the time of writing...
Definition font.c:1881
void gl_printRawH(const glFont *ft_font, const mat4 *H, const glColour *c, const double outlineR, const char *text)
Prints text on screen using a transformation matrix.
Definition font.c:680
static int gl_fontstashAddFallback(glFontStash *stsh, const char *fname, unsigned int h)
Adds a fallback font to a stash.
Definition font.c:1810
int gl_printText(const glFont *ft_font, const int width, const int height, double bx, double by, int line_height, const glColour *c, const char *fmt,...)
Prints a block of text that fits in the dimensions given.
Definition font.c:956
void gl_printRestore(const glFontRestore *restore)
Restores last colour from a restore structure.
Definition font.c:425
static glFontStash * gl_fontGetStash(const glFont *font)
Gets the font stash corresponding to a font.
Definition font.c:200
int gl_printMax(const glFont *ft_font, const int max, double x, double y, const glColour *c, const char *fmt,...)
Behaves like gl_print but stops displaying text after reaching a certain length.
Definition font.c:792
int gl_printLinesRaw(const glFont *ft_font, const int width, const char *text)
Gets the number of lines of a non-formatted string.
Definition font.c:1203
void gl_printLineIteratorInit(glPrintLineIterator *iter, const glFont *ft_font, const char *text, int width)
Initialize an iterator object for breaking text into lines.
Definition font.c:528
int gl_printMaxRaw(const glFont *ft_font, const int max, double x, double y, const glColour *c, double outlineR, const char *text)
Behaves like gl_printRaw but stops displaying text after a certain distance.
Definition font.c:753
static FT_Library font_library
Definition font.c:49
#define FONT_DISTANCE_FIELD_SIZE
Definition font.c:38
static glFontGlyph * gl_fontGetGlyph(glFontStash *stsh, uint32_t ch)
Gets or caches a glyph to render.
Definition font.c:1480
int gl_fontInit(glFont *font, const char *fname, const unsigned int h, const char *prefix, unsigned int flags)
Initializes a font.
Definition font.c:1670
void gl_fontExit(void)
Frees all resources associated with the font system. This also resets font ID tracking,...
Definition font.c:1930
static const glColour * font_lastCol
Definition font.c:163
static mat4 font_projection_mat
Definition font.c:48
#define HASH_LUT_SIZE
Definition font.c:39
glFont gl_defFontMono
Definition font.c:160
static size_t font_limitSize(glFontStash *stsh, int *width, const char *text, const int max)
Limits the text to max.
Definition font.c:477
static glFontStash * avail_fonts
Definition font.c:154
void gl_printStoreMax(glFontRestore *restore, const char *text, int max)
Stores the colour information from a piece of text limited to max characters.
Definition font.c:440
void gl_printStore(glFontRestore *restore, const char *text)
Stores the colour information from a piece of text.
Definition font.c:463
int gl_printMid(const glFont *ft_font, const int width, double x, double y, const glColour *c, const char *fmt,...)
Displays text centered in position and width.
Definition font.c:863
static void gl_fontRenderEnd(void)
Ends the rendering engine.
Definition font.c:1626
#define MAX_EFFECT_RADIUS
Definition font.c:36
void gl_printRestoreLast(void)
Restores last colour.
Definition font.c:406
static int gl_fontAddGlyphTex(glFontStash *stsh, font_char_t *ch, glFontGlyph *glyph)
Adds a font glyph to the texture stash.
Definition font.c:208
void gl_fontSetFilter(const glFont *ft_font, GLint min, GLint mag)
Sets the minification and magnification filters for a font.
Definition font.c:1645
void gl_printMarkerRaw(const glFont *ft_font, double x, double y, const glColour *c, const char *text)
Wrapper for gl_printRaw for map overlay markers.
Definition font.c:708
static const glColour * gl_fontGetColour(uint32_t ch)
Gets the colour from a character.
Definition font.c:1394
static void gl_fontKernStart(void)
Call at the start of a string/line.
Definition font.c:1538
int gl_printWidth(const glFont *ft_font, const char *fmt,...)
Gets the width that it would take to print some text.
Definition font.c:1026
int gl_printEndRaw(int *xo, int *yo, const glFont *ft_font, int width, const char *text)
Gets the position at which text would end writing.
Definition font.c:1107
const char * gettext_getLanguage(void)
Gets the active (primary) translation language. Even in case of a complex locale, this will be the na...
Definition gettext.c:122
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition mat4.c:113
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define MAX(x, y)
Definition naev.h:37
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
glInfo gl_screen
Definition opengl.c:47
void gl_vboDestroy(gl_vbo *vbo)
Destroys a VBO.
Definition opengl_vbo.c:244
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition opengl_vbo.c:224
void gl_vboData(gl_vbo *vbo, GLsizei size, const void *data)
Reloads new data or grows the size of the vbo.
Definition opengl_vbo.c:96
gl_vbo * gl_vboCreateStatic(GLsizei size, const void *data)
Creates a stream vbo.
Definition opengl_vbo.c:177
static const double c[]
Definition rng.c:256
uint32_t ch
Definition font.c:539
size_t i
Definition font.c:538
GLfloat w
Definition font.c:541
Stores a font character.
Definition font.c:87
GLfloat * dataf
Definition font.c:89
int w
Definition font.c:90
int tx
Definition font.c:97
int ft_index
Definition font.c:92
GLfloat adv_x
Definition font.c:95
GLubyte * data
Definition font.c:88
int tw
Definition font.c:99
int th
Definition font.c:100
int h
Definition font.c:91
int off_y
Definition font.c:94
int ty
Definition font.c:98
int off_x
Definition font.c:93
GLfloat m
Definition font.c:96
Stores a font file. May be referenced by multiple glFonts for size or fallback reasons.
Definition font.c:107
FT_Byte * data
Definition font.c:110
size_t datasize
Definition font.c:111
int refcount
Definition font.c:109
char * name
Definition font.c:108
Represents a character in the font.
Definition font.c:74
GLushort vbo_id
Definition font.c:80
GLfloat m
Definition font.c:77
uint32_t codepoint
Definition font.c:75
int tex_index
Definition font.c:79
int next
Definition font.c:81
GLfloat adv_x
Definition font.c:76
int ft_index
Definition font.c:78
Evil hack to allow restoring, yes it makes me cry myself to sleep.
Definition font.h:28
const glColour * col
Definition font.h:29
Stores the row information for a font.
Definition font.c:57
int x
Definition font.c:58
int y
Definition font.c:59
int h
Definition font.c:60
Freetype Font structure.
Definition font.c:117
glFontFile * file
Definition font.c:118
Font structure.
Definition font.c:125
GLint magfilter
Definition font.c:130
glFontGlyph * glyphs
Definition font.c:142
char * fname
Definition font.c:127
GLint minfilter
Definition font.c:131
int tw
Definition font.c:133
int refcount
Definition font.c:148
int mvbo
Definition font.c:141
int th
Definition font.c:134
int h
Definition font.c:132
gl_vbo * vbo_tex
Definition font.c:136
gl_vbo * vbo_vert
Definition font.c:137
int lut[HASH_LUT_SIZE]
Definition font.c:143
GLfloat * vbo_tex_data
Definition font.c:138
glFontTex * tex
Definition font.c:135
int nvbo
Definition font.c:140
GLshort * vbo_vert_data
Definition font.c:139
Stores a texture stash for fonts.
Definition font.c:66
GLuint id
Definition font.c:67
glFontRow rows[MAX_ROWS]
Definition font.c:68
Represents a font in memory.
Definition font.h:17
int h
Definition font.h:19
int id
Definition font.h:18
The state of a line iteration. This matches the process of rendering text into an on-screen box: An e...
Definition font.h:44
const char * text
Definition font.h:45
uint8_t no_soft_breaks
Definition font.h:54
const glFont * ft_font
Definition font.h:46
size_t l_next
Definition font.h:51
uint8_t dead
Definition font.h:53
Definition mat4.h:12