#include "text.h" #include #include #include #include #include "cglm/types.h" #include "cglm/vec2.h" #include "freetype/freetype.h" #include "hash.h" #include "rendering.h" #include "utils.h" static bool wayc_font_cache_lookup(struct font_s* font, codepoint_t codepoint, struct glyph_s* out) { struct glyph_s glyph; bool ok = wayc_hashmap_get(&font->cache, &codepoint, &glyph); if (ok) *out = glyph; return ok; } static void wayc_font_cache_insert(struct font_s* font, codepoint_t codepoint, struct glyph_s glyph) { wayc_notnull(font); wayc_hashmap_insert(&font->cache, &codepoint, &glyph); } enum font_context_error_e wayc_font_context_init(struct font_context_s* ctx) { wayc_notnull(ctx); FT_Error err = FT_Init_FreeType(&ctx->library); if (err) return FONT_CONTEXT_ERROR_LIBRARY_LOADING; return FONT_CONTEXT_ERROR_NONE; } void wayc_font_context_deinit(struct font_context_s* ctx) { wayc_notnull(ctx); FT_Done_FreeType(ctx->library); } enum font_error_e wayc_font_init(struct font_s* font, struct font_context_s* ctx, u32 fsize, const char* path) { wayc_notnull(font); wayc_notnull(ctx); wayc_notnull(path); bool success = false; i32 fd = open(path, O_RDONLY); if (fd < 0) return FONT_ERROR_FILE_LOAD; wayc_defer(close(fd)); isize size = lseek(fd, 0, SEEK_END); if (size < 0) return FONT_ERROR_FILE_LOAD; lseek(fd, 0, SEEK_SET); u8* data = (u8*)mi_malloc(size); wayc_defer_cond({ mi_free(data); }, success, true); isize _read = read(fd, data, size); if (_read < 0 || _read != size) return FONT_ERROR_FILE_LOAD; FT_Error err = FT_New_Memory_Face(ctx->library, data, size, 0, &font->face); if (err) return FONT_ERROR_FONT_LOAD; wayc_defer_cond(FT_Done_Face(font->face);, success, true); err = FT_Set_Pixel_Sizes(font->face, 0, fsize); if (err) return FONT_ERROR_FONT_LOAD; font->data = data; wayc_hashmap_init(&font->cache); success = true; return FONT_ERROR_NONE; } enum font_error_e wayc_font_lookup(struct font_s* font, struct atlas_s* atlas, struct atlas_packer_s* packer, codepoint_t codepoint, struct glyph_s* out) { wayc_notnull(font); wayc_notnull(atlas); wayc_notnull(out); FT_Face face = font->face; bool found = wayc_font_cache_lookup(font, codepoint, out); if (found) return FONT_ERROR_NONE; FT_UInt glyph_index = FT_Get_Char_Index(face, codepoint); if (glyph_index == 0) return FONT_ERROR_NOT_MATCH; FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); FT_GlyphSlot slot = face->glyph; FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); FT_Bitmap bitmap = slot->bitmap; if (bitmap.pitch != (i32)bitmap.width) wayc_panic("unsupported pitch"); ivec2 size = {(i32)bitmap.width, (i32)bitmap.rows}; ivec2 uv0; bool ok = wayc_atlas_packer_allocate(packer, WAYC_X(size), WAYC_Y(size), uv0); if (!ok) return FONT_ERROR_ATLAS_FULL; ok = wayc_atlas_set(atlas, IMAGE_DATA_TYPE_UNSIGNED_BYTE, uv0, WAYC_X(size), WAYC_Y(size), IMAGE_FORMAT_RED_ALIGNMENT, bitmap.buffer); if (!ok) return FONT_ERROR_ATLAS_FULL; struct glyph_s glyph = {}; glyph.bearing_x = slot->bitmap_left; glyph.bearing_y = slot->bitmap_top; glyph.advance = (f32)slot->advance.x / WAYC_SCALE; wayc_font_cache_insert(font, codepoint, glyph); *out = glyph; return FONT_ERROR_NONE; } void wayc_font_deinit(struct font_s* font) { wayc_notnull(font); wayc_hashmap_deinit(&font->cache); FT_Done_Face(font->face); mi_free(font->data); } enum text_error_e wayc_text_vertices(struct text_s* text, struct text_vertex_s** out) { wayc_notnull(text); wayc_notnull(out); bool success = false; usize nverts = WAYC_LETTER_NVERTICES * wayc_string_length(&text->string); usize verts_bytes = nverts * sizeof(struct text_vertex_s); struct text_vertex_s* verts = (struct text_vertex_s*)mi_malloc(verts_bytes); wayc_defer_cond(mi_free(verts), success, true); vec2 pen = GLM_VEC2_ZERO_INIT; usize idx = 0; for (usize i = 0; i < wayc_string_length(&text->string); i++) { codepoint_t codep = wayc_string_data(&text->string)[i]; // TODO: do proper UTF-8 decoding struct glyph_s glyph; enum font_error_e err = wayc_font_lookup(text->font, text->atlas, text->packer, codep, &glyph); if (err != FONT_ERROR_NONE) return TEXT_ERROR_ATLAS; f32 x0 = WAYC_X(pen) + glyph.bearing_x; f32 y0 = WAYC_Y(pen) - glyph.bearing_y; f32 x1 = x0 + WAYC_X(glyph.uv1) - WAYC_X(glyph.uv0); f32 y1 = y0 + WAYC_Y(glyph.uv1) - WAYC_Y(glyph.uv0); f32 u0 = (f32)WAYC_X(glyph.uv0) / text->atlas->width; f32 v0 = (f32)WAYC_Y(glyph.uv0) / text->atlas->height; f32 u1 = (f32)WAYC_X(glyph.uv1) / text->atlas->width; f32 v1 = (f32)WAYC_Y(glyph.uv1) / text->atlas->height; struct text_vertex_s ps0 = {{x0, y0}, {u0, v0}}; struct text_vertex_s ps1 = {{x0, y1}, {u0, v1}}; struct text_vertex_s ps2 = {{x1, y1}, {u1, v1}}; struct text_vertex_s ps3 = {{x1, y0}, {u1, v0}}; verts[idx++] = ps0; verts[idx++] = ps1; verts[idx++] = ps2; verts[idx++] = ps0; verts[idx++] = ps2; verts[idx++] = ps3; } success = true; *out = verts; return TEXT_ERROR_NONE; }