From: David Herrmann Date: Sat, 26 Nov 2011 15:33:06 +0000 (+0100) Subject: Add kmscon_font type X-Git-Tag: kmscon-7~1363 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=27807e913e8d1a87c5663c0eebad46a783a8faa5;p=platform%2Fupstream%2Fkmscon.git Add kmscon_font type A kmscon_font object is used to store the current font information. It allows to draw any kind of UTF-8 string to the screen. Internally, it uses kmscon_glyph to store glyph information for every character that has been drawn so redrawing it is much faster. Currently, we only support GLYPH_LAYOUT as caching method which is quite slow. However, it supports any kind of input and always works. Better and faster caching algorithms like cairo_scaled_font_t will be added later. Signed-off-by: David Herrmann --- diff --git a/src/console.h b/src/console.h index b6c1131..aa653ba 100644 --- a/src/console.h +++ b/src/console.h @@ -14,10 +14,12 @@ * of it. */ +#include #include #include struct kmscon_char; +struct kmscon_font; struct kmscon_console; /* single printable characters */ @@ -31,6 +33,15 @@ const char *kmscon_char_get_u8(const struct kmscon_char *ch); size_t kmscon_char_get_len(const struct kmscon_char *ch); int kmscon_char_append_u8(struct kmscon_char *ch, const char *str, size_t len); +/* font objects with cached glyphs */ + +int kmscon_font_new(struct kmscon_font **out); +void kmscon_font_ref(struct kmscon_font *font); +void kmscon_font_unref(struct kmscon_font *font); + +int kmscon_font_draw(struct kmscon_font *font, const struct kmscon_char *ch, + cairo_t *cr, uint32_t x, uint32_t y); + /* console objects */ int kmscon_console_new(struct kmscon_console **out); diff --git a/src/console_char.c b/src/console_char.c index c47e32c..a70cc62 100644 --- a/src/console_char.c +++ b/src/console_char.c @@ -24,6 +24,10 @@ #include #include +#include +#include +#include +#include #include "console.h" /* maximum size of a single character */ @@ -35,6 +39,31 @@ struct kmscon_char { size_t len; }; +enum glyph_type { + GLYPH_NONE, + GLYPH_LAYOUT, +}; + +struct kmscon_glyph { + size_t ref; + struct kmscon_char *ch; + + int type; + + union { + struct layout { + PangoLayout *layout; + } layout; + } src; +}; + +struct kmscon_font { + size_t ref; + + GHashTable *glyphs; + PangoContext *ctx; +}; + int kmscon_char_new(struct kmscon_char **out) { struct kmscon_char *ch; @@ -157,3 +186,325 @@ int kmscon_char_append_u8(struct kmscon_char *ch, const char *str, size_t len) return 0; } + +/* + * Create a hash for a kmscon_char. This uses a simple hash technique described + * by Daniel J. Bernstein. + */ +static guint kmscon_char_hash(gconstpointer key) +{ + guint val = 5381; + size_t i; + const struct kmscon_char *ch = (void*)key; + + for (i = 0; i < ch->len; ++i) + val = val * 33 + ch->buf[i]; + + return val; +} + +/* compare two kmscon_char for equality */ +static gboolean kmscon_char_equal(gconstpointer a, gconstpointer b) +{ + const struct kmscon_char *ch1 = (void*)a; + const struct kmscon_char *ch2 = (void*)b; + + if (ch1->len != ch2->len) + return FALSE; + + return (memcpy(ch1->buf, ch2->buf, ch1->len) == 0); +} + +/* + * 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, + const struct kmscon_char *ch) +{ + struct kmscon_glyph *glyph; + int ret; + + if (!out || !ch || !ch->len) + return -EINVAL; + + glyph = malloc(sizeof(*glyph)); + if (!glyph) + return -ENOMEM; + glyph->ref = 1; + glyph->type = GLYPH_NONE; + + ret = kmscon_char_dup(&glyph->ch, ch); + if (ret) + goto err_free; + + *out = glyph; + return 0; + +err_free: + free(glyph); + return ret; +} + +/* + * 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; + } + + glyph->type = GLYPH_NONE; +} + +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); + kmscon_char_free(glyph->ch); + 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; + + if (!glyph || !font) + return -EINVAL; + + layout = pango_layout_new(font->ctx); + if (!layout) + return -EINVAL; + + pango_layout_set_text(layout, glyph->ch->buf, glyph->ch->len); + + kmscon_glyph_reset(glyph); + glyph->type = GLYPH_LAYOUT; + glyph->src.layout.layout = layout; + + return 0; +} + +/* + * Creates a new font + * Returns 0 on success and stores the new font in \out. + */ +int kmscon_font_new(struct kmscon_font **out) +{ + struct kmscon_font *font; + int ret; + PangoFontDescription *desc; + PangoFontMap *map; + PangoLanguage *lang; + cairo_font_options_t *opt; + + if (!out) + return -EINVAL; + + font = malloc(sizeof(*font)); + if (!font) + return -ENOMEM; + font->ref = 1; + + 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); + pango_cairo_context_set_resolution(font->ctx, 72); + + desc = pango_font_description_from_string("monospace 18"); + if (!desc) { + ret = -EFAULT; + goto err_ctx; + } + + 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(kmscon_char_hash, + kmscon_char_equal, (GDestroyNotify) kmscon_char_free, + (GDestroyNotify) kmscon_glyph_unref); + if (!font->glyphs) { + ret = -ENOMEM; + goto err_ctx; + } + + *out = font; + return 0; + +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); + free(font); +} + +/* + * 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, + const struct kmscon_char *key, struct kmscon_glyph **out) +{ + struct kmscon_glyph *glyph; + struct kmscon_char *ch; + int ret; + + if (!font || !key || !out) + return -EINVAL; + + glyph = g_hash_table_lookup(font->glyphs, key); + if (!glyph) { + ret = kmscon_char_dup(&ch, key); + if (ret) + return ret; + + ret = kmscon_glyph_new(&glyph, key); + if (ret) + goto err_char; + + ret = kmscon_glyph_set(glyph, font); + if (ret) + goto err_glyph; + + g_hash_table_insert(font->glyphs, ch, glyph); + } + + + kmscon_glyph_ref(glyph); + *out = glyph; + return 0; + +err_glyph: + kmscon_glyph_unref(glyph); +err_char: + kmscon_char_free(ch); + 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, const struct kmscon_char *ch, + cairo_t *cr, uint32_t x, uint32_t y) +{ + struct kmscon_glyph *glyph; + int ret; + + if (!font || !ch || !cr) + return -EINVAL; + + ret = kmscon_font_lookup(font, ch, &glyph); + if (ret) + return ret; + + cairo_move_to(cr, x, y); + + switch (glyph->type) { + case GLYPH_LAYOUT: + pango_cairo_update_layout(cr, glyph->src.layout.layout); + pango_cairo_show_layout(cr, glyph->src.layout.layout); + break; + default: + ret = -EFAULT; + break; + } + + kmscon_glyph_unref(glyph); + + return 0; +}