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 initialize_freetype()
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 = atlas_new(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 AlbaDynArray* vertices, AlbaDynArray* attributes, AlbaDynArray* 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_dynarray_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_dynarray_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_dynarray_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 initialize_freetype();
122
123 if (length == 0)
124 {
125 length = strlen(text);
126 }
127
128 pthread_mutex_lock(&freetype_mutex);
129 atlas_add_chars(&atlas, length, text);
130 atlas_generate_image(&atlas);
131 pthread_mutex_unlock(&freetype_mutex);
132
133 // TODO: do this like buffers lazily
134 window->text.texture = create_texture(
135 window,
136 WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
137 atlas.size,
138 WGPUTextureFormat_BGRA8UnormSrgb, 1, // TODO: do not assume format
139 atlas.image
140 );
141 window->text.dirty = 1;
142
143 AlbaDynArray vertices = dynarray_new(sizeof(AlbaVector), length * 4);
144 AlbaDynArray attributes = dynarray_new(sizeof(AlbaAttribute), length * 4);
145 AlbaDynArray indices = dynarray_new(sizeof(uint32_t), length * 6);
146
147 atlas_generate_text_geometry(
148 &atlas,
149 pos, color, length, text,
150 &vertices, &attributes, &indices
151 );
152
153 draw_triangles_indexed_render_pass(
154 &window->text,
155 length * 4, vertices.data, attributes.data,
156 length * 6, indices.data
157 );
158}