diff options
author | Robert Bragg <robert@linux.intel.com> | 2009-07-29 17:21:07 +0100 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2009-10-16 18:58:51 +0100 |
commit | 17e899a49d7d657f57103804bc2c59f526528a34 (patch) | |
tree | feac2ae32bbcff057e4fe22a2576ab56a2df404e /pango | |
parent | 0bce7eac5375936d9ac6486f13cef94298f8590e (diff) | |
download | cogl-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.am | 33 | ||||
-rw-r--r-- | pango/cogl-pango-display-list.c | 417 | ||||
-rw-r--r-- | pango/cogl-pango-display-list.h | 70 | ||||
-rw-r--r-- | pango/cogl-pango-fontmap.c | 209 | ||||
-rw-r--r-- | pango/cogl-pango-glyph-cache.c | 352 | ||||
-rw-r--r-- | pango/cogl-pango-glyph-cache.h | 78 | ||||
-rw-r--r-- | pango/cogl-pango-private.h | 38 | ||||
-rw-r--r-- | pango/cogl-pango-render.c | 697 | ||||
-rw-r--r-- | pango/cogl-pango.h | 85 |
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__ */ |