summaryrefslogtreecommitdiff
path: root/text.c
blob: 7007ad6c6026a697743015603ced6473ebc3aad8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "base.h"
#include "text.h"
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
no_warn_start
#include "stb_truetype.h"
no_warn_end
#include <stdarg.h>
#include <stdlib.h>
#include <GL/gl.h>

struct Font {
	float char_height;
	GLuint texture;
	u32 nchars;
	stbtt_bakedchar chars[];
};

static char text_err[200];
static void text_clear_err(void) {
	text_err[0] = '\0';
}

static bool text_has_err(void) {
	return text_err[0] != '\0';
}

char const *text_get_err(void) {
	return text_err;
}

static void text_set_err(char const *fmt, ...) {
	if (!text_has_err()) {
		va_list args;
		va_start(args, fmt);
		vsnprintf(text_err, sizeof text_err - 1, fmt, args);
		va_end(args);
	}
}

Font *text_font_load(char const *ttf_filename, float font_size) {
	Font *font = NULL;
	u32 nchars = 128;
	FILE *ttf_file = fopen(ttf_filename, "rb");
	
	text_clear_err();

	if (ttf_file) {
		fseek(ttf_file, 0, SEEK_END);
		u32 file_size = (u32)ftell(ttf_file);
		if (file_size < (50UL<<20)) { // fonts aren't usually bigger than 50 MB
			u8 *file_data = calloc(1, file_size);
			font = calloc(1, sizeof *font + nchars * sizeof *font->chars);
			if (file_data && font) {
				if (fread(file_data, 1, file_size, ttf_file) == file_size) {
					font->nchars = nchars;
					font->char_height = font_size;

					for (int bitmap_width = 256, bitmap_height = 256; bitmap_width <= 4096; bitmap_width *= 2, bitmap_height *= 2) {
						u8 *bitmap = calloc((size_t)bitmap_width, (size_t)bitmap_height);
						if (bitmap) {
							int err = stbtt_BakeFontBitmap(file_data, 0, font->char_height, bitmap,
								bitmap_width, bitmap_height, 0, (int)font->nchars, font->chars);
							if (err > 0) {
								// font converted to bitmap successfully.
								GLuint texture = 0;
								glGenTextures(1, &texture);
								glBindTexture(GL_TEXTURE_2D, texture);
								glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap_width, bitmap_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap);
								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
							#if DEBUG
								debug_println("Loaded font %s with %dx%d bitmap.", ttf_filename, bitmap_width, bitmap_height);
							#endif
								font->texture = texture;
								if (glGetError()) {
									text_set_err("Couldn't create texture for font.");
								}
							}
						} else {
							text_set_err("Not enough memory for font bitmap.");
						}
						free(bitmap);
						if (font->texture) { // if font loaded successfully
							break;
						}
					}
					if (!font->texture && !text_has_err()) {
						text_set_err("Couldn't convert font to bitmap.");
					}
				} else {
					text_set_err("Couldn't read font file.", ttf_filename);
				}
			} else {
				text_set_err("Not enough memory for font.");
			}
			free(file_data);
			if (text_has_err()) {
				free(font);
				font = NULL;
			}
			fclose(ttf_file);
		} else {
			text_set_err("Font file too big (%u megabytes).", (uint)(file_size >> 20));
		}
	} else {
		text_set_err("Couldn't open font file.", ttf_filename);
	}
	return font;
}

#if 0
void text_render(Font *font, char const *text, float x, float y) {
	
}

void text_get_size(Font *font, char const *text, float *width, float *height) {
}
#endif