1#include <stdio.h>
2#include <string.h>
3
4#include "client/font.h"
5#include "renderer/renderer.h"
6
7#define PATH_SIZE 4096
8#define PAGES_CNT 256
9
10static i32 glyph_width(u16 c);
11static u8 load_glyph_sizes(const char* path);
12static MGTexture load_page(u8 page);
13static u8 make_path(char* path, size_t size, const char* suffix);
14
15static MGTexture ascii;
16static MGTexture unicode_pages[PAGES_CNT];
17static u8 glyph_sizes[65536];
18static char root[PATH_SIZE];
19
20/* the glyph bounds are stored in a byte:
21 high nibble is the start
22 low nibble is the end
23 of the glyph (in half pixels). the width is found by
24 (end - start + 1) / 2 + 1. */
25static i32 glyph_width(u16 c)
26{
27 u8 size = glyph_sizes[c];
28 i32 high = size >> 4; /* high nibble */
29 i32 low = size & 15; /* low nibble */
30
31 if (!size)
32 return 0;
33
34 return (low - high + 1) / 2 + 1;
35}
36
37/* load the glyph sizes from a binary file */
38static u8 load_glyph_sizes(const char* path)
39{
40 FILE* file = fopen(path, "rb");
41 size_t cnt = 0;
42
43 if (!file)
44 return 0;
45
46 cnt = fread(glyph_sizes, 1, sizeof(glyph_sizes), file);
47 fclose(file);
48
49 return cnt == sizeof(glyph_sizes);
50}
51
52/* load a unicode page texture */
53static MGTexture load_page(u8 page)
54{
55 char path[PATH_SIZE] = { 0 };
56 int n = 0;
57
58 if (unicode_pages[page] != MGTEXTURE_INVALID)
59 return unicode_pages[page];
60
61 n = snprintf(path, sizeof(path), "%s/textures/font/unicode_page_%02x.png", root, (unsigned)page);
62 if (n < 0 || (size_t)n >= sizeof(path))
63 return MGTEXTURE_INVALID;
64
65 unicode_pages[page] = renderer_texture_load(path);
66 return unicode_pages[page];
67}
68
69/* concatenating the root and a suffix */
70static u8 make_path(char* path, size_t size, const char* suffix)
71{
72 int n = snprintf(path, size, "%s/%s", root, suffix);
73 return n >= 0 && (size_t)n < size;
74}
75
76i8 font_load(const char* dir)
77{
78 char path[PATH_SIZE];
79 size_t len;
80
81 font_destroy();
82 if (!dir)
83 return 0;
84
85 len = strlen(dir);
86 if (len >= sizeof(root))
87 return 0;
88 memcpy(root, dir, len + 1);
89
90 if (!make_path(path, sizeof(path), "textures/font/ascii.png"))
91 goto fail;
92 ascii = renderer_texture_load(path);
93 if (ascii == MGTEXTURE_INVALID)
94 goto fail;
95
96 if (!make_path(path, sizeof(path), "font/glyph_sizes.bin") || !load_glyph_sizes(path))
97 goto fail;
98
99 return 1;
100
101fail:
102 font_destroy();
103 return 0;
104}
105
106void font_destroy(void)
107{
108 i32 i;
109
110 if (ascii != MGTEXTURE_INVALID)
111 renderer_texture_destroy(ascii);
112 ascii = MGTEXTURE_INVALID;
113
114 for (i = 0; i < PAGES_CNT; i++) {
115 if (unicode_pages[i] != MGTEXTURE_INVALID)
116 renderer_texture_destroy(unicode_pages[i]);
117 unicode_pages[i] = MGTEXTURE_INVALID;
118 }
119
120 memset(glyph_sizes, 0, sizeof(glyph_sizes));
121 root[0] = '\0';
122}