shl: move log.[ch] to shl_log.[ch]
[platform/upstream/kmscon.git] / src / font_unifont.c
1 /*
2  * kmscon - Fixed unifont font
3  *
4  * Copyright (c) 2012 Ted Kotz <ted@kotz.us>
5  * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files
9  * (the "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 /**
28  * SECTION:font_unifont.c
29  * @short_description: Fixed unifont font
30  * @include: font.h
31  *
32  * This is a fixed font renderer backend that supports just one font which is
33  * statically compiled into the file. This bitmap font has 8x16 and 16x16
34  * glyphs. This can statically compile in any font defined as a unifont style
35  * hex format. This font is from the GNU unifont project available at:
36  *   http://unifoundry.com/unifont.html
37  *
38  * This file is heavily based on font_8x16.c
39  */
40
41 #include <errno.h>
42 #include <pthread.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include "font.h"
46 #include "shl_hashtable.h"
47 #include "shl_log.h"
48 #include "uterm_video.h"
49
50 #define LOG_SUBSYSTEM "font_unifont"
51
52 /*
53  * Glyph data is linked to the binary externally as binary data. The data layout
54  * is a size-byte followed by 32 data bytes. The data bytes are padded with 0 if
55  * the size is smaller than 32.
56  * Sizes bigger than 32 are not used.
57  */
58
59 struct unifont_data {
60         uint8_t len;
61         uint8_t data[32];
62 } __attribute__((__packed__));
63
64 extern const struct unifont_data _binary_src_font_unifont_data_bin_start[];
65 extern const struct unifont_data _binary_src_font_unifont_data_bin_end[];
66
67 /*
68  * Global glyph cache
69  * The linked binary glyph data cannot be directly passed to the caller as it
70  * has the wrong format. Hence, use a glyph-cache with all used glyphs and add
71  * new ones as soon as they are used.
72  */
73
74 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static struct shl_hashtable *cache;
76 static unsigned long cache_refnum;
77
78 static void cache_ref(void)
79 {
80         pthread_mutex_lock(&cache_mutex);
81         ++cache_refnum;
82         pthread_mutex_unlock(&cache_mutex);
83 }
84
85 static void cache_unref(void)
86 {
87         pthread_mutex_lock(&cache_mutex);
88         if (!--cache_refnum) {
89                 shl_hashtable_free(cache);
90                 cache = NULL;
91         }
92         pthread_mutex_unlock(&cache_mutex);
93 }
94
95 static void free_glyph(void *data)
96 {
97         struct kmscon_glyph *g = data;
98
99         free(g->buf.data);
100         free(g);
101 }
102
103 static void unfold(uint8_t *dst, uint8_t val)
104 {
105         *dst = 0xff * !!val;
106 }
107
108 static int find_glyph(uint32_t id, const struct kmscon_glyph **out)
109 {
110         struct kmscon_glyph *g;
111         int ret;
112         bool res;
113         const struct unifont_data *start, *end, *d;
114         unsigned int i, w;
115
116         pthread_mutex_lock(&cache_mutex);
117
118         if (!cache) {
119                 ret = shl_hashtable_new(&cache, shl_direct_hash,
120                                         shl_direct_equal, NULL, free_glyph);
121                 if (ret) {
122                         log_error("cannot create unifont hashtable: %d", ret);
123                         goto out_unlock;
124                 }
125         } else {
126                 res = shl_hashtable_find(cache, (void**)out,
127                                          (void*)(long)id);
128                 if (res) {
129                         ret = 0;
130                         goto out_unlock;
131                 }
132         }
133
134         if (id > 0xffff) {
135                 ret = -ERANGE;
136                 goto out_unlock;
137         }
138
139         start = _binary_src_font_unifont_data_bin_start;
140         end = _binary_src_font_unifont_data_bin_end;
141         d = &start[id];
142
143         if (d >= end) {
144                 ret = -ERANGE;
145                 goto out_unlock;
146         }
147
148         switch (d->len) {
149         case 16:
150                 w = 1;
151                 break;
152         case 32:
153                 w = 2;
154                 break;
155         default:
156                 ret = -EFAULT;
157                 goto out_unlock;
158         }
159
160         g = malloc(sizeof(*g));
161         if (!g) {
162                 ret = -ENOMEM;
163                 goto out_unlock;
164         }
165         memset(g, 0, sizeof(*g));
166         g->width = w;
167         g->buf.width = w * 8;
168         g->buf.height = 16;
169         g->buf.stride = w * 8;
170         g->buf.format = UTERM_FORMAT_GREY;
171
172         g->buf.data = malloc(g->buf.stride * g->buf.height);
173         if (!g->buf.data) {
174                 ret = -ENOMEM;
175                 goto err_free;
176         }
177
178         for (i = 0; i < d->len; ++i) {
179                 unfold(&g->buf.data[i * 8 + 0], d->data[i] & 0x80);
180                 unfold(&g->buf.data[i * 8 + 1], d->data[i] & 0x40);
181                 unfold(&g->buf.data[i * 8 + 2], d->data[i] & 0x20);
182                 unfold(&g->buf.data[i * 8 + 3], d->data[i] & 0x10);
183                 unfold(&g->buf.data[i * 8 + 4], d->data[i] & 0x08);
184                 unfold(&g->buf.data[i * 8 + 5], d->data[i] & 0x04);
185                 unfold(&g->buf.data[i * 8 + 6], d->data[i] & 0x02);
186                 unfold(&g->buf.data[i * 8 + 7], d->data[i] & 0x01);
187         }
188
189         ret = shl_hashtable_insert(cache, (void*)(long)id, g);
190         if (ret) {
191                 log_error("cannot insert glyph into glyph-cache: %d", ret);
192                 goto err_data;
193         }
194
195         *out = g;
196         ret = 0;
197         goto out_unlock;
198
199 err_data:
200         free(g->buf.data);
201 err_free:
202         free(g);
203 out_unlock:
204         pthread_mutex_unlock(&cache_mutex);
205         return ret;
206 }
207
208 static int kmscon_font_unifont_init(struct kmscon_font *out,
209                                     const struct kmscon_font_attr *attr)
210 {
211         static const char name[] = "static-unifont";
212         const struct unifont_data *start, *end;
213
214         log_debug("loading static unifont font");
215
216         start = _binary_src_font_unifont_data_bin_start;
217         end = _binary_src_font_unifont_data_bin_end;
218         if (start == end) {
219                 log_error("unifont glyph information not found in binary");
220                 return -EFAULT;
221         }
222
223
224         memset(&out->attr, 0, sizeof(out->attr));
225         memcpy(out->attr.name, name, sizeof(name));
226         out->attr.bold = false;
227         out->attr.italic = false;
228         out->attr.width = 8;
229         out->attr.height = 16;
230         kmscon_font_attr_normalize(&out->attr);
231         out->baseline = 4;
232
233         cache_ref();
234         return 0;
235 }
236
237 static void kmscon_font_unifont_destroy(struct kmscon_font *font)
238 {
239         log_debug("unloading static unifont font");
240         cache_unref();
241 }
242
243 static int kmscon_font_unifont_render(struct kmscon_font *font, uint32_t id,
244                                       const uint32_t *ch, size_t len,
245                                       const struct kmscon_glyph **out)
246 {
247         if (len > 1)
248                 return -ERANGE;
249
250         return find_glyph(id, out);
251 }
252
253 static int kmscon_font_unifont_render_inval(struct kmscon_font *font,
254                                             const struct kmscon_glyph **out)
255 {
256         return find_glyph(0xfffd, out);
257 }
258
259 static int kmscon_font_unifont_render_empty(struct kmscon_font *font,
260                                             const struct kmscon_glyph **out)
261 {
262         return find_glyph(' ', out);
263 }
264
265 struct kmscon_font_ops kmscon_font_unifont_ops = {
266         .name = "unifont",
267         .owner = NULL,
268         .init = kmscon_font_unifont_init,
269         .destroy = kmscon_font_unifont_destroy,
270         .render = kmscon_font_unifont_render,
271         .render_empty = kmscon_font_unifont_render_empty,
272         .render_inval = kmscon_font_unifont_render_inval,
273 };