aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-09-10 22:14:18 +0200
committerdec05eba <dec05eba@protonmail.com>2024-09-10 22:14:18 +0200
commit2a91f0dca320f6be061224cb42ceb31c32a51b6c (patch)
treef6126c8f5fee5ead053bc9ef58e5d5c61b5939cc
parentb52ed4028309fd20e3955c5f1a6adce9aa12c87b (diff)
Add mgl_text_set_max_width and mgl_text_set_max_rows
-rw-r--r--include/mgl/graphics/text.h6
-rw-r--r--src/graphics/text.c152
-rw-r--r--tests/main.c2
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);