225 lines
6.0 KiB
C
225 lines
6.0 KiB
C
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <ft2build.h>
|
|
#include <stdio.h>
|
|
#include FT_FREETYPE_H
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <ctype.h>
|
|
|
|
#define N_CHARS 128
|
|
|
|
#define round_up_26_6(val) (((val) + 63) >> 6u)
|
|
|
|
int max_advance;
|
|
int max_top = -9999;
|
|
int min_bottom = 9999;
|
|
int line_height;
|
|
int baseline_offset;
|
|
|
|
typedef struct {
|
|
int width;
|
|
int height;
|
|
int top;
|
|
int left;
|
|
uint8_t * bitmap;
|
|
} char_info_t;
|
|
|
|
static char_info_t char_infos[N_CHARS];
|
|
|
|
static void load_char(FT_Face face, int char_code)
|
|
{
|
|
if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
|
|
{
|
|
return;
|
|
}
|
|
int advance = round_up_26_6(face->glyph->advance.x);
|
|
if (advance > max_advance)
|
|
{
|
|
max_advance = advance;
|
|
}
|
|
if ((face->glyph->bitmap.width == 0) ||
|
|
(face->glyph->bitmap.rows == 0))
|
|
{
|
|
return;
|
|
}
|
|
char_infos[char_code].width = face->glyph->bitmap.width;
|
|
char_infos[char_code].height = face->glyph->bitmap.rows;
|
|
char_infos[char_code].top = face->glyph->bitmap_top;
|
|
if (char_infos[char_code].top > max_top)
|
|
{
|
|
max_top = char_infos[char_code].top;
|
|
}
|
|
int bottom = char_infos[char_code].top - char_infos[char_code].height;
|
|
if (bottom < min_bottom)
|
|
{
|
|
min_bottom = bottom;
|
|
}
|
|
char_infos[char_code].left = face->glyph->bitmap_left;
|
|
char_infos[char_code].bitmap = malloc(char_infos[char_code].width * char_infos[char_code].height);
|
|
memcpy(char_infos[char_code].bitmap,
|
|
face->glyph->bitmap.buffer,
|
|
char_infos[char_code].width * char_infos[char_code].height);
|
|
}
|
|
|
|
static const char * bare_header_name(const char * h_file_name)
|
|
{
|
|
const char * p = rindex(h_file_name, '/');
|
|
if (p == NULL)
|
|
{
|
|
p = h_file_name;
|
|
}
|
|
else
|
|
{
|
|
p++;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static char * include_guard_name(const char * h_file_name)
|
|
{
|
|
const char * p = bare_header_name(h_file_name);
|
|
char * guard_name = malloc(strlen(p) + 1);
|
|
strcpy(guard_name, p);
|
|
char * m = guard_name;
|
|
while (*m != '\0')
|
|
{
|
|
if ('a' <= *m && *m <= 'z')
|
|
{
|
|
*m = toupper(*m);
|
|
}
|
|
else if (('0' <= *m && *m <= '9') || ('A' <= *m && *m <= 'Z'))
|
|
{
|
|
/* no change */
|
|
}
|
|
else
|
|
{
|
|
*m = '_';
|
|
}
|
|
m++;
|
|
}
|
|
return guard_name;
|
|
}
|
|
|
|
static void generate_bytes(FILE * file, const uint8_t * bytes, int count)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (i % 8 == 0)
|
|
{
|
|
fprintf(file, " ");
|
|
}
|
|
fprintf(file, "0x%02xu,", bytes[i]);
|
|
if ((i + 1) % 8 == 0)
|
|
{
|
|
fprintf(file, "\n");
|
|
}
|
|
else if (i < (count - 1))
|
|
{
|
|
fprintf(file, " ");
|
|
}
|
|
}
|
|
if (count % 8 != 0)
|
|
{
|
|
fprintf(file, "\n");
|
|
}
|
|
}
|
|
|
|
static void generate(const char * c_file_name)
|
|
{
|
|
char * h_file_name = malloc(strlen(c_file_name) + 1);
|
|
strcpy(h_file_name, c_file_name);
|
|
h_file_name[strlen(h_file_name) - 1] = 'h';
|
|
char * guard = include_guard_name(h_file_name);
|
|
|
|
FILE * h_file = fopen(h_file_name, "wb");
|
|
fprintf(h_file, "#ifndef %s\n", guard);
|
|
fprintf(h_file, "#define %s\n\n", guard);
|
|
fprintf(h_file, "#include <stdint.h>\n");
|
|
fprintf(h_file, "typedef struct {\n int width;\n int height;\n int top;\n int left;\n const uint8_t * bitmap;\n} fontgen_char_info_t;\n");
|
|
fprintf(h_file, "typedef struct {\n int line_height;\n int advance;\n int baseline_offset;\n const fontgen_char_info_t ** char_infos;\n} fontgen_font_t;\n");
|
|
fprintf(h_file, "extern const fontgen_font_t kfont;\n");
|
|
fprintf(h_file, "#endif\n");
|
|
fclose(h_file);
|
|
|
|
FILE * c_file = fopen(c_file_name, "wb");
|
|
fprintf(c_file, "#include \"%s\"\n", bare_header_name(h_file_name));
|
|
fprintf(c_file, "#include <stddef.h>\n");
|
|
for (int i = 0; i < N_CHARS; i++)
|
|
{
|
|
if (char_infos[i].width > 0)
|
|
{
|
|
fprintf(c_file, "static const uint8_t char_bitmap_%d[] = {\n", i);
|
|
generate_bytes(c_file, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height);
|
|
fprintf(c_file, "};\n");
|
|
}
|
|
fprintf(c_file, "static const fontgen_char_info_t char_%d = {\n", i);
|
|
fprintf(c_file, " %d,\n", char_infos[i].width);
|
|
fprintf(c_file, " %d,\n", char_infos[i].height);
|
|
fprintf(c_file, " %d,\n", char_infos[i].top);
|
|
fprintf(c_file, " %d,\n", char_infos[i].left);
|
|
if (char_infos[i].width > 0)
|
|
{
|
|
fprintf(c_file, " char_bitmap_%d,\n", i);
|
|
}
|
|
else
|
|
{
|
|
fprintf(c_file, " NULL,\n");
|
|
}
|
|
fprintf(c_file, "};\n\n");
|
|
}
|
|
fprintf(c_file, "const fontgen_char_info_t * char_infos[] = {\n");
|
|
for (int i = 0; i < N_CHARS; i++)
|
|
{
|
|
fprintf(c_file, " &char_%d,\n", i);
|
|
}
|
|
fprintf(c_file, "};\n");
|
|
fprintf(c_file, "const fontgen_font_t kfont = {\n");
|
|
fprintf(c_file, " %d,\n", line_height);
|
|
fprintf(c_file, " %d,\n", max_advance);
|
|
fprintf(c_file, " %d,\n", baseline_offset);
|
|
fprintf(c_file, " char_infos,\n");
|
|
fprintf(c_file, "};\n");
|
|
fclose(c_file);
|
|
}
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
/* Expect: font file, size, out file */
|
|
if (argc != 4)
|
|
{
|
|
fprintf(stderr, "Incorrect arguments\n");
|
|
return 1;
|
|
}
|
|
const char * font_file = argv[1];
|
|
int size = atoi(argv[2]);
|
|
const char * out_file = argv[3];
|
|
|
|
FT_Library ft_library;
|
|
if (FT_Init_FreeType(&ft_library) != 0)
|
|
{
|
|
fprintf(stderr, "Could not initialize freetype\n");
|
|
return 2;
|
|
}
|
|
|
|
FT_Face face;
|
|
if (FT_New_Face(ft_library, font_file, 0, &face) != 0)
|
|
{
|
|
fprintf(stderr, "Could not open %s\n", font_file);
|
|
return 3;
|
|
}
|
|
|
|
FT_Set_Pixel_Sizes(face, 0, size);
|
|
|
|
for (int i = 0; i < N_CHARS; i++)
|
|
{
|
|
load_char(face, i);
|
|
}
|
|
line_height = round_up_26_6(face->size->metrics.height);
|
|
baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom;
|
|
|
|
generate(out_file);
|
|
|
|
return 0;
|
|
}
|