text.c

///
// @module Lil
// @license MPL-2.0
// @author eiko
/* #ifdef LIL_USE_FREETYPE */
#ifdef LIL_USE_FREETYPE
#include "text.h"
static FT_Library ft;
static FcConfig* fontConfig;

/// @type Text

wchar_t* lil_utf8ToWchar(const unsigned char* const text, const size_t textLen, size_t* const lenRet){
	size_t i = 0;
	size_t c = 0;
	wchar_t* wtext = malloc((textLen + 1) * sizeof(wchar_t)); // Gerir ráð fyrir því stærsta sem strengurinn gæti verið, einum kóðapunkti fyrir hvert bæti, og minnkar svo síðar
	while(i < textLen){
		wchar_t cp;
		if(text[i] < 127){
			cp = text[i++];
		} else if((text[i] & 0b11100000) == 0b11000000){
			if(i + 2 > textLen)
				break;
			cp = ((text[i] & 0b00011111) << 6) | (text[i+1] & 0b00111111);
			i += 2;
		} else if((text[i] & 0b11110000) == 0b11100000){
			if(i + 3 > textLen)
				break;
			cp = ((text[i] & 0b00001111) << 12) | ((text[i+1] & 0b00111111) << 6) | (text[i+2] & 0b00111111);
			i += 3;
		} else if((text[i] & 0b11111000) == 0b11110000){
			if(i + 4 > textLen)
				break;
			cp = /*((text[i] & 0b00000111) << 16) |*/ ((text[i+1] & 0b00111111) << 12) | ((text[i+2] & 0b00111111) << 6) | (text[i+3] & 0b00111111);
			i += 4;
		} else {
			continue;
		}
		if(cp < 0x20){
			if(cp == '\n' || cp == '\t')
				wtext[c++] = cp;
		} else {
			wtext[c++] = cp;
		}
	}
	/* if(c != textLen) */
	/* 	wtext = realloc(wtext, (c + 1) * sizeof(wchar_t)); */
	wtext[c] = '\0';
	if(lenRet)
		*lenRet = c;
	return wtext;
}

char* lil_findFontPath(const char* patStr){
	FcResult r;
	FcPattern* pat = FcNameParse((FcChar8*) patStr);
	if(!pat)
		return NULL;
	FcConfigSubstitute(fontConfig, pat, FcMatchPattern);
	FcDefaultSubstitute(pat);
	FcFontSet* fs = FcFontSetCreate();
	FcObjectSet* os = FcObjectSetBuild(FC_FILE, NULL);
	FcFontSet* fontPatterns = FcFontSort(fontConfig, pat, FcTrue, 0, &r);
	if(!fontPatterns || fontPatterns->nfont == 0){
		FcObjectSetDestroy(os);
		return NULL;
	}
	FcPattern* fontPattern = FcFontRenderPrepare(fontConfig, pat, fontPatterns->fonts[0]);
	if(!fontPattern){
		FcObjectSetDestroy(os);
		return NULL;
	}
	FcFontSetAdd(fs, fontPattern);
	FcFontSetSortDestroy(fontPatterns);
	FcPatternDestroy(pat);
	if(!fs || fs->nfont == 0){
		FcObjectSetDestroy(os);
		return NULL;
	}
	FcValue v;
	FcPattern* font = FcPatternFilter(fs->fonts[0], os);
	FcPatternGet(font, FC_FILE, 0, &v);
	const char* path = (char*)v.u.f;
	const size_t len = strlen(path);
	char* pathClone = malloc(len+1);
	strcpy(pathClone, path);
	FcPatternDestroy(font);
	return pathClone;
}

lil_Font* lil_getFont(lua_State* L, int i){
	lil_Font* font = *((lil_Font**)luaL_checkudata(L, i, LIL_FONT_UD_MT));
	return font;
}

lil_Font* lil_newFont(const char* path, int w, int h, lil_Colour c){
	const int faceNum = 0; // TODO
	FT_Face face;
	if(FT_New_Face(ft, path, faceNum, &face))
		return NULL;
	if(FT_Set_Pixel_Sizes(face, w, h))
		return 0;
	lil_Font* font = malloc(sizeof(lil_Font));
	font->face = face;
	font->vector = false;
	font->colour = c;
	font->w = w;
	font->h = h;
	return font;
}

void lil_freeFont(lil_Font* font){
	FT_Done_Face(font->face);
	free(font);
}

int lil_initFreetype(){
	if(FT_Init_FreeType(&ft))
		return 1;
	if(!(fontConfig = FcInitLoadConfigAndFonts()))
		return 1;
	return 0;
}
void lil_freeFreetype(){
	FT_Done_FreeType(ft);
	FcConfigDestroy(fontConfig);
}

void lil_getTextSize(const lil_Font* font, const wchar_t* text, size_t len, int* w, int* h){
	FT_Face face = font->face;
	int baseHeight = face->size->metrics.height >> 6;
	int neededWidth  = 0;
	int widest = 0;
	int neededHeight = baseHeight;
	for(int i = 0; i < len; i++){
		wchar_t cha = text[i];
		if(cha == '\n'){
			neededHeight += baseHeight;
			if(neededWidth > widest)
				widest = neededWidth;
			neededWidth = 0;
		} else if(cha == '\t'){
			FT_Load_Char(face, ' ', 0);
			neededWidth += (face->glyph->advance.x >> 6) * 4;
		} else {
			FT_Load_Char(face, cha, 0);
			neededWidth += face->glyph->advance.x >> 6;
		}
	}
	if(widest > neededWidth)
		neededWidth = widest;
	if(w) *w = neededWidth;
	if(h) *h = neededHeight;
}

void lil_writeTextToImage(const lil_Font* font, const wchar_t* wtext, size_t len, int neededWidth, int neededHeight, const lil_Image* img, enum lil_BlendMode bm, int imgOffsetX, int imgOffsetY){
	if(neededWidth == 0 || neededHeight == 0)
		lil_getTextSize(font, wtext, len, &neededWidth, &neededHeight);
	FT_Face face = font->face;
	lil_Colour c = font->colour;
	uint8_t ca = c.a;
	int height = face->size->metrics.height >> 6;
	int offsetX = 0;
	int offsetY = -(height * max((neededHeight / height) - 1, 0));
	int bottom = face->size->metrics.descender >> 6;
	for(int i = 0; i < len; i++){
		wchar_t cha = wtext[i];
		if(cha == '\n'){
			offsetY += face->size->metrics.height >> 6;
			offsetX = 0;
		} else if(cha == '\t'){
			FT_Load_Char(face, ' ', 0);
			offsetX += (face->glyph->advance.x >> 6) * 4;
		} else {
			if(FT_Load_Char(face, cha, FT_LOAD_RENDER))
				continue;
			FT_GlyphSlot glyph = face->glyph;
			int bitMapLeft = glyph->bitmap_left;
			int bitMapTop = glyph->bitmap_top;
			int xp = offsetX + bitMapLeft;
			int yp = offsetY - (bitMapTop - neededHeight) + bottom;
			for(int fy = 0; fy < glyph->bitmap.rows; fy++){
				for(int fx = 0; fx < glyph->bitmap.width; fx++){
					if(yp+fy > neededHeight - 1)
						continue;
					int a = glyph->bitmap.buffer[(fy * glyph->bitmap.pitch) + fx];
					int x = fx + xp + imgOffsetX;
					int y = fy + yp + imgOffsetY;
					if(x >= 0 && x < img->w && y >= 0 && y < img->h){
						c.a = (a * ca) / 255;
						size_t o = XY(img, x, y);
						lil_composite(bm, &img->d[o], &c);
					}
				}
			}
			offsetX += glyph->advance.x >> 6;
		}
	}
}

/// Creates an image containing just text
// @function Font:text
// @string text
// @tab[opt={}] opts
static LUAFUNC(font_ud_text){
	const lil_Font* font = lil_getFont(L, 1);
	const char* text = luaL_checkstring(L, 2);
	size_t len;
	wchar_t* wtext = lil_utf8ToWchar((const unsigned char*)text, strlen(text), &len);
	int neededWidth  = 0;
	int neededHeight = 0;
	lil_getTextSize(font, wtext, len, &neededWidth, &neededHeight);
	lil_Image* img = lil_newImageCalloc(L, neededWidth, neededHeight);
	if(!img){
		free(wtext);
		return 0;
	}
	lil_writeTextToImage(font, wtext, len, neededWidth, neededHeight, img, LIL_BMREPLACE, 0, 0);
	lil_pushImage(L, img);
	free(wtext);
	return 1;
}

/// Sets font size
// @function Font:size
// @int w
// @int[opt=w] y
static LUAFUNC(font_ud_size){
	lil_Font* font = lil_getFont(L, 1);
	const int w = luaL_checkinteger(L, 2);
	const int h = luaL_optinteger(L, 3, w);
	if(FT_Set_Pixel_Sizes(font->face, w, h))
		luaL_error(L, "Failed to set size");
	font->w = w;
	font->h = h;
	lua_pushvalue(L, 1);
	return 1;
}

/// Sets font colour
// @function Font:colour
// @colour colour
static LUAFUNC(font_ud_colour){
	lil_Font* font = lil_getFont(L, 1);
	const lil_Colour col = lil_getColour(L, 2, NULL);
	font->colour = col;
	lua_pushvalue(L, 1);
	return 1;
}

/// Gets font size
// @function Font:getSize
// @return w, h
static LUAFUNC(font_ud_getSize){
	lil_Font* font = lil_getFont(L, 1);
	lua_pushinteger(L, font->w);
	lua_pushinteger(L, font->h);
	return 2;
}

/// Gets font colour
// @function Font:getColour
// @return r, g, b, a
static LUAFUNC(font_ud_getColour){
	lil_Font* font = lil_getFont(L, 1);
	lil_Colour* col = &font->colour;
	lil_Number fc[4];
	#pragma omp simd
	for(int i = 0; i < 4; i++)
		fc[i] = (lil_Number)col->arr[i] / (lil_Number)0xff;
	lua_pushnumber(L, fc[0]);
	lua_pushnumber(L, fc[1]);
	lua_pushnumber(L, fc[2]);
	lua_pushnumber(L, fc[3]);
	return 4;
}

/// Returns calculated width and height of text
// @function Font:measure
// @string text
// @return w, h
static LUAFUNC(font_ud_measure){
	int w, h;
	size_t len;
	lil_Font* font = lil_getFont(L, 1);
	const char* text = luaL_checkstring(L, 2);
	const wchar_t* wtext = lil_utf8ToWchar((const unsigned char*)text, strlen(text), &len);
	lil_getTextSize(font, wtext, len, &w, &h);
	lua_pushinteger(L, w);
	lua_pushinteger(L, h);
	return 2;
}

static LUAFUNC(font_ud_gc){
	lil_Font* font = lil_getFont(L, 1);
	lil_freeFont(font);
	return 0;
};
LUAREG(fontUdLib) = {
	{ "text",      LUAFUNCD(font_ud_text) },
	{ "size",      LUAFUNCD(font_ud_size) },
	{ "colour",    LUAFUNCD(font_ud_colour) },
	{ "getSize",   LUAFUNCD(font_ud_getSize) },
	{ "getColour", LUAFUNCD(font_ud_getColour) },
	{ "measure",   LUAFUNCD(font_ud_measure) },
	{ "__gc",      LUAFUNCD(font_ud_gc) },
	{ NULL, NULL }
};

#endif
generated by LDoc 1.4.6 Last updated 2023-04-13 13:58:34