2 * kmscon - Cairo Text Renderer Backend
4 * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 * Cairo based text renderer
36 #include "shl_hashtable.h"
38 #include "uterm_video.h"
40 #define LOG_SUBSYSTEM "text_cairo"
43 const struct kmscon_glyph *glyph;
44 cairo_surface_t *surf;
49 struct shl_hashtable *glyphs;
50 struct shl_hashtable *bold_glyphs;
54 struct uterm_video_buffer buf[2];
55 cairo_surface_t *surf[2];
60 struct uterm_video_buffer vbuf;
63 static int tc_init(struct kmscon_text *txt)
67 tc = malloc(sizeof(*tc));
75 static void tc_destroy(struct kmscon_text *txt)
77 struct tc_cairo *tc = txt->data;
82 static void free_glyph(void *data)
84 struct tc_glyph *glyph = data;
86 cairo_surface_destroy(glyph->surf);
91 static unsigned int format_u2c(unsigned int f)
94 case UTERM_FORMAT_XRGB32:
95 return CAIRO_FORMAT_ARGB32;
96 case UTERM_FORMAT_RGB16:
97 return CAIRO_FORMAT_RGB16_565;
98 case UTERM_FORMAT_GREY:
99 return CAIRO_FORMAT_A8;
101 return CAIRO_FORMAT_INVALID;
105 static int alloc_indirect(struct kmscon_text *txt,
106 unsigned int w, unsigned int h)
108 struct tc_cairo *tc = txt->data;
109 unsigned int s, i, format;
112 log_info("using blitting engine");
114 format = format_u2c(UTERM_FORMAT_XRGB32);
115 s = cairo_format_stride_for_width(format, w);
117 tc->data[0] = malloc(s * h);
118 tc->data[1] = malloc(s * h);
119 if (!tc->data[0] || !tc->data[1]) {
120 log_error("cannot allocate memory for render-buffer");
125 for (i = 0; i < 2; ++i) {
126 tc->surf[i] = cairo_image_surface_create_for_data(tc->data[i],
130 ret = cairo_surface_status(tc->surf[i]);
131 if (ret != CAIRO_STATUS_SUCCESS) {
132 log_error("cannot create cairo surface: %d", ret);
140 tc->vbuf.format = UTERM_FORMAT_XRGB32;
141 tc->use_indirect = true;
145 cairo_surface_destroy(tc->surf[1]);
146 cairo_surface_destroy(tc->surf[0]);
155 static int tc_set(struct kmscon_text *txt)
157 struct tc_cairo *tc = txt->data;
159 unsigned int format, w, h;
160 struct uterm_mode *m;
162 memset(tc, 0, sizeof(*tc));
163 m = uterm_display_get_current(txt->disp);
164 w = uterm_mode_get_width(m);
165 h = uterm_mode_get_height(m);
167 ret = shl_hashtable_new(&tc->glyphs, shl_direct_hash,
168 shl_direct_equal, NULL,
173 ret = shl_hashtable_new(&tc->bold_glyphs, shl_direct_hash,
174 shl_direct_equal, NULL,
180 * TODO: It is actually faster to use a local shadow buffer and then
181 * blit all data to the framebuffer afterwards. Reads seem to be
182 * horribly slow on some mmap'ed framebuffers. However, that's not true
183 * for all so we actually don't know which to use here.
185 ret = uterm_display_get_buffers(txt->disp, tc->buf,
186 UTERM_FORMAT_XRGB32 |
189 log_warning("cannot get buffers for display %p",
191 ret = alloc_indirect(txt, w, h);
193 goto err_htable_bold;
195 format = format_u2c(tc->buf[0].format);
196 tc->surf[0] = cairo_image_surface_create_for_data(
202 format = format_u2c(tc->buf[1].format);
203 tc->surf[1] = cairo_image_surface_create_for_data(
210 ret = cairo_surface_status(tc->surf[0]);
211 ret2 = cairo_surface_status(tc->surf[1]);
212 if (ret != CAIRO_STATUS_SUCCESS ||
213 ret2 != CAIRO_STATUS_SUCCESS) {
214 log_error("cannot create cairo surface: %d %d",
216 cairo_surface_destroy(tc->surf[1]);
217 cairo_surface_destroy(tc->surf[0]);
218 ret = alloc_indirect(txt, w, h);
220 goto err_htable_bold;
224 tc->ctx[0] = cairo_create(tc->surf[0]);
225 tc->ctx[1] = cairo_create(tc->surf[1]);
226 ret = cairo_status(tc->ctx[0]);
227 ret2 = cairo_status(tc->ctx[1]);
228 if (ret != CAIRO_STATUS_SUCCESS || ret2 != CAIRO_STATUS_SUCCESS) {
229 log_error("cannot create cairo contexts: %d %d", ret, ret2);
233 txt->cols = w / txt->font->attr.width;
234 txt->rows = h / txt->font->attr.height;
239 cairo_destroy(tc->ctx[1]);
240 cairo_destroy(tc->ctx[0]);
241 cairo_surface_destroy(tc->surf[1]);
242 cairo_surface_destroy(tc->surf[0]);
246 shl_hashtable_free(tc->bold_glyphs);
248 shl_hashtable_free(tc->glyphs);
252 static void tc_unset(struct kmscon_text *txt)
254 struct tc_cairo *tc = txt->data;
256 cairo_destroy(tc->ctx[1]);
257 cairo_destroy(tc->ctx[0]);
258 cairo_surface_destroy(tc->surf[1]);
259 cairo_surface_destroy(tc->surf[0]);
262 shl_hashtable_free(tc->bold_glyphs);
263 shl_hashtable_free(tc->glyphs);
266 static int find_glyph(struct kmscon_text *txt, struct tc_glyph **out,
267 uint32_t id, const uint32_t *ch, size_t len, bool bold)
269 struct tc_cairo *tc = txt->data;
270 struct tc_glyph *glyph;
271 struct shl_hashtable *gtable;
272 struct kmscon_font *font;
273 const struct uterm_video_buffer *buf;
275 unsigned int format, i;
280 gtable = tc->bold_glyphs;
281 font = txt->bold_font;
287 res = shl_hashtable_find(gtable, (void**)&glyph,
288 (void*)(unsigned long)id);
294 glyph = malloc(sizeof(*glyph));
297 memset(glyph, 0, sizeof(*glyph));
300 ret = kmscon_font_render_empty(font, &glyph->glyph);
302 ret = kmscon_font_render(font, id, ch, len, &glyph->glyph);
305 ret = kmscon_font_render_inval(font, &glyph->glyph);
310 buf = &glyph->glyph->buf;
311 stride = buf->stride;
312 format = format_u2c(buf->format);
313 glyph->surf = cairo_image_surface_create_for_data(buf->data,
318 ret = cairo_surface_status(glyph->surf);
319 if (ret == CAIRO_STATUS_INVALID_STRIDE) {
320 stride = cairo_format_stride_for_width(format, buf->width);
321 if (!tc->new_stride) {
322 tc->new_stride = true;
323 log_debug("wrong stride, copy buffer (%d => %d)",
324 buf->stride, stride);
327 glyph->data = malloc(stride * buf->height);
329 log_error("cannot allocate memory for glyph storage");
336 for (i = 0; i < buf->height; ++i) {
337 memcpy(dst, src, buf->width);
342 cairo_surface_destroy(glyph->surf);
343 glyph->surf = cairo_image_surface_create_for_data(glyph->data,
348 ret = cairo_surface_status(glyph->surf);
350 if (ret != CAIRO_STATUS_SUCCESS) {
351 log_error("cannot create cairo-glyph: %d %p %d %d %d %d",
352 ret, glyph->data ? glyph->data : buf->data, format,
353 buf->width, buf->height, stride);
358 ret = shl_hashtable_insert(gtable, (void*)(long)id, glyph);
366 cairo_surface_destroy(glyph->surf);
373 static int tc_prepare(struct kmscon_text *txt)
375 struct tc_cairo *tc = txt->data;
378 ret = uterm_display_use(txt->disp, NULL);
380 log_error("cannot use display %p", txt->disp);
389 static int tc_draw(struct kmscon_text *txt,
390 uint32_t id, const uint32_t *ch, size_t len,
392 unsigned int posx, unsigned int posy,
393 const struct tsm_screen_attr *attr)
395 struct tc_cairo *tc = txt->data;
396 cairo_t *cr = tc->ctx[tc->cur];
397 struct tc_glyph *glyph;
403 ret = find_glyph(txt, &glyph, id, ch, len, attr->bold);
408 posx * txt->font->attr.width,
409 posy * txt->font->attr.height,
410 txt->font->attr.width,
411 txt->font->attr.height);
414 cairo_set_source_rgb(cr, attr->fr / 255.0, attr->fg / 255.0,
417 cairo_set_source_rgb(cr, attr->br / 255.0, attr->bg / 255.0,
423 cairo_set_source_rgb(cr, attr->br / 255.0, attr->bg / 255.0,
426 cairo_set_source_rgb(cr, attr->fr / 255.0, attr->fg / 255.0,
429 cairo_mask_surface(cr, glyph->surf,
430 posx * txt->font->attr.width,
431 posy * txt->font->attr.height);
436 static int tc_render(struct kmscon_text *txt)
438 struct tc_cairo *tc = txt->data;
441 cairo_surface_flush(tc->surf[tc->cur]);
443 if (!tc->use_indirect)
446 tc->vbuf.data = tc->data[tc->cur];
447 ret = uterm_display_blit(txt->disp, &tc->vbuf, 0, 0);
449 log_error("cannot blit back-buffer to display: %d", ret);
456 struct kmscon_text_ops kmscon_text_cairo_ops = {
460 .destroy = tc_destroy,
463 .prepare = tc_prepare,