#include #include FT_FREETYPE_H #include FT_BITMAP_H #include FT_STROKER_H #include "pistachio.h" #define FLOAT_FROM_16_16(n) ((float)((n) >> 16) + (float)((n) & 0xffff) / 65536.0) #define GAP_FACTOR 0.3 #define SLANT 0.25 #define POOL_SIZE 1024 * 1024 static Arena arena = {0}; FT_Library library; FT_Face face; FT_Stroker stroker; int glyph_indexof(char c) { return (c < MIN_CHAR || c > MAX_CHAR) ? -1 : c - MIN_CHAR; } bool open_font(char *font_path) { if (FT_Init_FreeType(&library) != 0) { fprintf(stderr, "Could not initialise libfreetype\n"); return false; } if (FT_New_Face(library, font_path, 0, &face) != 0) { fprintf(stderr, "Error loading font \"%s\"\n", font_path); return false; } FT_Stroker_New(library, &stroker); return true; } bool render_font(Screen_Info *info, Font_Attrs *attrs, u32 background, Glyph *chars) { if (!arena.initialized) make_arena(POOL_SIZE, &arena); ARGB fore, back; make_argb(attrs->color, &fore); make_argb(background, &back); float gap = 0; FT_Matrix matrix; if (attrs->oblique) { gap = SLANT * GAP_FACTOR * attrs->size; matrix = (FT_Matrix) { .xx = 0x10000, .xy = (int)(SLANT * 0x10000), .yx = 0, .yy = 0x10000 }; FT_Set_Transform(face, &matrix, NULL); } if (attrs->bold) FT_Stroker_Set(stroker, 32, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Set_Char_Size(face, 0, attrs->size * 64, info->dpi_w, info->dpi_h); for (int c = MIN_CHAR; c <= MAX_CHAR; c++) { FT_Bitmap bmp; FT_Glyph glyph; int left, top; if (attrs->bold) { FT_Load_Char(face, c, FT_LOAD_NO_BITMAP); FT_Get_Glyph(face->glyph, &glyph); FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1); FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1); FT_BitmapGlyph bg = (FT_BitmapGlyph)glyph; bmp = bg->bitmap; left = bg->left; top = bg->top; } else { FT_Load_Char(face, c, FT_LOAD_RENDER); bmp = face->glyph->bitmap; left = face->glyph->bitmap_left; top = face->glyph->bitmap_top; } Glyph *gl = &chars[c - MIN_CHAR]; *gl = (Glyph) { .img_w = bmp.width, .img_h = bmp.rows, .pitch = bmp.width * 4, .box_w = gap + FLOAT_FROM_16_16(face->glyph->linearHoriAdvance), .box_h = FLOAT_FROM_16_16(face->glyph->linearVertAdvance), .left = left, .top = top }; int size = gl->pitch * gl->img_h; if (!size) continue; gl->data = allocate(&arena, size); if (!gl->data) { fprintf(stderr, "Failed to allocate glyph bitmap '%c'\n", c); return false; } u32 *p = (u32*)gl->data; for (int i = 0; i < gl->img_w * gl->img_h; i++) { float lum = (float)bmp.buffer[i] / 255.0; p[i] = ((u32)(back.a + (fore.a - back.a) * lum) << 24) | ((u32)(back.r + (fore.r - back.r) * lum) << 16) | ((u32)(back.g + (fore.g - back.g) * lum) << 8) | (u32)(back.b + (fore.b - back.b) * lum); } if (attrs->bold) FT_Done_Glyph(glyph); } FT_Set_Transform(face, NULL, NULL); return true; } void close_font() { FT_Stroker_Done(stroker); FT_Done_Face(face); FT_Done_FreeType(library); }