/* * kmscon - Fixed unifont font * * Copyright (c) 2012 Ted Kotz * Copyright (c) 2012-2013 David Herrmann * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * 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 * 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 * * This file is heavily based on font_8x16.c */ #include #include #include #include #include "font.h" #include "shl_hashtable.h" #include "shl_log.h" #include "uterm_video.h" #define LOG_SUBSYSTEM "font_unifont" /* * 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; out->attr.italic = false; out->attr.width = 8; out->attr.height = 16; 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) return -ERANGE; return find_glyph(id, out); } static int kmscon_font_unifont_render_inval(struct kmscon_font *font, const struct kmscon_glyph **out) { return find_glyph(0xfffd, out); } static int kmscon_font_unifont_render_empty(struct kmscon_font *font, const struct kmscon_glyph **out) { return find_glyph(' ', out); } struct kmscon_font_ops kmscon_font_unifont_ops = { .name = "unifont", .owner = NULL, .init = kmscon_font_unifont_init, .destroy = kmscon_font_unifont_destroy, .render = kmscon_font_unifont_render, .render_empty = kmscon_font_unifont_render_empty, .render_inval = kmscon_font_unifont_render_inval, };