summaryrefslogtreecommitdiff
path: root/pango
diff options
context:
space:
mode:
authorRobert Bragg <robert@linux.intel.com>2009-07-29 17:21:07 +0100
committerRobert Bragg <robert@linux.intel.com>2009-10-16 18:58:51 +0100
commit17e899a49d7d657f57103804bc2c59f526528a34 (patch)
treefeac2ae32bbcff057e4fe22a2576ab56a2df404e /pango
parent0bce7eac5375936d9ac6486f13cef94298f8590e (diff)
downloadcogl-17e899a49d7d657f57103804bc2c59f526528a34.tar.gz
[cogl] move clutter/pango to clutter/cogl/pango
As part of the re-organisation of Cogl; move clutter/pango to be part of the cogl sub-project.
Diffstat (limited to 'pango')
-rw-r--r--pango/Makefile.am33
-rw-r--r--pango/cogl-pango-display-list.c417
-rw-r--r--pango/cogl-pango-display-list.h70
-rw-r--r--pango/cogl-pango-fontmap.c209
-rw-r--r--pango/cogl-pango-glyph-cache.c352
-rw-r--r--pango/cogl-pango-glyph-cache.h78
-rw-r--r--pango/cogl-pango-private.h38
-rw-r--r--pango/cogl-pango-render.c697
-rw-r--r--pango/cogl-pango.h85
9 files changed, 1979 insertions, 0 deletions
diff --git a/pango/Makefile.am b/pango/Makefile.am
new file mode 100644
index 00000000..d098e3b9
--- /dev/null
+++ b/pango/Makefile.am
@@ -0,0 +1,33 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+source_c = \
+ cogl-pango-display-list.c \
+ cogl-pango-fontmap.c \
+ cogl-pango-render.c \
+ cogl-pango-glyph-cache.c
+
+source_h = cogl-pango.h
+
+source_h_priv = \
+ cogl-pango-display-list.h \
+ cogl-pango-private.h \
+ cogl-pango-glyph-cache.h
+
+noinst_LTLIBRARIES = libcoglpango.la
+
+libcoglpango_la_SOURCES = $(source_c) $(source_h) $(source_h_priv)
+libcoglpango_la_CPPFLAGS = $(CLUTTER_CFLAGS) $(COGL_DEBUG_CFLAGS) $(MAINTAINER_CFLAGS)
+libcoglpango_la_LIBADD = $(CLUTTER_LIBS)
+
+INCLUDES = \
+ -DG_DISABLE_SINGLE_INCLUDES \
+ -DCLUTTER_COMPILATION \
+ -DG_LOG_DOMAIN=\"CoglPango\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/clutter \
+ -I$(top_srcdir)/clutter/cogl \
+ -I$(top_builddir)/clutter \
+ -I$(top_builddir)/clutter/cogl
+
+coglpangoheadersdir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl
+coglpangoheaders_HEADERS = $(source_h)
diff --git a/pango/cogl-pango-display-list.c b/pango/cogl-pango-display-list.c
new file mode 100644
index 00000000..46925838
--- /dev/null
+++ b/pango/cogl-pango-display-list.c
@@ -0,0 +1,417 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Neil Roberts <neil@linux.intel.com>
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "cogl-pango-display-list.h"
+
+typedef enum
+{
+ COGL_PANGO_DISPLAY_LIST_TEXTURE,
+ COGL_PANGO_DISPLAY_LIST_RECTANGLE,
+ COGL_PANGO_DISPLAY_LIST_TRAPEZOID
+} CoglPangoDisplayListNodeType;
+
+typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode;
+typedef struct _CoglPangoDisplayListVertex CoglPangoDisplayListVertex;
+
+struct _CoglPangoDisplayList
+{
+ gboolean color_override;
+ CoglColor color;
+ GSList *nodes;
+ GSList *last_node;
+};
+
+struct _CoglPangoDisplayListNode
+{
+ CoglPangoDisplayListNodeType type;
+
+ gboolean color_override;
+ CoglColor color;
+
+ union
+ {
+ struct
+ {
+ /* The texture to render these coords from */
+ CoglHandle texture;
+ /* Array of vertex data to render out of this texture */
+ GArray *verts;
+ /* A VBO representing those vertices */
+ CoglHandle vertex_buffer;
+ } texture;
+
+ struct
+ {
+ float x_1, y_1;
+ float x_2, y_2;
+ } rectangle;
+
+ struct
+ {
+ float y_1;
+ float x_11;
+ float x_21;
+ float y_2;
+ float x_12;
+ float x_22;
+ } trapezoid;
+ } d;
+};
+
+struct _CoglPangoDisplayListVertex
+{
+ float x, y, t_x, t_y;
+};
+
+CoglPangoDisplayList *
+_cogl_pango_display_list_new (void)
+{
+ return g_slice_new0 (CoglPangoDisplayList);
+}
+
+static void
+_cogl_pango_display_list_append_node (CoglPangoDisplayList *dl,
+ CoglPangoDisplayListNode *node)
+{
+ if (dl->last_node)
+ dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node);
+ else
+ dl->last_node = dl->nodes = g_slist_prepend (NULL, node);
+}
+
+void
+_cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl,
+ const CoglColor *color)
+{
+ dl->color_override = TRUE;
+ dl->color = *color;
+}
+
+void
+_cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl)
+{
+ dl->color_override = FALSE;
+}
+
+void
+_cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl,
+ CoglHandle texture,
+ float x_1, float y_1,
+ float x_2, float y_2,
+ float tx_1, float ty_1,
+ float tx_2, float ty_2)
+{
+ CoglPangoDisplayListNode *node;
+ CoglPangoDisplayListVertex *verts;
+
+ /* Add to the last node if it is a texture node with the same
+ target texture */
+ if (dl->last_node
+ && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE
+ && node->d.texture.texture == texture
+ && (dl->color_override
+ ? (node->color_override && cogl_color_equal (&dl->color, &node->color))
+ : !node->color_override))
+ {
+ /* Get rid of the vertex buffer so that it will be recreated */
+ if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE)
+ {
+ cogl_vertex_buffer_unref (node->d.texture.vertex_buffer);
+ node->d.texture.vertex_buffer = COGL_INVALID_HANDLE;
+ }
+ }
+ else
+ {
+ /* Otherwise create a new node */
+ node = g_slice_new (CoglPangoDisplayListNode);
+
+ node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE;
+ node->color_override = dl->color_override;
+ node->color = dl->color;
+ node->d.texture.texture = cogl_texture_ref (texture);
+ node->d.texture.verts
+ = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListVertex));
+ node->d.texture.vertex_buffer = COGL_INVALID_HANDLE;
+
+ _cogl_pango_display_list_append_node (dl, node);
+ }
+
+ g_array_set_size (node->d.texture.verts,
+ node->d.texture.verts->len + 4);
+ verts = &g_array_index (node->d.texture.verts,
+ CoglPangoDisplayListVertex,
+ node->d.texture.verts->len - 4);
+
+ verts->x = x_1;
+ verts->y = y_1;
+ verts->t_x = tx_1;
+ verts->t_y = ty_1;
+ verts++;
+ verts->x = x_1;
+ verts->y = y_2;
+ verts->t_x = tx_1;
+ verts->t_y = ty_2;
+ verts++;
+ verts->x = x_2;
+ verts->y = y_2;
+ verts->t_x = tx_2;
+ verts->t_y = ty_2;
+ verts++;
+ verts->x = x_2;
+ verts->y = y_1;
+ verts->t_x = tx_2;
+ verts->t_y = ty_1;
+}
+
+void
+_cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
+ float x_1, float y_1,
+ float x_2, float y_2)
+{
+ CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
+
+ node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE;
+ node->color_override = dl->color_override;
+ node->color = dl->color;
+ node->d.rectangle.x_1 = x_1;
+ node->d.rectangle.y_1 = y_1;
+ node->d.rectangle.x_2 = x_2;
+ node->d.rectangle.y_2 = y_2;
+
+ _cogl_pango_display_list_append_node (dl, node);
+}
+
+void
+_cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl,
+ float y_1,
+ float x_11,
+ float x_21,
+ float y_2,
+ float x_12,
+ float x_22)
+{
+ CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
+
+ node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID;
+ node->color_override = dl->color_override;
+ node->color = dl->color;
+ node->d.trapezoid.y_1 = y_1;
+ node->d.trapezoid.x_11 = x_11;
+ node->d.trapezoid.x_21 = x_21;
+ node->d.trapezoid.y_2 = y_2;
+ node->d.trapezoid.x_12 = x_12;
+ node->d.trapezoid.x_22 = x_22;
+
+ _cogl_pango_display_list_append_node (dl, node);
+}
+
+static void
+_cogl_pango_display_list_render_texture (CoglHandle material,
+ const CoglColor *color,
+ CoglPangoDisplayListNode *node)
+{
+ CoglColor premult_color = *color;
+ cogl_material_set_layer (material, 0, node->d.texture.texture);
+ cogl_material_set_color (material, &premult_color);
+ cogl_set_source (material);
+
+ /* For small runs of text like icon labels, we can get better performance
+ * going through the Cogl journal since text may then be batched together
+ * with other geometry. */
+ /* FIXME: 100 is a number I plucked out of thin air; it would be good
+ * to determine this empirically! */
+ if (node->d.texture.verts->len < 100)
+ {
+ int i;
+
+ for (i = 0; i < node->d.texture.verts->len; i += 4)
+ {
+ CoglPangoDisplayListVertex *v0 =
+ &g_array_index (node->d.texture.verts,
+ CoglPangoDisplayListVertex, i);
+ CoglPangoDisplayListVertex *v1 =
+ &g_array_index (node->d.texture.verts,
+ CoglPangoDisplayListVertex, i + 2);
+ cogl_rectangle_with_texture_coords (v0->x, v0->y, v1->x, v1->y,
+ v0->t_x, v0->t_y,
+ v1->t_x, v1->t_y);
+ }
+
+ return;
+ }
+
+ /* It's expensive to go through the Cogl journal for large runs
+ * of text in part because the journal transforms the quads in software
+ * to avoid changing the modelview matrix. So for larger runs of text
+ * we load the vertices into a VBO, and this has the added advantage
+ * that if the text doesn't change from frame to frame the VBO can
+ * be re-used avoiding the repeated cost of validating the data and
+ * mapping it into the GPU... */
+
+ if (node->d.texture.vertex_buffer == COGL_INVALID_HANDLE)
+ {
+ CoglHandle vb = cogl_vertex_buffer_new (node->d.texture.verts->len);
+
+ cogl_vertex_buffer_add (vb, "gl_Vertex", 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (CoglPangoDisplayListVertex),
+ &g_array_index (node->d.texture.verts,
+ CoglPangoDisplayListVertex, 0).x);
+ cogl_vertex_buffer_add (vb, "gl_MultiTexCoord0", 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (CoglPangoDisplayListVertex),
+ &g_array_index (node->d.texture.verts,
+ CoglPangoDisplayListVertex,
+ 0).t_x);
+ cogl_vertex_buffer_submit (vb);
+
+ node->d.texture.vertex_buffer = vb;
+ }
+
+
+#ifdef CLUTTER_COGL_HAS_GL
+
+ cogl_vertex_buffer_draw (node->d.texture.vertex_buffer,
+ GL_QUADS,
+ 0, node->d.texture.verts->len);
+
+#else /* CLUTTER_COGL_HAS_GL */
+ {
+ /* GLES doesn't support GL_QUADS so instead we use a VBO with
+ indexed vertices to generate GL_TRIANGLES from the quads */
+
+ int n_indices = node->d.texture.verts->len / 4 * 6;
+ CoglHandle indices_vbo
+ = cogl_vertex_buffer_indices_get_for_quads (n_indices);
+
+ cogl_vertex_buffer_draw_elements (node->d.texture.vertex_buffer,
+ COGL_VERTICES_MODE_TRIANGLES,
+ indices_vbo,
+ 0, node->d.texture.verts->len - 1,
+ 0, n_indices);
+ }
+#endif /* CLUTTER_COGL_HAS_GL */
+}
+
+void
+_cogl_pango_display_list_render (CoglPangoDisplayList *dl,
+ const CoglColor *color,
+ CoglHandle glyph_material,
+ CoglHandle solid_material)
+{
+ GSList *l;
+
+ for (l = dl->nodes; l; l = l->next)
+ {
+ CoglPangoDisplayListNode *node = l->data;
+ CoglColor draw_color;
+
+ if (node->color_override)
+ /* Use the override color but preserve the alpha from the
+ draw color */
+ cogl_color_set_from_4ub (&draw_color,
+ cogl_color_get_red_byte (&node->color),
+ cogl_color_get_green_byte (&node->color),
+ cogl_color_get_blue_byte (&node->color),
+ cogl_color_get_alpha_byte (color));
+ else
+ draw_color = *color;
+ cogl_color_premultiply (&draw_color);
+
+ switch (node->type)
+ {
+ case COGL_PANGO_DISPLAY_LIST_TEXTURE:
+ _cogl_pango_display_list_render_texture (glyph_material,
+ &draw_color, node);
+ break;
+
+ case COGL_PANGO_DISPLAY_LIST_RECTANGLE:
+ cogl_material_set_color (solid_material, &draw_color);
+ cogl_set_source (solid_material);
+ cogl_rectangle (node->d.rectangle.x_1,
+ node->d.rectangle.y_1,
+ node->d.rectangle.x_2,
+ node->d.rectangle.y_2);
+ break;
+
+ case COGL_PANGO_DISPLAY_LIST_TRAPEZOID:
+ {
+ float points[8];
+
+ points[0] = node->d.trapezoid.x_11;
+ points[1] = node->d.trapezoid.y_1;
+ points[2] = node->d.trapezoid.x_12;
+ points[3] = node->d.trapezoid.y_2;
+ points[4] = node->d.trapezoid.x_22;
+ points[5] = node->d.trapezoid.y_2;
+ points[6] = node->d.trapezoid.x_21;
+ points[7] = node->d.trapezoid.y_1;
+
+ cogl_material_set_color (solid_material, &draw_color);
+ cogl_set_source (solid_material);
+ cogl_path_polygon (points, 4);
+ cogl_path_fill ();
+ }
+ break;
+ }
+ }
+}
+
+static void
+_cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node)
+{
+ if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
+ {
+ g_array_free (node->d.texture.verts, TRUE);
+ if (node->d.texture.texture != COGL_INVALID_HANDLE)
+ cogl_texture_unref (node->d.texture.texture);
+ if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE)
+ cogl_vertex_buffer_unref (node->d.texture.vertex_buffer);
+ }
+
+ g_slice_free (CoglPangoDisplayListNode, node);
+}
+
+void
+_cogl_pango_display_list_clear (CoglPangoDisplayList *dl)
+{
+ g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL);
+ g_slist_free (dl->nodes);
+ dl->nodes = NULL;
+ dl->last_node = NULL;
+}
+
+void
+_cogl_pango_display_list_free (CoglPangoDisplayList *dl)
+{
+ _cogl_pango_display_list_clear (dl);
+ g_slice_free (CoglPangoDisplayList, dl);
+}
diff --git a/pango/cogl-pango-display-list.h b/pango/cogl-pango-display-list.h
new file mode 100644
index 00000000..6971b7e4
--- /dev/null
+++ b/pango/cogl-pango-display-list.h
@@ -0,0 +1,70 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Neil Roberts <neil@linux.intel.com>
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COGL_PANGO_DISPLAY_LIST_H__
+#define __COGL_PANGO_DISPLAY_LIST_H__
+
+#include <glib.h>
+#include <cogl/cogl.h>
+
+G_BEGIN_DECLS
+
+typedef struct _CoglPangoDisplayList CoglPangoDisplayList;
+
+CoglPangoDisplayList *_cogl_pango_display_list_new (void);
+
+void _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl,
+ const CoglColor *color);
+void _cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl);
+
+void _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl,
+ CoglHandle texture,
+ float x_1, float y_1,
+ float x_2, float y_2,
+ float tx_1, float ty_1,
+ float tx_2, float ty_2);
+
+void _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
+ float x_1, float y_1,
+ float x_2, float y_2);
+
+void _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl,
+ float y_1,
+ float x_11,
+ float x_21,
+ float y_2,
+ float x_12,
+ float x_22);
+
+void _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
+ const CoglColor *color,
+ CoglHandle glyph_material,
+ CoglHandle solid_material);
+
+void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl);
+
+void _cogl_pango_display_list_free (CoglPangoDisplayList *dl);
+
+G_END_DECLS
+
+#endif /* __COGL_PANGO_DISPLAY_LIST_H__ */
diff --git a/pango/cogl-pango-fontmap.c b/pango/cogl-pango-fontmap.c
new file mode 100644
index 00000000..1bee8f0c
--- /dev/null
+++ b/pango/cogl-pango-fontmap.c
@@ -0,0 +1,209 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:cogl-pango
+ * @short_description: COGL-based text rendering using Pango
+ *
+ * FIXME
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* This is needed to get the Pango headers to export stuff needed to
+ subclass */
+#ifndef PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_BACKEND 1
+#endif
+
+#include <pango/pango-fontmap.h>
+#include <pango/pangocairo.h>
+#include <pango/pango-renderer.h>
+
+#include "cogl-pango.h"
+#include "cogl-pango-private.h"
+
+static GQuark cogl_pango_font_map_get_renderer_key (void) G_GNUC_CONST;
+
+/**
+ * cogl_pango_font_map_new:
+ *
+ * Creates a new font map.
+ *
+ * Return value: the newly created #PangoFontMap
+ *
+ * Since: 1.0
+ */
+PangoFontMap *
+cogl_pango_font_map_new (void)
+{
+ return pango_cairo_font_map_new ();
+}
+
+/**
+ * cogl_pango_font_map_create_context:
+ * @fm: a #CoglPangoFontMap
+ *
+ * Creates a new #PangoContext from the passed font map.
+ *
+ * Return value: the newly created #PangoContext
+ *
+ * Since: 1.0
+ */
+PangoContext *
+cogl_pango_font_map_create_context (CoglPangoFontMap *fm)
+{
+ g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (fm), NULL);
+
+ /* We can just directly use the pango context from the Cairo font
+ map */
+ return pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fm));
+}
+
+/**
+ * cogl_pango_font_map_get_renderer:
+ * @fm: a #CoglPangoFontMap
+ *
+ * Retrieves the #CoglPangoRenderer for the passed font map.
+ *
+ * Return value: a #PangoRenderer
+ *
+ * Since: 1.0
+ */
+PangoRenderer *
+cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm)
+{
+ PangoRenderer *renderer;
+
+ g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (fm), NULL);
+
+ /* We want to keep a cached pointer to the renderer from the font
+ map instance but as we don't have a proper subclass we have to
+ store it in the object data instead */
+
+ renderer = g_object_get_qdata (G_OBJECT (fm),
+ cogl_pango_font_map_get_renderer_key ());
+
+ if (G_UNLIKELY (renderer == NULL))
+ {
+ renderer = g_object_new (COGL_PANGO_TYPE_RENDERER, NULL);
+ g_object_set_qdata_full (G_OBJECT (fm),
+ cogl_pango_font_map_get_renderer_key (),
+ renderer,
+ g_object_unref);
+ }
+
+ return renderer;
+}
+
+/**
+ * cogl_pango_font_map_set_resolution:
+ * @font_map: a #CoglPangoFontMap
+ * @dpi: DPI to set
+ *
+ * Sets the resolution to be used by @font_map at @dpi.
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
+ double dpi)
+{
+ g_return_if_fail (COGL_PANGO_IS_FONT_MAP (font_map));
+
+ pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (font_map), dpi);
+}
+
+/**
+ * cogl_pango_font_map_clear_glyph_cache:
+ * @fm: a #CoglPangoFontMap
+ *
+ * Clears the glyph cache for @fm.
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *fm)
+{
+ PangoRenderer *renderer;
+
+ renderer = cogl_pango_font_map_get_renderer (fm);
+
+ _cogl_pango_renderer_clear_glyph_cache (COGL_PANGO_RENDERER (renderer));
+}
+
+/**
+ * cogl_pango_font_map_set_use_mipmapping:
+ * @fm: a #CoglPangoFontMap
+ * @value: %TRUE to enable the use of mipmapping
+ *
+ * Sets whether the renderer for the passed font map should use
+ * mipmapping when rendering a #PangoLayout.
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *fm,
+ gboolean value)
+{
+ CoglPangoRenderer *renderer;
+
+ renderer = COGL_PANGO_RENDERER (cogl_pango_font_map_get_renderer (fm));
+
+ _cogl_pango_renderer_set_use_mipmapping (renderer, value);
+}
+
+/**
+ * cogl_pango_font_map_get_use_mipmapping:
+ * @fm: a #CoglPangoFontMap
+ *
+ * Retrieves whether the #CoglPangoRenderer used by @fm will
+ * use mipmapping when rendering the glyphs.
+ *
+ * Return value: %TRUE if mipmapping is used, %FALSE otherwise.
+ *
+ * Since: 1.0
+ */
+gboolean
+cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *fm)
+{
+ CoglPangoRenderer *renderer;
+
+ renderer = COGL_PANGO_RENDERER (cogl_pango_font_map_get_renderer (fm));
+
+ return _cogl_pango_renderer_get_use_mipmapping (renderer);
+}
+
+static GQuark
+cogl_pango_font_map_get_renderer_key (void)
+{
+ static GQuark renderer_key = 0;
+
+ if (G_UNLIKELY (renderer_key == 0))
+ renderer_key = g_quark_from_static_string ("CoglPangoFontMap");
+
+ return renderer_key;
+}
diff --git a/pango/cogl-pango-glyph-cache.c b/pango/cogl-pango-glyph-cache.c
new file mode 100644
index 00000000..df26f672
--- /dev/null
+++ b/pango/cogl-pango-glyph-cache.c
@@ -0,0 +1,352 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#include "cogl-pango-glyph-cache.h"
+#include "cogl-pango-private.h"
+
+/* Minimum width/height for each texture */
+#define MIN_TEXTURE_SIZE 256
+/* All glyph with heights within this margin from each other can be
+ put in the same band */
+#define BAND_HEIGHT_ROUND 4
+
+typedef struct _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey;
+typedef struct _CoglPangoGlyphCacheTexture CoglPangoGlyphCacheTexture;
+typedef struct _CoglPangoGlyphCacheBand CoglPangoGlyphCacheBand;
+
+struct _CoglPangoGlyphCache
+{
+ /* Hash table to quickly check whether a particular glyph in a
+ particular font is already cached */
+ GHashTable *hash_table;
+
+ /* List of textures */
+ CoglPangoGlyphCacheTexture *textures;
+
+ /* List of horizontal bands of glyphs */
+ CoglPangoGlyphCacheBand *bands;
+};
+
+struct _CoglPangoGlyphCacheKey
+{
+ PangoFont *font;
+ PangoGlyph glyph;
+};
+
+/* Represents one texture that will be used to store glyphs. The
+ texture is divided into horizontal bands which all contain glyphs
+ of approximatly the same height */
+struct _CoglPangoGlyphCacheTexture
+{
+ /* The width and height of the texture which should always be a
+ power of two. This can vary so that glyphs larger than
+ MIN_TEXTURE_SIZE can use a bigger texture */
+ int texture_size;
+
+ /* The remaining vertical space not taken up by any bands */
+ int space_remaining;
+
+ /* The actual texture */
+ CoglHandle texture;
+
+ CoglPangoGlyphCacheTexture *next;
+};
+
+/* Represents one horizontal band of a texture. Each band contains
+ glyphs of a similar height */
+struct _CoglPangoGlyphCacheBand
+{
+ /* The y position of the top of the band */
+ int top;
+
+ /* The height of the band */
+ int height;
+
+ /* The remaining horizontal space not taken up by any glyphs */
+ int space_remaining;
+
+ /* The size of the texture. Needed to calculate texture
+ coordinates */
+ int texture_size;
+
+ /* The texture containing this band */
+ CoglHandle texture;
+
+ CoglPangoGlyphCacheBand *next;
+};
+
+static void
+cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value)
+{
+ cogl_handle_unref (value->texture);
+ g_slice_free (CoglPangoGlyphCacheValue, value);
+}
+
+static void
+cogl_pango_glyph_cache_key_free (CoglPangoGlyphCacheKey *key)
+{
+ g_object_unref (key->font);
+ g_slice_free (CoglPangoGlyphCacheKey, key);
+}
+
+static guint
+cogl_pango_glyph_cache_hash_func (gconstpointer key)
+{
+ const CoglPangoGlyphCacheKey *cache_key
+ = (const CoglPangoGlyphCacheKey *) key;
+
+ /* Generate a number affected by both the font and the glyph
+ number. We can safely directly compare the pointers because the
+ key holds a reference to the font so it is not possible that a
+ different font will have the same memory address */
+ return GPOINTER_TO_UINT (cache_key->font) ^ cache_key->glyph;
+}
+
+static gboolean
+cogl_pango_glyph_cache_equal_func (gconstpointer a,
+ gconstpointer b)
+{
+ const CoglPangoGlyphCacheKey *key_a
+ = (const CoglPangoGlyphCacheKey *) a;
+ const CoglPangoGlyphCacheKey *key_b
+ = (const CoglPangoGlyphCacheKey *) b;
+
+ /* We can safely directly compare the pointers for the fonts because
+ the key holds a reference to the font so it is not possible that
+ a different font will have the same memory address */
+ return key_a->font == key_b->font
+ && key_a->glyph == key_b->glyph;
+}
+
+static void
+cogl_pango_glyph_cache_free_textures (CoglPangoGlyphCacheTexture *node)
+{
+ CoglPangoGlyphCacheTexture *next;
+
+ while (node)
+ {
+ next = node->next;
+ cogl_handle_unref (node->texture);
+ g_slice_free (CoglPangoGlyphCacheTexture, node);
+ node = next;
+ }
+}
+
+static void
+cogl_pango_glyph_cache_free_bands (CoglPangoGlyphCacheBand *node)
+{
+ CoglPangoGlyphCacheBand *next;
+
+ while (node)
+ {
+ next = node->next;
+ cogl_handle_unref (node->texture);
+ g_slice_free (CoglPangoGlyphCacheBand, node);
+ node = next;
+ }
+}
+
+CoglPangoGlyphCache *
+cogl_pango_glyph_cache_new (void)
+{
+ CoglPangoGlyphCache *cache;
+
+ cache = g_malloc (sizeof (CoglPangoGlyphCache));
+
+ cache->hash_table = g_hash_table_new_full
+ (cogl_pango_glyph_cache_hash_func,
+ cogl_pango_glyph_cache_equal_func,
+ (GDestroyNotify) cogl_pango_glyph_cache_key_free,
+ (GDestroyNotify) cogl_pango_glyph_cache_value_free);
+
+ cache->textures = NULL;
+ cache->bands = NULL;
+
+ return cache;
+}
+
+void
+cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
+{
+ cogl_pango_glyph_cache_free_textures (cache->textures);
+ cache->textures = NULL;
+ cogl_pango_glyph_cache_free_bands (cache->bands);
+ cache->bands = NULL;
+
+ g_hash_table_remove_all (cache->hash_table);
+}
+
+void
+cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
+{
+ cogl_pango_glyph_cache_clear (cache);
+
+ g_hash_table_unref (cache->hash_table);
+
+ g_free (cache);
+}
+
+CoglPangoGlyphCacheValue *
+cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
+ PangoFont *font,
+ PangoGlyph glyph)
+{
+ CoglPangoGlyphCacheKey key;
+
+ key.font = font;
+ key.glyph = glyph;
+
+ return (CoglPangoGlyphCacheValue *)
+ g_hash_table_lookup (cache->hash_table, &key);
+}
+
+CoglPangoGlyphCacheValue *
+cogl_pango_glyph_cache_set (CoglPangoGlyphCache *cache,
+ PangoFont *font,
+ PangoGlyph glyph,
+ gconstpointer pixels,
+ int width,
+ int height,
+ int stride,
+ int draw_x,
+ int draw_y)
+{
+ int band_height;
+ CoglPangoGlyphCacheBand *band;
+ CoglPangoGlyphCacheKey *key;
+ CoglPangoGlyphCacheValue *value;
+
+ /* Reserve an extra pixel gap around the glyph so that it can pull
+ in blank pixels when linear filtering is enabled */
+ width++;
+ height++;
+
+ /* Round the height up to the nearest multiple of
+ BAND_HEIGHT_ROUND */
+ band_height = (height + BAND_HEIGHT_ROUND - 1) & ~(BAND_HEIGHT_ROUND - 1);
+
+ /* Look for a band with the same height and enough width available */
+ for (band = cache->bands;
+ band && (band->height != band_height || band->space_remaining < width);
+ band = band->next);
+ if (band == NULL)
+ {
+ CoglPangoGlyphCacheTexture *texture;
+
+ /* Look for a texture with enough vertical space left for a band
+ with this height */
+ for (texture = cache->textures;
+ texture && (texture->space_remaining < band_height
+ || texture->texture_size < width);
+ texture = texture->next);
+ if (texture == NULL)
+ {
+ guchar *clear_data;
+
+ /* Allocate a new texture that is the nearest power of two
+ greater than the band height or the minimum size,
+ whichever is lower */
+ texture = g_slice_new (CoglPangoGlyphCacheTexture);
+
+ texture->texture_size = MIN_TEXTURE_SIZE;
+ while (texture->texture_size < band_height ||
+ texture->texture_size < width)
+ {
+ texture->texture_size *= 2;
+ }
+
+ /* Allocate an empty buffer to clear the texture */
+ clear_data =
+ g_malloc0 (texture->texture_size * texture->texture_size);
+
+ texture->texture =
+ cogl_texture_new_from_data (texture->texture_size,
+ texture->texture_size,
+ COGL_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_A_8,
+ COGL_PIXEL_FORMAT_A_8,
+ texture->texture_size,
+ clear_data);
+
+ g_free (clear_data);
+
+ texture->space_remaining = texture->texture_size;
+ texture->next = cache->textures;
+ cache->textures = texture;
+ }
+
+ band = g_slice_new (CoglPangoGlyphCacheBand);
+ band->top = texture->texture_size - texture->space_remaining;
+ band->height = band_height;
+ band->space_remaining = texture->texture_size;
+ band->texture = cogl_handle_ref (texture->texture);
+ band->texture_size = texture->texture_size;
+ band->next = cache->bands;
+ cache->bands = band;
+ texture->space_remaining -= band_height;
+ }
+
+ band->space_remaining -= width;
+
+ width--;
+ height--;
+
+ cogl_texture_set_region (band->texture,
+ 0, 0,
+ band->space_remaining,
+ band->top,
+ width, height,
+ width, height,
+ COGL_PIXEL_FORMAT_A_8,
+ stride,
+ pixels);
+
+ key = g_slice_new (CoglPangoGlyphCacheKey);
+ key->font = g_object_ref (font);
+ key->glyph = glyph;
+
+ value = g_slice_new (CoglPangoGlyphCacheValue);
+ value->texture = cogl_handle_ref (band->texture);
+ value->tx1 = (float)(band->space_remaining)
+ / band->texture_size;
+ value->tx2 = (float)(band->space_remaining + width)
+ / band->texture_size;
+ value->ty1 = (float)(band->top)
+ / band->texture_size;
+ value->ty2 = (float)(band->top + height)
+ / band->texture_size;
+ value->draw_x = draw_x;
+ value->draw_y = draw_y;
+ value->draw_width = width;
+ value->draw_height = height;
+
+ g_hash_table_insert (cache->hash_table, key, value);
+
+ return value;
+}
diff --git a/pango/cogl-pango-glyph-cache.h b/pango/cogl-pango-glyph-cache.h
new file mode 100644
index 00000000..ab5265af
--- /dev/null
+++ b/pango/cogl-pango-glyph-cache.h
@@ -0,0 +1,78 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COGL_PANGO_GLYPH_CACHE_H__
+#define __COGL_PANGO_GLYPH_CACHE_H__
+
+#include <glib.h>
+#include <cogl/cogl.h>
+#include <pango/pango-font.h>
+
+G_BEGIN_DECLS
+
+typedef struct _CoglPangoGlyphCache CoglPangoGlyphCache;
+typedef struct _CoglPangoGlyphCacheValue CoglPangoGlyphCacheValue;
+
+struct _CoglPangoGlyphCacheValue
+{
+ CoglHandle texture;
+
+ float tx1;
+ float ty1;
+ float tx2;
+ float ty2;
+
+ int draw_x;
+ int draw_y;
+ int draw_width;
+ int draw_height;
+};
+
+CoglPangoGlyphCache *
+cogl_pango_glyph_cache_new (void);
+
+void
+cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache);
+
+CoglPangoGlyphCacheValue *
+cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
+ PangoFont *font,
+ PangoGlyph glyph);
+
+CoglPangoGlyphCacheValue *
+cogl_pango_glyph_cache_set (CoglPangoGlyphCache *cache,
+ PangoFont *font,
+ PangoGlyph glyph,
+ gconstpointer pixels,
+ int width,
+ int height,
+ int stride,
+ int draw_x,
+ int draw_y);
+
+void
+cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache);
+
+G_END_DECLS
+
+#endif /* __COGL_PANGO_GLYPH_CACHE_H__ */
diff --git a/pango/cogl-pango-private.h b/pango/cogl-pango-private.h
new file mode 100644
index 00000000..447e853f
--- /dev/null
+++ b/pango/cogl-pango-private.h
@@ -0,0 +1,38 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COGL_PANGO_PRIVATE_H__
+#define __COGL_PANGO_PRIVATE_H__
+
+#include "cogl-pango.h"
+
+G_BEGIN_DECLS
+
+void _cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer);
+void _cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer,
+ gboolean value);
+gboolean _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer);
+
+G_END_DECLS
+
+#endif /* __COGL_PANGO_PRIVATE_H__ */
diff --git a/pango/cogl-pango-render.c b/pango/cogl-pango-render.c
new file mode 100644
index 00000000..052233f8
--- /dev/null
+++ b/pango/cogl-pango-render.c
@@ -0,0 +1,697 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_BACKEND 1
+#endif
+
+#include <pango/pango-fontmap.h>
+#include <pango/pangocairo.h>
+#include <pango/pango-renderer.h>
+#include <cairo.h>
+
+#include "cogl-pango-private.h"
+#include "cogl-pango-glyph-cache.h"
+#include "cogl-pango-display-list.h"
+
+struct _CoglPangoRenderer
+{
+ PangoRenderer parent_instance;
+
+ /* The material used to texture from the glyph cache with */
+ CoglHandle glyph_material;
+ /* The material used for solid fills. (boxes, rectangles + trapezoids) */
+ CoglHandle solid_material;
+
+ /* Caches of glyphs as textures */
+ CoglPangoGlyphCache *glyph_cache;
+
+ /* The current display list that is being built */
+ CoglPangoDisplayList *display_list;
+};
+
+struct _CoglPangoRendererClass
+{
+ PangoRendererClass class_instance;
+};
+
+typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata;
+
+/* An instance of this struct gets attached to each PangoLayout to
+ cache the VBO and to detect changes to the layout */
+struct _CoglPangoRendererQdata
+{
+ /* The cache of the geometry for the layout */
+ CoglPangoDisplayList *display_list;
+ /* A reference to the first line of the layout. This is just used to
+ detect changes */
+ PangoLayoutLine *first_line;
+};
+
+static void
+cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
+ CoglPangoGlyphCacheValue *cache_value,
+ float x1,
+ float y1)
+{
+ float x2, y2;
+
+ g_return_if_fail (priv->display_list != NULL);
+
+ x2 = x1 + (float) cache_value->draw_width;
+ y2 = y1 + (float) cache_value->draw_height;
+
+ _cogl_pango_display_list_add_texture (priv->display_list,
+ cache_value->texture,
+ x1, y1, x2, y2,
+ cache_value->tx1,
+ cache_value->ty1,
+ cache_value->tx2,
+ cache_value->ty2);
+}
+
+static void cogl_pango_renderer_finalize (GObject *object);
+static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ int x,
+ int y);
+static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
+ PangoRenderPart part,
+ int x,
+ int y,
+ int width,
+ int height);
+static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
+ PangoRenderPart part,
+ double y1,
+ double x11,
+ double x21,
+ double y2,
+ double x12,
+ double x22);
+
+G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER);
+
+static void
+cogl_pango_renderer_init (CoglPangoRenderer *priv)
+{
+ priv->glyph_material = cogl_material_new ();
+
+ /* The default combine mode of materials is to modulate (A x B) the texture
+ * RGBA channels with the RGBA channels of the previous layer (which in our
+ * case is just the font color)
+ *
+ * Since the RGB for an alpha texture is defined as 0, this gives us:
+ *
+ * result.rgb = color.rgb * 0
+ * result.a = color.a * texture.a
+ *
+ * What we want is premultiplied rgba values:
+ *
+ * result.rgba = color.rgb * texture.a
+ * result.a = color.a * texture.a
+ */
+ cogl_material_set_layer_combine (priv->glyph_material, 0, /* layer */
+ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+ NULL);
+
+ priv->solid_material = cogl_material_new ();
+
+ priv->glyph_cache = cogl_pango_glyph_cache_new ();
+
+ _cogl_pango_renderer_set_use_mipmapping (priv, FALSE);
+}
+
+static void
+cogl_pango_renderer_class_init (CoglPangoRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
+
+ object_class->finalize = cogl_pango_renderer_finalize;
+
+ renderer_class->draw_glyphs = cogl_pango_renderer_draw_glyphs;
+ renderer_class->draw_rectangle = cogl_pango_renderer_draw_rectangle;
+ renderer_class->draw_trapezoid = cogl_pango_renderer_draw_trapezoid;
+}
+
+static void
+cogl_pango_renderer_finalize (GObject *object)
+{
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object);
+
+ cogl_pango_glyph_cache_free (priv->glyph_cache);
+
+ G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object);
+}
+
+static CoglPangoRenderer *
+cogl_pango_get_renderer_from_context (PangoContext *context)
+{
+ PangoFontMap *font_map;
+ PangoRenderer *renderer;
+ CoglPangoFontMap *font_map_priv;
+
+ font_map = pango_context_get_font_map (context);
+ g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (font_map), NULL);
+
+ font_map_priv = COGL_PANGO_FONT_MAP (font_map);
+ renderer = cogl_pango_font_map_get_renderer (font_map_priv);
+ g_return_val_if_fail (COGL_PANGO_IS_RENDERER (renderer), NULL);
+
+ return COGL_PANGO_RENDERER (renderer);
+}
+
+static GQuark
+cogl_pango_render_get_qdata_key (void)
+{
+ static GQuark key = 0;
+
+ if (G_UNLIKELY (key == 0))
+ key = g_quark_from_static_string ("CoglPangoDisplayList");
+
+ return key;
+}
+
+static void
+cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata)
+{
+ if (qdata->display_list)
+ _cogl_pango_display_list_free (qdata->display_list);
+ if (qdata->first_line)
+ pango_layout_line_unref (qdata->first_line);
+ g_slice_free (CoglPangoRendererQdata, qdata);
+}
+
+/**
+ * cogl_pango_render_layout_subpixel:
+ * @layout: a #PangoLayout
+ * @x: FIXME
+ * @y: FIXME
+ * @color: color to use when rendering the layout
+ * @flags: flags to pass to the renderer
+ *
+ * FIXME
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_render_layout_subpixel (PangoLayout *layout,
+ int x,
+ int y,
+ const CoglColor *color,
+ int flags)
+{
+ PangoContext *context;
+ CoglPangoRenderer *priv;
+ CoglPangoRendererQdata *qdata;
+
+ context = pango_layout_get_context (layout);
+ priv = cogl_pango_get_renderer_from_context (context);
+ if (G_UNLIKELY (!priv))
+ return;
+
+ qdata = g_object_get_qdata (G_OBJECT (layout),
+ cogl_pango_render_get_qdata_key ());
+
+ if (qdata == NULL)
+ {
+ qdata = g_slice_new0 (CoglPangoRendererQdata);
+ g_object_set_qdata_full (G_OBJECT (layout),
+ cogl_pango_render_get_qdata_key (),
+ qdata,
+ (GDestroyNotify)
+ cogl_pango_render_qdata_destroy);
+ }
+
+ /* Check if the layout has changed since the last build of the
+ display list. This trick was suggested by Behdad Esfahbod here:
+ http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */
+ if (qdata->display_list && qdata->first_line
+ && qdata->first_line->layout != layout)
+ {
+ _cogl_pango_display_list_free (qdata->display_list);
+ qdata->display_list = NULL;
+ }
+
+ if (qdata->display_list == NULL)
+ {
+ qdata->display_list = _cogl_pango_display_list_new ();
+
+ priv->display_list = qdata->display_list;
+ pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0);
+ priv->display_list = NULL;
+ }
+
+ cogl_push_matrix ();
+ cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0);
+ _cogl_pango_display_list_render (qdata->display_list,
+ color,
+ priv->glyph_material,
+ priv->solid_material);
+ cogl_pop_matrix ();
+
+ /* Keep a reference to the first line of the layout so we can detect
+ changes */
+ if (qdata->first_line)
+ {
+ pango_layout_line_unref (qdata->first_line);
+ qdata->first_line = NULL;
+ }
+ if (pango_layout_get_line_count (layout) > 0)
+ {
+ qdata->first_line = pango_layout_get_line (layout, 0);
+ pango_layout_line_ref (qdata->first_line);
+ }
+}
+
+/**
+ * cogl_pango_render_layout:
+ * @layout: a #PangoLayout
+ * @x: X coordinate to render the layout at
+ * @y: Y coordinate to render the layout at
+ * @color: color to use when rendering the layout
+ * @flags: flags to pass to the renderer
+ *
+ * Renders @layout.
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_render_layout (PangoLayout *layout,
+ int x,
+ int y,
+ const CoglColor *color,
+ int flags)
+{
+ cogl_pango_render_layout_subpixel (layout,
+ x * PANGO_SCALE,
+ y * PANGO_SCALE,
+ color,
+ flags);
+}
+
+/**
+ * cogl_pango_render_layout_line:
+ * @line: a #PangoLayoutLine
+ * @x: X coordinate to render the line at
+ * @y: Y coordinate to render the line at
+ * @color: color to use when rendering the line
+ *
+ * Renders @line at the given coordinates using the given color.
+ *
+ * Since: 1.0
+ */
+void
+cogl_pango_render_layout_line (PangoLayoutLine *line,
+ int x,
+ int y,
+ const CoglColor *color)
+{
+ PangoContext *context;
+ CoglPangoRenderer *priv;
+
+ context = pango_layout_get_context (line->layout);
+ priv = cogl_pango_get_renderer_from_context (context);
+ if (G_UNLIKELY (!priv))
+ return;
+
+ priv->display_list = _cogl_pango_display_list_new ();
+
+ pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y);
+
+ _cogl_pango_display_list_render (priv->display_list,
+ color,
+ priv->glyph_material,
+ priv->solid_material);
+
+ _cogl_pango_display_list_free (priv->display_list);
+ priv->display_list = NULL;
+}
+
+void
+_cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer)
+{
+ cogl_pango_glyph_cache_clear (renderer->glyph_cache);
+}
+
+void
+_cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer,
+ gboolean value)
+{
+ if (value)
+ cogl_material_set_layer_filters (renderer->glyph_material, 0,
+ COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR,
+ COGL_MATERIAL_FILTER_LINEAR);
+ else
+ cogl_material_set_layer_filters (renderer->glyph_material, 0,
+ COGL_MATERIAL_FILTER_LINEAR,
+ COGL_MATERIAL_FILTER_LINEAR);
+}
+
+gboolean
+_cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer)
+{
+ const GList *layers = cogl_material_get_layers (renderer->glyph_material);
+
+ g_return_val_if_fail (layers != NULL, FALSE);
+
+ return (cogl_material_layer_get_min_filter (layers->data)
+ == COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR);
+}
+
+static CoglPangoGlyphCacheValue *
+cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
+ PangoFont *font,
+ PangoGlyph glyph)
+{
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
+ CoglPangoGlyphCacheValue *value;
+
+ value = cogl_pango_glyph_cache_lookup (priv->glyph_cache, font, glyph);
+ if (value == NULL)
+ {
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_scaled_font_t *scaled_font;
+ PangoRectangle ink_rect;
+ cairo_glyph_t cairo_glyph;
+
+ pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ ink_rect.width,
+ ink_rect.height);
+ cr = cairo_create (surface);
+
+ scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
+ cairo_set_scaled_font (cr, scaled_font);
+
+ cairo_glyph.x = -ink_rect.x;
+ cairo_glyph.y = -ink_rect.y;
+ /* The PangoCairo glyph numbers directly map to Cairo glyph
+ numbers */
+ cairo_glyph.index = glyph;
+ cairo_show_glyphs (cr, &cairo_glyph, 1);
+
+ cairo_destroy (cr);
+ cairo_surface_flush (surface);
+
+ /* Copy the glyph to the cache */
+ value =
+ cogl_pango_glyph_cache_set (priv->glyph_cache, font, glyph,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ cairo_image_surface_get_stride (surface),
+ ink_rect.x, ink_rect.y);
+
+ cairo_surface_destroy (surface);
+
+ COGL_NOTE (PANGO, "cache fail %i", glyph);
+ }
+ else
+ {
+ COGL_NOTE (PANGO, "cache success %i", glyph);
+ }
+
+ return value;
+}
+
+void
+cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout)
+{
+ PangoContext *context;
+ PangoRenderer *renderer;
+ PangoLayoutIter *iter;
+
+ g_return_if_fail (PANGO_IS_LAYOUT (layout));
+
+ if ((iter = pango_layout_get_iter (layout)) == NULL)
+ return;
+
+ context = pango_layout_get_context (layout);
+ renderer =
+ PANGO_RENDERER (cogl_pango_get_renderer_from_context (context));
+
+ do
+ {
+ PangoLayoutLine *line;
+ GSList *l;
+
+ line = pango_layout_iter_get_line_readonly (iter);
+
+ for (l = line->runs; l; l = l->next)
+ {
+ PangoLayoutRun *run = l->data;
+ PangoGlyphString *glyphs = run->glyphs;
+ int i;
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ cogl_pango_renderer_get_cached_glyph (renderer,
+ run->item->analysis.font,
+ gi->glyph);
+ }
+ }
+ }
+ while (pango_layout_iter_next_line (iter));
+
+ pango_layout_iter_free (iter);
+}
+
+static void
+cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
+ PangoRenderPart part)
+{
+ PangoColor *pango_color = pango_renderer_get_color (renderer, part);
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
+
+ if (pango_color)
+ {
+ CoglColor color;
+
+ cogl_color_set_from_4ub (&color,
+ pango_color->red >> 8,
+ pango_color->green >> 8,
+ pango_color->blue >> 8,
+ 0xff);
+
+ _cogl_pango_display_list_set_color_override (priv->display_list, &color);
+ }
+ else
+ _cogl_pango_display_list_remove_color_override (priv->display_list);
+}
+
+static void
+cogl_pango_renderer_draw_box (PangoRenderer *renderer,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
+
+ g_return_if_fail (priv->display_list != NULL);
+
+ _cogl_pango_display_list_add_rectangle (priv->display_list,
+ x,
+ y - height,
+ x + width,
+ y);
+}
+
+static void
+cogl_pango_renderer_get_device_units (PangoRenderer *renderer,
+ int xin,
+ int yin,
+ float *xout,
+ float *yout)
+{
+ const PangoMatrix *matrix;
+
+ if ((matrix = pango_renderer_get_matrix (renderer)))
+ {
+ /* Convert user-space coords to device coords */
+ *xout = ((xin * matrix->xx + yin * matrix->xy)
+ / PANGO_SCALE + matrix->x0);
+ *yout = ((yin * matrix->yy + xin * matrix->yx)
+ / PANGO_SCALE + matrix->y0);
+ }
+ else
+ {
+ *xout = PANGO_PIXELS (xin);
+ *yout = PANGO_PIXELS (yin);
+ }
+}
+
+static void
+cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
+ PangoRenderPart part,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
+ float x1, x2, y1, y2;
+
+ g_return_if_fail (priv->display_list != NULL);
+
+ cogl_pango_renderer_set_color_for_part (renderer, part);
+
+ cogl_pango_renderer_get_device_units (renderer,
+ x, y,
+ &x1, &y1);
+ cogl_pango_renderer_get_device_units (renderer,
+ x + width, y + height,
+ &x2, &y2);
+
+ _cogl_pango_display_list_add_rectangle (priv->display_list,
+ x1, y1, x2, y2);
+}
+
+static void
+cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
+ PangoRenderPart part,
+ double y1,
+ double x11,
+ double x21,
+ double y2,
+ double x12,
+ double x22)
+{
+ CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
+ float points[8];
+
+ g_return_if_fail (priv->display_list != NULL);
+
+ points[0] = (x11);
+ points[1] = (y1);
+ points[2] = (x12);
+ points[3] = (y2);
+ points[4] = (x22);
+ points[5] = points[3];
+ points[6] = (x21);
+ points[7] = points[1];
+
+ cogl_pango_renderer_set_color_for_part (renderer, part);
+
+ _cogl_pango_display_list_add_trapezoid (priv->display_list,
+ y1,
+ x11,
+ x21,
+ y2,
+ x12,
+ x22);
+}
+
+static void
+cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ int xi,
+ int yi)
+{
+ CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer;
+ CoglPangoGlyphCacheValue *cache_value;
+ int i;
+
+ cogl_pango_renderer_set_color_for_part (renderer,
+ PANGO_RENDER_PART_FOREGROUND);
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = glyphs->glyphs + i;
+ float x, y;
+
+ cogl_pango_renderer_get_device_units (renderer,
+ xi + gi->geometry.x_offset,
+ yi + gi->geometry.y_offset,
+ &x, &y);
+
+ if ((gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ PangoFontMetrics *metrics;
+
+ if (font == NULL ||
+ (metrics = pango_font_get_metrics (font, NULL)) == NULL)
+ {
+ cogl_pango_renderer_draw_box (renderer,
+ x,
+ y,
+ PANGO_UNKNOWN_GLYPH_WIDTH,
+ PANGO_UNKNOWN_GLYPH_HEIGHT);
+ }
+ else
+ {
+ cogl_pango_renderer_draw_box (renderer,
+ x,
+ y,
+ metrics->approximate_char_width
+ / PANGO_SCALE,
+ metrics->ascent / PANGO_SCALE);
+
+ pango_font_metrics_unref (metrics);
+ }
+ }
+ else
+ {
+ /* Get the texture containing the glyph. This will create
+ the cache entry if there isn't already one */
+ cache_value =
+ cogl_pango_renderer_get_cached_glyph (renderer,
+ font,
+ gi->glyph);
+
+ if (cache_value == NULL)
+ cogl_pango_renderer_draw_box (renderer,
+ x,
+ y,
+ PANGO_UNKNOWN_GLYPH_WIDTH,
+ PANGO_UNKNOWN_GLYPH_HEIGHT);
+ else
+ {
+ float width, height;
+
+ x += (float)(cache_value->draw_x);
+ y += (float)(cache_value->draw_y);
+
+ width = x + (float)(cache_value->draw_width);
+ height = y + (float)(cache_value->draw_height);
+
+ cogl_pango_renderer_draw_glyph (priv, cache_value, x, y);
+ }
+ }
+
+ xi += gi->geometry.width;
+ }
+}
diff --git a/pango/cogl-pango.h b/pango/cogl-pango.h
new file mode 100644
index 00000000..8e7165df
--- /dev/null
+++ b/pango/cogl-pango.h
@@ -0,0 +1,85 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PANGO_CLUTTER_H__
+#define __PANGO_CLUTTER_H__
+
+#include <glib-object.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <cogl/cogl.h>
+
+G_BEGIN_DECLS
+
+/* It's too difficult to actually subclass the pango cairo font
+ * map. Instead we just make a fake set of macros that actually just
+ * directly use the original type
+ */
+#define COGL_PANGO_TYPE_FONT_MAP PANGO_TYPE_CAIRO_FONT_MAP
+#define COGL_PANGO_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_FONT_MAP, CoglPangoFontMap))
+#define COGL_PANGO_IS_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_FONT_MAP))
+
+typedef PangoCairoFontMap CoglPangoFontMap;
+
+PangoFontMap * cogl_pango_font_map_new (void);
+PangoContext * cogl_pango_font_map_create_context (CoglPangoFontMap *fm);
+void cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
+ double dpi);
+void cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *fm);
+void cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout);
+void cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *fm,
+ gboolean value);
+gboolean cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *fm);
+PangoRenderer *cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm);
+
+#define COGL_PANGO_TYPE_RENDERER (cogl_pango_renderer_get_type ())
+#define COGL_PANGO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRenderer))
+#define COGL_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass))
+#define COGL_PANGO_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_RENDERER))
+#define COGL_PANGO_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COGL_PANGO_TYPE_RENDERER))
+#define COGL_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass))
+
+/* opaque types */
+typedef struct _CoglPangoRenderer CoglPangoRenderer;
+typedef struct _CoglPangoRendererClass CoglPangoRendererClass;
+
+GType cogl_pango_renderer_get_type (void) G_GNUC_CONST;
+
+void cogl_pango_render_layout_subpixel (PangoLayout *layout,
+ int x,
+ int y,
+ const CoglColor *color,
+ int flags);
+void cogl_pango_render_layout (PangoLayout *layout,
+ int x,
+ int y,
+ const CoglColor *color,
+ int flags);
+void cogl_pango_render_layout_line (PangoLayoutLine *line,
+ int x,
+ int y,
+ const CoglColor *color);
+
+G_END_DECLS
+
+#endif /* __PANGO_CLUTTER_H__ */