Last active
July 9, 2021 18:03
-
-
Save aolo2/c7781190e5e8c71f45a7fd45916b485d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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