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