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