Mirai's Miscellaneous Misadventures

M54 / core / text.c

1/* license: AGPLv3 or later */
2/* copyright 2024 zamfofex */
3
4#include "../mimimi.h"
5
6static unsigned char mimimi_paragraph_end(char ch)
7{
8	if (ch == 0) return 1;
9	if (ch == 0x0A || ch == 0x0B) return 1;
10	if (ch == 0x0C || ch == 0x0D) return 1;
11	return 0;
12}
13
14static unsigned char mimimi_space(char ch)
15{
16	if (mimimi_paragraph_end(ch)) return 1;
17	if (ch == 0x20 || ch == 0x09) return 1;
18	return 0;
19}
20
21int mimimi_measure_character(struct mimimi_font *font, char ch)
22{
23	int width;
24	int i;
25	int x, y;
26	
27	if (ch == 0x09) return 4;
28	if (ch == 0x20) return 4;
29	
30	i = ch - 0x20;
31	
32	width = 0;
33	for (x = 0 ; x < 8 ; x++) {
34		for (y = 0 ; y < 16 ; y++) {
35			if ((font->glyphs[i][y] >> (7 - x) & 1) != 0) width = x + 1;
36		}
37	}
38	return width + 1;
39}
40
41int mimimi_draw_character(struct mimimi_font *font, char ch, struct mimimi_image *target, int x0, int y0, unsigned char color)
42{
43	int width;
44	int x, y;
45	int x1, y1;
46	int i;
47	
48	width = mimimi_measure_character(font, ch);
49	i = ch - 0x20;
50	
51	for (x1 = 0 ; x1 < width ; x1++) {
52		
53		for (y1 = 0 ; y1 < 16 ; y1++) {
54			
55			x = x0 + x1;
56			y = y0 + y1;
57			
58			if (x < 0) continue;
59			if (y < 0) continue;
60			if (x >= target->width) continue;
61			if (y >= target->height) continue;
62			
63			if ((font->glyphs[i][y1] >> (7 - x1) & 1) == 0) continue;
64			
65			target->colors[x + y * target->width] = color;
66		}
67	}
68	return x0 + width;
69}
70
71static int mimimi_kern(struct mimimi_font *font, char ch1, char ch2)
72{
73	int width;
74	int widths[16];
75	int i, j;
76	int x, y;
77	
78	if (ch1 == 0x19 || ch1 == 0x20) return 4;
79	if (ch2 == 0x19 || ch2 == 0x20 || ch2 == 0) return mimimi_measure_character(font, ch1);
80	
81	i = ch1 - 0x20;
82	j = ch2 - 0x20;
83	
84	for (y = 0 ; y < 16 ; y++) {
85		widths[y] = 0;
86		for (x = 0 ; x < 8 ; x++) {
87			if ((font->glyphs[i][y] >> (7 - x) & 1) != 0) {
88				if (x + 1 > widths[y]) widths[y] = x + 1;
89			}
90		}
91	}
92	
93	for (y = 0 ; y < 16 ; y++) {
94		for (x = 0 ; x < 8 ; x++) {
95			if ((font->glyphs[j][y] >> (7 - x) & 1) != 0) break;
96			if (y > 0 && (font->glyphs[j][y - 1] >> (7 - x) & 1) != 0) break;
97			if (y < 15 && (font->glyphs[j][y + 1] >> (7 - x) & 1) != 0) break;
98		}
99		widths[y] -= x;
100	}
101	
102	width = 0;
103	for (y = 0 ; y < 16 ; y++) {
104		if (widths[y] > width) width = widths[y];
105	}
106	return width + 1;
107}
108
109int mimimi_measure_segment(struct mimimi_font *font, char *text, int count)
110{
111	int x;
112	char ch;
113	
114	x = 0;
115	while (count-- > 0) {
116		ch = *text++;
117		if (ch == 0) break;
118		x += mimimi_kern(font, ch, *text);
119	}
120	
121	return x;
122}
123
124int mimimi_draw_segment(struct mimimi_font *font, char *text, int count, struct mimimi_image *target, int x, int y, unsigned char color)
125{
126	char ch;
127	
128	while (count-- > 0) {
129		ch = *text++;
130		if (ch == 0) break;
131		mimimi_draw_character(font, ch, target, x, y, color);
132		x += mimimi_kern(font, ch, *text);
133	}
134	
135	return x;
136}
137
138static char *mimimi_skip_nonspace(char *text)
139{
140	while (mimimi_space(*text) == 0) text++;
141	return text;
142}
143
144static char *mimimi_skip_space(char *text)
145{
146	while (mimimi_space(*text) != 0 && mimimi_paragraph_end(*text) == 0) text++;
147	return text;
148}
149
150static int mimimi_count_space(char *text)
151{
152	return mimimi_skip_space(text) - text;
153}
154
155static int mimimi_measure_space(struct mimimi_font *font, char *text)
156{
157	(void) font;
158	return 4 * mimimi_count_space(text);
159}
160
161char *mimimi_skip_word(char *text)
162{
163	text = mimimi_skip_nonspace(text);
164	text = mimimi_skip_space(text);
165	return text;
166}
167
168int mimimi_count_word(char *text)
169{
170	return mimimi_skip_nonspace(text) - text;
171}
172
173int mimimi_measure_word(struct mimimi_font *font, char *text)
174{
175	return mimimi_measure_segment(font, text, mimimi_count_word(text));
176}
177
178int mimimi_draw_word(struct mimimi_font *font, char *text, struct mimimi_image *target, int x, int y, unsigned char color)
179{
180	return mimimi_draw_segment(font, text, mimimi_count_word(text), target, x, y, color);
181}
182
183char *mimimi_skip_paragraph(char *text)
184{
185	while (mimimi_paragraph_end(*text) == 0) text++;
186	if (text[0] == '\r' && text[1] == '\n') text++;
187	if (text[0] != 0) text++;
188	return text;
189}
190
191int mimimi_count_paragraph(char *text)
192{
193	int n;
194	n = 0;
195	while (mimimi_paragraph_end(text[n]) == 0) n++;
196	return n;
197}
198
199int mimimi_measure_paragraph(struct mimimi_font *font, char *text)
200{
201	return mimimi_measure_segment(font, text, mimimi_count_paragraph(text));
202}
203
204int mimimi_draw_paragraph(struct mimimi_font *font, char *text, struct mimimi_image *target, int x, int y, unsigned char color)
205{
206	return mimimi_draw_segment(font, text, mimimi_count_paragraph(text), target, x, y, color);
207}
208
209static char *mimimi_skip_line_by_word(struct mimimi_font *font, char *text, int width)
210{
211	for (;;) {
212		if (mimimi_paragraph_end(*text)) return text;
213		width -= mimimi_measure_word(font, text);
214		if (width < 0) return text;
215		text += mimimi_count_word(text);
216		width -= mimimi_measure_space(font, text);
217		text = mimimi_skip_word(text);
218	}
219}
220
221static char *mimimi_skip_line_by_character(struct mimimi_font *font, char *text, int width)
222{
223	for (;;) {
224		if (mimimi_paragraph_end(*text)) return text;
225		width -= mimimi_measure_character(font, *text);
226		if (width < 0) return text;
227		text++;
228	}
229}
230
231int mimimi_count_line(struct mimimi_font *font, char *text0, int width)
232{
233	char *text;
234	
235	text = text0;
236	width++;
237	
238	if (mimimi_measure_segment(font, text, mimimi_count_word(text)) > width) text = mimimi_skip_line_by_character(font, text, width);
239	else text = mimimi_skip_line_by_word(font, text, width);
240	
241	text = mimimi_skip_space(text);
242	return text - text0;
243}
244
245char *mimimi_skip_line(struct mimimi_font *font, char *text, int width)
246{
247	text += mimimi_count_line(font, text, width);
248	while (mimimi_paragraph_end(*text) != 0 && *text != 0) text++;
249	return text;
250}
251
252int mimimi_measure_line(struct mimimi_font *font, char *text, int width)
253{
254	int count;
255	count = mimimi_count_line(font, text, width);
256	return mimimi_measure_segment(font, text, count);
257}
258
259int mimimi_draw_line(struct mimimi_font *font, char *text, int width, struct mimimi_image *target, int x, int y, unsigned char color)
260{
261	int count;
262	count = mimimi_count_line(font, text, width);
263	return mimimi_draw_segment(font, text, count, target, x, y, color);
264}
265
266int mimimi_measure_text(struct mimimi_font *font, char *text, int width, int line_height)
267{
268	int y;
269	
270	y = 0;
271	while (*text != 0) {
272		text = mimimi_skip_line(font, text, width);
273		y += line_height;
274	}
275	
276	return y;
277}
278
279int mimimi_draw_text(struct mimimi_font *font, char *text, int width, int line_height, struct mimimi_image *target, int x, int y, unsigned char color)
280{
281	while (*text != 0) {
282		mimimi_draw_line(font, text, width, target, x, y, color);
283		text = mimimi_skip_line(font, text, width);
284		y += line_height;
285	}
286	
287	return y;
288}