kmscon/src/font_pango.c
David Herrmann e5f5c2729a font: require compositor reference
To avoid cairo dependencies we now take a compositor reference in the
font backend so fonts can be drawn with GL textures instead of cairo.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
2012-01-22 13:51:23 +01:00

532 lines
11 KiB
C

/*
* kmscon - Font Management - Pango backend
*
* Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
* Copyright (c) 2011 University of Tuebingen
*
* 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.
*/
/*
* Pango Font Management
* This is the font backend using the pango library in conjunction with cairo as
* output. See glyph type for detailed information on the caching algorithms
* used.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <glib.h>
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include "font.h"
#include "log.h"
#include "output.h"
#include "unicode.h"
enum glyph_type {
GLYPH_NONE,
GLYPH_LAYOUT,
GLYPH_STR,
};
struct kmscon_glyph {
size_t ref;
kmscon_symbol_t ch;
unsigned int width;
int type;
union {
struct layout {
PangoLayout *layout;
} layout;
struct str {
PangoFont *font;
PangoGlyphString *str;
uint32_t ascent;
} str;
} src;
};
struct kmscon_font_factory {
unsigned long ref;
struct kmscon_symbol_table *st;
struct kmscon_compositor *comp;
struct kmscon_context *ctx;
};
struct kmscon_font {
size_t ref;
struct kmscon_symbol_table *st;
unsigned int width;
unsigned int height;
GHashTable *glyphs;
PangoContext *ctx;
};
static int kmscon_font_lookup(struct kmscon_font *font,
kmscon_symbol_t key, struct kmscon_glyph **out);
/*
* Glyphs
* Glyphs are for internal use only! The outside world uses kmscon_char
* objects in combination with kmscon_font to draw characters. Internally, we
* cache a kmscon_glyph for every character that is drawn.
* This allows us to speed up the drawing operations because most characters are
* already cached.
*
* Glyphs are cached in a hash-table by each font. If a character is drawn, we
* look it up in the hash-table (or create a new one if none is found) and draw
* it to the framebuffer.
* A glyph may use several ways to cache the glyph description:
* GLYPH_NONE:
* No information is currently attached so the glyph cannot be drawn.
* GLYPH_LAYOUT:
* The most basic drawing operation. This is the slowest of all but can draw
* any text you want. It uses a PangoLayout internally and recalculates the
* character sizes each time we draw them.
*/
static int kmscon_glyph_new(struct kmscon_glyph **out, kmscon_symbol_t ch)
{
struct kmscon_glyph *glyph;
if (!out)
return -EINVAL;
glyph = malloc(sizeof(*glyph));
if (!glyph)
return -ENOMEM;
memset(glyph, 0, sizeof(*glyph));
glyph->ref = 1;
glyph->type = GLYPH_NONE;
glyph->ch = ch;
*out = glyph;
return 0;
}
/*
* Reset internal glyph description. You must use kmscon_glyph_set() again to
* attach new glyph descriptions.
*/
static void kmscon_glyph_reset(struct kmscon_glyph *glyph)
{
if (!glyph)
return;
switch (glyph->type) {
case GLYPH_LAYOUT:
g_object_unref(glyph->src.layout.layout);
break;
case GLYPH_STR:
g_object_unref(glyph->src.str.font);
pango_glyph_string_free(glyph->src.str.str);
break;
}
glyph->type = GLYPH_NONE;
glyph->width = 0;
}
static void kmscon_glyph_ref(struct kmscon_glyph *glyph)
{
if (!glyph)
return;
++glyph->ref;
}
static void kmscon_glyph_unref(struct kmscon_glyph *glyph)
{
if (!glyph || !glyph->ref)
return;
if (--glyph->ref)
return;
kmscon_glyph_reset(glyph);
free(glyph);
}
/*
* Generate glyph description.
* This connects the glyph with the given font an generates the fastest glyph
* description.
* Returns 0 on success.
*/
static int kmscon_glyph_set(struct kmscon_glyph *glyph,
struct kmscon_font *font)
{
PangoLayout *layout;
PangoLayoutLine *line;
PangoGlyphItem *tmp;
PangoGlyphString *str;
PangoRectangle rec;
size_t len;
const char *val;
if (!glyph || !font)
return -EINVAL;
layout = pango_layout_new(font->ctx);
if (!layout)
return -EINVAL;
val = kmscon_symbol_get_u8(font->st, glyph->ch, &len);
pango_layout_set_text(layout, val, len);
kmscon_symbol_free_u8(val);
pango_layout_get_extents(layout, NULL, &rec);
line = pango_layout_get_line_readonly(layout, 0);
if (!line || !line->runs || line->runs->next) {
kmscon_glyph_reset(glyph);
glyph->type = GLYPH_LAYOUT;
glyph->src.layout.layout = layout;
} else {
tmp = line->runs->data;
str = pango_glyph_string_copy(tmp->glyphs);
if (!str) {
g_object_unref(layout);
return -ENOMEM;
}
kmscon_glyph_reset(glyph);
glyph->type = GLYPH_STR;
glyph->src.str.str = str;
glyph->src.str.font =
g_object_ref(tmp->item->analysis.font);
glyph->src.str.ascent =
PANGO_PIXELS_CEIL(pango_layout_get_baseline(layout));
g_object_unref(layout);
}
glyph->width = PANGO_PIXELS(rec.width);
return 0;
}
int kmscon_font_factory_new(struct kmscon_font_factory **out,
struct kmscon_symbol_table *st, struct kmscon_compositor *comp)
{
struct kmscon_font_factory *ff;
if (!out || !st || !comp)
return -EINVAL;
ff = malloc(sizeof(*ff));
if (!ff)
return -ENOMEM;
memset(ff, 0, sizeof(*ff));
ff->ref = 1;
ff->st = st;
ff->comp = comp;
ff->ctx = kmscon_compositor_get_context(comp);
kmscon_compositor_ref(ff->comp);
kmscon_symbol_table_ref(ff->st);
*out = ff;
return 0;
}
void kmscon_font_factory_ref(struct kmscon_font_factory *ff)
{
if (!ff)
return;
++ff->ref;
}
void kmscon_font_factory_unref(struct kmscon_font_factory *ff)
{
if (!ff || !ff->ref)
return;
if (--ff->ref)
return;
kmscon_compositor_unref(ff->comp);
kmscon_symbol_table_unref(ff->st);
free(ff);
}
/*
* Measure font width
* We simply draw all ASCII characters and use the average width as default
* character width.
* This has the side effect that all ASCII characters are already cached and the
* console will speed up.
*/
static int measure_width(struct kmscon_font *font)
{
unsigned int i, num, width;
int ret;
kmscon_symbol_t ch;
struct kmscon_glyph *glyph;
if (!font)
return -EINVAL;
width = 0;
num = 0;
for (i = 0; i < 127; ++i) {
ch = kmscon_symbol_make(i);
ret = kmscon_font_lookup(font, ch, &glyph);
if (ret)
continue;
if (glyph->width > 0) {
width += glyph->width;
num++;
}
kmscon_glyph_unref(glyph);
}
if (!num)
return -EFAULT;
font->width = width / num;
log_debug("font: width is %u\n", font->width);
return 0;
}
/*
* Creates a new font
* \height is the height in pixel that we have for each character.
* Returns 0 on success and stores the new font in \out.
*/
int kmscon_font_factory_load(struct kmscon_font_factory *ff,
struct kmscon_font **out, unsigned int width, unsigned int height)
{
struct kmscon_font *font;
int ret;
PangoFontDescription *desc;
PangoFontMap *map;
PangoLanguage *lang;
cairo_font_options_t *opt;
if (!ff || !out || !height)
return -EINVAL;
log_debug("font: new font (height %u)\n", height);
font = malloc(sizeof(*font));
if (!font)
return -ENOMEM;
font->ref = 1;
font->height = height;
font->st = ff->st;
map = pango_cairo_font_map_get_default();
if (!map) {
ret = -EFAULT;
goto err_free;
}
font->ctx = pango_font_map_create_context(map);
if (!font->ctx) {
ret = -EFAULT;
goto err_free;
}
pango_context_set_base_dir(font->ctx, PANGO_DIRECTION_LTR);
desc = pango_font_description_from_string("monospace");
if (!desc) {
ret = -EFAULT;
goto err_ctx;
}
pango_font_description_set_absolute_size(desc, PANGO_SCALE * height);
pango_context_set_font_description(font->ctx, desc);
pango_font_description_free(desc);
lang = pango_language_get_default();
if (!lang) {
ret = -EFAULT;
goto err_ctx;
}
pango_context_set_language(font->ctx, lang);
if (!pango_cairo_context_get_font_options(font->ctx)) {
opt = cairo_font_options_create();
if (!opt) {
ret = -EFAULT;
goto err_ctx;
}
pango_cairo_context_set_font_options(font->ctx, opt);
cairo_font_options_destroy(opt);
}
font->glyphs = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) kmscon_glyph_unref);
if (!font->glyphs) {
ret = -ENOMEM;
goto err_ctx;
}
ret = measure_width(font);
if (ret)
goto err_hash;
kmscon_symbol_table_ref(font->st);
*out = font;
return 0;
err_hash:
g_hash_table_unref(font->glyphs);
err_ctx:
g_object_unref(font->ctx);
err_free:
free(font);
return ret;
}
void kmscon_font_ref(struct kmscon_font *font)
{
if (!font)
return;
++font->ref;
}
void kmscon_font_unref(struct kmscon_font *font)
{
if (!font || !font->ref)
return;
if (--font->ref)
return;
g_hash_table_unref(font->glyphs);
g_object_unref(font->ctx);
kmscon_symbol_table_unref(font->st);
free(font);
log_debug("font: destroying font\n");
}
unsigned int kmscon_font_get_width(struct kmscon_font *font)
{
if (!font)
return 0;
return font->width;
}
unsigned int kmscon_font_get_height(struct kmscon_font *font)
{
if (!font)
return 0;
return font->height;
}
/*
* Get glyph for given key. If no glyph can be found in the hash-table, then a
* new glyph is created and added to the hash-table.
* Returns 0 on success and stores the glyph with a new reference in \out.
*/
static int kmscon_font_lookup(struct kmscon_font *font,
kmscon_symbol_t key, struct kmscon_glyph **out)
{
struct kmscon_glyph *glyph;
int ret;
if (!font || !out)
return -EINVAL;
glyph = g_hash_table_lookup(font->glyphs, GUINT_TO_POINTER(key));
if (!glyph) {
ret = kmscon_glyph_new(&glyph, key);
if (ret)
return ret;
ret = kmscon_glyph_set(glyph, font);
if (ret)
goto err_glyph;
g_hash_table_insert(font->glyphs, GUINT_TO_POINTER(key), glyph);
}
kmscon_glyph_ref(glyph);
*out = glyph;
return 0;
err_glyph:
kmscon_glyph_unref(glyph);
return ret;
}
/*
* This draws a glyph for characters \ch into the given cairo context \cr.
* The glyph will be drawn with the upper-left corner at x/y.
* Returns 0 on success.
*/
int kmscon_font_draw(struct kmscon_font *font, kmscon_symbol_t ch, void *dcr,
uint32_t x, uint32_t y)
{
struct kmscon_glyph *glyph;
int ret;
cairo_t *cr = dcr;
if (!font || !ch || !cr)
return -EINVAL;
ret = kmscon_font_lookup(font, ch, &glyph);
if (ret)
return ret;
switch (glyph->type) {
case GLYPH_LAYOUT:
cairo_move_to(cr, x, y);
pango_cairo_update_layout(cr, glyph->src.layout.layout);
pango_cairo_show_layout(cr, glyph->src.layout.layout);
break;
case GLYPH_STR:
cairo_move_to(cr, x, y + glyph->src.str.ascent);
pango_cairo_show_glyph_string(cr, glyph->src.str.font,
glyph->src.str.str);
break;
default:
ret = -EFAULT;
break;
}
kmscon_glyph_unref(glyph);
return 0;
}