#include #include #include #include #include #include "eek-clutter-renderer.h" #include "eek-key.h" G_DEFINE_TYPE (EekClutterRenderer, eek_clutter_renderer, EEK_TYPE_RENDERER); #define EEK_CLUTTER_RENDERER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_CLUTTER_RENDERER, EekClutterRendererPrivate)) struct _EekClutterRendererPrivate { GHashTable *outline_texture_cache; }; /* This routine is copied from librsvg: Copyright © 2005 Dom Lachowicz Copyright © 2005 Caleb Moore Copyright © 2005 Red Hat, Inc. */ static void cairo_pixels_to_pixbuf (guint8 *pixels, int rowstride, int height) { int row; /* un-premultiply data */ for (row = 0; row < height; row++) { guint8 *row_data = (pixels + (row * rowstride)); int i; for (i = 0; i < rowstride; i += 4) { guint8 *b = &row_data[i]; guint32 pixel; guint8 alpha; memcpy (&pixel, b, sizeof (guint32)); alpha = (pixel & 0xff000000) >> 24; if (alpha == 0) { b[0] = b[1] = b[2] = b[3] = 0; } else { b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; b[3] = alpha; } } } } static void eek_clutter_renderer_class_init (EekClutterRendererClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); g_type_class_add_private (gobject_class, sizeof (EekClutterRendererPrivate)); } static void eek_clutter_renderer_init (EekClutterRenderer *self) { EekClutterRendererPrivate *priv; priv = self->priv = EEK_CLUTTER_RENDERER_GET_PRIVATE(self); priv->outline_texture_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, cogl_handle_unref); } void eek_clutter_renderer_render_key (EekClutterRenderer *renderer, ClutterActor *actor, EekKey *key) { EekClutterRendererPrivate *priv; EekOutline *outline; CoglHandle *outline_texture; PangoLayout *layout; PangoRectangle extents = { 0, }; CoglColor color = { 0x00, 0x00, 0x00, 0xFF }; ClutterGeometry geom; g_assert (EEK_IS_CLUTTER_RENDERER(renderer)); g_assert (CLUTTER_IS_ACTOR(actor)); g_assert (EEK_IS_KEY(key)); priv = EEK_CLUTTER_RENDERER_GET_PRIVATE(renderer); outline = eek_key_get_outline (key); outline_texture = g_hash_table_lookup (priv->outline_texture_cache, outline); if (!outline_texture) { gint rowstride; guint8 *data; cairo_surface_t *surface; cairo_t *cr; EekBounds bounds; gdouble scale; GdkPixbuf *pixbuf; eek_element_get_bounds (EEK_ELEMENT(key), &bounds); scale = eek_renderer_get_scale (EEK_RENDERER(renderer)); rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, bounds.width * scale); data = g_malloc0 (rowstride * bounds.height); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, bounds.width * scale, bounds.height * scale, rowstride); cr = cairo_create (surface); eek_renderer_render_key_outline (EEK_RENDERER(renderer), cr, key, 1.0, FALSE); cairo_destroy (cr); cairo_surface_destroy (surface); cairo_pixels_to_pixbuf (data, rowstride, bounds.height * scale); pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, 8, bounds.width * scale, bounds.height * scale, rowstride, (GdkPixbufDestroyNotify) g_free, data); outline_texture = cogl_texture_new_from_data (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), COGL_TEXTURE_NONE, gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, gdk_pixbuf_get_rowstride (pixbuf), gdk_pixbuf_get_pixels (pixbuf)); g_object_unref (pixbuf); g_hash_table_insert (priv->outline_texture_cache, outline, outline_texture); } clutter_actor_get_allocation_geometry (actor, &geom); cogl_set_source_texture (outline_texture); cogl_rectangle (0.0f, 0.0f, geom.width, geom.height); layout = eek_renderer_create_pango_layout (EEK_RENDERER(renderer)); eek_renderer_render_key_label (EEK_RENDERER(renderer), layout, key); pango_layout_get_extents (layout, NULL, &extents); cogl_pango_render_layout (layout, (geom.width - extents.width / PANGO_SCALE) / 2, (geom.height - extents.height / PANGO_SCALE) / 2, &color, 0); g_object_unref (layout); } EekClutterRenderer * eek_clutter_renderer_new (EekKeyboard *keyboard, PangoContext *pcontext) { EekClutterRenderer *renderer; renderer = g_object_new (EEK_TYPE_CLUTTER_RENDERER, "keyboard", keyboard, "pango-context", pcontext); return renderer; }