21#include "linebreakdef.h"
30#include "distance_field.h"
36#define MAX_EFFECT_RADIUS 4
37#define FONT_DISTANCE_FIELD_SIZE 55
38#define HASH_LUT_SIZE 512
39#define DEFAULT_TEXTURE_SIZE 1024
54typedef struct glFontRow_s {
63typedef struct glFontTex_s {
71typedef struct glFontGlyph_s {
84typedef struct font_char_s {
103typedef struct glFontFile_s {
113typedef struct glFontStashFreetype_s {
121typedef struct glFontStash_s {
176static void gl_fontRenderStartH(
const glFontStash* stsh,
const mat4 *H,
const glColour *
c,
double outlineR );
202 GLfloat tx, ty, txw, tyh;
204 GLshort vx, vy, vw, vh;
214 if ((r->h != 0) && (r->h != ch->
h))
218 if (r->x + ch->
w <= stsh->
tw) {
230 assert( ch->
h <= stsh->
th );
238 if (lr->
y + lr->
h + ch->
h <= stsh->
th) {
240 r->y = lr->
y + lr->
h;
256 glGenTextures( 1, &tex->
id );
257 glBindTexture( GL_TEXTURE_2D, tex->
id );
260 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, stsh->
magfilter);
261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, stsh->
minfilter);
264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
268 glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, stsh->
tw, stsh->
th, 0,
269 GL_RED, GL_UNSIGNED_BYTE, NULL );
280 glBindTexture( GL_TEXTURE_2D, tex->
id );
281 glPixelStorei(GL_UNPACK_ALIGNMENT,1);
282 if (ch->
dataf != NULL)
283 glTexSubImage2D( GL_TEXTURE_2D, 0, gr->
x, gr->
y, ch->
w, ch->
h,
284 GL_RED, GL_FLOAT, ch->
dataf );
286 glTexSubImage2D( GL_TEXTURE_2D, 0, gr->
x, gr->
y, ch->
w, ch->
h,
287 GL_RED, GL_UNSIGNED_BYTE, ch->
data );
326 fw = (GLfloat) stsh->
tw;
327 fh = (GLfloat) stsh->
th;
330 tx = (GLfloat) (gr->
x+1.) / fw;
331 ty = (GLfloat) (gr->
y+1.) / fh;
332 txw = (GLfloat) (gr->
x + ch->
w-1.) / fw;
333 tyh = (GLfloat) (gr->
y + ch->
h-1.) / fh;
335 vy = ch->
off_y - ch->
h + mx;
349 vbo_vert[ 1 ] = vy+vh;
350 vbo_vert[ 2 ] = vx+vw;
351 vbo_vert[ 3 ] = vy+vh;
354 vbo_vert[ 6 ] = vx+vw;
406 if (restore->
col != NULL) {
420 const glColour *col = restore->
col;
421 for (
int i=0; (text[i]!=
'\0') && (i<=max); i++) {
423 if (text[i] != FONT_COLOUR_CODE)
427 if ((i+1<=max) && (text[i+1]!=
'\0')) {
462 if ((text == NULL) || (text[0]==
'\0'))
469 while ((ch = u8_nextchar( text, &i ))) {
473 if (ch == FONT_COLOUR_CODE) {
485 if ((
int)round(n) > max) {
493 (*width) = (int)round(n);
513typedef struct _linepos_t_ {
528 int brk, can_break, can_fit, any_char_fit = 0, any_word_fit;
529 size_t char_end = iter->
l_next;
530 struct LineBreakContext lbc;
539 iter->l_begin = iter->
l_next;
540 iter->
l_end = iter->l_begin;
545 while (pos.
ch !=
'\0') {
548 _linepos_t nextpos = { .
i = char_end, .w = pos.
w + glyph_w };
550 brk = lb_process_next_char( &lbc, nextpos.
ch );
551 can_break = (brk == LINEBREAK_ALLOWBREAK && !iter->
no_soft_breaks) || brk == LINEBREAK_MUSTBREAK;
552 can_fit = (iter->
width >= (int)round(nextpos.
w));
553 any_word_fit = (iter->
l_end != iter->l_begin);
555 can_break |= !can_fit && !any_word_fit;
556 can_fit |= !any_char_fit;
558 if (can_break && iswspace( pos.
ch )) {
564 else if (can_break && can_fit) {
565 iter->
l_width = (int)round(nextpos.
w);
568 else if (!can_fit && !any_word_fit) {
573 if (!can_fit || brk == LINEBREAK_MUSTBREAK)
594 ch = u8_nextchar(s, i);
595 if (ch != FONT_COLOUR_CODE)
597 ch = u8_nextchar(s, i);
598 if (ch == FONT_COLOUR_CODE)
617 double outlineR,
const char *text )
631 while ((ch = u8_nextchar( text, &i )))
648 const glColour*
c,
const double outlineR ,
const char *text )
661 gl_fontRenderStartH( stsh, H,
c, outlineR );
662 while ((ch = u8_nextchar( text, &i )))
674 const glColour*
c,
const char *text)
691 const double x,
const double y,
692 const glColour*
c,
const char *fmt, ... )
694 char text[STRMAX_SHORT];
701 vsnprintf(text,
sizeof(text), fmt, ap);
721 const glColour*
c,
double outlineR,
const char *text)
738 while ((ch = u8_nextchar( text, &i )) && (i <= ret))
757 const glColour*
c,
const char *fmt, ... )
759 char text[STRMAX_SHORT];
766 vsnprintf(text,
sizeof(text), fmt, ap);
808 x += (double)(width - n)/2.;
814 while ((ch = u8_nextchar( text, &i )) && (i <= ret))
836 const glColour*
c,
const char *fmt, ... )
838 char text[STRMAX_SHORT];
845 vsnprintf(text,
sizeof(text), fmt, ap);
870 const int width,
const int height,
871 double bx,
double by,
int line_height,
887 y = by + height - (double)ft_font->
h;
890 if (line_height == 0)
891 line_height = 1.5*(double)ft_font->
h;
904 for (
size_t i = iter.l_begin; i < iter.
l_end; ) {
905 ch = u8_nextchar( text, &i );
933 const int width,
const int height,
934 double bx,
double by,
int line_height,
935 const glColour*
c,
const char *fmt, ... )
944 vsnprintf(text,
sizeof(text), fmt, ap);
948 return gl_printTextRaw( ft_font, width, height, bx, by, line_height,
c, -1., text );
977 nmax =
MAX( nmax, n );
986 nmax =
MAX( nmax, n );
988 return (
int)round(nmax);
1002 char text[STRMAX_SHORT];
1009 vsnprintf(text,
sizeof(text), fmt, ap);
1027 const int width,
const char *text )
1034 if (text[0] ==
'\0')
1037 if (ft_font == NULL)
1040 line_height = 1.5*(double)ft_font->
h;
1045 return (
int)y - line_height + ft_font->
h + 1;
1059 const int width,
const char *fmt, ... )
1061 char text[STRMAX_SHORT];
1068 vsnprintf(text,
sizeof(text), fmt, ap);
1086 const int width,
const char *text )
1092 if (text[0] ==
'\0')
1095 if (ft_font == NULL)
1116 const int width,
const char *fmt, ... )
1118 char text[STRMAX_SHORT];
1125 vsnprintf(text,
sizeof(text), fmt, ap);
1142 for (
int i=0; i<len; i++) {
1143 FT_UInt glyph_index;
1151 glyph_index = FT_Get_Char_Index( ft->
face, ch );
1153 if (glyph_index==0) {
1157 WARN(_(
"Font '%s' unicode character '%#x' not found in font! Using missing glyph."), ft->
file->
name, ch);
1163 if (FT_Load_Glyph( ft->
face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_NORMAL)) {
1164 WARN(_(
"FT_Load_Glyph failed."));
1168 slot = ft->
face->glyph;
1169 bitmap = slot->bitmap;
1170 if (bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1171 WARN(_(
"Font '%s' not using FT_PIXEL_MODE_GRAY!"), ft->
file->
name);
1179 if (bitmap.buffer == NULL) {
1184 c->data = malloc(
sizeof(GLubyte) * w*h );
1185 memset(
c->data, 0,
sizeof(GLubyte) * w*h );
1194 buffer = calloc( rw*rh,
sizeof(GLubyte) );
1195 for (
int v=0; v<h; v++)
1196 for (
int u=0; u<w; u++)
1197 buffer[ (b+v)*rw+(b+u) ] = bitmap.buffer[ v*w+u ];
1205 c->off_x = slot->bitmap_left-
b;
1206 c->off_y = slot->bitmap_top +
b;
1207 c->adv_x = (GLfloat)slot->metrics.horiAdvance / 64.;
1212 WARN(_(
"Unable to load character '%#x'!"), ch);
1222 mat4 H = gl_view_matrix;
1224 gl_fontRenderStartH( stsh, &H,
c, outlineR );
1226static void gl_fontRenderStartH(
const glFontStash* stsh,
const mat4 *H,
const glColour *
c,
double outlineR )
1229 const glColour *col;
1231 outlineR = (outlineR==-1) ? 1 :
MAX( outlineR, 0 );
1234 a = (
c==NULL) ? 1. :
c->a;
1242 glUseProgram(shaders.font.program);
1243 gl_uniformAColor(shaders.font.color, col, a);
1245 gl_uniformAColor(shaders.font.outline_color, col, 0.);
1247 gl_uniformAColor(shaders.font.outline_color, &cGrey10, a);
1257 glEnableVertexAttribArray( shaders.font.vertex );
1259 glEnableVertexAttribArray( shaders.font.tex_coord );
1264 glEnable( GL_DEPTH_TEST );
1272 const glColour *col;
1282 case 'r': col = &cFontRed;
break;
1283 case 'g': col = &cFontGreen;
break;
1284 case 'b': col = &cFontBlue;
break;
1285 case 'o': col = &cFontOrange;
break;
1286 case 'y': col = &cFontYellow;
break;
1287 case 'w': col = &cFontWhite;
break;
1288 case 'p': col = &cFontPurple;
break;
1289 case 'n': col = &cFontGrey;
break;
1291 case 'F': col = &cFriend;
break;
1292 case 'H': col = &cHostile;
break;
1293 case 'N': col = &cNeutral;
break;
1294 case 'I': col = &cInert;
break;
1295 case 'R': col = &cRestricted;
break;
1296 case 'C': col = &cFontGreen;
break;
1297 case '0': col = NULL;
break;
1299 WARN(
"Unknown font escape code '%c'", ch);
1346 if (font_makeChar( stsh, &ft_char, ch ))
1353 glyph->
m = ft_char.
m;
1356 idx = glyph - stsh->
glyphs;
1377 free(ft_char.
dataf);
1396 FT_UInt ft_glyph_index;
1400 ft_glyph_index = FT_Get_Char_Index( ft_face, ch );
1403 FT_Get_Kerning( ft_face,
prev_glyph_index, ft_glyph_index, FT_KERNING_DEFAULT, &kerning );
1404 kern_adv_x = kerning.x / 64;
1421 if ((ch == FONT_COLOUR_CODE) && (state==0)) {
1424 if ((state == 1) && (ch != FONT_COLOUR_CODE)) {
1426 double a = (
c==NULL) ? 1. :
c->a;
1428 gl_uniformAColor(shaders.font.color, col, a );
1430 gl_uniformColor(shaders.font.color, &cWhite);
1432 gl_uniformColor(shaders.font.color,
c);
1440 if (glyph == NULL) {
1441 WARN(_(
"Unable to find glyph '%d'!"), ch );
1450 kern_adv_x/scale, 0, 0 );
1456 glUniform1f(shaders.font.m, glyph->
m);
1460 glDrawArrays( GL_TRIANGLE_STRIP, glyph->
vbo_id, 4 );
1464 glyph->
adv_x/scale, 0, 0 );
1474 glDisableVertexAttribArray( shaders.font.vertex );
1475 glDisableVertexAttribArray( shaders.font.tex_coord );
1478 glDisable( GL_DEPTH_TEST );
1498 glBindTexture( GL_TEXTURE_2D, stsh->
tex[i].
id );
1499 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, stsh->
magfilter);
1500 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, stsh->
minfilter);
1516int gl_fontInit(
glFont* font,
const char *fname,
const unsigned int h,
const char *prefix,
unsigned int flags )
1526 WARN(_(
"FT_Init_FreeType failed with font %s."), fname);
1533 fname = FONT_DEFAULT_PATH;
1540 reusable_stsh_slot = NULL;
1541 if (!(flags & FONT_FLAG_DONTREUSE)) {
1544 if (stsh->
fname == NULL) {
1546 reusable_stsh_slot = stsh;
1549 if (strcmp(stsh->
fname,fname)!=0 || stsh->
h != (int)h)
1560 if (reusable_stsh_slot != NULL)
1561 stsh = reusable_stsh_slot;
1566 stsh->
fname = strdup(fname);
1580 len = strlen(fname);
1581 plen = strlen(prefix);
1582 for (
size_t i=0; i<=len; i++) {
1583 if ((fname[i]==
'\0') || (fname[i]==
',')) {
1584 strncpy( fullname, prefix,
sizeof(fullname)-1 );
1585 strncat( fullname, &fname[ch],
MIN(
sizeof(fullname)-1-plen, i-ch ) );
1625 len = strlen(fname);
1626 plen = strlen(prefix);
1627 for (
size_t i=0; i<=len; i++) {
1628 if ((fname[i]==
'\0') || (fname[i]==
',')) {
1630 strncpy( fullname, prefix,
sizeof(fullname)-1 );
1631 strncat( fullname, &fname[ch],
MIN(
sizeof(fullname)-1-plen, i-ch ) );
1659 if (!strcmp( fname,
avail_fonts[i].ft[j].file->name ))
1661 if (ft.file != NULL) {
1662 ft.file->refcount++;
1667 if (ft.
file == NULL) {
1674 WARN(_(
"Unable to read font: %s"), fname );
1682 WARN(_(
"FT_New_Memory_Face failed loading library from %s"), fname);
1688 if (FT_IS_SCALABLE(ft.
face)) {
1690 if (FT_Set_Char_Size( ft.
face,
1695 WARN(_(
"FT_Set_Char_Size failed."));
1697 scale.xy = scale.yx = 0;
1698 FT_Set_Transform( ft.
face, &scale, NULL );
1701 WARN(_(
"Font isn't resizable!"));
1704 if (FT_Select_Charmap( ft.
face, FT_ENCODING_UNICODE ))
1705 WARN(_(
"FT_Select_Charmap failed to change character mapping."));
1738 free( stsh->
fname );
1740 glDeleteTextures( 1, &stsh->
tex->
id );
1763 FT_Done_Face(ft->
face);
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
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.
static void gl_fontstashftDestroy(glFontStashFreetype *ft)
Frees resources referenced by a glFontStashFreetype struct.
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.
int gl_printHeightRaw(const glFont *ft_font, const int width, const char *text)
Gets the height of a non-formatted string.
static FT_UInt prev_glyph_index
static void gl_fontRenderStart(const glFontStash *stsh, double x, double y, const glColour *c, double outlineR)
Starts the rendering engine.
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.
void gl_printRestoreClear(void)
Clears the restoration.
void gl_printRestoreInit(glFontRestore *restore)
Initializes a restore structure.
int gl_printLineIteratorNext(glPrintLineIterator *iter)
Updates iter with the next line's information.
static int gl_fontRenderGlyph(glFontStash *stsh, uint32_t ch, const glColour *c, int state)
Renders a character.
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
static int font_restoreLast
int gl_printHeight(const glFont *ft_font, const int width, const char *fmt,...)
Gets the height of the text if it were printed.
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
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.
static int prev_glyph_ft_index
#define DEFAULT_TEXTURE_SIZE
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.
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.
int gl_fontAddFallback(glFont *font, const char *fname, const char *prefix)
Adds a fallback font to a font.
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.
void gl_freeFont(glFont *font)
Frees a loaded font. Caution: its glFontStash still has a slot in avail_fonts. At the time of writing...
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.
static int gl_fontstashAddFallback(glFontStash *stsh, const char *fname, unsigned int h)
Adds a fallback font to a stash.
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.
void gl_printRestore(const glFontRestore *restore)
Restores last colour from a restore structure.
static glFontStash * gl_fontGetStash(const glFont *font)
Gets the font stash corresponding to a font.
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.
int gl_printLinesRaw(const glFont *ft_font, const int width, const char *text)
Gets the number of lines of a non-formatted string.
void gl_printLineIteratorInit(glPrintLineIterator *iter, const glFont *ft_font, const char *text, int width)
Initialize an iterator object for breaking text into lines.
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.
static FT_Library font_library
#define FONT_DISTANCE_FIELD_SIZE
static glFontGlyph * gl_fontGetGlyph(glFontStash *stsh, uint32_t ch)
Gets or caches a glyph to render.
int gl_fontInit(glFont *font, const char *fname, const unsigned int h, const char *prefix, unsigned int flags)
Initializes a font.
void gl_fontExit(void)
Frees all resources associated with the font system. This also resets font ID tracking,...
static const glColour * font_lastCol
static mat4 font_projection_mat
static size_t font_limitSize(glFontStash *stsh, int *width, const char *text, const int max)
Limits the text to max.
static glFontStash * avail_fonts
void gl_printStoreMax(glFontRestore *restore, const char *text, int max)
Stores the colour information from a piece of text limited to max characters.
void gl_printStore(glFontRestore *restore, const char *text)
Stores the colour information from a piece of text.
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.
static void gl_fontRenderEnd(void)
Ends the rendering engine.
#define MAX_EFFECT_RADIUS
void gl_printRestoreLast(void)
Restores last colour.
static int gl_fontAddGlyphTex(glFontStash *stsh, font_char_t *ch, glFontGlyph *glyph)
Adds a font glyph to the texture stash.
void gl_fontSetFilter(const glFont *ft_font, GLint min, GLint mag)
Sets the minification and magnification filters for a font.
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.
static const glColour * gl_fontGetColour(uint32_t ch)
Gets the colour from a character.
static void gl_fontKernStart(void)
Call at the start of a string/line.
int gl_printWidth(const glFont *ft_font, const char *fmt,...)
Gets the width that it would take to print some text.
const char * gettext_getLanguage(void)
Gets the active (primary) translation language. Even in case of a complex locale, this will be the na...
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Header file with generic functions and naev-specifics.
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
void gl_vboDestroy(gl_vbo *vbo)
Destroys a VBO.
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
void gl_vboData(gl_vbo *vbo, GLsizei size, const void *data)
Reloads new data or grows the size of the vbo.
gl_vbo * gl_vboCreateStatic(GLsizei size, const void *data)
Creates a stream vbo.
Stores a font file. May be referenced by multiple glFonts for size or fallback reasons.
Represents a character in the font.
Evil hack to allow restoring, yes it makes me cry myself to sleep.
Stores the row information for a font.
Stores a texture stash for fonts.
Represents a font in memory.
The state of a line iteration. This matches the process of rendering text into an on-screen box: An e...