aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin Borner2021-07-02 22:36:33 +0200
committerMarvin Borner2021-07-02 22:41:09 +0200
commita81a011d738a0cbd36e0d4f8e17691424d6bb1a0 (patch)
tree143c71ff59f2790bf628b572a58d9f6996edd3bd
parentf3e85eedc434da973267f360abdbb79cb6f24100 (diff)
Added UTF8 rendering support
-rw-r--r--libs/libc/inc/utf8.h40
-rw-r--r--libs/libgui/gfx.c53
-rw-r--r--libs/libgui/gfx.h10
-rw-r--r--libs/libgui/gui.c2
-rw-r--r--libs/libgui/psf.c108
-rw-r--r--libs/libgui/psf.h25
-rwxr-xr-xrun2
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
diff --git a/run b/run
index 900f0d4..47ac40c 100755
--- a/run
+++ b/run
@@ -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 .