* SECTION:font_unifont.c
* @short_description: Fixed unifont font
* @include: font.h
- *
+ *
* This is a fixed font renderer backend that supports just one font which is
- * statically compiled into the file. This bitmap font has 8x16 and 16x16
+ * statically compiled into the file. This bitmap font has 8x16 and 16x16
* glyphs. This can statically compile in any font defined as a unifont style
- * hex format. This font is from the GNU unifont project available at:
- * http://unifoundry.com/unifont.html
+ * hex format. This font is from the GNU unifont project available at:
+ * http://unifoundry.com/unifont.html
*
* This file is heavily based on font_8x16.c
- *
*/
#include <errno.h>
+#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "font.h"
-#include "log.h"
+#include "shl_hashtable.h"
+#include "shl_log.h"
#include "uterm_video.h"
#define LOG_SUBSYSTEM "font_unifont"
-/* array is generated and compiled externally */
-extern const struct kmscon_glyph kmscon_font_unifont_data_hex_glyphs[];
-extern size_t kmscon_font_unifont_data_hex_len;
+/*
+ * Glyph data is linked to the binary externally as binary data. The data layout
+ * is a size-byte followed by 32 data bytes. The data bytes are padded with 0 if
+ * the size is smaller than 32.
+ * Sizes bigger than 32 are not used.
+ */
+
+struct unifont_data {
+ uint8_t len;
+ uint8_t data[32];
+} __attribute__((__packed__));
+
+extern const struct unifont_data _binary_src_font_unifont_data_bin_start[];
+extern const struct unifont_data _binary_src_font_unifont_data_bin_end[];
+
+/*
+ * Global glyph cache
+ * The linked binary glyph data cannot be directly passed to the caller as it
+ * has the wrong format. Hence, use a glyph-cache with all used glyphs and add
+ * new ones as soon as they are used.
+ */
+
+static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct shl_hashtable *cache;
+static unsigned long cache_refnum;
+
+static void cache_ref(void)
+{
+ pthread_mutex_lock(&cache_mutex);
+ ++cache_refnum;
+ pthread_mutex_unlock(&cache_mutex);
+}
+
+static void cache_unref(void)
+{
+ pthread_mutex_lock(&cache_mutex);
+ if (!--cache_refnum) {
+ shl_hashtable_free(cache);
+ cache = NULL;
+ }
+ pthread_mutex_unlock(&cache_mutex);
+}
+
+static void free_glyph(void *data)
+{
+ struct kmscon_glyph *g = data;
+
+ free(g->buf.data);
+ free(g);
+}
+
+static void unfold(uint8_t *dst, uint8_t val)
+{
+ *dst = 0xff * !!val;
+}
+
+static int find_glyph(uint32_t id, const struct kmscon_glyph **out)
+{
+ struct kmscon_glyph *g;
+ int ret;
+ bool res;
+ const struct unifont_data *start, *end, *d;
+ unsigned int i, w;
+
+ pthread_mutex_lock(&cache_mutex);
+
+ if (!cache) {
+ ret = shl_hashtable_new(&cache, shl_direct_hash,
+ shl_direct_equal, NULL, free_glyph);
+ if (ret) {
+ log_error("cannot create unifont hashtable: %d", ret);
+ goto out_unlock;
+ }
+ } else {
+ res = shl_hashtable_find(cache, (void**)out,
+ (void*)(long)id);
+ if (res) {
+ ret = 0;
+ goto out_unlock;
+ }
+ }
+
+ if (id > 0xffff) {
+ ret = -ERANGE;
+ goto out_unlock;
+ }
+
+ start = _binary_src_font_unifont_data_bin_start;
+ end = _binary_src_font_unifont_data_bin_end;
+ d = &start[id];
+
+ if (d >= end) {
+ ret = -ERANGE;
+ goto out_unlock;
+ }
+
+ switch (d->len) {
+ case 16:
+ w = 1;
+ break;
+ case 32:
+ w = 2;
+ break;
+ default:
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+
+ g = malloc(sizeof(*g));
+ if (!g) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ memset(g, 0, sizeof(*g));
+ g->width = w;
+ g->buf.width = w * 8;
+ g->buf.height = 16;
+ g->buf.stride = w * 8;
+ g->buf.format = UTERM_FORMAT_GREY;
+
+ g->buf.data = malloc(g->buf.stride * g->buf.height);
+ if (!g->buf.data) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ for (i = 0; i < d->len; ++i) {
+ unfold(&g->buf.data[i * 8 + 0], d->data[i] & 0x80);
+ unfold(&g->buf.data[i * 8 + 1], d->data[i] & 0x40);
+ unfold(&g->buf.data[i * 8 + 2], d->data[i] & 0x20);
+ unfold(&g->buf.data[i * 8 + 3], d->data[i] & 0x10);
+ unfold(&g->buf.data[i * 8 + 4], d->data[i] & 0x08);
+ unfold(&g->buf.data[i * 8 + 5], d->data[i] & 0x04);
+ unfold(&g->buf.data[i * 8 + 6], d->data[i] & 0x02);
+ unfold(&g->buf.data[i * 8 + 7], d->data[i] & 0x01);
+ }
+
+ ret = shl_hashtable_insert(cache, (void*)(long)id, g);
+ if (ret) {
+ log_error("cannot insert glyph into glyph-cache: %d", ret);
+ goto err_data;
+ }
+
+ *out = g;
+ ret = 0;
+ goto out_unlock;
+
+err_data:
+ free(g->buf.data);
+err_free:
+ free(g);
+out_unlock:
+ pthread_mutex_unlock(&cache_mutex);
+ return ret;
+}
static int kmscon_font_unifont_init(struct kmscon_font *out,
const struct kmscon_font_attr *attr)
{
static const char name[] = "static-unifont";
+ const struct unifont_data *start, *end;
log_debug("loading static unifont font");
+ start = _binary_src_font_unifont_data_bin_start;
+ end = _binary_src_font_unifont_data_bin_end;
+ if (start == end) {
+ log_error("unifont glyph information not found in binary");
+ return -EFAULT;
+ }
+
+
memset(&out->attr, 0, sizeof(out->attr));
memcpy(out->attr.name, name, sizeof(name));
out->attr.bold = false;
kmscon_font_attr_normalize(&out->attr);
out->baseline = 4;
+ cache_ref();
return 0;
}
static void kmscon_font_unifont_destroy(struct kmscon_font *font)
{
log_debug("unloading static unifont font");
+ cache_unref();
}
static int kmscon_font_unifont_render(struct kmscon_font *font, uint32_t id,
const uint32_t *ch, size_t len,
const struct kmscon_glyph **out)
{
- if (len > 1 || *ch >= kmscon_font_unifont_data_hex_len)
+ if (len > 1)
return -ERANGE;
- *out = &kmscon_font_unifont_data_hex_glyphs[*ch];
- return 0;
+ return find_glyph(id, out);
}
static int kmscon_font_unifont_render_inval(struct kmscon_font *font,
const struct kmscon_glyph **out)
{
- if (0xfffd < kmscon_font_unifont_data_hex_len)
- *out = &kmscon_font_unifont_data_hex_glyphs[0xfffd];
- else if ('?' < kmscon_font_unifont_data_hex_len)
- *out = &kmscon_font_unifont_data_hex_glyphs['?'];
- else
- *out = &kmscon_font_unifont_data_hex_glyphs[0];
-
- return 0;
+ return find_glyph(0xfffd, out);
}
static int kmscon_font_unifont_render_empty(struct kmscon_font *font,
const struct kmscon_glyph **out)
{
- if (' ' < kmscon_font_unifont_data_hex_len) {
- *out = &kmscon_font_unifont_data_hex_glyphs[' '];
- return 0;
- } else {
- return kmscon_font_unifont_render_inval(font, out);
- }
+ return find_glyph(' ', out);
}
struct kmscon_font_ops kmscon_font_unifont_ops = {