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 <dh.herrmann@googlemail.com>
This commit is contained in:
parent
a33f9f589c
commit
27807e913e
@ -14,10 +14,12 @@
|
||||
* of it.
|
||||
*/
|
||||
|
||||
#include <cairo.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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);
|
||||
|
@ -24,6 +24,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <glib.h>
|
||||
#include <pango/pango.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user