#include "font.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_STROKER_H
#include <vector>
typedef unsigned char u8;
#define N_GLYPHS 384
#define SLANT 0.2
#define FLOAT_FROM_16_16(n) ((float)((n) >> 16) + (float)((n) & 0xffff) / 65536.0)
static FT_Library library = nullptr;
static FT_Stroker stroker = nullptr;
static std::vector<FT_Face> font_faces;
static bool g_italic = false;
Font_Handle load_font_face(const char *path) {
if (!library) {
if (FT_Init_FreeType(&library) != 0) {
fprintf(stderr, "Failed to load freetype\n");
return nullptr;
}
}
if (!stroker)
FT_Stroker_New(library, &stroker);
FT_Face face;
if (FT_New_Face(library, path, 0, &face) != 0) {
fprintf(stderr, "Error loading font \"%s\"\n", path);
return nullptr;
}
font_faces.push_back(face);
return (Font_Handle)face;
}
const char *get_font_type_name(bool bold) {
if (g_italic) {
if (bold)
return "bold-italic";
else
return "italic";
}
else if (bold)
return "bold";
return "regular";
}
template<bool bold>
void render_ascii(Font_Render render, FT_Face face, int start_idx) {
for (int c = 0x20; c <= 0x7e; c++) {
FT_Bitmap bmp;
int left;
int top;
if constexpr (bold) {
FT_Load_Char(face, c, FT_LOAD_NO_BITMAP);
FT_Glyph glyph;
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, 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;
}
int w = (int)bmp.width;
int h = (int)bmp.rows;
int x = render.overlap_w + left;
int y = render.baseline - top;
// HACK
if (x < 0) x = 0;
if (x < 0 || y < 0 || x + w > render.glyph_img_w || y + h > render.glyph_h) {
fprintf(stderr, "%s: '%c' doesn't fit: glyph=(%d,%d,%dx%d), frame=%dx%d\n", get_font_type_name(false), c, x, y, w, h, render.glyph_img_w, render.glyph_h);
continue;
}
int idx = start_idx + c - 0x20;
int offset = (idx * render.glyph_h + y) * render.glyph_img_w;
for (int i = 0; i < h; i++) {
u8 *in = &bmp.buffer[i*w];
u8 *out = &render.buf[offset + x];
memcpy(out, in, w);
offset += render.glyph_img_w;
}
}
}
Font_Render size_up_font_render(Font_Handle fh, float size, float dpi_w, float dpi_h) {
auto face = (FT_Face)fh;
float req_size = size * 64.0;
int pts = (int)(req_size + 0.5);
FT_Set_Char_Size(face, 0, pts, dpi_w, dpi_h);
float pt_w = size * dpi_w / 72.0;
float pt_h = size * dpi_h / 72.0;
float em_units = (float)(face->units_per_EM ? face->units_per_EM : 1);
int glyph_h = (int)(pt_h * (float)face->max_advance_height / em_units);
float glyph_w_long = pt_w * (float)face->max_advance_width / em_units;
int glyph_img_w = (int)((1.40 * glyph_w_long) + 0.5);
int glyph_w = (int)((0.75 * glyph_w_long) + 0.5);
int overlap_w = (glyph_img_w - glyph_w) / 2;
overlap_w = (overlap_w < 1) ? 0 : overlap_w - 1;
return (Font_Render) {
.dpi_w = dpi_w,
.dpi_h = dpi_h,
.points = pts,
.baseline = (int)(0.75*glyph_h),
.overlap_w = overlap_w,
.glyph_img_w = glyph_img_w,
.glyph_w = glyph_w,
.glyph_h = glyph_h,
.total_size = N_GLYPHS * glyph_img_w * glyph_h
};
}
void make_font_render(Font_Handle fh, Font_Render render) {
auto face = (FT_Face)fh;
FT_Set_Char_Size(face, 0, render.points, render.dpi_w, render.dpi_h);
FT_Stroker_Set(stroker, render.points / 20, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
render_ascii<false>(render, face, 0);
render_ascii<true>(render, face, 96);
FT_Matrix matrix = {
.xx = 0x10000,
.xy = (int)(SLANT * 0x10000),
.yx = 0,
.yy = 0x10000
};
FT_Set_Transform(face, &matrix, nullptr);
g_italic = true;
render_ascii<false>(render, face, 192);
render_ascii<true>(render, face, 288);
}
void ft_quit() {
for (auto& face : font_faces)
FT_Done_Face(face);
FT_Stroker_Done(stroker);
FT_Done_FreeType(library);
}