src/text.c
  1#include "alba.h"
  2#include "internal.h"
  3
  4#include "freetype/ftglyph.h"
  5
  6extern char _binary_Roboto_Regular_ttf_start[];
  7extern char _binary_Roboto_Regular_ttf_end[];
  8
  9void ensure_freetype_initialized()
 10{
 11    // already initialized
 12    if (freetype != NULL) return;
 13
 14    pthread_mutex_lock(&freetype_mutex);
 15    if (FT_Init_FreeType(&freetype))
 16    {
 17        fprintf(stderr, "error: initializeing FreeType failed");
 18        pthread_mutex_unlock(&freetype_mutex);
 19        exit(1);
 20    }
 21
 22    FT_Error error = FT_New_Memory_Face(
 23        freetype,
 24        _binary_Roboto_Regular_ttf_start,
 25        _binary_Roboto_Regular_ttf_end - _binary_Roboto_Regular_ttf_start,
 26        0, // face index
 27        &roboto
 28    );
 29    if (error)
 30    {
 31        fprintf(stderr, "error: cound not load built in Roboto font");
 32    }
 33    error = FT_Set_Pixel_Sizes(roboto, 0 /* width, same as height */, 18);
 34    if (error)
 35    {
 36        fprintf(stderr, "error: cound not set Roboto face size");
 37    }
 38
 39    atlas = create_atlas(1024);
 40
 41    pthread_mutex_unlock(&freetype_mutex);
 42}
 43
 44void atlas_add_chars(AlbaAtlas* atlas, uint64_t length, const char* text)
 45{
 46    for (uint64_t i = 0; i < length; i++)
 47    {
 48        atlas_append(atlas, roboto, text[i]); // TODO: unicode
 49    }
 50}
 51
 52uint64_t hash_glyph2(const void* item, const uint64_t seed0, const uint64_t seed1)
 53{
 54    return hashmap_murmur((FT_Long*)item, sizeof(FT_Long), seed0, seed1);
 55}
 56
 57void atlas_generate_text_geometry(
 58    const AlbaAtlas* atlas,
 59    const AlbaVector pos, const AlbaColor color,
 60    const uint64_t length, const char* text,
 61    AlbaArray* vertices, AlbaArray* attributes, AlbaArray* indices
 62)
 63{
 64    AlbaVector current = pos;
 65    for (uint64_t i = 0; i < length; i++)
 66    {
 67        // TODO: mutex?
 68        const FT_Long character = text[i]; // TODO: unicode
 69        const AlbaGlyph* glyph = hashmap_get(atlas->glyphs, &character);
 70        if (glyph == NULL)
 71        {
 72            fprintf(stderr, "error: unable to find glyph for character '%s'\n", (char*)&character);
 73            exit(1);
 74        }
 75        const FT_Glyph_Metrics metrics = glyph->metrics;
 76
 77        const AlbaVector origin = {
 78            current.x + metrics.horiBearingX / 64,
 79            current.y - metrics.horiBearingY / 64,
 80        };
 81        const uint32_t vertex_count = vertices->length;
 82        alba_array_extend(
 83            vertices, 4, (AlbaVector[]){
 84                origin,
 85                origin.x, origin.y + metrics.height / 64,
 86                origin.x + metrics.width / 64, origin.y + metrics.height / 64,
 87                origin.x + metrics.width / 64, origin.y,
 88            });
 89
 90        alba_array_extend(
 91            attributes, 4, (AlbaAttribute[]){
 92                {color, glyph->start},
 93                {color, (AlbaVector){glyph->start.x, glyph->end.y}},
 94                {color, glyph->end},
 95                {color, (AlbaVector){glyph->end.x, glyph->start.y}},
 96            });
 97
 98        alba_array_extend(
 99            indices, 6, (uint32_t[]){
100                // first triangle
101                vertex_count,
102                vertex_count + 1,
103                vertex_count + 2,
104                // second triangle
105                vertex_count,
106                vertex_count + 2,
107                vertex_count + 3
108            });
109
110        current.x += metrics.horiAdvance / 64;
111        // current_y += metrics.vertAdvance / 64;
112    }
113}
114
115void alba_draw_text(
116    AlbaWindow* window,
117    const AlbaVector pos, const AlbaColor color,
118    uint64_t length, const char* text
119)
120{
121    ensure_freetype_initialized();
122
123    if (length == 0)
124    {
125        length = strlen(text);
126    }
127
128
129    pthread_mutex_lock(&freetype_mutex);
130    atlas_add_chars(&atlas, length, text);
131    atlas_generate_image(&atlas);
132    pthread_mutex_unlock(&freetype_mutex);
133
134    // TODO: do this like buffers lazily
135    window->layers[0].text.texture = create_texture(
136        window,
137        WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
138        atlas.size,
139        WGPUTextureFormat_BGRA8UnormSrgb, 1, // TODO: do not assume format
140        atlas.image
141    );
142    window->layers[0].text.dirty = 1;
143
144    AlbaArray vertices = alba_create_array(sizeof(AlbaVector), length * 4);
145    AlbaArray attributes = alba_create_array(sizeof(AlbaAttribute), length * 4);
146    AlbaArray indices = alba_create_array(sizeof(uint32_t), length * 6);
147
148    atlas_generate_text_geometry(
149        &atlas,
150        pos, color, length, text,
151        &vertices, &attributes, &indices
152    );
153
154    draw_triangles_indexed_render_pass(
155        &window->layers[0].text,
156        length * 4, vertices.data, attributes.data,
157        length * 6, indices.data
158    );
159}