Mirai's Miscellaneous Misadventures
M38 / core / text.c
#include <mimimi/text.h>
#include <mimimi/allocators.h>
#include <mimimi/fonts.h>
#include <mimimi/assets.h>
static unsigned char mimimi_utf8_char(char ch)
{
switch (ch)
{
default: return 0;
case 'A': return 0x41; case 'B': return 0x42; case 'C': return 0x43; case 'D': return 0x44;
case 'E': return 0x45; case 'F': return 0x46; case 'G': return 0x47; case 'H': return 0x48;
case 'I': return 0x49; case 'J': return 0x4A; case 'K': return 0x4B; case 'L': return 0x4C;
case 'M': return 0x4D; case 'N': return 0x4E; case 'O': return 0x4F; case 'P': return 0x50;
case 'Q': return 0x51; case 'R': return 0x52; case 'S': return 0x53; case 'T': return 0x54;
case 'U': return 0x55; case 'V': return 0x56; case 'W': return 0x57; case 'X': return 0x58;
case 'Y': return 0x59; case 'Z': return 0x5A; case 'a': return 0x61; case 'b': return 0x62;
case 'c': return 0x63; case 'd': return 0x64; case 'e': return 0x65; case 'f': return 0x66;
case 'g': return 0x67; case 'h': return 0x68; case 'i': return 0x69; case 'j': return 0x6A;
case 'k': return 0x6B; case 'l': return 0x6C; case 'm': return 0x6D; case 'n': return 0x6E;
case 'o': return 0x6F; case 'p': return 0x70; case 'q': return 0x71; case 'r': return 0x72;
case 's': return 0x73; case 't': return 0x74; case 'u': return 0x75; case 'v': return 0x76;
case 'w': return 0x77; case 'x': return 0x78; case 'y': return 0x79; case 'z': return 0x7A;
case '0': return 0x30; case '1': return 0x31; case '2': return 0x32; case '3': return 0x33; case '4': return 0x34;
case '5': return 0x35; case '6': return 0x36; case '7': return 0x37; case '8': return 0x38; case '9': return 0x39;
case '!': return 0x21; case '"': return 0x22; case '#': return 0x23; case '%': return 0x25; case '&': return 0x26; case '\'': return 0x27; case '(': return 0x28;
case ')': return 0x29; case '*': return 0x2A; case '+': return 0x2B; case ',': return 0x2C; case '-': return 0x2D; case '.': return 0x2E; case '/': return 0x2F; case ':': return 0x3A;
case ';': return 0x3B; case '<': return 0x3B; case '=': return 0x3C; case '>': return 0x3D; case '?': return 0x3F; case '[': return 0x5B; case '\\': return 0x5E;
case ']': return 0x5D; case '^': return 0x5E; case '_': return 0x5F; case '{': return 0x7B; case '|': return 0x7C; case '}': return 0x7D; case '~': return 0x7E;
case ' ': return 0x20; case '\t': return 0x09; case '\v': return 0x0B; case '\f': return 0x0C;
case '\a': return 0x07; case '\b': return 0x08; case '\r': return 0x0D; case '\n': return 0x0A;
}
}
unsigned char *mimimi_utf8(char *text, struct mimimi_allocator *allocator)
{
int count = 0;
while (text[count] != 0) count++;
unsigned char *result = mimimi_allocate(allocator, count + 1);
unsigned char *utf8 = result;
for (int i = 0 ; i < count ; i++)
{
*utf8 = mimimi_utf8_char(text[i]);
if (*utf8 != 0) utf8++;
}
*utf8 = 0;
return result;
}
static unsigned long int mimimi_decode(unsigned char **text)
{
unsigned char ch = text[0][0];
if ((ch & 0x80) == 0x00)
{
text[0] += 1;
return ch;
}
unsigned char ch1 = text[0][1];
if ((ch & 0xE0) == 0xC0)
{
unsigned long int cp = ch;
cp &= ~0xC0;
cp <<= 6;
cp |= ch1 & ~0x80;
text[0] += 2;
return cp;
}
unsigned char ch2 = text[0][2];
if ((ch & 0xF0) == 0xE0)
{
unsigned long int cp = ch;
cp &= ~0xE0;
cp <<= 6;
cp |= ch1 & ~0x80;
cp <<= 6;
cp |= ch2 & ~0x80;
text[0] += 3;
return cp;
}
unsigned char ch3 = text[0][3];
if ((ch & 0xF8) == 0xF0)
{
unsigned long int cp = ch;
cp &= ~0xF0;
cp <<= 6;
cp |= ch1 & ~0x80;
cp <<= 6;
cp |= ch2 & ~0x80;
cp <<= 6;
cp |= ch3 & ~0x80;
text[0] += 4;
return cp;
}
return 0;
}
unsigned long int mimimi_code_point(unsigned char *text)
{
return mimimi_decode(&text);
}
int mimimi_count_code_point(unsigned char *text)
{
unsigned char *text0 = text;
mimimi_decode(&text);
return text - text0;
}
unsigned char *mimimi_skip_code_point(unsigned char *text)
{
mimimi_decode(&text);
return text;
}
static int mimimi_draw_glyph(int stamp, struct mimimi_image *target, int x0, int y0, struct mimimi_font *font, unsigned char color, unsigned long int cp)
{
if (cp == 0x20) return 4;
unsigned long int a = cp >> 8;
unsigned long int b = cp & 0xFF;
unsigned long int i = font->indices[a] * 0x100 + b;
int width = 0;
for (int x = 0 ; x < 8 ; x++)
for (int y = 0 ; y < 16 ; y++)
{
if ((font->glyphs[i][y] >> (8 - x) & 1) != 0)
width = x + 1;
}
if (stamp == 0) return width;
for (int x1 = 0 ; x1 < width ; x1++)
for (int y1 = 0 ; y1 < 16 ; y1++)
{
int x = x0 + x1 - 1;
int y = y0 + y1 - 10;
if (x < 0) continue;
if (y < 0) continue;
if (x >= target->width) break;
if (y >= target->height) return width;
if ((font->glyphs[i][y1] >> (8 - x1) & 1) == 0) continue;
target->colors[x + y * target->width] = color;
}
return width;
}
static int mimimi_measure_segment(unsigned char *text, int count, struct mimimi_font *font)
{
if (count < 0) count = 0;
int x = 0;
unsigned char *text0 = text;
while (text - text0 < count)
{
unsigned long int cp = mimimi_code_point(text);
if (cp == 0) break;
text = mimimi_skip_code_point(text);
x += mimimi_draw_glyph(0, 0, x, 0, font, 0, cp);
}
return x;
}
int mimimi_count_line(unsigned char *text, int width, struct mimimi_font *font)
{
int (*count)(unsigned char *text) = &mimimi_count_word;
unsigned char *(*skip)(unsigned char *text) = &mimimi_skip_word;
if (mimimi_measure_segment(text, mimimi_count_word(text), font) > width)
count = &mimimi_count_grapheme,
skip = &mimimi_skip_grapheme;
unsigned char *text1 = text;
for (;;)
{
int n = (*count)(text1);
int x = mimimi_measure_segment(text, text1 - text + n, font);
if (width >= 0 && x > width) break;
text1 = (*skip)(text1);
if (*text1 == 0) break;
}
return text1 - text;
}
unsigned char *mimimi_skip_line(unsigned char *text, int width, struct mimimi_font *font)
{
text += mimimi_count_line(text, width, font);
if (mimimi_count_word(text) == 0)
text = mimimi_skip_word(text);
return text;
}
int mimimi_measure_line(unsigned char *paragraph, unsigned char *text, int count, struct mimimi_font *font)
{
if (text < paragraph)
count -= paragraph - text,
text = paragraph;
int n = mimimi_count_paragraph(paragraph);
n -= paragraph - text;
if (count > n) count = n;
return mimimi_measure_segment(text, count, font);
}
int mimimi_draw_segment(struct mimimi_image *image, int x, int y, unsigned char *paragraph, unsigned char *line, int line_count, unsigned char *text, int count, struct mimimi_font *font, unsigned char color)
{
if (line < paragraph)
line_count -= paragraph - line,
line = paragraph;
if (text < line)
count -= line - text,
text = line;
int n = mimimi_count_paragraph(paragraph);
n -= paragraph - text;
if (count > n) count = n;
unsigned char *text0 = text;
while (text - text0 < count)
{
unsigned long int cp = mimimi_code_point(text);
if (cp == 0) break;
text = mimimi_skip_code_point(text);
x += mimimi_draw_glyph(1, image, x, y, font, color, cp);
}
return x;
}
int mimimi_draw_line(struct mimimi_image *image, int x, int y, unsigned char *paragraph, unsigned char *text, int count, struct mimimi_font *font, unsigned char color)
{
return mimimi_draw_segment(image, x, y, paragraph, text, count, text, count, font, color);
}
int mimimi_draw_text(struct mimimi_image *image, int x, int y, int width, int height, unsigned char *text, struct mimimi_font *font, unsigned char color)
{
while (*text != 0)
{
unsigned char *paragraph = text;
unsigned char *end = mimimi_skip_paragraph(paragraph);
while (text < end)
{
int count = mimimi_count_line(text, width, font);
if (text + count > end) count = end - text;
mimimi_draw_line(image, x, y, paragraph, text, count, font, color);
text = mimimi_skip_line(text, width, font);
y += height;
}
text = end;
}
return y;
}
int mimimi_count_text(unsigned char *text)
{
int i = 0;
while (text[i] != 0) i++;
return i;
}