aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mgl/graphics/font.h2
-rw-r--r--include/mgl/system/utf8.h7
-rw-r--r--src/graphics/font.c19
-rw-r--r--src/graphics/font_char_map.c1
-rw-r--r--src/system/utf8.c17
-rw-r--r--tests/main.c27
6 files changed, 66 insertions, 7 deletions
diff --git a/include/mgl/graphics/font.h b/include/mgl/graphics/font.h
index adcc8a7..6b7eb34 100644
--- a/include/mgl/graphics/font.h
+++ b/include/mgl/graphics/font.h
@@ -25,7 +25,7 @@ typedef struct {
} mgl_font_atlas;
struct mgl_font {
- mgl_texture texture;
+ mgl_texture texture; /* Font texture coordinates are in pixel space */
mgl_font_atlas font_atlas;
unsigned int character_size;
mgl_font_char_map char_map;
diff --git a/include/mgl/system/utf8.h b/include/mgl/system/utf8.h
index 7120f1a..794884d 100644
--- a/include/mgl/system/utf8.h
+++ b/include/mgl/system/utf8.h
@@ -9,6 +9,13 @@
Returns false on failure. |decoded_codepoint| is set to |str[0]| if size > 0 and |codepoint_length| is set to 1
*/
bool mgl_utf8_decode(const unsigned char *str, size_t size, uint32_t *decoded_codepoint, size_t *codepoint_length);
+/*
+ |str| should be the start of the utf8 string and |size| is the size of the string.
+ Returns the index of the start of the codepoint that starts at or before |offset|,
+ or if the string contains invalid utf8 then the index to the invalid character is returned.
+ Returns 0 if start of codepoint is not found.
+*/
+size_t mgl_utf8_get_start_of_codepoint(const unsigned char *str, size_t size, size_t offset);
#endif /* MGL_UTF8_H */
diff --git a/src/graphics/font.c b/src/graphics/font.c
index 0044d96..f73c023 100644
--- a/src/graphics/font.c
+++ b/src/graphics/font.c
@@ -4,8 +4,6 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include "../../external/stb_truetype.h"
-/* TODO: Use correct atlas padding after resize. Its incorrect because padding from previous size doesn't accumulate */
-
/* Need padding so filtering doesn't touch pixels in another glyphs area */
#define GLYPH_PADDING 2
#define GLYPH_UPSAMPLE 1
@@ -57,6 +55,14 @@ void mgl_font_unload(mgl_font *self) {
self->font_info = NULL;
}
+static int align_up_to_next_value_of(int value, int alignment) {
+ int offset_to_next_alignment = value % alignment;
+ if(offset_to_next_alignment == 0)
+ return value;
+ else
+ return value + (alignment - offset_to_next_alignment);
+}
+
static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width) {
if(self->font_atlas.pointer_position.x + glyph_width + GLYPH_PADDING >= self->font_atlas.width) {
if(self->font_atlas.pointer_position.y + self->current_line_max_height + GLYPH_PADDING >= self->font_atlas.height && self->font_atlas.render_section != MGL_ATLAS_SECTION_RIGHT) {
@@ -87,8 +93,8 @@ static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width)
if(mgl_texture_resize(&self->texture, self->font_atlas.width * 2, self->font_atlas.height * 2, NULL) == 0) {
self->font_atlas.prev_width = self->font_atlas.width;
self->font_atlas.prev_height = self->font_atlas.height;
- self->font_atlas.width *= 2;
- self->font_atlas.height *= 2;
+ self->font_atlas.width = align_up_to_next_value_of(self->font_atlas.width * 2, self->character_size);
+ self->font_atlas.height = align_up_to_next_value_of(self->font_atlas.height * 2, self->character_size);
self->font_atlas.render_section = MGL_ATLAS_SECTION_RIGHT;
self->font_atlas.pointer_position.x = self->font_atlas.prev_width;
@@ -99,6 +105,7 @@ static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width)
mgl_font_glyph glyph_dummy;
mgl_font_char_iterator font_char_it = mgl_font_char_map_begin(&self->char_map);
while(font_char_it.value) {
+ /* TODO: Update glyph without calling this, since this needs to get glyph from hash map again */
mgl_font_get_glyph(self, font_char_it.value->key, &glyph_dummy);
mgl_font_char_iterator_next(&font_char_it);
}
@@ -108,7 +115,7 @@ static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width)
} else if(self->font_atlas.render_section == MGL_ATLAS_SECTION_RIGHT && self->font_atlas.pointer_position.y + self->current_line_max_height + GLYPH_PADDING >= self->font_atlas.prev_height) {
self->font_atlas.render_section = MGL_ATLAS_SECTION_BOTTOM;
self->font_atlas.pointer_position.x = GLYPH_PADDING;
- self->font_atlas.pointer_position.y = self->font_atlas.prev_height;
+ self->font_atlas.pointer_position.y += self->current_line_max_height + GLYPH_PADDING;
} else {
if(self->font_atlas.render_section != MGL_ATLAS_SECTION_RIGHT) {
self->font_atlas.pointer_position.x = GLYPH_PADDING;
@@ -123,7 +130,7 @@ static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width)
int mgl_font_get_glyph(mgl_font *self, uint32_t codepoint, mgl_font_glyph *glyph) {
if(self->font_atlas.width == 0) {
- const int initial_atlas_size = 128;
+ const int initial_atlas_size = align_up_to_next_value_of(128, self->character_size);
/* TODO: Scale by character size */
mgl_texture_load_options load_options = {
.compressed = false,
diff --git a/src/graphics/font_char_map.c b/src/graphics/font_char_map.c
index abaa56a..64c4674 100644
--- a/src/graphics/font_char_map.c
+++ b/src/graphics/font_char_map.c
@@ -12,6 +12,7 @@
#error Unsupported system. Only systems where a pointer is 32-bits or 64-bits are supported.
#endif
+/* |align| should be a multiple of 2 */
static size_t align_to(size_t value, size_t align) {
const size_t is_aligned = (value & (align - 1));
if(is_aligned == 0) {
diff --git a/src/system/utf8.c b/src/system/utf8.c
index 201fedf..5d5ec79 100644
--- a/src/system/utf8.c
+++ b/src/system/utf8.c
@@ -73,3 +73,20 @@ bool mgl_utf8_decode(const unsigned char *str, size_t size, uint32_t *decoded_co
*decoded_codepoint = codepoint;
return true;
}
+
+/* TODO: Optimize (remove branching, etc) */
+size_t mgl_utf8_get_start_of_codepoint(const unsigned char *str, size_t size, size_t offset) {
+ if(size == 0)
+ return 0;
+
+ if(offset > size - 1)
+ offset = size - 1;
+
+ /* i <= offset is an overflow (underflow?) check */
+ for(size_t i = offset; i <= offset; --i) {
+ if((str[i] & 0xC0) != 0x80)
+ return i;
+ }
+
+ return 0;
+}
diff --git a/tests/main.c b/tests/main.c
index cee3ec7..b341bb3 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -13,6 +13,7 @@
#include <mgl/graphics/shader.h>
#include <mgl/system/clock.h>
#include <mgl/system/fileutils.h>
+#include <mgl/system/utf8.h>
#define require_equals(a, b) do { if((a) != (b)) { fprintf(stderr, "Assert failed on line %d: %s == %s\n", __LINE__, #a, #b); abort(); } } while(0)
#define require_not_equals(a, b) do { if((a) == (b)) { fprintf(stderr, "Assert failed on line %d: %s != %s\n", __LINE__, #a, #b); abort(); } } while(0)
@@ -189,8 +190,34 @@ static void test_hash_map() {
mgl_font_char_map_deinit(&char_map);
}
+static void test_utf8() {
+ uint32_t codepoint = 0;
+ size_t codepoint_length = 0;
+
+ require_equals(mgl_utf8_decode("a", 1, &codepoint, &codepoint_length), true);
+ require_equals(codepoint, 0x61);
+ require_equals(codepoint_length, 1);
+
+ require_equals(mgl_utf8_decode("á", 2, &codepoint, &codepoint_length), true);
+ require_equals(codepoint, 0xE1);
+ require_equals(codepoint_length, 2);
+
+ require_equals(mgl_utf8_decode("‡", 3, &codepoint, &codepoint_length), true);
+ require_equals(codepoint, 0x2021);
+ require_equals(codepoint_length, 3);
+
+ require_equals(mgl_utf8_decode("𒀀", 4, &codepoint, &codepoint_length), true);
+ require_equals(codepoint, 0x12000);
+ require_equals(codepoint_length, 4);
+
+ require_equals(mgl_utf8_get_start_of_codepoint("abc", 3, 0), 0);
+ require_equals(mgl_utf8_get_start_of_codepoint("abc", 3, 2), 2);
+ require_equals(mgl_utf8_get_start_of_codepoint("abö", 3, 4), 2);
+}
+
int main(int argc, char **argv) {
test_hash_map();
+ test_utf8();
if(mgl_init() != 0)
return 1;