Mirai's Miscellaneous Misadventures

M34 / core / text.c

// copyright 2022 zamfofex
// license: AGPLv3 or later

#include <mimimi/chapters.h>
#include <mimimi/fonts.h>
#include <mimimi/assets.h>

static int mimimi_stamp_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;
}

// todo: validate
// todo: disallow surrogates
// todo: disallow overlongs
static unsigned long int mimimi_utf8(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;
}

static int mimimi_measure_text_until(struct mimimi_font *font, char **text, unsigned long int last, unsigned long int last2)
{
	int x = 0;
	for (;;)
	{
		if (**text == 0) break;
		unsigned long int cp = mimimi_utf8(text);
		if (cp == last) break;
		if (cp == last2) break;
		x += mimimi_stamp_glyph(0, 0, x, 0, font, 0, cp);
	}
	return x;
}

int mimimi_text_until(struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, char **text, unsigned long int last, unsigned long int last2)
{
	for (;;)
	{
		if (**text == 0) break;
		unsigned long int cp = mimimi_utf8(text);
		if (cp == last) break;
		if (cp == last2) break;
		x += mimimi_stamp_glyph(1, image, x, y, font, color, cp);
	}
	return x;
}

int mimimi_measure_text(struct mimimi_font *font, char *text)
{
	return mimimi_measure_text_until(font, &text, 0, 0);
}

int mimimi_text(struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, char *text)
{
	return mimimi_text_until(image, font, color, x, y, &text, 0, 0);
}

int mimimi_measure_word(struct mimimi_font *font, char **text)
{
	return mimimi_measure_text_until(font, text, 0x20, 0x0A);
}

int mimimi_word(struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, char **text)
{
	return mimimi_text_until(image, font, color, x, y, text, 0x20, 0x0A);
}

static int mimimi_stamp_line(int stamp, struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, int width, char **text)
{
	width += x;
	int sp = mimimi_measure_text(font, " ");
	for (;;)
	{
		char *text1 = *text;
		int n = mimimi_measure_text_until(font, &text1, 0x20, 0x0A);
		
		if (x + n > width) return x;
		
		if (stamp == 0) *text = text1;
		else mimimi_text_until(image, font, color, x, y, text, 0x20, 0x0A);
		
		if (**text == 0) return x + n;
		if ((*text)[-1] == 0x0A) return x + n;
		
		while (**text == 0x20) mimimi_utf8(text);
		
		x += n + sp;
	}
}

int mimimi_measure_line(struct mimimi_font *font, int width, char **text)
{
	return mimimi_stamp_line(0, 0, font, 0, 0, 0, width, text);
}

int mimimi_line(struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, int width, char **text)
{
	return mimimi_stamp_line(1, image, font, color, x, y, width, text);
}

int mimimi_count_lines(struct mimimi_font *font, int width, char *text)
{
	int count = 0;
	for (;;)
	{
		char *text1 = text;
		mimimi_measure_line(font, width, &text);
		if (text == text1) mimimi_measure_word(font, &text);
		if (*text == 0) return count;
		count++;
	}
}

void mimimi_lines(struct mimimi_image *image, struct mimimi_font *font, unsigned char color, int x, int y, int width, int height, char *text)
{
	for (;;)
	{
		char *text1 = text;
		mimimi_line(image, font, color, x, y, width, &text);
		if (text == text1) mimimi_word(image, font, color, x, y, &text);
		if (*text == 0) return;
		y += height;
	}
}