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 );
288 glPixelStorei(GL_UNPACK_ALIGNMENT,4);
327 fw = (GLfloat) stsh->
tw;
328 fh = (GLfloat) stsh->
th;
331 tx = (GLfloat) (gr->
x+1.) / fw;
332 ty = (GLfloat) (gr->
y+1.) / fh;
333 txw = (GLfloat) (gr->
x + ch->
w-1.) / fw;
334 tyh = (GLfloat) (gr->
y + ch->
h-1.) / fh;
336 vy = ch->
off_y - ch->
h + mx;
350 vbo_vert[ 1 ] = vy+vh;
351 vbo_vert[ 2 ] = vx+vw;
352 vbo_vert[ 3 ] = vy+vh;
355 vbo_vert[ 6 ] = vx+vw;
407 if (restore->
col != NULL) {
421 const glColour *col = restore->
col;
422 for (
int i=0; (text[i]!=
'\0') && (i<=max); i++) {
424 if (text[i] != FONT_COLOUR_CODE)
428 if ((i+1<=max) && (text[i+1]!=
'\0')) {
463 if ((text == NULL) || (text[0]==
'\0'))
470 while ((ch = u8_nextchar( text, &i ))) {
474 if (ch == FONT_COLOUR_CODE) {
486 if ((
int)round(n) > max) {
494 (*width) = (int)round(n);
514typedef struct _linepos_t_ {
529 int brk, can_break, can_fit, any_char_fit = 0, any_word_fit;
530 size_t char_end = iter->
l_next;
531 struct LineBreakContext lbc;
540 iter->l_begin = iter->
l_next;
541 iter->
l_end = iter->l_begin;
546 while (pos.
ch !=
'\0') {
549 _linepos_t nextpos = { .i = char_end, .w = pos.
w + glyph_w };
551 brk = lb_process_next_char( &lbc, nextpos.
ch );
552 can_break = (brk == LINEBREAK_ALLOWBREAK && !iter->
no_soft_breaks) || brk == LINEBREAK_MUSTBREAK;
553 can_fit = (iter->
width >= (int)round(nextpos.
w));
554 any_word_fit = (iter->
l_end != iter->l_begin);
556 can_break |= !can_fit && !any_word_fit;
557 can_fit |= !any_char_fit;
559 if (can_break && iswspace( pos.
ch )) {
565 else if (can_break && can_fit) {
566 iter->
l_width = (int)round(nextpos.
w);
569 else if (!can_fit && !any_word_fit) {
574 if (!can_fit || brk == LINEBREAK_MUSTBREAK)
595 ch = u8_nextchar(s, i);
596 if (ch != FONT_COLOUR_CODE)
598 ch = u8_nextchar(s, i);
599 if (ch == FONT_COLOUR_CODE)
618 double outlineR,
const char *text )
632 while ((ch = u8_nextchar( text, &i )))
649 const glColour*
c,
const double outlineR ,
const char *text )
662 gl_fontRenderStartH( stsh, H,
c, outlineR );
663 while ((ch = u8_nextchar( text, &i )))
675 const glColour*
c,
const char *text)
692 const double x,
const double y,
693 const glColour*
c,
const char *fmt, ... )
695 char text[STRMAX_SHORT];
702 vsnprintf(text,
sizeof(text), fmt, ap);
722 const glColour*
c,
double outlineR,
const char *text)
739 while ((ch = u8_nextchar( text, &i )) && (i <= ret))
758 const glColour*
c,
const char *fmt, ... )
760 char text[STRMAX_SHORT];
767 vsnprintf(text,
sizeof(text), fmt, ap);
809 x += (double)(width - n)/2.;
815 while ((ch = u8_nextchar( text, &i )) && (i <= ret))
837 const glColour*
c,
const char *fmt, ... )
839 char text[STRMAX_SHORT];
846 vsnprintf(text,
sizeof(text), fmt, ap);
871 const int width,
const int height,
872 double bx,
double by,
int line_height,
888 y = by + height - (double)ft_font->
h;
891 if (line_height == 0)
892 line_height = 1.5*(double)ft_font->
h;
905 for (
size_t i = iter.l_begin; i < iter.
l_end; ) {
906 ch = u8_nextchar( text, &i );
934 const int width,
const int height,
935 double bx,
double by,
int line_height,
936 const glColour*
c,
const char *fmt, ... )
945 vsnprintf(text,
sizeof(text), fmt, ap);
949 return gl_printTextRaw( ft_font, width, height, bx, by, line_height,
c, -1., text );
978 nmax =
MAX( nmax, n );
987 nmax =
MAX( nmax, n );
989 return (
int)round(nmax);
1003 char text[STRMAX_SHORT];
1010 vsnprintf(text,
sizeof(text), fmt, ap);
1028 const int width,
const char *text )
1035 if (text[0] ==
'\0')
1038 if (ft_font == NULL)
1041 line_height = 1.5*(double)ft_font->
h;
1046 return (
int)y - line_height + ft_font->
h + 1;
1060 const int width,
const char *fmt, ... )
1062 char text[STRMAX_SHORT];
1069 vsnprintf(text,
sizeof(text), fmt, ap);
1087 const int width,
const char *text )
1093 if (text[0] ==
'\0')
1096 if (ft_font == NULL)
1117 const int width,
const char *fmt, ... )
1119 char text[STRMAX_SHORT];
1126 vsnprintf(text,
sizeof(text), fmt, ap);
1143 for (
int i=0; i<len; i++) {
1144 FT_UInt glyph_index;
1152 glyph_index = FT_Get_Char_Index( ft->
face, ch );
1154 if (glyph_index==0) {
1158 WARN(_(
"Font '%s' unicode character '%#x' not found in font! Using missing glyph."), ft->
file->
name, ch);
1164 if (FT_Load_Glyph( ft->
face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_NORMAL)) {
1165 WARN(_(
"FT_Load_Glyph failed."));
1169 slot = ft->
face->glyph;
1170 bitmap = slot->bitmap;
1171 if (bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1172 WARN(_(
"Font '%s' not using FT_PIXEL_MODE_GRAY!"), ft->
file->
name);
1180 if (bitmap.buffer == NULL) {
1185 c->data = malloc(
sizeof(GLubyte) * w*h );
1186 memset(
c->data, 0,
sizeof(GLubyte) * w*h );
1195 buffer = calloc( rw*rh,
sizeof(GLubyte) );
1196 for (
int v=0; v<h; v++)
1197 for (
int u=0; u<w; u++)
1198 buffer[ (b+v)*rw+(b+u) ] = bitmap.buffer[ v*w+u ];
1206 c->off_x = slot->bitmap_left-b;
1207 c->off_y = slot->bitmap_top +b;
1208 c->adv_x = (GLfloat)slot->metrics.horiAdvance / 64.;
1213 WARN(_(
"Unable to load character '%#x'!"), ch);
1223 mat4 H = gl_view_matrix;
1225 gl_fontRenderStartH( stsh, &H,
c, outlineR );
1227static void gl_fontRenderStartH(
const glFontStash* stsh,
const mat4 *H,
const glColour *
c,
double outlineR )
1230 const glColour *col;
1232 outlineR = (outlineR==-1) ? 1 :
MAX( outlineR, 0 );
1235 a = (
c==NULL) ? 1. :
c->a;
1243 glUseProgram(shaders.font.program);
1244 gl_uniformAColour(shaders.font.colour, col, a);
1246 gl_uniformAColour(shaders.font.outline_colour, col, 0.);
1248 gl_uniformAColour(shaders.font.outline_colour, &cGrey10, a);
1258 glEnableVertexAttribArray( shaders.font.vertex );
1260 glEnableVertexAttribArray( shaders.font.tex_coord );
1265 glEnable( GL_DEPTH_TEST );
1273 const glColour *col;
1283 case 'r': col = &cFontRed;
break;
1284 case 'g': col = &cFontGreen;
break;
1285 case 'b': col = &cFontBlue;
break;
1286 case 'o': col = &cFontOrange;
break;
1287 case 'y': col = &cFontYellow;
break;
1288 case 'w': col = &cFontWhite;
break;
1289 case 'p': col = &cFontPurple;
break;
1290 case 'n': col = &cFontGrey;
break;
1292 case 'F': col = &cFriend;
break;
1293 case 'H': col = &cHostile;
break;
1294 case 'N': col = &cNeutral;
break;
1295 case 'I': col = &cInert;
break;
1296 case 'R': col = &cRestricted;
break;
1297 case 'C': col = &cFontGreen;
break;
1298 case '0': col = NULL;
break;
1300 WARN(
"Unknown font escape code '%c'", ch);
1347 if (font_makeChar( stsh, &ft_char, ch ))
1354 glyph->
m = ft_char.
m;
1357 idx = glyph - stsh->
glyphs;
1378 free(ft_char.
dataf);
1397 FT_UInt ft_glyph_index;
1401 ft_glyph_index = FT_Get_Char_Index( ft_face, ch );
1404 FT_Get_Kerning( ft_face,
prev_glyph_index, ft_glyph_index, FT_KERNING_DEFAULT, &kerning );
1405 kern_adv_x = kerning.x / 64;
1422 if ((ch == FONT_COLOUR_CODE) && (state==0)) {
1425 if ((state == 1) && (ch != FONT_COLOUR_CODE)) {
1427 double a = (
c==NULL) ? 1. :
c->a;
1429 gl_uniformAColour(shaders.font.colour, col, a );
1431 gl_uniformColour(shaders.font.colour, &cWhite);
1433 gl_uniformColour(shaders.font.colour,
c);
1441 if (glyph == NULL) {
1442 WARN(_(
"Unable to find glyph '%d'!"), ch );
1451 kern_adv_x/scale, 0, 0 );
1457 glUniform1f(shaders.font.m, glyph->
m);
1461 glDrawArrays( GL_TRIANGLE_STRIP, glyph->
vbo_id, 4 );
1465 glyph->
adv_x/scale, 0, 0 );
1475 glDisableVertexAttribArray( shaders.font.vertex );
1476 glDisableVertexAttribArray( shaders.font.tex_coord );
1479 glDisable( GL_DEPTH_TEST );
1499 glBindTexture( GL_TEXTURE_2D, stsh->
tex[i].
id );
1500 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, stsh->
magfilter);
1501 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, stsh->
minfilter);
1517int gl_fontInit(
glFont* font,
const char *fname,
const unsigned int h,
const char *prefix,
unsigned int flags )
1527 WARN(_(
"FT_Init_FreeType failed with font %s."), fname);
1534 fname = FONT_DEFAULT_PATH;
1541 reusable_stsh_slot = NULL;
1542 if (!(flags & FONT_FLAG_DONTREUSE)) {
1545 if (stsh->
fname == NULL) {
1547 reusable_stsh_slot = stsh;
1550 if (strcmp(stsh->
fname,fname)!=0 || stsh->
h != (
int)h)
1561 if (reusable_stsh_slot != NULL)
1562 stsh = reusable_stsh_slot;
1567 stsh->
fname = strdup(fname);
1581 len = strlen(fname);
1582 plen = strlen(prefix);
1583 for (
size_t i=0; i<=len; i++) {
1584 if ((fname[i]==
'\0') || (fname[i]==
',')) {
1585 strncpy( fullname, prefix,
sizeof(fullname)-1 );
1586 strncat( fullname, &fname[ch],
MIN(
sizeof(fullname)-1-plen, i-ch ) );
1626 len = strlen(fname);
1627 plen = strlen(prefix);
1628 for (
size_t i=0; i<=len; i++) {
1629 if ((fname[i]==
'\0') || (fname[i]==
',')) {
1631 strncpy( fullname, prefix,
sizeof(fullname)-1 );
1632 strncat( fullname, &fname[ch],
MIN(
sizeof(fullname)-1-plen, i-ch ) );
1660 if (!strcmp( fname,
avail_fonts[i].ft[j].file->name ))
1662 if (ft.file != NULL) {
1663 ft.file->refcount++;
1668 if (ft.
file == NULL) {
1675 WARN(_(
"Unable to read font: %s"), fname );
1683 WARN(_(
"FT_New_Memory_Face failed loading library from %s"), fname);
1689 if (FT_IS_SCALABLE(ft.
face)) {
1691 if (FT_Set_Char_Size( ft.
face,
1696 WARN(_(
"FT_Set_Char_Size failed."));
1698 scale.xy = scale.yx = 0;
1699 FT_Set_Transform( ft.
face, &scale, NULL );
1702 WARN(_(
"Font isn't resizable!"));
1705 if (FT_Select_Charmap( ft.
face, FT_ENCODING_UNICODE ))
1706 WARN(_(
"FT_Select_Charmap failed to change character mapping."));
1739 free( stsh->
fname );
1741 glDeleteTextures( 1, &stsh->
tex->
id );
1764 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...