#include "gl_2d_font.h" #include "gl.h" FT_Library libft = NULL; THREAD_MUTEX font_mutex; void freetype_init() { U32 err; if( !libft ) { err = FT_Init_FreeType( &libft ); if( err ) dlog( "gl_font_create() : FT_Init_FreeType() failed\n" ); } } void gl_font_create_bitmap( GL_FONT* font ) { U32 char_width = (font->face->size->metrics.max_advance >> 6) + 1; U32 char_height = (font->face->size->metrics.height >> 6) + 1; U32 max_width = char_width * 16; U32 max_height = char_height * 16; if( !max_width || !max_height ) { dlog( "gl_font_create() : invalid font size\n" ); return; } font->bitmap = (U32*)malloc( sizeof(U32) * max_width * max_height ); memset( font->bitmap, 0, sizeof(U32) * max_width * max_height ); font->bitmap_width = max_width; font->bitmap_height = max_height; font->char_width = char_width; font->char_height = char_height; U32 cur_x = 0, cur_y = 1; for( I32 c = 0; c < GL_FONT_MAX_GLYPHS; ++c ) { U32 err = FT_Load_Char( font->face, c, FT_LOAD_RENDER ); if( !err ) { FT_Bitmap* bitmap = &font->face->glyph->bitmap; for( U32 y = 0; y < bitmap->rows; ++y ) { for( U32 x = 0; x < bitmap->width; ++x ) { U32 dest_x = x + cur_x; U32 dest_y = y + cur_y; U8 pixel = bitmap->buffer[x + bitmap->width * y]; U32 out_pixel = 0x00FFFFFF; out_pixel |= ( pixel << 24 ); font->bitmap[dest_x + max_width * dest_y] = out_pixel; } } font->glyphs[c].offset_x = (F32)cur_x; font->glyphs[c].offset_y = (F32)cur_y; font->glyphs[c].width = bitmap->width; font->glyphs[c].height = bitmap->rows; font->glyphs[c].advance_x = (F32)(font->face->glyph->metrics.horiAdvance >> 6); font->glyphs[c].advance_y = (F32)(char_height - (font->face->glyph->metrics.horiBearingY >> 6)); font->glyphs[c].bearing = (F32)(font->face->glyph->metrics.horiBearingX >> 6); } else { dlog( "gl_font_create() : FT_Load_Char() failed (%s: %c) (%x)\n", font->name, c, err ); } if( cur_x + char_width >= max_width ) { cur_x = 0; cur_y += char_height; } else { cur_x += char_width; } } } void gl_font_create_atlas( GL_DATA* gl, GL_FONT* font ) { if( !font->bitmap ) { dlog( "gl_font_create_atlas() : no bitmap\n" ); return; } char texture_name[256]; sprintf( texture_name, "%s_%d_atlas", font->name, font->size ); font->atlas = gl_texture_from_bitmap( gl, texture_name, (U8*)font->bitmap, font->bitmap_width, font->bitmap_height ); free( font->bitmap ); } GL_FONT* gl_font_create( GL_DATA* gl, const char* path, I32 size ) { freetype_init(); if( size <= 2 ) { dlog( "gl_font_create() : size too small.\n" ); return 0; } char full_path[256]; GL_FONT* font = (GL_FONT*)malloc( sizeof( GL_FONT ) ); font->size = size; font->name = path; sprintf( full_path, "../assets/fonts/%s", path ); U32 err = FT_New_Face( libft, full_path, 0, &font->face ); if( err ) { dlog( "gl_font_create() : FT_New_Face() failed (%x)\n", err ); free( font ); return 0; } err = FT_Set_Pixel_Sizes( font->face, 0, size ); if( err ) { dlog( "gl_font_create() : FT_Set_Char_Size() failed\n" ); free( font ); return 0; } gl_font_create_bitmap( font ); FT_Done_Face( font->face ); gl_font_create_atlas( gl, font ); gl->fonts.push( font ); return font; } void gl_font_destroy( GL_DATA* gl, GL_FONT* font ) { gl_texture_destroy( gl, font->atlas ); I32 idx = gl->fonts.idx_of( font ); if( idx != -1 ) gl->fonts.erase( idx ); free( font ); } void gl_font_calc_vertices_uvs( GL_FONT* font, VEC2 origin, const char* text, F32 _scale, VERTEX* vertices, U16* indices, VEC2* coords, CLR clr ) { U32 len = (U32)strlen( text ); F32 cur_x = origin.x; F32 cur_y = origin.y; for( U32 i = 0; i < len; ++i ) { VERTEX* v = &vertices[i * 6]; U16* idx = &indices[i * 6]; if( text[i] == '\n' ) { v[0] = v[1] = v[2] = v[3] = v[4] = v[5] = { { cur_x, cur_y }, {} }; idx[0] = idx[1] = idx[2] = idx[3] = idx[4] = idx[5] = i * 6; cur_x = origin.x; cur_y += (F32)font->char_height * _scale; continue; } U32 c = (U8)text[i]; FONT_GLYPH* glyph = &font->glyphs[c]; F32 final_y = cur_y + glyph->advance_y * _scale; F32 final_x = cur_x + (F32)glyph->bearing * _scale; v[0].pos = { final_x, final_y }; v[1].pos = { final_x + glyph->width * _scale, final_y }; v[2].pos = { final_x, final_y + glyph->height * _scale }; v[3].pos = { final_x + glyph->width * _scale, final_y + glyph->height * _scale }; v[4].pos = { final_x, final_y + glyph->height * _scale }; v[5].pos = { final_x + glyph->width * _scale, final_y }; idx[0] = i * 6; idx[1] = i * 6 + 1; idx[2] = i * 6 + 2; idx[3] = i * 6 + 3; idx[4] = i * 6 + 4; idx[5] = i * 6 + 5; v[0].uv = { glyph->offset_x / (F32)font->bitmap_width, glyph->offset_y / font->bitmap_height }; v[1].uv = { (glyph->offset_x + glyph->width) / (F32)font->bitmap_width, glyph->offset_y / font->bitmap_height }; v[2].uv = { glyph->offset_x / (F32)font->bitmap_width, (glyph->offset_y + glyph->height) / font->bitmap_height }; v[3].uv = { (glyph->offset_x + glyph->width) / (F32)font->bitmap_width, (glyph->offset_y + glyph->height) / font->bitmap_height }; v[4].uv = { glyph->offset_x / (F32)font->bitmap_width, (glyph->offset_y + glyph->height) / font->bitmap_height }; v[5].uv = { (glyph->offset_x + glyph->width) / (F32)font->bitmap_width, glyph->offset_y / font->bitmap_height }; v[0].clr = v[1].clr = v[2].clr = v[3].clr = v[4].clr = v[5].clr = clr; v[0].sampler = v[1].sampler = v[2].sampler = v[3].sampler = v[4].sampler = v[5].sampler = 0; cur_x += glyph->advance_x * _scale; } } void gl_font_draw( GL_FONT* font, GL_SHADER_PROGRAM* shader, VEC2 origin, const char* text, CLR clr, F32 _scale ) { U32 len = strlen( text ); VERTEX* vertices = (VERTEX*)malloc( sizeof(VERTEX) * 6 * len ); U16* indices = (U16*)malloc( sizeof(U16) * 6 * len ); VEC2* coords = (VEC2*)malloc( sizeof(VEC2) * 6 * len ); gl_font_calc_vertices_uvs( font, origin, text, _scale, vertices, indices, coords, clr ); glUseProgram( shader->id ); glBindBuffer( GL_ARRAY_BUFFER, shader->gl->vbuffer ); glBufferData( GL_ARRAY_BUFFER, sizeof(VERTEX) * 6 * len, vertices, GL_STATIC_DRAW ); I32 position = glGetAttribLocation( shader->id, "in_pos" ); glEnableVertexAttribArray( position ); glVertexAttribPointer( position, 2, GL_FLOAT, 0, sizeof(VERTEX), &( (VERTEX*)nullptr)->pos ); I32 color = glGetAttribLocation( shader->id, "in_clr" ); glEnableVertexAttribArray( color ); glVertexAttribPointer( color, 4, GL_FLOAT, 0, sizeof(VERTEX), &( (VERTEX*)nullptr)->clr ); I32 texcoord = glGetAttribLocation( shader->id, "in_texcoord" ); glEnableVertexAttribArray( texcoord ); glVertexAttribPointer( texcoord, 2, GL_FLOAT, 0, sizeof(VERTEX), &( (VERTEX*)nullptr)->uv ); I32 sampler = glGetAttribLocation( shader->id, "in_sampler" ); glEnableVertexAttribArray( sampler ); glVertexAttribPointer( sampler, 1, GL_UNSIGNED_BYTE, 1, sizeof(VERTEX), &( (VERTEX*)nullptr)->sampler ); glBindBuffer( GL_ARRAY_BUFFER, 0 ); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, font->atlas->id ); glDrawElements( GL_TRIANGLES, len * 6, GL_UNSIGNED_SHORT, indices ); glBindTexture( GL_TEXTURE_2D, 0 ); free( vertices ); free( indices ); free( coords ); } void gl_font_textured( GL_FONT* font, GL_SHADER_PROGRAM* shader, VEC2 origin, const char* text, GL_TEX2D* tex, CLR clr, F32 _scale ) { U32 len = strlen( text ); struct FONT_CUSTOM_VERTEX { VERTEX v; VEC2 uv2; }; FONT_CUSTOM_VERTEX* custom_vertices = (FONT_CUSTOM_VERTEX*)malloc( sizeof(FONT_CUSTOM_VERTEX) * 6 * len ); VERTEX* vertices = (VERTEX*)malloc( sizeof(VERTEX) * 6 * len ); U16* indices = (U16*)malloc( sizeof(U16) * 6 * len ); VEC2* coords = (VEC2*)malloc( sizeof(VEC2) * 6 * len ); gl_font_calc_vertices_uvs( font, origin, text, _scale, vertices, indices, coords, clr ); VEC2 dim = gl_font_dim( font, text, _scale ); for( U32 i = 0; i < len * 6; ++i ) { custom_vertices[i].v = vertices[i]; custom_vertices[i].uv2 = { (vertices[i].pos.x - origin.x) / dim.x, (vertices[i].pos.y - origin.y - vertices[0].pos.y) / dim.y }; } glUseProgram( shader->id ); glBindBuffer( GL_ARRAY_BUFFER, shader->gl->vbuffer ); glBufferData( GL_ARRAY_BUFFER, sizeof(FONT_CUSTOM_VERTEX) * 6 * len, custom_vertices, GL_STATIC_DRAW ); I32 position = glGetAttribLocation( shader->id, "in_pos" ); glEnableVertexAttribArray( position ); glVertexAttribPointer( position, 2, GL_FLOAT, 0, sizeof(FONT_CUSTOM_VERTEX), 0 ); I32 texcoord = glGetAttribLocation( shader->id, "in_texcoord" ); glEnableVertexAttribArray( texcoord ); glVertexAttribPointer( texcoord, 2, GL_FLOAT, 0, sizeof(FONT_CUSTOM_VERTEX), (void*)(sizeof(VEC2)) ); I32 texcoord2 = glGetAttribLocation( shader->id, "in_texcoord2" ); glEnableVertexAttribArray( texcoord2 ); glVertexAttribPointer( texcoord2, 2, GL_FLOAT, 0, sizeof(FONT_CUSTOM_VERTEX), (void*)(sizeof(VERTEX)) ); glBindBuffer( GL_ARRAY_BUFFER, 0 ); I32 color = glGetUniformLocation( shader->id, "in_color" ); glUniform4fv( color, 1, (F32*)&clr ); glActiveTexture( GL_TEXTURE0 ); glUniform1iv( glGetUniformLocation( shader->id, "in_sampler" ), 0, 0 ); glBindTexture( GL_TEXTURE_2D, font->atlas->id ); glDrawElements( GL_TRIANGLES, len * 6, GL_UNSIGNED_SHORT, indices ); glBindTexture( GL_TEXTURE_2D, 0 ); free( vertices ); free( indices ); free( coords ); free( custom_vertices ); } VEC2 gl_font_dim( GL_FONT* font, const char* text, F32 _scale ) { U32 len = strlen( text ); F32 cur_x = 0.f; F32 cur_y = font->char_height * _scale; F32 max_x = 0.f; for( U32 i = 0; i < len; ++i ) { if( text[i] == '\n' ) { cur_x = 0; cur_y += font->char_height * _scale; continue; } U32 c = (U8)text[i]; FONT_GLYPH* glyph = &font->glyphs[c]; cur_x += glyph->advance_x * _scale; if( cur_x > max_x ) max_x = cur_x; } return { max_x, cur_y }; } void gl_font_fit_into_box( GL_FONT* font, const char* source, char* out, F32 box_width, F32 box_height, F32 scale ) { U32 len = strlen( source ); U32 last_space = 0; F32 cur_x = 0.f; for( U32 i = 0; i < len; ++i ) { if( source[i] == ' ' ) last_space = i; if( source[i] == '\n' ) { cur_x = 0; continue; } FONT_GLYPH* glyph = &font->glyphs[(U8)source[i]]; cur_x += glyph->advance_x * scale; out[i] = source[i]; if( cur_x > box_width ) { if( last_space == 0 ) { out[i] = '\n'; cur_x = 0; } else { out[last_space] = '\n'; cur_x = 0; i = last_space; last_space = 0; } } } out[len] = '\0'; }