Mirai's Miscellaneous Misadventures

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