diff options
-rw-r--r-- | include/mgl/graphics/font.h | 2 | ||||
-rw-r--r-- | include/mgl/system/utf8.h | 7 | ||||
-rw-r--r-- | src/graphics/font.c | 19 | ||||
-rw-r--r-- | src/graphics/font_char_map.c | 1 | ||||
-rw-r--r-- | src/system/utf8.c | 17 | ||||
-rw-r--r-- | tests/main.c | 27 |
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; |