Hide non-applicable links
[llpp.git] / glfont.c
blob1297ac86ebd1020b7731ccb6f46ce988ef9fee57
1 /* This is a slightly modified
2 https://github.com/ccxvii/snippets/blob/master/glfont.c by Tor Andersson
3 */
4 /*
5 * A very simple font cache and rasterizer that uses freetype
6 * to draw fonts from a single OpenGL texture. The code uses
7 * a linear-probe hashtable, and writes new glyphs into
8 * the texture using glTexSubImage2D. When the texture fills
9 * up, or the hash table gets too crowded, everything is wiped.
11 * This is designed to be used for horizontal text only,
12 * and draws unhinted text with subpixel accurate metrics
13 * and kerning. As such, you should always call the drawing
14 * function with an identity transform that maps units
15 * to pixels accurately.
17 * If you wish to use it to draw arbitrarily transformed
18 * text, change the min and mag filters to GL_LINEAR and
19 * add a pixel of padding between glyphs and rows, and
20 * make sure to clear the texture when wiping the cache.
23 #include FT_ADVANCES_H
24 typedef int Rune; /* 32 bits */
26 #define PADDING 1 /* set to 0 to save some space but disallow arbitrary transforms */
28 #define MAXGLYPHS 4093 /* prime number for hash table goodness */
29 #define CACHESIZE 256
30 #define XPRECISION 4
31 #define YPRECISION 1
33 struct key
35 FT_Face face;
36 short size;
37 short gid;
38 short subx;
39 short suby;
42 struct glyph
44 signed char lsb, top, w, h;
45 short s, t;
46 float advance;
49 struct table
51 struct key key;
52 struct glyph glyph;
55 static FT_Library g_freetype_lib = NULL;
56 static struct table g_table[MAXGLYPHS];
57 static int g_table_load = 0;
58 static unsigned int g_cache_tex = 0;
59 static int g_cache_w = CACHESIZE;
60 static int g_cache_h = CACHESIZE;
61 static int g_cache_row_y = 0;
62 static int g_cache_row_x = 0;
63 static int g_cache_row_h = 0;
65 static void init_font_cache(void)
67 int code;
69 code = FT_Init_FreeType(&g_freetype_lib);
70 if (code)
71 errx(1, "cannot initialize freetype");
73 glGenTextures(1, &g_cache_tex);
74 glBindTexture(GL_TEXTURE_2D, g_cache_tex);
75 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
76 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
77 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
78 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
79 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g_cache_w, g_cache_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
82 static void clear_font_cache(void)
84 #if PADDING > 0
85 unsigned char *zero = calloc(g_cache_w, g_cache_h);
86 if (!zero)
87 err(1, "malloc zero (%u bytes failed)", g_cache_w * g_cache_h);
88 glBindTexture(GL_TEXTURE_2D, g_cache_tex);
89 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_cache_w, g_cache_h, GL_ALPHA, GL_UNSIGNED_BYTE, zero);
90 free(zero);
91 #endif
93 memset(g_table, 0, sizeof(g_table));
94 g_table_load = 0;
96 g_cache_row_y = PADDING;
97 g_cache_row_x = PADDING;
98 g_cache_row_h = 0;
101 static FT_Face load_font(char *fontname)
103 FT_Face face;
104 int code;
106 if (g_freetype_lib == NULL)
108 init_font_cache();
109 clear_font_cache();
112 code = FT_New_Face (g_freetype_lib, fontname, 0, &face);
113 if (code) {
114 fprintf (stderr, "failed to load font `%s'\n", fontname);
115 return NULL;
118 FT_Select_Charmap(face, ft_encoding_unicode);
119 return face;
122 static FT_Face load_builtin_font(void *base, int len)
124 FT_Face face;
125 int code;
127 if (g_freetype_lib == NULL)
129 init_font_cache();
130 clear_font_cache();
133 code = FT_New_Memory_Face(g_freetype_lib, base, len, 0, &face);
134 if (code) {
135 fprintf (stderr, "failed to load builtin font\n");
136 return NULL;
139 FT_Select_Charmap(face, ft_encoding_unicode);
140 return face;
143 static void UNUSED free_font(FT_Face face)
145 clear_font_cache();
146 FT_Done_Face(face);
149 static unsigned int hashfunc(struct key *key)
151 unsigned char *buf = (unsigned char *)key;
152 unsigned int len = sizeof(struct key);
153 unsigned int h = 0;
154 while (len--)
155 h = *buf++ + (h << 6) + (h << 16) - h;
156 return h;
159 static unsigned int lookup_table(struct key *key)
161 unsigned int pos = hashfunc(key) % MAXGLYPHS;
162 while (1)
164 if (!g_table[pos].key.face) /* empty slot */
165 return pos;
166 if (!memcmp(key, &g_table[pos].key, sizeof(struct key))) /* matching slot */
167 return pos;
168 pos = (pos + 1) % MAXGLYPHS;
172 static struct glyph * lookup_glyph(FT_Face face, int size, int gid, int subx, int suby)
174 FT_Vector subv;
175 struct key key;
176 unsigned int pos;
177 int code;
178 int w, h;
181 * Look it up in the table
184 key.face = face;
185 key.size = size;
186 key.gid = gid;
187 key.subx = subx;
188 key.suby = suby;
190 pos = lookup_table(&key);
191 if (g_table[pos].key.face)
192 return &g_table[pos].glyph;
195 * Render the bitmap
198 glEnd();
200 subv.x = subx;
201 subv.y = suby;
203 FT_Set_Transform(face, NULL, &subv);
205 code = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
206 if (code < 0)
207 return NULL;
209 code = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LIGHT);
210 if (code < 0)
211 return NULL;
213 w = face->glyph->bitmap.width;
214 h = face->glyph->bitmap.rows;
217 * Find an empty slot in the texture
220 if (g_table_load == (MAXGLYPHS * 3) / 4)
222 lprintf("font cache table full, clearing cache");
223 clear_font_cache();
224 pos = lookup_table(&key);
227 if (h + PADDING > g_cache_h || w + PADDING > g_cache_w)
228 errx(1, "rendered glyph exceeds cache dimensions");
230 if (g_cache_row_x + w + PADDING > g_cache_w)
232 g_cache_row_y += g_cache_row_h + PADDING;
233 g_cache_row_x = PADDING;
235 if (g_cache_row_y + h + PADDING > g_cache_h)
237 lprintf("font cache texture full, clearing cache");
238 clear_font_cache();
239 pos = lookup_table(&key);
243 * Copy bitmap into texture
246 memcpy(&g_table[pos].key, &key, sizeof(struct key));
247 g_table[pos].glyph.w = face->glyph->bitmap.width;
248 g_table[pos].glyph.h = face->glyph->bitmap.rows;
249 g_table[pos].glyph.lsb = face->glyph->bitmap_left;
250 g_table[pos].glyph.top = face->glyph->bitmap_top;
251 g_table[pos].glyph.s = g_cache_row_x;
252 g_table[pos].glyph.t = g_cache_row_y;
253 g_table[pos].glyph.advance = face->glyph->advance.x / 64.0;
254 g_table_load ++;
256 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
257 glPixelStorei(GL_UNPACK_ROW_LENGTH, face->glyph->bitmap.pitch);
258 glTexSubImage2D(GL_TEXTURE_2D, 0, g_cache_row_x, g_cache_row_y, w, h,
259 GL_ALPHA, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
260 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
262 glBegin(GL_QUADS);
264 g_cache_row_x += w + PADDING;
265 if (g_cache_row_h < h + PADDING)
266 g_cache_row_h = h + PADDING;
268 return &g_table[pos].glyph;
271 static float draw_glyph(FT_Face face, int size, int gid, float x, float y)
273 struct glyph *glyph;
274 int subx = (x - floor(x)) * XPRECISION;
275 int suby = (y - floor(y)) * YPRECISION;
276 subx = (subx * 64) / XPRECISION;
277 suby = (suby * 64) / YPRECISION;
279 glyph = lookup_glyph(face, size, gid, subx, suby);
280 if (!glyph)
281 return 0.0;
283 float s0 = (float) glyph->s / g_cache_w;
284 float t0 = (float) glyph->t / g_cache_h;
285 float s1 = (float) (glyph->s + glyph->w) / g_cache_w;
286 float t1 = (float) (glyph->t + glyph->h) / g_cache_h;
287 float xc = floor(x) + glyph->lsb;
288 float yc = floor(y) - glyph->top + glyph->h;
290 glTexCoord2f(s0, t0); glVertex2f(xc, yc - glyph->h);
291 glTexCoord2f(s1, t0); glVertex2f(xc + glyph->w, yc - glyph->h);
292 glTexCoord2f(s1, t1); glVertex2f(xc + glyph->w, yc);
293 glTexCoord2f(s0, t1); glVertex2f(xc, yc);
295 return glyph->advance;
298 static float measure_string(FT_Face face, float fsize, char *str)
300 int size = fsize * 64;
301 FT_Fixed advance;
302 FT_Vector kern;
303 Rune ucs, gid;
304 float w = 0.0;
305 int left = 0;
307 FT_Set_Char_Size(face, size, size, 72, 72);
309 while (*str)
311 str += chartorune(&ucs, str);
312 gid = FT_Get_Char_Index(face, ucs);
313 FT_Get_Advance(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING, &advance);
314 w += advance / 65536.0;
315 FT_Get_Kerning(face, left, gid, FT_KERNING_UNFITTED, &kern);
316 w += kern.x / 64.0;
317 left = gid;
320 return w;
323 static float draw_string(FT_Face face, float fsize, float x, float y, char *str)
325 int size = fsize * 64;
326 FT_Vector kern;
327 Rune ucs, gid;
328 int left = 0;
330 FT_Set_Char_Size(face, size, size, 72, 72);
332 glBindTexture(GL_TEXTURE_2D, g_cache_tex);
333 glBegin(GL_QUADS);
335 while (*str)
337 str += chartorune(&ucs, str);
338 gid = FT_Get_Char_Index(face, ucs);
339 x += draw_glyph(face, size, gid, x, y);
340 FT_Get_Kerning(face, left, gid, FT_KERNING_UNFITTED, &kern);
341 x += kern.x / 64.0;
342 left = gid;
345 glEnd();
347 return x;