Mirai's Miscellaneous Misadventures

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