#include "../../include/mgl/graphics/font.h" #include "../../include/mgl/system/fileutils.h" #include #define STB_RECT_PACK_IMPLEMENTATION #include "../../external/stb_rect_pack.h" #define STB_TRUETYPE_IMPLEMENTATION #include "../../external/stb_truetype.h" /* TODO: Test and fix .tcc files */ static unsigned int to_div2_ceil(unsigned int value) { const uint32_t v = value; return v + (v & 1); } int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int character_size) { self->texture.id = 0; self->font_atlas.atlas = NULL; self->font_atlas.width = 0; self->font_atlas.height = 0; self->character_size = character_size; self->packed_chars = NULL; self->num_packed_chars = 0; mgl_memory_mapped_file mapped_file; if(mgl_mapped_file_load(filepath, &mapped_file, &(mgl_memory_mapped_file_load_options){ .readable = true, .writable = false }) != 0) { fprintf(stderr, "Error: failed to load font %s, error: mgl_load_file failed\n", filepath); return -1; } stbtt_fontinfo font; if(!stbtt_InitFont(&font, mapped_file.data, stbtt_GetFontOffsetForIndex(mapped_file.data, 0))) { fprintf(stderr, "Error: failed to load font %s, error: stbtt_InitFont failed\n", filepath); mgl_mapped_file_unload(&mapped_file); return -1; } self->num_packed_chars = 256; self->packed_chars = malloc(self->num_packed_chars * sizeof(stbtt_packedchar)); if(!self->packed_chars) { fprintf(stderr, "Error: failed to load font %s, error: out of memory\n", filepath); goto error; } bool atlas_created = false; /* TODO: Optimize */ /* Find optimal size for atlas, starting from small to large */ for(int i = 0; i < 4; ++i) { /* This to_div2_ceil is needed because otherwise for character sizes such as 33 which are not divisable by 2 causes the font texture to be skewed. I dont know why this happens. Maybe a bug in stbtt? TODO: Figure out why it happens. */ self->font_atlas.width = (14 + (8 * i)) * to_div2_ceil(self->character_size); self->font_atlas.height = (14 + (8 * i)) * to_div2_ceil(self->character_size); unsigned char *new_atlas = realloc(self->font_atlas.atlas, self->font_atlas.width * self->font_atlas.height); if(!new_atlas) { fprintf(stderr, "Error: failed to load font %s, error: out of memory\n", filepath); goto error; } self->font_atlas.atlas = new_atlas; stbtt_pack_context pc; if(!stbtt_PackBegin(&pc, self->font_atlas.atlas, self->font_atlas.width, self->font_atlas.height, self->font_atlas.width, 1, NULL)) { fprintf(stderr, "Error: failed to load font %s, error: stbtt_PackBegin failed\n", filepath); goto error; } stbtt_PackSetOversampling(&pc, 2, 2); if(!stbtt_PackFontRange(&pc, mapped_file.data, 0, self->character_size, 0, self->num_packed_chars, self->packed_chars)) { stbtt_PackEnd(&pc); continue; } /*if(!stbtt_PackFontRange(&pc, mapped_file.data, 0, self->character_size, 0x00004E00, self->num_packed_chars, self->packed_chars)) { stbtt_PackEnd(&pc); continue; }*/ stbtt_PackEnd(&pc); atlas_created = true; break; } if(!atlas_created) { fprintf(stderr, "Error: failed to load font %s, error: failed to create atlas\n", filepath); goto error; } if(mgl_texture_load_from_memory(&self->texture, self->font_atlas.atlas, self->font_atlas.width, self->font_atlas.height, MGL_IMAGE_FORMAT_ALPHA, NULL) != 0) { fprintf(stderr, "Error: failed to load font %s, error: mgl_texture_load_from_memory failed\n", filepath); goto error; } /* TODO: Use stbtt_GetCodepointSDF */ mgl_mapped_file_unload(&mapped_file); return 0; error: mgl_mapped_file_unload(&mapped_file); mgl_font_unload(self); return -1; } void mgl_font_unload(mgl_font *self) { mgl_texture_unload(&self->texture); free(self->font_atlas.atlas); self->font_atlas.atlas = NULL; self->font_atlas.width = 0; self->font_atlas.height = 0; free(self->packed_chars); self->packed_chars = NULL; self->num_packed_chars = 0; } int mgl_font_get_glyph(const mgl_font *self, uint32_t codepoint, mgl_font_glyph *glyph) { stbtt_packedchar *packed_chars = self->packed_chars; if(codepoint < 0 || codepoint >= 0 + self->num_packed_chars) return -1; float x = 0.0f; float y = 0.0f; stbtt_aligned_quad quad; stbtt_GetPackedQuad(packed_chars, self->font_atlas.width, self->font_atlas.height, codepoint, &x, &y, &quad, 0); glyph->position = (mgl_vec2f){ quad.x0, quad.y0 }; glyph->size = (mgl_vec2f){ quad.x1 - quad.x0, quad.y1 - quad.y0 }; glyph->texture_position = (mgl_vec2f){ quad.s0, quad.t0 }; glyph->texture_size = (mgl_vec2f){ quad.s1 - quad.s0, quad.t1 - quad.t0 }; glyph->advance = x; return 0; }