diff options
-rw-r--r-- | include/mgl/graphics/text.h | 6 | ||||
-rw-r--r-- | src/graphics/text.c | 152 | ||||
-rw-r--r-- | tests/main.c | 2 |
3 files changed, 92 insertions, 68 deletions
diff --git a/include/mgl/graphics/text.h b/include/mgl/graphics/text.h index 89ae541..4dd0219 100644 --- a/include/mgl/graphics/text.h +++ b/include/mgl/graphics/text.h @@ -23,6 +23,8 @@ typedef struct { mgl_vec2f position; mgl_vec2f bounds; bool bounds_dirty; + float max_width; + unsigned int max_rows; } mgl_text; /* @@ -41,6 +43,10 @@ void mgl_text_set_string(mgl_text *self, const char *str, size_t str_size); void mgl_text_set_font(mgl_text *self, mgl_font *font); void mgl_text_set_position(mgl_text *self, mgl_vec2f position); void mgl_text_set_color(mgl_text *self, mgl_color color); +/* If |max_width| is 0 then the text has no max width */ +void mgl_text_set_max_width(mgl_text *self, float max_width); +/* If |max_rows| is 0 then the text can display an unlimited amount of rows */ +void mgl_text_set_max_rows(mgl_text *self, unsigned int max_rows); mgl_vec2f mgl_text_get_bounds(mgl_text *self); /* Returns the position of the character on the screen (relative to the current mgl_view) */ mgl_vec2f mgl_text_find_character_pos(mgl_text *self, size_t index); diff --git a/src/graphics/text.c b/src/graphics/text.c index f84169a..89994c5 100644 --- a/src/graphics/text.c +++ b/src/graphics/text.c @@ -9,15 +9,38 @@ #define TAB_WIDTH 4 +typedef struct { + size_t codepoint_index; + uint32_t codepoint; + float kerning; + float glyph_width; + const mgl_font_glyph *glyph; + mgl_vec2f glyph_offset; +} codepoint_loop_callback_data; + +static float max_float(float a, float b) { + return a >= b ? a : b; +} + +static float float_abs(float value) { + return value >= 0.0f ? value : -value; +} + /* 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); +typedef bool (*codepoint_loop_callback)(codepoint_loop_callback_data callback_data, void *userdata); static void mgl_text_for_each_codepoint(const mgl_text *self, codepoint_loop_callback callback, void *userdata) { mgl_font_glyph glyph; size_t codepoint_index = 0; + float kerning = 0.0f; + float glyph_width = 0.0f; + uint32_t prev_codepoint = 0; + mgl_vec2f glyph_offset = {0, 0}; + unsigned int rows = 0; + for(size_t i = 0; i < self->text_size;) { unsigned char *cp = (unsigned char*)&self->text[i]; - uint32_t codepoint; - size_t clen; + uint32_t codepoint = 0; + size_t clen = 0; if(!mgl_utf8_decode(cp, self->text_size - i, &codepoint, &clen)) { codepoint = *cp; clen = 1; @@ -25,52 +48,59 @@ static void mgl_text_for_each_codepoint(const mgl_text *self, codepoint_loop_cal if(codepoint == '\t') { if(mgl_font_get_glyph(self->font, ' ', &glyph) == 0) { - if(!callback(codepoint_index, codepoint, &glyph, userdata)) - return; + kerning = mgl_font_get_kerning(self->font, prev_codepoint, codepoint); + glyph_width = glyph.advance * TAB_WIDTH; + } else { + goto next; } } else if(codepoint == '\n') { - if(!callback(codepoint_index, codepoint, &glyph, userdata)) - return; + kerning = 0.0f; + glyph_width = 0.0f; } else { if(mgl_font_get_glyph(self->font, codepoint, &glyph) == 0) { - if(!callback(codepoint_index, codepoint, &glyph, userdata)) - return; + kerning = mgl_font_get_kerning(self->font, prev_codepoint, codepoint); + glyph_width = glyph.advance; + } else { + goto next; } } + if(((glyph_offset.x + kerning + glyph_width > self->max_width) && self->max_width > 0.001f) || codepoint == '\n') { + glyph_offset.x = 0.0f; + if(codepoint != '\n') + glyph_offset.y += self->font->character_size; + prev_codepoint = 0; + + ++rows; + if(rows == self->max_rows) + return; + } else { + glyph_offset.x += kerning; + } + + if(!callback((codepoint_loop_callback_data){ codepoint_index, codepoint, kerning, glyph_width, &glyph, glyph_offset }, userdata)) + return; + + glyph_offset.x += glyph_width; + if(codepoint == '\n') + glyph_offset.y += self->font->character_size; + + prev_codepoint = codepoint; + + next: 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; - uint32_t prev_codepoint; } CalculateBoundsUserdata; -static bool calculate_bounds_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { - (void)codepoint_index; - +static bool calculate_bounds_callback(codepoint_loop_callback_data callback_data, void *userdata) { CalculateBoundsUserdata *calculate_bounds_userdata = userdata; - if(codepoint == '\t') { - calculate_bounds_userdata->line_width += (glyph->advance * TAB_WIDTH) + mgl_font_get_kerning(calculate_bounds_userdata->font, calculate_bounds_userdata->prev_codepoint, codepoint); - 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; - calculate_bounds_userdata->prev_codepoint = 0; - } else { - calculate_bounds_userdata->line_width += glyph->advance + mgl_font_get_kerning(calculate_bounds_userdata->font, calculate_bounds_userdata->prev_codepoint, codepoint); - calculate_bounds_userdata->bounds.x = max_float(calculate_bounds_userdata->bounds.x, calculate_bounds_userdata->line_width); - } - - calculate_bounds_userdata->prev_codepoint = codepoint; + calculate_bounds_userdata->bounds.x = max_float(calculate_bounds_userdata->bounds.x, callback_data.glyph_offset.x + callback_data.glyph_width); + calculate_bounds_userdata->bounds.y = max_float(calculate_bounds_userdata->bounds.y, callback_data.glyph_offset.y); return true; } @@ -78,9 +108,6 @@ 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; - calculate_bounds_userdata.prev_codepoint = 0; mgl_text_for_each_codepoint(self, calculate_bounds_callback, &calculate_bounds_userdata); return calculate_bounds_userdata.bounds; } @@ -92,6 +119,8 @@ void mgl_text_init(mgl_text *self, mgl_font *font, const char *str, size_t str_s self->text = NULL; self->text_size = 0; self->bounds_dirty = true; + self->max_width = 0.0f; + self->max_rows = 0; mgl_text_set_string(self, str, str_size); } @@ -122,6 +151,18 @@ void mgl_text_set_color(mgl_text *self, mgl_color color) { self->color = color; } +void mgl_text_set_max_width(mgl_text *self, float max_width) { + if(float_abs(max_width - self->max_width < 0.001)) + self->bounds_dirty = true; + self->max_width = max_width; +} + +void mgl_text_set_max_rows(mgl_text *self, unsigned int max_rows) { + if(max_rows != self->max_rows) + self->bounds_dirty = true; + self->max_rows = max_rows; +} + mgl_vec2f mgl_text_get_bounds(mgl_text *self) { if(self->bounds_dirty) { self->bounds_dirty = false; @@ -139,26 +180,13 @@ typedef struct { mgl_vec2f position; const mgl_text *text; size_t index; - uint32_t prev_codepoint; } FindCharacterPosUserdata; -static bool find_character_pos_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { +static bool find_character_pos_callback(codepoint_loop_callback_data callback_data, 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) + mgl_font_get_kerning(find_character_pos_userdata->text->font, find_character_pos_userdata->prev_codepoint, codepoint); - } 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; - find_character_pos_userdata->prev_codepoint = 0; - } else { - find_character_pos_userdata->position.x += glyph->advance + mgl_font_get_kerning(find_character_pos_userdata->text->font, find_character_pos_userdata->prev_codepoint, codepoint); - } - - find_character_pos_userdata->prev_codepoint = codepoint; - return true; + find_character_pos_userdata->position.x = find_character_pos_userdata->position.x + callback_data.glyph_offset.x; + find_character_pos_userdata->position.y = find_character_pos_userdata->position.y + callback_data.glyph_offset.y; + return callback_data.codepoint_index < find_character_pos_userdata->index; } mgl_vec2f mgl_text_find_character_pos(mgl_text *self, size_t index) { @@ -166,7 +194,6 @@ mgl_vec2f mgl_text_find_character_pos(mgl_text *self, size_t index) { find_character_pos_userdata.position = self->position; find_character_pos_userdata.text = self; find_character_pos_userdata.index = index; - find_character_pos_userdata.prev_codepoint = 0; 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 }; } @@ -189,24 +216,14 @@ typedef struct { mgl_vec2f position; const mgl_text *text; mgl_context *context; - uint32_t prev_codepoint; } TextDrawUserdata; -static bool text_draw_callback(size_t codepoint_index, uint32_t codepoint, const mgl_font_glyph *glyph, void *userdata) { - (void)codepoint_index; +static bool text_draw_callback(codepoint_loop_callback_data callback_data, void *userdata) { TextDrawUserdata *text_draw_userdata = userdata; - if(codepoint == '\t') { - text_draw_userdata->position.x += (glyph->advance * TAB_WIDTH) + mgl_font_get_kerning(text_draw_userdata->text->font, text_draw_userdata->prev_codepoint, codepoint); - } 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; - text_draw_userdata->prev_codepoint = 0; - } else { - text_draw_userdata->position.x += mgl_font_get_kerning(text_draw_userdata->text->font, text_draw_userdata->prev_codepoint, codepoint); - mgl_text_draw_glyph(text_draw_userdata->context, glyph, (mgl_vec2i){ text_draw_userdata->position.x, text_draw_userdata->position.y }); - text_draw_userdata->position.x += glyph->advance; + if(callback_data.codepoint != '\t' && callback_data.codepoint != '\n') { + mgl_text_draw_glyph(text_draw_userdata->context, callback_data.glyph, + (mgl_vec2i){ text_draw_userdata->position.x + callback_data.glyph_offset.x, text_draw_userdata->position.y + callback_data.glyph_offset.y }); } - text_draw_userdata->prev_codepoint = codepoint; return true; } @@ -224,7 +241,6 @@ void mgl_text_draw(mgl_context *context, mgl_text *text) { text_draw_userdata.position = (mgl_vec2f){ text->position.x, text->position.y }; text_draw_userdata.text = text; text_draw_userdata.context = context; - text_draw_userdata.prev_codepoint = 0; mgl_window_set_texture_blend_func(context->current_window); diff --git a/tests/main.c b/tests/main.c index f52e3d4..3ec5738 100644 --- a/tests/main.c +++ b/tests/main.c @@ -61,6 +61,8 @@ static void draw(mgl_window *window, void *userdata) { mgl_text text; mgl_text_init(&text, u->font, str, strlen(str)); + mgl_text_set_max_width(&text, 200.0f); + mgl_text_set_max_rows(&text, 5); mgl_text_draw(context, &text); mgl_text_deinit(&text); |