diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index a42d38d6..215f4367 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -391,6 +391,7 @@ view_compute_cursor(System_Functions *system, View *view, Buffer_Seek seek, b32 Buffer_Cursor_Seek_Params params; params.buffer = &file->state.buffer; params.seek = seek; + params.system = system; params.font = font; params.wrap_line_index = file->state.wrap_line_index; params.character_starts = file->state.character_starts; @@ -951,7 +952,7 @@ file_allocate_character_starts_as_needed(General_Memory *general, Editing_File * internal void file_measure_character_starts(System_Functions *system, Models *models, Editing_File *file){ file_allocate_character_starts_as_needed(&models->mem.general, file); - buffer_measure_character_starts(&file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); + buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); file_update_cursor_positions(system, models, file); } @@ -1132,7 +1133,7 @@ wrap_state_consume_token(Code_Wrap_State *state, i32 fixed_end_point){ u8 ch = (u8)state->stream.data[i]; - translating_fully_process_byte(&state->tran, ch, i, state->size, &state->emits); + translating_fully_process_byte(system, font, &state->tran, ch, i, state->size, &state->emits); for (TRANSLATION_OUTPUT(state->J, state->emits)){ TRANSLATION_GET_STEP(state->step, state->behavior, state->J, state->emits); @@ -1452,7 +1453,7 @@ get_current_shift(Code_Wrap_State *wrap_state, i32 next_line_start, b32 *adjust_ } internal void -file_measure_wraps(Models *models, Editing_File *file, Render_Font *font){ +file_measure_wraps(System_Functions *system, Models *models, Editing_File *file, Render_Font *font){ General_Memory *general = &models->mem.general; Partition *part = &models->mem.part; @@ -1465,6 +1466,7 @@ file_measure_wraps(Models *models, Editing_File *file, Render_Font *font){ Buffer_Measure_Wrap_Params params; params.buffer = &file->state.buffer; params.wrap_line_index = file->state.wrap_line_index; + params.system = system; params.font = font; params.virtual_white = file->settings.virtual_white; @@ -5977,6 +5979,7 @@ draw_file_loaded(System_Functions *system, View *view, i32_Rect rect, b32 is_act params.height = (f32)max_y; params.start_cursor = render_cursor; params.wrapped = wrapped; + params.system = system; params.font = font; params.virtual_white = file->settings.virtual_white; params.wrap_slashes = file->settings.wrap_indicator; diff --git a/file/4coder_buffer.cpp b/file/4coder_buffer.cpp index 69eb7f97..7bcc9a0b 100644 --- a/file/4coder_buffer.cpp +++ b/file/4coder_buffer.cpp @@ -325,10 +325,6 @@ is_match_insensitive(char *a, char *b, i32 len){ return(result); } -// -// Translation state for turning byte streams into unicode/trash byte streams -// - struct Buffer_Model_Step{ u32 type; u32 value; @@ -348,215 +344,7 @@ enum{ BufferModelUnit_Numbers, }; -struct Translation_State{ - u8 fill_buffer[4]; - u32 fill_start_i; - u32 fill_i; - u32 fill_expected; -}; -global_const Translation_State null_buffer_translating_state = {0}; - -struct Translation_Byte_Description{ - u32 byte_class; - b32 rebuffer_current; - b32 emit_current_as_cp; - - u32 prelim_emit_type; -}; - -struct Translation_Emit_Type{ - u32 byte_class; - b32 rebuffer_current; - b32 emit_current_as_cp; - - u32 codepoint; - u32 codepoint_length; - b32 do_codepoint; - b32 do_numbers; -}; - -struct Translation_Emits{ - Buffer_Model_Step steps[5]; - Buffer_Model_Step step_current; - u32 step_count; -}; - -internal void -translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Byte_Description *desc_out){ - desc_out->byte_class = 0; - if ((ch >= ' ' && ch < 0x7F) || ch == '\t' || ch == '\n' || ch == '\r'){ - desc_out->byte_class = 1; - } - else if (ch < 0xC0){ - desc_out->byte_class = 1000; - } - else if (ch < 0xE0){ - desc_out->byte_class = 2; - } - else if (ch < 0xF0){ - desc_out->byte_class = 3; - } - else{ - desc_out->byte_class = 4; - } - - desc_out->prelim_emit_type = BufferModelUnit_None; - desc_out->rebuffer_current = false; - desc_out->emit_current_as_cp = false; - if (tran->fill_expected == 0){ - tran->fill_buffer[0] = ch; - tran->fill_start_i = i; - tran->fill_i = 1; - - if (desc_out->byte_class == 1){ - desc_out->prelim_emit_type = BufferModelUnit_Codepoint; - } - else if (desc_out->byte_class == 0 || desc_out->byte_class == 1000){ - desc_out->prelim_emit_type = BufferModelUnit_Numbers; - } - else{ - tran->fill_expected = desc_out->byte_class; - } - } - else{ - if (desc_out->byte_class == 1000){ - tran->fill_buffer[tran->fill_i] = ch; - ++tran->fill_i; - - if (tran->fill_i == tran->fill_expected){ - desc_out->prelim_emit_type = BufferModelUnit_Codepoint; - } - } - else{ - if (desc_out->byte_class >= 2 && desc_out->byte_class <= 4){ - desc_out->rebuffer_current = true; - } - else if (desc_out->byte_class == 1){ - desc_out->emit_current_as_cp = true; - } - else{ - tran->fill_buffer[tran->fill_i] = ch; - ++tran->fill_i; - } - desc_out->prelim_emit_type = BufferModelUnit_Numbers; - } - } - - if (desc_out->prelim_emit_type == BufferModelUnit_None && i+1 == size){ - desc_out->prelim_emit_type = BufferModelUnit_Numbers; - } -} - -internal void -translating_select_emit_type(Translation_State *tran, Translation_Byte_Description desc, Translation_Emit_Type *type_out){ - type_out->byte_class = desc.byte_class; - type_out->rebuffer_current = desc.rebuffer_current; - type_out->emit_current_as_cp = desc.emit_current_as_cp; - - type_out->codepoint = 0; - type_out->codepoint_length = 0; - type_out->do_codepoint = false; - type_out->do_numbers = false; - if (desc.prelim_emit_type == BufferModelUnit_Codepoint){ - type_out->codepoint = utf8_to_u32_length_unchecked(tran->fill_buffer, &type_out->codepoint_length); - if ((type_out->codepoint >= ' ' && type_out->codepoint <= 255 && type_out->codepoint != 127) || type_out->codepoint == '\t' || type_out->codepoint == '\n' || type_out->codepoint == '\r'){ - type_out->do_codepoint = true; - } - else{ - type_out->do_numbers = true; - } - } - else if (desc.prelim_emit_type == BufferModelUnit_Numbers){ - type_out->do_numbers = true; - } - - Assert((type_out->do_codepoint + type_out->do_numbers) <= 1); -} - -internal void -translating_generate_emits(Translation_State *tran, Translation_Emit_Type emit_types, u8 ch, u32 i, Translation_Emits *emits_out){ - emits_out->step_count = 0; - if (emit_types.do_codepoint){ - emits_out->steps[0].type = 1; - emits_out->steps[0].value = emit_types.codepoint; - emits_out->steps[0].i = tran->fill_start_i; - emits_out->steps[0].byte_length = emit_types.codepoint_length; - emits_out->step_count = 1; - } - else if (emit_types.do_numbers){ - for (u32 j = 0; j < tran->fill_i; ++j){ - emits_out->steps[j].type = 0; - emits_out->steps[j].value = tran->fill_buffer[j]; - emits_out->steps[j].i = tran->fill_start_i + j; - emits_out->steps[j].byte_length = 1; - } - emits_out->step_count = tran->fill_i; - } - - if (emit_types.do_codepoint || emit_types.do_numbers){ - tran->fill_start_i = 0; - tran->fill_i = 0; - tran->fill_expected = 0; - } - - if (emit_types.rebuffer_current){ - Assert(emit_types.do_codepoint || emit_types.do_numbers); - - tran->fill_buffer[0] = ch; - tran->fill_start_i = i; - tran->fill_i = 1; - tran->fill_expected = emit_types.byte_class; - } - else if (emit_types.emit_current_as_cp){ - Assert(emit_types.do_codepoint || emit_types.do_numbers); - - emits_out->steps[emits_out->step_count].type = 1; - emits_out->steps[emits_out->step_count].value = ch; - emits_out->steps[emits_out->step_count].i = i; - emits_out->steps[emits_out->step_count].byte_length = 1; - ++emits_out->step_count; - } -} - -internal void -translating_fully_process_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Emits *emits_out){ - Translation_Byte_Description description = {0}; - translating_consume_byte(tran, ch, i, size, &description); - Translation_Emit_Type emit_type = {0}; - translating_select_emit_type(tran, description, &emit_type); - translating_generate_emits(tran, emit_type, ch, i, emits_out); -} - -internal void -translation_step_read(Buffer_Model_Step step, Buffer_Model_Behavior *behavior_out){ - behavior_out->do_newline = false; - behavior_out->do_codepoint_advance = false; - behavior_out->do_number_advance = false; - if (step.type == 1){ - switch (step.value){ - case '\n': - { - behavior_out->do_newline = true; - }break; - default: - { - behavior_out->do_codepoint_advance = true; - }break; - } - } - else{ - behavior_out->do_number_advance = true; - } -} - -#define TRANSLATION_DECL_OUTPUT(_j,_emit) u32 _j = 0; _j < (_emit).step_count; ++_j -#define TRANSLATION_DECL_GET_STEP(_step,_behav,_j,_emit) \ -Buffer_Model_Step _step = _emit.steps[_j]; Buffer_Model_Behavior _behav; \ -translation_step_read(_step, &_behav) - -#define TRANSLATION_OUTPUT(_j,_emit) _j = 0; _j < (_emit).step_count; ++_j -#define TRANSLATION_GET_STEP(_step,_behav,_j,_emit)\ -(_step) = _emit.steps[_j]; translation_step_read((_step), &(_behav)) +#include "4coder_translation.cpp" // // Implementation of the gap buffer @@ -952,7 +740,7 @@ buffer_measure_starts(Buffer_Measure_Starts *state, Gap_Buffer *buffer){ } internal void -buffer_measure_character_starts(Gap_Buffer *buffer, i32 *character_starts, i32 mode, i32 virtual_white){ +buffer_measure_character_starts(System_Functions *system, Render_Font *font, Gap_Buffer *buffer, i32 *character_starts, i32 mode, i32 virtual_white){ assert(mode == 0); Gap_Buffer_Stream stream = {0}; @@ -981,7 +769,7 @@ buffer_measure_character_starts(Gap_Buffer *buffer, i32 *character_starts, i32 m for (; i < stream.end; ++i){ u8 ch = (u8)stream.data[i]; - translating_fully_process_byte(&tran, ch, i, size, &emits); + translating_fully_process_byte(system, font, &tran, ch, i, size, &emits); for (TRANSLATION_DECL_OUTPUT(J, emits)){ TRANSLATION_DECL_GET_STEP(step, behavior, J, emits); @@ -1030,6 +818,7 @@ struct Buffer_Layout_Stop{ struct Buffer_Measure_Wrap_Params{ Gap_Buffer *buffer; i32 *wrap_line_index; + System_Functions *system; Render_Font *font; b32 virtual_white; }; @@ -1106,7 +895,7 @@ buffer_measure_wrap_y(Buffer_Measure_Wrap_State *S_ptr, Buffer_Measure_Wrap_Para S.skipping_whitespace = false; } - translating_fully_process_byte(&S.tran, ch, S.i, S.size, &S.emits); + translating_fully_process_byte(params.system, params.font, &S.tran, ch, S.i, S.size, &S.emits); } for (TRANSLATION_OUTPUT(S.J, S.emits)){ @@ -1273,7 +1062,7 @@ buffer_remeasure_starts(Gap_Buffer *buffer, i32 line_start, i32 line_end, i32 li } internal void -buffer_remeasure_character_starts(Gap_Buffer *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 *character_starts, i32 mode, i32 virtual_whitespace){ +buffer_remeasure_character_starts(System_Functions *system, Render_Font *font, Gap_Buffer *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 *character_starts, i32 mode, i32 virtual_whitespace){ assert(mode == 0); i32 new_line_count = buffer->line_count; @@ -1321,7 +1110,7 @@ buffer_remeasure_character_starts(Gap_Buffer *buffer, i32 line_start, i32 line_e do{ for (; char_i < stream.end; ++char_i){ u8 ch = (u8)stream.data[char_i]; - translating_fully_process_byte(&tran, ch, char_i, size, &emits); + translating_fully_process_byte(system, font, &tran, ch, char_i, size, &emits); for (TRANSLATION_DECL_OUTPUT(J, emits)){ TRANSLATION_DECL_GET_STEP(step, behavior, J, emits); @@ -1585,6 +1374,7 @@ buffer_partial_from_line_character(Gap_Buffer *buffer, i32 line, i32 character){ struct Buffer_Cursor_Seek_Params{ Gap_Buffer *buffer; Buffer_Seek seek; + System_Functions *system; Render_Font *font; i32 *wrap_line_index; i32 *character_starts; @@ -1804,7 +1594,7 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa for (; S.i < S.stream.end; ++S.i){ { u8 ch = (u8)S.stream.data[S.i]; - translating_fully_process_byte(&S.tran, ch, S.i, S.size, &S.emits); + translating_fully_process_byte(params.system, params.font, &S.tran, ch, S.i, S.size, &S.emits); } for (TRANSLATION_OUTPUT(S.J, S.emits)){ @@ -2063,7 +1853,6 @@ write_render_item(Render_Item_Write write, i32 index, u32 codepoint, u32 flags){ return(write); } -// TODO(allen): Reduce the number of parameters. struct Buffer_Render_Params{ Gap_Buffer *buffer; Buffer_Render_Item *items; @@ -2078,6 +1867,7 @@ struct Buffer_Render_Params{ f32 height; Full_Cursor start_cursor; i32 wrapped; + System_Functions *system; Render_Font *font; b32 virtual_white; i32 wrap_slashes; @@ -2173,7 +1963,7 @@ buffer_render_data(Buffer_Render_State *S_ptr, Buffer_Render_Params params, f32 for (; S.i < S.stream.end; ++S.i){ { u8 ch = (u8)S.stream.data[S.i]; - translating_fully_process_byte(&S.tran, ch, S.i, S.size, &S.emits); + translating_fully_process_byte(params.system, params.font, &S.tran, ch, S.i, S.size, &S.emits); } for (TRANSLATION_OUTPUT(S.J, S.emits)){ diff --git a/file/4coder_translation.cpp b/file/4coder_translation.cpp index e31ed8d0..83579f2a 100644 --- a/file/4coder_translation.cpp +++ b/file/4coder_translation.cpp @@ -12,33 +12,32 @@ struct Translation_State{ u8 fill_buffer[4]; u32 fill_start_i; - u32 fill_i; - u32 fill_expected; + u8 fill_i; + u8 fill_expected; }; global_const Translation_State null_buffer_translating_state = {0}; +enum{ + TranLBH_Rebuffer, + TranLBH_EmitAsCP, +}; struct Translation_Byte_Description{ - u32 byte_class; - b32 rebuffer_current; - b32 emit_current_as_cp; - - u32 prelim_emit_type; + u8 byte_class; + u8 last_byte_handler; + u8 prelim_emit_type; }; -struct Translation_Emit_Type{ - u32 byte_class; - b32 rebuffer_current; - b32 emit_current_as_cp; +struct Translation_Emit_Rule{ + u8 byte_class; + u8 last_byte_handler; + u8 emit_type; u32 codepoint; u32 codepoint_length; - b32 do_codepoint; - b32 do_numbers; }; struct Translation_Emits{ Buffer_Model_Step steps[5]; - Buffer_Model_Step step_current; u32 step_count; }; @@ -49,7 +48,7 @@ translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Transl desc_out->byte_class = 1; } else if (ch < 0xC0){ - desc_out->byte_class = 1000; + desc_out->byte_class = max_u8; } else if (ch < 0xE0){ desc_out->byte_class = 2; @@ -62,8 +61,7 @@ translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Transl } desc_out->prelim_emit_type = BufferModelUnit_None; - desc_out->rebuffer_current = false; - desc_out->emit_current_as_cp = false; + desc_out->last_byte_handler = 0; if (tran->fill_expected == 0){ tran->fill_buffer[0] = ch; tran->fill_start_i = i; @@ -90,10 +88,10 @@ translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Transl } else{ if (desc_out->byte_class >= 2 && desc_out->byte_class <= 4){ - desc_out->rebuffer_current = true; + desc_out->last_byte_handler = TranLBH_Rebuffer; } else if (desc_out->byte_class == 1){ - desc_out->emit_current_as_cp = true; + desc_out->last_byte_handler = TranLBH_EmitAsCP; } else{ tran->fill_buffer[tran->fill_i] = ch; @@ -109,83 +107,110 @@ translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Transl } internal void -translating_select_emit_type(Translation_State *tran, Translation_Byte_Description desc, Translation_Emit_Type *type_out){ +translating_select_emit_rule_ASCII(Translation_State *tran, Translation_Byte_Description desc, Translation_Emit_Rule *type_out){ type_out->byte_class = desc.byte_class; - type_out->rebuffer_current = desc.rebuffer_current; - type_out->emit_current_as_cp = desc.emit_current_as_cp; + type_out->last_byte_handler = desc.last_byte_handler; + type_out->emit_type = desc.prelim_emit_type; type_out->codepoint = 0; type_out->codepoint_length = 0; - type_out->do_codepoint = false; - type_out->do_numbers = false; if (desc.prelim_emit_type == BufferModelUnit_Codepoint){ - type_out->codepoint = utf8_to_u32_length_unchecked(tran->fill_buffer, &type_out->codepoint_length); - if ((type_out->codepoint >= ' ' && type_out->codepoint <= 255 && type_out->codepoint != 127) || type_out->codepoint == '\t' || type_out->codepoint == '\n' || type_out->codepoint == '\r'){ - type_out->do_codepoint = true; - } - else{ - type_out->do_numbers = true; + u32 cp = utf8_to_u32_length_unchecked(tran->fill_buffer, &type_out->codepoint_length); + type_out->codepoint = cp; + if (!(cp == '\n' || cp == '\t' || cp == '\r' || (cp >= ' ' && cp <= 255 && cp != 127))){ + type_out->emit_type = BufferModelUnit_Numbers; } } - else if (desc.prelim_emit_type == BufferModelUnit_Numbers){ - type_out->do_numbers = true; - } - - Assert((type_out->do_codepoint + type_out->do_numbers) <= 1); } internal void -translating_generate_emits(Translation_State *tran, Translation_Emit_Type emit_types, u8 ch, u32 i, Translation_Emits *emits_out){ - emits_out->step_count = 0; - if (emit_types.do_codepoint){ - emits_out->steps[0].type = 1; - emits_out->steps[0].value = emit_types.codepoint; - emits_out->steps[0].i = tran->fill_start_i; - emits_out->steps[0].byte_length = emit_types.codepoint_length; - emits_out->step_count = 1; - } - else if (emit_types.do_numbers){ - for (u32 j = 0; j < tran->fill_i; ++j){ - emits_out->steps[j].type = 0; - emits_out->steps[j].value = tran->fill_buffer[j]; - emits_out->steps[j].i = tran->fill_start_i + j; - emits_out->steps[j].byte_length = 1; +translating_select_emit_rule_with_font(System_Functions *system, Render_Font *font, Translation_State *tran, Translation_Byte_Description desc, Translation_Emit_Rule *type_out){ + type_out->byte_class = desc.byte_class; + type_out->last_byte_handler = desc.last_byte_handler; + type_out->emit_type = desc.prelim_emit_type; + + type_out->codepoint = 0; + type_out->codepoint_length = 0; + if (desc.prelim_emit_type == BufferModelUnit_Codepoint){ + u32 cp = utf8_to_u32_length_unchecked(tran->fill_buffer, &type_out->codepoint_length); + type_out->codepoint = cp; + if (!font_can_render(system, font, cp)){ + type_out->emit_type = BufferModelUnit_Numbers; } - emits_out->step_count = tran->fill_i; - } - - if (emit_types.do_codepoint || emit_types.do_numbers){ - tran->fill_start_i = 0; - tran->fill_i = 0; - tran->fill_expected = 0; - } - - if (emit_types.rebuffer_current){ - Assert(emit_types.do_codepoint || emit_types.do_numbers); - - tran->fill_buffer[0] = ch; - tran->fill_start_i = i; - tran->fill_i = 1; - tran->fill_expected = emit_types.byte_class; - } - else if (emit_types.emit_current_as_cp){ - Assert(emit_types.do_codepoint || emit_types.do_numbers); - - emits_out->steps[emits_out->step_count].type = 1; - emits_out->steps[emits_out->step_count].value = ch; - emits_out->steps[emits_out->step_count].i = i; - emits_out->steps[emits_out->step_count].byte_length = 1; - ++emits_out->step_count; } } +internal void +translating_generate_emits(Translation_State *tran, Translation_Emit_Rule emit_rule, u8 ch, u32 i, Translation_Emits *emits_out){ + emits_out->step_count = 0; + switch (emit_rule.emit_type){ + default: goto skip_all; + + case BufferModelUnit_Codepoint: + { + emits_out->steps[0].type = 1; + emits_out->steps[0].value = emit_rule.codepoint; + emits_out->steps[0].i = tran->fill_start_i; + emits_out->steps[0].byte_length = emit_rule.codepoint_length; + emits_out->step_count = 1; + }break; + + case BufferModelUnit_Numbers: + { + for (u32 j = 0; j < tran->fill_i; ++j){ + emits_out->steps[j].type = 0; + emits_out->steps[j].value = tran->fill_buffer[j]; + emits_out->steps[j].i = tran->fill_start_i + j; + emits_out->steps[j].byte_length = 1; + } + emits_out->step_count = tran->fill_i; + }break; + } + + tran->fill_start_i = 0; + tran->fill_i = 0; + tran->fill_expected = 0; + + switch (emit_rule.last_byte_handler){ + case TranLBH_Rebuffer: + { + tran->fill_buffer[0] = ch; + tran->fill_start_i = i; + tran->fill_i = 1; + tran->fill_expected = emit_rule.byte_class; + }break; + + case TranLBH_EmitAsCP: + { + emits_out->steps[emits_out->step_count].type = 1; + emits_out->steps[emits_out->step_count].value = ch; + emits_out->steps[emits_out->step_count].i = i; + emits_out->steps[emits_out->step_count].byte_length = 1; + ++emits_out->step_count; + }break; + } + + skip_all:; +} + +#if 0 internal void translating_fully_process_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Emits *emits_out){ Translation_Byte_Description description = {0}; translating_consume_byte(tran, ch, i, size, &description); - Translation_Emit_Type emit_type = {0}; - translating_select_emit_type(tran, description, &emit_type); - translating_generate_emits(tran, emit_type, ch, i, emits_out); + Translation_Emit_Rule emit_rule = {0}; + translating_select_emit_rule_ASCII(tran, description, &emit_rule); + translating_generate_emits(tran, emit_rule, ch, i, emits_out); +} +#endif + +internal void +translating_fully_process_byte(System_Functions *system, Render_Font *font, Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Emits *emits_out){ + Translation_Byte_Description description = {0}; + translating_consume_byte(tran, ch, i, size, &description); + Translation_Emit_Rule emit_rule = {0}; + translating_select_emit_rule_with_font(system, font, tran, description, &emit_rule); + translating_generate_emits(tran, emit_rule, ch, i, emits_out); } internal void diff --git a/font/4coder_font_interface.h b/font/4coder_font_interface.h index 1558e830..1d17269a 100644 --- a/font/4coder_font_interface.h +++ b/font/4coder_font_interface.h @@ -46,6 +46,8 @@ internal i32 font_get_descent(Render_Font *font); internal i32 font_get_line_skip(Render_Font *font); internal i32 font_get_advance(Render_Font *font); +internal b32 font_can_render(struct System_Functions *system, Render_Font *font, u32 codepoint); + #endif // BOTTOM