From dcc00b359e313166f7f155bddc5cebc336c744b7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 16 Nov 2021 02:19:46 +0100 Subject: Text: add function to get character position by index, use same character loop everywhere --- src/graphics/text.c | 150 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 46 deletions(-) (limited to 'src/graphics') diff --git a/src/graphics/text.c b/src/graphics/text.c index ea60b6a..cd9d0d9 100644 --- a/src/graphics/text.c +++ b/src/graphics/text.c @@ -8,18 +8,11 @@ #define TAB_WIDTH 4 -static float max_float(float a, float b) { - return a >= b ? a : b; -} - -static mgl_vec2f mgl_text_calculate_bounds(mgl_text *self) { - mgl_vec2f bounds; - bounds.x = 0.0f; - bounds.y = self->font->character_size; - - /* TODO: Combine this with the loop in mgl_text_draw */ +/* Return false to stop the loop. |glyph| is not valid if codepoint is '\n' */ +typedef bool (*codepoint_loop_callback)(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata); +static void mgl_text_for_each_codepoint(const mgl_text *self, codepoint_loop_callback callback, void *userdata) { mgl_font_glyph glyph; - float width = 0.0f; + size_t codepoint_index = 0; for(size_t i = 0; i < self->text_size;) { unsigned char *cp = (unsigned char*)&self->text[i]; uint32_t codepoint; @@ -31,22 +24,57 @@ static mgl_vec2f mgl_text_calculate_bounds(mgl_text *self) { if(codepoint == '\t') { if(mgl_font_get_glyph(self->font, ' ', &glyph) == 0) { - width += (glyph.advance * TAB_WIDTH); - bounds.x = max_float(bounds.x, width); + if(!callback(codepoint_index, codepoint, &glyph, userdata)) + return; } } else if(codepoint == '\n') { - width = 0.0f; - bounds.y += self->font->character_size; + if(!callback(codepoint_index, codepoint, &glyph, userdata)) + return; } else { if(mgl_font_get_glyph(self->font, codepoint, &glyph) == 0) { - width += glyph.advance; - bounds.x = max_float(bounds.x, width); + if(!callback(codepoint_index, codepoint, &glyph, userdata)) + return; } } i += clen; + ++codepoint_index; + } +} + +static float max_float(float a, float b) { + return a >= b ? a : b; +} + +typedef struct { + mgl_vec2f bounds; + float line_width; + const mgl_font *font; +} CalculateBoundsUserdata; + +static bool calculate_bounds_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { + (void)codepoint_index; + CalculateBoundsUserdata *calculate_bounds_userdata = userdata; + if(codepoint == '\t') { + calculate_bounds_userdata->line_width += (glyph->advance * TAB_WIDTH); + calculate_bounds_userdata->bounds.x = max_float(calculate_bounds_userdata->bounds.x, calculate_bounds_userdata->line_width); + } else if(codepoint == '\n') { + calculate_bounds_userdata->line_width = 0.0f; + calculate_bounds_userdata->bounds.y += calculate_bounds_userdata->font->character_size; + } else { + calculate_bounds_userdata->line_width += glyph->advance; + calculate_bounds_userdata->bounds.x = max_float(calculate_bounds_userdata->bounds.x, calculate_bounds_userdata->line_width); } + return true; +} - return bounds; +static mgl_vec2f mgl_text_calculate_bounds(mgl_text *self) { + CalculateBoundsUserdata calculate_bounds_userdata; + calculate_bounds_userdata.bounds.x = 0.0f; + calculate_bounds_userdata.bounds.y = self->font->character_size; + calculate_bounds_userdata.line_width = 0.0f; + calculate_bounds_userdata.font = self->font; + mgl_text_for_each_codepoint(self, calculate_bounds_callback, &calculate_bounds_userdata); + return calculate_bounds_userdata.bounds; } void mgl_text_init(mgl_text *self, mgl_font *font, const char *str, size_t str_size) { @@ -97,7 +125,38 @@ mgl_vec2f mgl_text_get_bounds(const mgl_text *self) { return self->bounds; } -static void mgl_text_draw_glyph(mgl_context *context, mgl_font_glyph *glyph, mgl_vec2i position) { +typedef struct { + mgl_vec2i position; + const mgl_text *text; + size_t index; +} FindCharacterPosUserdata; + +static bool find_character_pos_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { + FindCharacterPosUserdata *find_character_pos_userdata = userdata; + if(codepoint_index >= find_character_pos_userdata->index) + return false; + + if(codepoint == '\t') { + find_character_pos_userdata->position.x += (glyph->advance * TAB_WIDTH); + } else if(codepoint == '\n') { + find_character_pos_userdata->position.x = find_character_pos_userdata->text->position.x; + find_character_pos_userdata->position.y += find_character_pos_userdata->text->font->character_size; + } else { + find_character_pos_userdata->position.x += glyph->advance; + } + return true; +} + +mgl_vec2f mgl_text_find_character_pos(mgl_text *self, size_t index) { + FindCharacterPosUserdata find_character_pos_userdata; + find_character_pos_userdata.position = (mgl_vec2i){ self->position.x, self->position.y }; + find_character_pos_userdata.text = self; + find_character_pos_userdata.index = index; + mgl_text_for_each_codepoint(self, find_character_pos_callback, &find_character_pos_userdata); + return (mgl_vec2f){ find_character_pos_userdata.position.x, find_character_pos_userdata.position.y }; +} + +static void mgl_text_draw_glyph(mgl_context *context, const mgl_font_glyph *glyph, mgl_vec2i position) { context->gl.glTexCoord2f(glyph->texture_position.x, glyph->texture_position.y); context->gl.glVertex3f(position.x + glyph->position.x, position.y + glyph->position.y, 0.0f); @@ -111,43 +170,42 @@ static void mgl_text_draw_glyph(mgl_context *context, mgl_font_glyph *glyph, mgl context->gl.glVertex3f(position.x + glyph->position.x, position.y + glyph->position.y + glyph->size.y, 0.0f); } +typedef struct { + mgl_vec2i position; + const mgl_text *text; + mgl_context *context; +} TextDrawUserdata; + +static bool text_draw_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { + (void)codepoint_index; + TextDrawUserdata *text_draw_userdata = userdata; + if(codepoint == '\t') { + text_draw_userdata->position.x += (glyph->advance * TAB_WIDTH); + } else if(codepoint == '\n') { + text_draw_userdata->position.x = text_draw_userdata->text->position.x; + text_draw_userdata->position.y += text_draw_userdata->text->font->character_size; + } else { + mgl_text_draw_glyph(text_draw_userdata->context, glyph, text_draw_userdata->position); + text_draw_userdata->position.x += glyph->advance; + } + return true; +} + /* TODO: Use opengl buffer object instead */ /* TODO: Cache texture bind to not bind texture if its already bound and do not bind texture 0 */ void mgl_text_draw(mgl_context *context, mgl_text *text) { if(!text->text || text->text_size == 0 || !text->font) return; - mgl_font_glyph glyph; - mgl_vec2i position = (mgl_vec2i){ text->position.x, text->position.y }; - position.y += text->font->character_size; + TextDrawUserdata text_draw_userdata; + text_draw_userdata.position = (mgl_vec2i){ text->position.x, text->position.y + text->font->character_size }; + text_draw_userdata.text = text; + text_draw_userdata.context = context; context->gl.glColor4ub(text->color.r, text->color.g, text->color.b, text->color.a); mgl_texture_use(&text->font->texture); context->gl.glBegin(GL_QUADS); - for(size_t i = 0; i < text->text_size;) { - unsigned char *cp = (unsigned char*)&text->text[i]; - uint32_t codepoint; - size_t clen; - if(!mgl_utf8_decode(cp, text->text_size - i, &codepoint, &clen)) { - codepoint = *cp; - clen = 1; - } - - if(codepoint == '\t') { - if(mgl_font_get_glyph(text->font, ' ', &glyph) == 0) { - position.x += (glyph.advance * TAB_WIDTH); - } - } else if(codepoint == '\n') { - position.x = text->position.x; - position.y += text->font->character_size; - } else { - if(mgl_font_get_glyph(text->font, codepoint, &glyph) == 0) { - mgl_text_draw_glyph(context, &glyph, position); - position.x += glyph.advance; - } - } - i += clen; - } + mgl_text_for_each_codepoint(text, text_draw_callback, &text_draw_userdata); context->gl.glEnd(); mgl_texture_use(NULL); } -- cgit v1.2.3