Skip to content

Instantly share code, notes, and snippets.

@aolo2
Last active July 9, 2021 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aolo2/c7781190e5e8c71f45a7fd45916b485d to your computer and use it in GitHub Desktop.
Save aolo2/c7781190e5e8c71f45a7fd45916b485d to your computer and use it in GitHub Desktop.
#define BDF_MAX_LINE_LENGTH 1024
#define BDF_GLYPH_SELF_OFFSET_X 0
#define BDF_GLYPH_SELF_OFFSET_Y 1
#define BDF_GLYPH_NEXT_OFFSET_X 2
#define BDF_GLYPH_NEXT_OFFSET_Y 3
struct bdf_header {
/* FONT */
u32 version_major;
u32 version_minor;
char *xlog;
/* SIZE */
u32 size_points;
u32 size_dpix;
u32 size_dpiy;
/* BL = bottom-left corner */
/* FONTBOUNDINGBOX WIDTH HEIGHT BL-x BL-y */
/* This property can be *overriden* on per-glyph basis! */
s16 font_bbox[4];
char *family_name;
u32 pixel_size;
u32 point_size;
u32 resx;
u32 resy;
u32 min_space;
u32 font_ascent;
u32 font_descent;
u32 defaut_char;
u32 char_count;
};
struct bdf_letter {
s16 offset[4]; /* self: x, y next: x, y */
u32 bitmap[BDF_MAX_POINTS_PER_LETTER];
u32 width;
};
struct bdf_font {
struct bdf_header header;
struct bdf_letter letters[BDF_LETTER_COUNT];
};
static u32
store_line(char *src, char *buffer)
{
u32 length = 0;
while (src[length] != '\n' && length < BDF_MAX_LINE_LENGTH - 1) {
buffer[length] = src[length];
++length;
}
buffer[length] = src[length];
return(length + 1);
}
static inline bool
is_space(char c)
{
bool result = (c == ' ' || c == '\t' || c == '\r' || c == '\n');
return(result);
}
static u32
chars_until_first_whitespace(char *text, u32 len)
{
for (u32 i = 0; i < len; ++i) {
if (is_space(text[i])) {
return(i);
}
}
return(len - 1);
}
/*
* Skips all non-whitespace characters. On encountering whitespace
* skips all the whitespaces and then returns the pointer to the
* first non-whitespace character
*
* Returns NULL there is no next token (that is \n or \0 have
* been reached)
*
* Intended to be used like this:
* char *tok1 = next_token(str);
* char *tok2 = next_token(tok2);
* ...
*/
static char *
next_token(char *src)
{
while (*src && !is_space(*src)) {
++src;
}
while (*src && is_space(*src)) {
++src;
}
if (*src == '\0' || *src == '\n') {
return(NULL);
} else {
return(src);
}
}
static char *
get_string_literal(char *src)
{
char *start = src;
if (*src != '\"') {
return(NULL);
}
u32 len = 0;
++src; /* Skip the opening quote */
/* We do not allow multi-line string literals */
while (*src != '\n' && *src != '\"') {
++len;
++src;
}
char *result = malloc(len + 1);
memcpy(result, start + 1, len);
result[len] = '\0';
return(result);
}
static u32
set_bytes_of_u32(u8 byte)
{
u32 result = (u32) byte << 24 | (u32) byte << 16 | (u32) byte << 8 | (u32) byte;
return(result);
}
static enum eee_return_code
font_parse_bdf_header(char **src, struct bdf_header *dest)
{
char line[BDF_MAX_LINE_LENGTH];
for (;;) {
u32 line_length = store_line(*src, line);
u32 token_length = chars_until_first_whitespace(line, line_length);
if (strncmp("STARTFONT", line, token_length) == 0) {
char *maj_tok = next_token(line);
dest->version_major = strtol(maj_tok, NULL, 10);
dest->version_minor = strtol(maj_tok + 2, NULL, 10);
} else if (strncmp("FONT", line, token_length) == 0) {
u32 xlog_length = chars_until_first_whitespace(line + token_length + 1,
line_length - token_length - 1);
dest->xlog = malloc(xlog_length + 1);
memcpy(dest->xlog, line + token_length + 1, xlog_length);
dest->xlog[xlog_length] = '\0';
} else if (strncmp("SIZE", line, token_length) == 0) {
char *ptok = next_token(line);
char *dxtok = next_token(ptok);
char *dytok = next_token(dxtok);
dest->size_points = strtol(ptok, NULL, 10);
dest->size_dpix = strtol(dxtok, NULL, 10);
dest->size_dpiy = strtol(dytok, NULL, 10);
} else if (strncmp("FONTBOUNDINGBOX", line, token_length) == 0) {
char *wtok = next_token(line);
char *htok = next_token(wtok);
char *blxtok = next_token(htok);
char *blytok = next_token(blxtok);
dest->font_bbox[0] = strtol(wtok, NULL, 10);
dest->font_bbox[1] = strtol(htok, NULL, 10);
dest->font_bbox[2] = strtol(blxtok, NULL, 10);
dest->font_bbox[3] = strtol(blytok, NULL, 10);
} else if (strncmp("FAMILY_NAME", line, token_length) == 0) {
char *fnametok = next_token(line);
dest->family_name = get_string_literal(fnametok);
} else if (strncmp("PIXEL_SIZE", line, token_length) == 0) {
char *pstok = next_token(line);
dest->pixel_size = strtol(pstok, NULL, 10);
} else if (strncmp("POINT_SIZE", line, token_length) == 0) {
char *pstok = next_token(line);
dest->point_size = strtol(pstok, NULL, 10);
} else if (strncmp("RESOLUTION_X", line, token_length) == 0) {
char *rxtok = next_token(line);
dest->resx = strtol(rxtok, NULL, 10);
} else if (strncmp("RESOLUTION_Y", line, token_length) == 0) {
char *rytok = next_token(line);
dest->resy = strtol(rytok, NULL, 10);
} else if (strncmp("MIN_SPACE", line, token_length) == 0) {
char *mstok = next_token(line);
dest->min_space = strtol(mstok, NULL, 10);
} else if (strncmp("FONT_ASCENT", line, token_length) == 0) {
char *fatok = next_token(line);
dest->font_ascent = strtol(fatok, NULL, 10);
} else if (strncmp("FONT_DESCENT", line, token_length) == 0) {
char *fdtok = next_token(line);
dest->font_descent = strtol(fdtok, NULL, 10);
} else if (strncmp("DEFAULT_CHAR", line, token_length) == 0) {
char *dctok = next_token(line);
dest->defaut_char = strtol(dctok, NULL, 10);
} else if (strncmp("CHARS", line, token_length) == 0) {
char *cstok = next_token(line);
dest->char_count = strtol(cstok, NULL, 10);
} else if (strncmp("STARTCHAR", line, token_length) == 0) {
break;
}
*src += line_length;
}
return(EEE_SUCCESS);
}
static u32
font_read_one_glyph(char **src, struct bdf_header *header, struct bdf_letter *dest)
{
char line[BDF_MAX_LINE_LENGTH];
u32 codepoint = 0;
u32 char_width = header->font_bbox[0];
u32 char_height = header->font_bbox[1];
for (;;) {
u32 line_length = store_line(*src, line);
if (streq(line, "ENCODING")) {
// printf("] %s %d\n", line, token_length);
char *etok = next_token(line);
codepoint = strtol(etok, NULL, 10);
} else if (streq(line, "DWIDTH")) {
char *oxtok = next_token(line);
char *oytok = next_token(oxtok);
dest->offset[2] = strtol(oxtok, NULL, 10);
dest->offset[3] = strtol(oytok, NULL, 10);
} else if (streq(line, "BBX")) {
char *bbwtok = next_token(line);
char *bbhtok = next_token(bbwtok);
char *selfoffxtok = next_token(bbhtok);
char *selfoffytok = next_token(selfoffxtok);
char_width = strtol(bbwtok, NULL, 10);
char_height = strtol(bbhtok, NULL, 10);
dest->offset[0] = strtol(selfoffxtok, NULL, 10);
dest->offset[1] = strtol(selfoffytok, NULL, 10);
} else if (streq(line, "BITMAP")) {
*src += line_length;
/* Glyph rows (padded to whole bytes) */
for (u32 y = 0; y < char_height; ++y) {
u32 whole_bytes = (char_width + 7) / 8;
char *tok = *src;
for (u32 byte = 0; byte < whole_bytes; ++byte) {
//printf("%10s\n", tok);
u8 eight_bits = strtol(tok, NULL, 16);
u32 bits_until = 8;
if (byte == whole_bytes - 1) {
bits_until = char_width % 8;
}
for (u32 bit = 0; bit < bits_until; ++bit) {
u8 bit_value = (eight_bits >> (7 - bit)) & 1;
if (bit_value != 0) {
bit_value = 0xFF;
}
dest->bitmap[y * char_width + byte * 8 + bit] = set_bytes_of_u32(bit_value);
}
tok = next_token(tok);
}
/* [CHAR CHAR SPACE]* CHAR CHAR NEWLINE */
*src += (whole_bytes * 3);
}
continue;
} else if (streq(line, "ENDCHAR")) {
*src += line_length;
break;
}
*src += line_length;
}
dest->width = char_width;
return(codepoint);
}
static enum eee_return_code
font_parse_bdf(struct buffer *file, struct bdf_font *dest)
{
char *text = file->data;
enum eee_return_code status = font_parse_bdf_header(&text, &dest->header);
if (status != EEE_SUCCESS) {
return(status);
}
#if VERBOSE_INTERNAL
printf("[DEBUG] Read bfd header\n");
#endif
/*
* Read the glyphs
* We are ONLY reading a very small subset of characters (not even full ASCI)
*/
for (u32 char_index = 0; char_index < dest->header.char_count; ++char_index) {
struct bdf_letter glyph;
u32 codepoint = font_read_one_glyph(&text, &dest->header, &glyph);
if (BDF_LETTER_FIRST <= codepoint && codepoint <= BDF_LETTER_LAST) {
dest->letters[codepoint - BDF_LETTER_FIRST] = glyph;
}
}
#if VERBOSE_INTERNAL
printf("[DEBUG] Read bdf glyphs\n");
#endif
return(0);
}
static struct v2
font_text_bb(struct bdf_font *font, char text[])
{
u32 width = 0;
u32 height = 0;
u32 len = strlen(text);
s32 x = 0;
for (u32 i = 0; i < len; ++i) {
char c = text[i];
if (c == '\n') {
height += font->header.font_bbox[1];
x = 0;
} else if (c == '\t') {
x += 4 * font->header.font_bbox[1];
} else {
if (c < BDF_LETTER_FIRST || c > BDF_LETTER_LAST) {
c = '?';
}
struct bdf_letter *glyph = font->letters + c - BDF_LETTER_FIRST;
x += glyph->offset[2];
}
if (x > (s32) width) {
width = x;
}
}
if (x < 0) {
x = 0;
}
return((struct v2) { width, height });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment