236 lines
7.2 KiB
C
236 lines
7.2 KiB
C
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.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 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 bool generate(const char * d_file_name)
|
|
{
|
|
FILE * fh = fopen(d_file_name, "wb");
|
|
fprintf(fh, "module hulk.kfont;\n");
|
|
fprintf(fh, "struct CharInfo {\n");
|
|
fprintf(fh, " /** Glyph bitmap width. */\n");
|
|
fprintf(fh, " ubyte width;\n");
|
|
fprintf(fh, " /** Glyph bitmap height. */\n");
|
|
fprintf(fh, " ubyte height;\n");
|
|
fprintf(fh, " /** Glyph bitmap top offset (descent from top of text row). */\n");
|
|
fprintf(fh, " ubyte top;\n");
|
|
fprintf(fh, " /** Glyph bitmap left offset. */\n");
|
|
fprintf(fh, " ubyte left;\n");
|
|
fprintf(fh, " /** Glyph bitmap. */\n");
|
|
fprintf(fh, " const ubyte[] bitmap;\n");
|
|
fprintf(fh, "};\n");
|
|
fprintf(fh, "struct FontInfo {\n");
|
|
fprintf(fh, " /** Font line height. */\n");
|
|
fprintf(fh, " ubyte line_height;\n");
|
|
fprintf(fh, " /** Font advance. */\n");
|
|
fprintf(fh, " ubyte advance;\n");
|
|
fprintf(fh, " /** Characters. */\n");
|
|
fprintf(fh, " const CharInfo[] chars;\n");
|
|
fprintf(fh, "};\n");
|
|
fprintf(fh, "/** Kernel font data. */\n");
|
|
fprintf(fh, "__gshared const FontInfo Kfont = {\n");
|
|
fprintf(fh, " %du,\n", line_height);
|
|
fprintf(fh, " %du,\n", max_advance);
|
|
fprintf(fh, " [\n");
|
|
for (int i = 0; i < N_CHARS; i++)
|
|
{
|
|
fprintf(fh, " /* %d */", i);
|
|
fprintf(fh, " CharInfo(\n");
|
|
fprintf(fh, " %du /* width */,\n", char_infos[i].width);
|
|
fprintf(fh, " %du /* height */,\n", char_infos[i].height);
|
|
fprintf(fh, " %du /* top */,\n", char_infos[i].top);
|
|
fprintf(fh, " %du /* left */,\n", char_infos[i].left);
|
|
if (char_infos[i].width > 0)
|
|
{
|
|
fprintf(fh, " [\n");
|
|
generate_bytes(fh, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height);
|
|
fprintf(fh, " ]\n");
|
|
}
|
|
else
|
|
{
|
|
fprintf(fh, " []\n");
|
|
}
|
|
fprintf(fh, " ),\n");
|
|
}
|
|
fprintf(fh, " ],\n");
|
|
fprintf(fh, "};\n");
|
|
fclose(fh);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
|
|
if ((line_height < 1) || (line_height > 0xFF))
|
|
{
|
|
fprintf(stderr, "Error: invalid line_height %d\n", line_height);
|
|
return false;
|
|
}
|
|
if ((max_advance < 1) || (max_advance > 0xFF))
|
|
{
|
|
fprintf(stderr, "Error: invalid max_advance %d\n", max_advance);
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < N_CHARS; i++)
|
|
{
|
|
if ((char_infos[i].width < 0) || (char_infos[i].width > max_advance))
|
|
{
|
|
fprintf(stderr, "Error: invalid character %d width: %d (max advance %d)\n", i, char_infos[i].width, max_advance);
|
|
return false;
|
|
}
|
|
if ((char_infos[i].height < 0) || (char_infos[i].height > line_height))
|
|
{
|
|
fprintf(stderr, "Error: invalid character %d height: %d (line height %d)\n", i, char_infos[i].height, line_height);
|
|
return false;
|
|
}
|
|
if ((char_infos[i].left < 0) || (char_infos[i].left >= max_advance))
|
|
{
|
|
fprintf(stderr, "Error: invalid character %d left: %d (max advance %d)\n", i, char_infos[i].left, max_advance);
|
|
return false;
|
|
}
|
|
/* Adjust character top to be index downward from top of font box. */
|
|
char_infos[i].top = line_height - baseline_offset - char_infos[i].top;
|
|
if ((char_infos[i].top < 0) || (char_infos[i].top >= line_height))
|
|
{
|
|
fprintf(stderr, "Error: invalid character %d top: %d (line height %d)\n", i, char_infos[i].top, line_height);
|
|
return false;
|
|
}
|
|
if ((char_infos[i].left + char_infos[i].width) > max_advance)
|
|
{
|
|
fprintf(stderr, "Error: character %d left (%d) + width (%d) > advance (%d)\n",
|
|
i, char_infos[i].left, char_infos[i].width, max_advance);
|
|
return false;
|
|
}
|
|
if ((char_infos[i].top + char_infos[i].height) > line_height)
|
|
{
|
|
fprintf(stderr, "Error: character %d top (%d) + height (%d) > line height (%d)\n",
|
|
i, char_infos[i].top, char_infos[i].height, line_height);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!generate(out_file))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|