--- /dev/null
+#include "vg_font.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "submodules/stb/stb_image.h"
+
+void vg_build_font_face_run( vg_font_face *face,
+ char first, char last, i16 x, i16 y )
+{
+ u32 count = (u32)last - (u32)first;
+ for( u32 i=0; i<=count; i ++ )
+ {
+ u32 index = (u32)first + i;
+ face->map[index].x = x+(i*face->cw);
+ face->map[index].y = y;
+ }
+}
+
+void vg_build_write_font_face( FILE *fp, vg_font_face *face )
+{
+ fprintf( fp, "vg_font_face %s = {\n", face->name );
+ fprintf( fp, " .name=\"%s\",\n", face->name );
+ fprintf( fp, " .cw=%hd,.ch=%hd,\n", face->cw,face->ch );
+ fprintf( fp, " .sx=%hd,.sy=%hd,\n", face->sx,face->sy );
+ fprintf( fp, " .baseline=%hd,\n", face->baseline );
+ fprintf( fp, " .map={\n" );
+
+ u32 chars = 0;
+
+ for( u32 i=0; i<256; i ++ )
+ {
+ if( face->map[i].x || face->map[i].y )
+ {
+ chars += fprintf( fp, "[%u]={%hd,%hd},",
+ i, face->map[i].x, face->map[i].y );
+
+ if( chars > 80 )
+ {
+ fprintf( fp, "\n" );
+ chars = 0;
+ }
+ }
+ }
+
+ fprintf( fp, "\n}};\n\n" );
+}
+
+void vg_build_font_sheet( FILE *fp, char *name, const char *source )
+{
+ int x,y,n;
+ unsigned char *data = stbi_load( source, &x, &y, &n, 4 );
+
+ if( !data )
+ {
+ vg_error( "Couldn't open source file\n" );
+ return;
+ }
+
+ fprintf( fp, "vg_font_sheet %s = {\n", name );
+ fprintf( fp, " .w=%d, .h=%d,\n", x,y );
+ fprintf( fp, " .bitmap={\n" );
+
+ u32 pixel_max = x*y;
+ u32 pixel = 0, chars = 0;
+ while( pixel_max )
+ {
+ u32 buff = 0;
+ for( int b = 31; b >= 0; b-- )
+ {
+ buff |= data[pixel*4]>128?0x1<<b:0;
+ pixel++;
+
+ if( pixel >= pixel_max )
+ {
+ pixel_max = 0;
+ break;
+ }
+ }
+
+ chars += fprintf( fp, "%#x,", buff );
+ if( chars > 80 )
+ {
+ fprintf( fp, "\n" );
+ chars = 0;
+ }
+ }
+
+ fprintf( fp, "\n}};\n" );
+ free( data );
+}
+
+void vg_build_default_font(void)
+{
+ FILE *fp = fopen( "vg/vg_default_font.gc", "w" );
+ vg_build_font_sheet( fp, "vg_default_font_sheet",
+ "vg/src/fonts/vg_font_thin_3.png" );
+ vg_font_face small =
+ {
+ .name = "vgf_default_small",
+ .cw=8, .ch=14,
+ .sx=8, .sy=14,
+ .baseline = 4
+ };
+ vg_build_font_face_run( &small, '!', '/', 8, 0 );
+ vg_build_font_face_run( &small, '[', '`', 128,0 );
+ vg_build_font_face_run( &small, '{', '~', 176,0 );
+ vg_build_font_face_run( &small, ':', '@', 208,0 );
+ vg_build_font_face_run( &small, 'A', 'Z', 0, 14 );
+ vg_build_font_face_run( &small, 'a', 'z', 0, 28 );
+ vg_build_font_face_run( &small, '0', '9', 208,14 );
+ vg_build_write_font_face( fp, &small );
+
+ vg_font_face large =
+ {
+ .name = "vgf_default_large",
+ .cw=12, .ch=21,
+ .sx=12, .sy=21,
+ .baseline=6,
+ };
+ vg_build_font_face_run( &large, '!', '/', 12, 56 );
+ vg_build_font_face_run( &large, '[', '`', 192,56 );
+ vg_build_font_face_run( &large, '{', '~', 264,56 );
+ vg_build_font_face_run( &large, ':', '@', 324,56 );
+ vg_build_font_face_run( &large, 'A', 'Z', 0, 77 );
+ vg_build_font_face_run( &large, 'a', 'z', 0, 98 );
+ vg_build_font_face_run( &large, '0', '9', 312,77 );
+ vg_build_write_font_face( fp, &large );
+
+ vg_font_face title =
+ {
+ .name = "vgf_default_title",
+ .cw=24, .ch=42,
+ .sx=24, .sy=42,
+ .baseline=12,
+ };
+ vg_build_font_face_run( &title, '!', '/', 24, 140 );
+ vg_build_font_face_run( &title, '[', '_', 384,140 );
+ vg_build_font_face_run( &title, '`', '`', 0, 182 );
+ vg_build_font_face_run( &title, '{', '~', 24, 182 );
+ vg_build_font_face_run( &title, ':', '@', 120,182 );
+ vg_build_font_face_run( &title, 'A', 'U', 0, 224 );
+ vg_build_font_face_run( &title, 'V', 'Z', 0, 308 );
+ vg_build_font_face_run( &title, 'a', 'u', 0, 266 );
+ vg_build_font_face_run( &title, 'v', 'z', 0, 350 );
+ vg_build_font_face_run( &title, '0', '9', 120,308 );
+ vg_build_write_font_face( fp, &title );
+
+ fclose( fp );
+}
k_ui_scale = 1,
k_ui_padding = 8;
-ui_font vg_ui_font_small = {
- .glyph_width = 8,
- .glyph_height = 14,
- .glyph_baseline = 4,
- .line_height = 14,
- .sheet_size = 256,
- .spacing = 8,
- .ascii_start = ' ',
- .offset_y = 0
-},
-vg_ui_font_big = {
- .glyph_width = 12,
- .glyph_height = 21,
- .glyph_baseline = 6,
- .line_height = 21,
- .sheet_size = 256,
- .spacing = 10,
- .ascii_start = ' ',
- .offset_y = 84
-};
+#include "vg/vg_default_font.gc"
struct vg_imgui vg_ui = {
.scheme = {
[ k_ui_purple + k_ui_brighter ] = UI_RGB( 0xd3869b ),
[ k_ui_gray + k_ui_brighter ] = UI_RGB( 0xa89984 ),
},
- .font = &vg_ui_font_small,
+ .font = &vgf_default_small,
.colour = {1.0f,1.0f,1.0f,1.0f},
.bg_inverse_ratio = {1,1}
};
"layout (location=2) in vec4 a_colour;"
"uniform mat3 uPv;"
"uniform vec2 uBGInverseRatio;"
+ "uniform vec2 uInverseFontSheet;"
""
"out vec4 aTexCoords;"
"out vec4 aColour;"
"void main(){"
"vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
"gl_Position = proj_pos;"
- "aTexCoords = vec4( a_uv * 0.00390625, "
+ "aTexCoords = vec4( a_uv * uInverseFontSheet, "
" (proj_pos.xy*0.5+0.5) * uBGInverseRatio );"
"aColour = a_colour;"
"}",
*/
/* Load default font */
- u32 compressed[] = {
- #include "vg/vg_pxfont_thin.h"
- };
- u32 pixels = 0, total = 256*256, data = 0;
- u8 image[256*256];
+ vg_font_sheet *sheet = &vg_default_font_sheet;
+ u32 pixels = 0,
+ total = sheet->w*sheet->h,
+ data = 0;
+
+ vg_linear_clear( vg_mem.scratch );
+ u8 *image = vg_linear_alloc( vg_mem.scratch, total );
- while( pixels < total ){
- for( int b = 31; b >= 0; b-- ){
- image[ pixels ++ ] = (compressed[data] & (0x1u << b))? 0xffu: 0x00u;
+ while( pixels < total )
+ {
+ for( int b = 31; b >= 0; b-- )
+ {
+ image[ pixels ++ ] = (sheet->bitmap[data] & (0x1u << b))? 0xffu: 0x00u;
- if( pixels >= total ){
+ if( pixels >= total )
+ {
total = 0;
break;
}
}
data++;
}
+
+ vg_ui.inverse_font_sheet[0] = 1.0/(f64)sheet->w;
+ vg_ui.inverse_font_sheet[1] = 1.0/(f64)sheet->h;
glGenTextures( 1, &vg_ui.tex_glyphs );
glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0,
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, sheet->w, sheet->h, 0,
GL_RED, GL_UNSIGNED_BYTE, image );
VG_CHECK_GL_ERR();
vg_ui.frosting );
glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ),
1, vg_ui.bg_inverse_ratio );
+ glUniform2fv( glGetUniformLocation( _shader_ui.id, "uInverseFontSheet" ),
+ 1, vg_ui.inverse_font_sheet );
}
else if( shader == k_ui_shader_image ){
glUseProgram( _shader_ui_image.id );
else if( c == '\n' ) break;
}
- return length * vg_ui.font->spacing;
+ return length * vg_ui.font->sx;
}
ui_px ui_text_string_height( const char *str )
const char *_c = str;
u8 c;
- while( (c = *(_c ++)) ){
+ while( (c = *(_c ++)) )
+ {
if( c == '\n' ) height ++;
}
- return height * 14;
+ return height * vg_ui.font->sy;
}
ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale,
return r | (g<<8) | (b<<16) | (a<<24);
}
-static void ui_text_glyph( const struct ui_font *font, ui_px scale,
- u8 glyph, ui_rect out_texcoords ){
- glyph -= font->ascii_start;
+static void ui_text_glyph( const struct vg_font_face *ff,
+ u8 glyph, ui_rect out_texcoords )
+{
+ const vg_font_char *ch = &ff->map[ glyph ];
- ui_px per_row = font->sheet_size / font->glyph_width,
- column = (ui_px)glyph % per_row,
- row = (glyph - column) / per_row;
-
- out_texcoords[0] = column * font->glyph_width;
- out_texcoords[1] = row * font->glyph_height + font->offset_y;
- out_texcoords[2] = out_texcoords[0] + font->glyph_width;
- out_texcoords[3] = out_texcoords[1] + font->glyph_height;
+ out_texcoords[0] = ch->x;
+ out_texcoords[1] = ch->y;
+ out_texcoords[2] = ch->x + ff->cw;
+ out_texcoords[3] = ch->y + ff->ch;
}
u32 ui_opacity( u32 colour, f32 opacity )
text_cursor[0] = ui_text_aligned_x( str, rect, scale, align );
text_cursor[1] = rect[1];
- text_cursor[2] = vg_ui.font->glyph_width*scale;
- text_cursor[3] = vg_ui.font->glyph_height*scale;
+ text_cursor[2] = vg_ui.font->cw*scale;
+ text_cursor[3] = vg_ui.font->ch*scale;
u32 printed_chars = 0;
- if( align & (k_ui_align_middle|k_ui_align_bottom) ){
+ if( align & (k_ui_align_middle|k_ui_align_bottom) )
+ {
ui_px height = ui_text_string_height( str ) * scale;
if( align & k_ui_align_bottom )
text_cursor[1] += (rect[3]-height)/2;
}
- while( (c = *(_c ++)) ){
- if( printed_chars >= len ){
+ while( (c = *(_c ++)) )
+ {
+ if( printed_chars >= len )
+ {
printed_chars = 0;
- text_cursor[1] += vg_ui.font->line_height*scale;
+ text_cursor[1] += vg_ui.font->sy*scale;
text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align );
- text_cursor[0] -= vg_ui.font->spacing*scale;
+ text_cursor[0] -= vg_ui.font->sx*scale;
ui_rect glyph;
- ui_text_glyph( vg_ui.font, scale, '\xb6' /*FIXME*/, glyph );
+ ui_text_glyph( vg_ui.font, '\xb6' /*FIXME*/, glyph );
ui_fill_rect( text_cursor, 0x00ffffff, glyph );
- text_cursor[0] += vg_ui.font->spacing*scale;
+ text_cursor[0] += vg_ui.font->sx*scale;
}
- if( c == '\n' ){
- text_cursor[1] += vg_ui.font->line_height*scale;
+ if( c == '\n' )
+ {
+ text_cursor[1] += vg_ui.font->sy*scale;
text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align );
printed_chars = 0;
continue;
}
- else if( c >= 33 ){
+ else if( c >= 33 )
+ {
ui_rect glyph;
- ui_text_glyph( vg_ui.font, scale, c, glyph );
+ ui_text_glyph( vg_ui.font, c, glyph );
ui_rect cursor_clipped;
- if( ui_clip( rect, text_cursor, cursor_clipped ) ){
+ if( ui_clip( rect, text_cursor, cursor_clipped ) )
+ {
ui_fill_rect( cursor_clipped, colour, glyph );
}
}
- else if( c == '\x1B' ){
+ else if( c == '\x1B' )
+ {
/* vt codes */
_c ++;
u16 colour_id = 0;
- for( int i=0; i<3; i ++ ){
- if( _c[i] ){
- if( _c[i] == 'm' ){
+ for( int i=0; i<3; i ++ )
+ {
+ if( _c[i] )
+ {
+ if( _c[i] == 'm' )
+ {
_c = _c + i + 1;
switch( colour_id ){
colour_id |= _c[i] << (i*8);
}
- else{
+ else
+ {
_c = _c +i;
break;
}
continue;
}
- else if( c == '\t' ){
- text_cursor[0] += vg_ui.font->spacing*scale*4;
+ else if( c == '\t' )
+ {
+ text_cursor[0] += vg_ui.font->sx*scale*4;
printed_chars += 4;
continue;
}
- text_cursor[0] += vg_ui.font->spacing*scale;
+ text_cursor[0] += vg_ui.font->sx*scale;
printed_chars ++;
}
ui_ntext( rect, str, 1024, scale, align, colour );
}
+void ui_font_face( vg_font_face *ff )
+{
+ vg_ui.font = ff;
+}
+
/*
* Standard layout stuff
* -----------------------------------------------------------------------------
ui_px gap, ui_rect r )
{
ui_rect l;
- ui_px width = (ui_text_line_width(text)+vg_ui.font->spacing) * size;
+ ui_px width = (ui_text_line_width(text)+vg_ui.font->sx) * size;
ui_split( rect, k_ui_axis_v, width, gap, l, r );
ui_text( l, text, 1, k_ui_align_middle_left, 0 );
}
void ui_standard_widget( ui_rect inout_panel, ui_rect out_rect, ui_px count )
{
- ui_px height = (count * vg_ui.font->glyph_height + 18) * k_ui_scale;
+ ui_px height = (count * vg_ui.font->sy + 18) * k_ui_scale;
ui_split( inout_panel, k_ui_axis_h, height, k_ui_padding,
out_rect, inout_panel );
}
ui_rect row0, row1, btn;
ui_split_ratio( message, k_ui_axis_h, 0.5f, 0, row0, row1 );
- row0[0] += vg_ui.font->spacing;
- ui_ntext( row0, vg_ui.modal.message, (box[2]/vg_ui.font->spacing)-2, 1,
+ row0[0] += vg_ui.font->sx;
+ ui_ntext( row0, vg_ui.modal.message, (box[2]/vg_ui.font->sx)-2, 1,
k_ui_align_left, colour );
rect_copy( row1, btn );
rect_copy( rect, text_rect );
if( flags & UI_TEXTBOX_MULTILINE ) text_rect[3] = rect[3]-16;
- else text_rect[3] = vg_ui.font->line_height;
+ else text_rect[3] = vg_ui.font->sy;
text_rect[2] -= 16;
ui_rect_center( rect, text_rect );
ui_px wrap_length = 1024;
if( flags & UI_TEXTBOX_WRAP )
- wrap_length = text_rect[2] / vg_ui.font->spacing;
+ wrap_length = text_rect[2] / vg_ui.font->sx;
- if( hover ){
+ if( hover )
+ {
vg_ui.cursor = k_ui_cursor_ibeam;
}
- if( vg_ui.focused_control_id == buf ){
+ if( vg_ui.focused_control_id == buf )
+ {
ui_fill( rect, col_base );
ui_ntext( text_rect, buf, wrap_length, 1, k_ui_align_left, 0 );
- if( !(flags & UI_TEXTBOX_AUTOFOCUS) && ((clickup||clickdown) && !target)){
+ if( !(flags & UI_TEXTBOX_AUTOFOCUS) && ((clickup||clickdown) && !target))
+ {
ui_defocus_all();
}
- else{
+ else
+ {
vg_ui.focused_control_hit = 1;
if( click && target ){
int p0[3] ={
- (vg_ui.mouse_click[0] - text_rect[0]) / vg_ui.font->spacing,
- (vg_ui.mouse_click[1] - text_rect[1]) / vg_ui.font->line_height,
+ (vg_ui.mouse_click[0] - text_rect[0]) / vg_ui.font->sx,
+ (vg_ui.mouse_click[1] - text_rect[1]) / vg_ui.font->sy,
-1
},
p1[3] = {
- (vg_ui.mouse[0] - text_rect[0]) / vg_ui.font->spacing,
- (vg_ui.mouse[1] - text_rect[1]) / vg_ui.font->line_height,
+ (vg_ui.mouse[0] - text_rect[0]) / vg_ui.font->sx,
+ (vg_ui.mouse[1] - text_rect[1]) / vg_ui.font->sy,
-1
};
- if( flags & UI_TEXTBOX_MULTILINE ){
+ if( flags & UI_TEXTBOX_MULTILINE )
+ {
_ui_textbox_calc_index_from_grid( p0, wrap_length );
_ui_textbox_calc_index_from_grid( p1, wrap_length );
vg_ui.textbox.cursor_pos = p0[2];
vg_ui.textbox.cursor_user = p1[2];
}
- else{
+ else
+ {
int max = strlen( buf );
vg_ui.textbox.cursor_pos = VG_MAX( 0, VG_MIN( max, p0[0] )),
vg_ui.textbox.cursor_user = VG_MAX( 0, VG_MIN( max, p1[0] ));
end = VG_MAX( c0, c1 ),
chars = end-start;
- if( flags & (UI_TEXTBOX_WRAP|UI_TEXTBOX_MULTILINE) ){
+ if( flags & (UI_TEXTBOX_WRAP|UI_TEXTBOX_MULTILINE) )
+ {
int pos[3], remaining = chars;
pos[2] = start;
_ui_textbox_index_calc_coords( pos, wrap_length );
- if( start==end ){
- cursor[0] = text_rect[0] + pos[0]*vg_ui.font->spacing-1;
+ if( start==end )
+ {
+ cursor[0] = text_rect[0] + pos[0]*vg_ui.font->sx-1;
cursor[1] = text_rect[1] + pos[1]*14;
cursor[2] = 2;
cursor[3] = 13;
ui_fill( cursor, col_cursor );
rect_copy( cursor, vg_ui.click_fader_end );
}
- else{
- while( remaining ){
+ else
+ {
+ while( remaining )
+ {
int run = _ui_textbox_run_remaining( pos, wrap_length );
run = VG_MIN( run, remaining );
- cursor[0] = text_rect[0] + pos[0]*vg_ui.font->spacing-1;
+ cursor[0] = text_rect[0] + pos[0]*vg_ui.font->sx-1;
cursor[1] = text_rect[1] + pos[1]*14;
- cursor[2] = (float)(run)*(float)vg_ui.font->spacing;
+ cursor[2] = (float)(run)*(float)vg_ui.font->sx;
cursor[3] = 13;
ui_fill( cursor, col_cursor );
rect_copy( cursor, vg_ui.click_fader_end );
}
}
- else{
- cursor[0] = text_rect[0] + start*vg_ui.font->spacing-1;
+ else
+ {
+ cursor[0] = text_rect[0] + start*vg_ui.font->sx-1;
cursor[1] = text_rect[1];
cursor[3] = 13;
- if( start==end ){
+ if( start==end )
+ {
cursor[2] = 2;
}
- else{
- cursor[2] = (float)(chars)*(float)vg_ui.font->spacing;
+ else
+ {
+ cursor[2] = (float)(chars)*(float)vg_ui.font->sx;
}
if( (vg_ui.click_fade_opacity<=0.0f) &&
- ui_clip( rect, cursor, cursor ) ){
+ ui_clip( rect, cursor, cursor ) )
+ {
ui_fill( cursor, col_cursor );
}
return 0;
}
- if( click || (flags & UI_TEXTBOX_AUTOFOCUS) ){
- if( (target && hover) || (flags & UI_TEXTBOX_AUTOFOCUS) ){
+ if( click || (flags & UI_TEXTBOX_AUTOFOCUS) )
+ {
+ if( (target && hover) || (flags & UI_TEXTBOX_AUTOFOCUS) )
+ {
ui_defocus_all();
ui_fill( rect, col_highlight );
vg_ui.textbox.cursor_pos = 0;
vg_ui.textbox.cursor_user = 0;
- if( callbacks ){
+ if( callbacks )
+ {
vg_ui.textbox.callbacks = *callbacks;
}
- else{
+ else
+ {
vg_ui.textbox.callbacks.change = NULL;
vg_ui.textbox.callbacks.down = NULL;
vg_ui.textbox.callbacks.up = NULL;
ui_fill( rect, col_base );
- if( hover ){
+ if( hover )
+ {
ui_outline( rect, -1, col_highlight, 0 );
}
ui_rect_pad( out_content_panel, (ui_px[2]){ k_ui_padding, k_ui_padding } );
/* place buttons */
- for( i32 i=0; i<count; i++ ){
+ for( i32 i=0; i<count; i++ )
+ {
ui_rect button = {
bar[0] + ((f32)i*width),
bar[1],
};
enum ui_scheme_colour colour = k_ui_bg+4;
- if( i == cur_page ){
+ if( i == cur_page )
+ {
colour = k_ui_bg+2;
ui_outline( button, 1, ui_colour( k_ui_bg+5 ),
UI_TOP|UI_LEFT|UI_RIGHT );