Modify it to adjust Tizen IVI enviroment
[platform/upstream/kmscon.git] / src / font_unifont.c
index 0dbb1aa..cdedbca 100644 (file)
  * 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;
@@ -68,47 +230,36 @@ static int kmscon_font_unifont_init(struct kmscon_font *out,
        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 = {