diff options
author | Marvin Borner | 2021-07-02 22:36:33 +0200 |
---|---|---|
committer | Marvin Borner | 2021-07-02 22:41:09 +0200 |
commit | a81a011d738a0cbd36e0d4f8e17691424d6bb1a0 (patch) | |
tree | 143c71ff59f2790bf628b572a58d9f6996edd3bd | |
parent | f3e85eedc434da973267f360abdbb79cb6f24100 (diff) |
Added UTF8 rendering support
-rw-r--r-- | libs/libc/inc/utf8.h | 40 | ||||
-rw-r--r-- | libs/libgui/gfx.c | 53 | ||||
-rw-r--r-- | libs/libgui/gfx.h | 10 | ||||
-rw-r--r-- | libs/libgui/gui.c | 2 | ||||
-rw-r--r-- | libs/libgui/psf.c | 108 | ||||
-rw-r--r-- | libs/libgui/psf.h | 25 | ||||
-rwxr-xr-x | run | 2 |
7 files changed, 175 insertions, 65 deletions
diff --git a/libs/libc/inc/utf8.h b/libs/libc/inc/utf8.h new file mode 100644 index 0000000..c805157 --- /dev/null +++ b/libs/libc/inc/utf8.h @@ -0,0 +1,40 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef UTF8_H +#define UTF8_H + +#include <def.h> + +static void *utf8_decode(void *buf, u32 *c, u32 *e) +{ + static const u8 lengths[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; + static const u8 masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 }; + static const u32 mins[] = { 4194304, 0, 128, 2048, 65536 }; + static const u8 shiftc[] = { 0, 18, 12, 6, 0 }; + static const u8 shifte[] = { 0, 6, 4, 2, 0 }; + + u8 *s = buf; + u8 len = lengths[s[0] >> 3]; + + u8 *next = s + len + !len; + + *c = (u32)(s[0] & masks[len]) << 18; + *c |= (u32)(s[1] & 0x3f) << 12; + *c |= (u32)(s[2] & 0x3f) << 6; + *c |= (u32)(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + *e = (*c < mins[len]) << 6; + *e |= ((*c >> 11) == 0x1b) << 7; + *e |= (*c > 0x10FFFF) << 8; + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3]) >> 6; + *e ^= 0x2a; + *e >>= shifte[len]; + + return next; +} + +#endif diff --git a/libs/libgui/gfx.c b/libs/libgui/gfx.c index 8f6f163..5aaee26 100644 --- a/libs/libgui/gfx.c +++ b/libs/libgui/gfx.c @@ -14,6 +14,7 @@ #include <mem.h> #include <str.h> #include <sys.h> +#include <utf8.h> // TODO: Move to some global config file #define FONT_COUNT 6 @@ -56,30 +57,36 @@ static void load_font(enum font_type font_type) return; } - fonts[font_type] = psf_parse(sread(path)); + fonts[font_type] = psf_parse(path); assert(fonts[font_type]); } static void free_fonts(void) { for (u8 i = 0; i < FONT_COUNT; i++) { - if (fonts[i]) { - free(fonts[i]->raw); - free(fonts[i]); - } + psf_free(fonts[i]); } } -static void write_char(struct gfx_context *ctx, vec2 pos, struct gfx_font *font, u32 c, char ch) +static void write_char(struct gfx_context *ctx, vec2 pos, struct gfx_font *font, u32 c, u32 ch) { u8 bypp = ctx->bpp >> 3; + u32 glyph = ch; + if (ch > 0x7f) { + glyph = psf_unicode(font, ch); + if (!glyph) { + log("Char '%x' is not renderable\n", ch); + glyph = '?'; + } + } + char *draw = (char *)&ctx->fb[pos.x * bypp + pos.y * ctx->pitch]; u32 stride = font->char_size / font->size.y; for (u32 cy = 0; cy < font->size.y; cy++) { for (u32 cx = 0; cx < font->size.x; cx++) { - u8 bits = font->chars[ch * font->char_size + cy * stride + cx / 8]; + u8 bits = font->chars[glyph * font->char_size + cy * stride + cx / 8]; u8 bit = bits >> (7 - cx % 8) & 1; if (bit) { draw[bypp * cx] = GET_BLUE(c); @@ -129,24 +136,30 @@ struct gfx_font *gfx_resolve_font(enum font_type font_type) return fonts[font_type]; } -void gfx_write_char(struct gfx_context *ctx, vec2 pos, enum font_type font_type, u32 c, char ch) -{ - struct gfx_font *font = gfx_resolve_font(font_type); - write_char(ctx, pos, font, c, ch); -} - void gfx_write(struct gfx_context *ctx, vec2 pos, enum font_type font_type, u32 c, const char *text) { struct gfx_font *font = gfx_resolve_font(font_type); u32 cnt = 0; - for (u32 i = 0; i < strlen(text); i++) { - // TODO: Should this be here? - if (text[i] == '\r') { + + u32 length = strlen(text); + void *buffer = zalloc(length + 5); // utf8_decode needs 3B zero padding + strlcpy(buffer, text, length + 1); + + char *next; + for (char *p = buffer; p && *p; p = next) { + u32 ch, err; + next = utf8_decode(p, &ch, &err); + if (err) { + log("Invalid utf8 sequence\n"); + continue; + } + + if (ch == '\r') { cnt = 0; - } else if (text[i] == '\n') { + } else if (ch == '\n') { cnt = 0; pos.y += font->size.y; - } else if (text[i] == '\t') { + } else if (ch == '\t') { cnt += 4; } else { // TODO: Overflow on single line input @@ -154,10 +167,12 @@ void gfx_write(struct gfx_context *ctx, vec2 pos, enum font_type font_type, u32 cnt = 0; pos.y += font->size.y; } - write_char(ctx, vec2(pos.x + cnt * font->size.x, pos.y), font, c, text[i]); + write_char(ctx, vec2(pos.x + cnt * font->size.x, pos.y), font, c, ch); cnt++; } } + + free(buffer); } /** diff --git a/libs/libgui/gfx.h b/libs/libgui/gfx.h index 899bf4d..d78750a 100644 --- a/libs/libgui/gfx.h +++ b/libs/libgui/gfx.h @@ -72,9 +72,13 @@ enum gfx_filter { // Generalized font struct struct gfx_font { void *raw; - char *chars; + u8 *chars; vec2 size; - int char_size; + u32 char_size; + u32 total_size; + struct { + u8 unicode : 1; + } flags; }; struct gfx_context { @@ -98,8 +102,6 @@ struct gfx_context *gfx_clone(struct gfx_context *ctx) NONNULL; */ struct gfx_font *gfx_resolve_font(enum font_type font_type); -void gfx_write_char(struct gfx_context *ctx, vec2 pos, enum font_type font_type, u32 c, - char ch) NONNULL; void gfx_write(struct gfx_context *ctx, vec2 pos, enum font_type font_type, u32 c, const char *text) NONNULL; diff --git a/libs/libgui/gui.c b/libs/libgui/gui.c index f768232..337fd81 100644 --- a/libs/libgui/gui.c +++ b/libs/libgui/gui.c @@ -70,7 +70,7 @@ static struct gui_widget *gui_widget_in_widget(struct gui_widget *parent, u32 wi while (iterator) { struct gui_widget *widget = iterator->data; if (widget->id == widget_id) - return iterator->data; + return widget; struct gui_widget *sub = gui_widget_in_widget(widget, widget_id); if (sub && sub->id == widget_id) diff --git a/libs/libgui/psf.c b/libs/libgui/psf.c index 5a6b1bb..5af04a7 100644 --- a/libs/libgui/psf.c +++ b/libs/libgui/psf.c @@ -1,58 +1,116 @@ // MIT License, Copyright (c) 2020 Marvin Borner // PSF parser +#include <assert.h> #include <def.h> #include <libgui/gfx.h> #include <libgui/psf.h> #include <mem.h> #include <print.h> -// Verifies the PSF magics -// Returns the PSF version or 0 -static int psf_verify(char *data) +static u8 psf_verify(void *data) { struct psf1_header *header1 = (struct psf1_header *)data; struct psf2_header *header2 = (struct psf2_header *)data; - if (header1->magic[0] == PSF1_MAGIC_0 && header1->magic[1] == PSF1_MAGIC_1) + if (header1->magic == PSF1_MAGIC) return 1; - else if (header2->magic[0] == PSF2_MAGIC_0 && header2->magic[1] == PSF2_MAGIC_1 && - header2->magic[2] == PSF2_MAGIC_2 && header2->magic[3] == PSF2_MAGIC_3) + else if (header2->magic == PSF2_MAGIC) return 2; else return 0; } -struct gfx_font *psf_parse(char *data) +void psf_free(struct gfx_font *font) { - int version = psf_verify(data); - - char *chars; - int height; - int width; - int char_size; - - if (version == 1) { - chars = data + sizeof(struct psf1_header); - height = ((struct psf1_header *)data)->char_size; - width = 8; - char_size = ((struct psf1_header *)data)->char_size; - } else if (version == 2) { - chars = data + ((struct psf2_header *)data)->size; - height = ((struct psf2_header *)data)->height; - width = ((struct psf2_header *)data)->width; - char_size = ((struct psf2_header *)data)->char_size; - } else { + free(font->raw); + free(font); +} + +// TODO: Improve bruteforce unitab search +u32 psf_unicode(struct gfx_font *font, u32 needle) +{ + if (!font->flags.unicode) + return 0; + + u8 *data = font->raw; + + u8 version = psf_verify(data); + assert(version == 2); + + struct psf2_header *header = (struct psf2_header *)data; + + u8 *table = (u8 *)(data + header->size + header->char_count * header->char_size); + + u32 glyph = 0; + while (table < data + font->total_size) { + u32 ch = table[0] & 0xff; + if (ch == 0xff) { + glyph++; + table++; + continue; + } else if (ch & 128) { + if ((ch & 32) == 0) { + ch = ((table[0] & 0x1f) << 6) + (table[1] & 0x3f); + table++; + } else if ((ch & 16) == 0) { + ch = ((((table[0] & 0xf) << 6) + (table[1] & 0x3f)) << 6) + + (table[2] & 0x3f); + table += 2; + } else if ((ch & 8) == 0) { + ch = ((((((table[0] & 0x7) << 6) + (table[1] & 0x3f)) << 6) + + (table[2] & 0x3f)) + << 6) + + (table[3] & 0x3f); + table += 3; + } else { + ch = 0; + } + } + + if (ch == needle) + return glyph; + + table++; + } + + return 0; +} + +struct gfx_font *psf_parse(const char *path) +{ + struct stat s = { 0 }; + if (stat(path, &s) != 0 || !s.size) + return NULL; + u8 *data = malloc(s.size); + read(path, data, 0, s.size); + + u8 version = psf_verify(data); + if (version == 0) { print("Unknown font!\n"); return NULL; + } else if (version == 1) { + log("PSF version 1 is no longer supported\n"); + return NULL; } + struct psf2_header *header = (struct psf2_header *)data; + u8 *chars = data + header->size; + u32 height = header->height; + u32 width = header->width; + u32 char_size = header->char_size; + + if (!(header->flags & PSF2_UNICODE)) + log("No unicode table found, fonts may not render correctly!\n"); + struct gfx_font *font = malloc(sizeof(*font)); font->raw = data; font->chars = chars; font->size.x = width; font->size.y = height; font->char_size = char_size; + font->total_size = s.size; + font->flags.unicode = (header->flags & PSF2_UNICODE) == PSF2_UNICODE; return font; } diff --git a/libs/libgui/psf.h b/libs/libgui/psf.h index 8298441..2494fe9 100644 --- a/libs/libgui/psf.h +++ b/libs/libgui/psf.h @@ -10,15 +10,10 @@ * PSF version 1 */ -#define PSF1_MAGIC_0 0x36 -#define PSF1_MAGIC_1 0x04 -#define PSF1_MODE_256 0 -#define PSF1_MODE_512 1 -#define PSF1_MODE_256_UNICODE 2 -#define PSF1_MODE_512_UNICODE 3 +#define PSF1_MAGIC 0x0436 struct psf1_header { - u8 magic[2]; + u16 magic; u8 mode; u8 char_size; }; @@ -27,22 +22,22 @@ struct psf1_header { * PSF version 2 */ -#define PSF2_MAGIC_0 0x72 -#define PSF2_MAGIC_1 0xb5 -#define PSF2_MAGIC_2 0x4a -#define PSF2_MAGIC_3 0x86 +enum psf2_flags { PSF2_UNICODE = 1 }; +#define PSF2_MAGIC 0x864ab572 struct psf2_header { - u8 magic[4]; + u32 magic; u32 version; u32 size; - u32 flags; - u32 glyph_count; + enum psf2_flags flags; + u32 char_count; u32 char_size; u32 height; u32 width; }; -struct gfx_font *psf_parse(char *data) NONNULL; +struct gfx_font *psf_parse(const char *path) NONNULL; +u32 psf_unicode(struct gfx_font *font, u32 needle); +void psf_free(struct gfx_font *font); #endif @@ -99,7 +99,7 @@ make_disk() { echo "Getting font" cd disk/font/ - VERSION="1.8.2" + VERSION="1.9.1" wget -q "https://github.com/fcambus/spleen/releases/download/$VERSION/spleen-$VERSION.tar.gz" tar xzf "spleen-$VERSION.tar.gz" mv spleen-"$VERSION"/*.psfu . |