cogl-pango: Use a CoglAtlas to maintain the glyph cache

The glyph cache is now stored in a CoglAtlas structure instead of the
custom atlasing code. This has the advantage that it can share code
with the main texture atlas and that it supports reorganizing the
atlas when it becomes full. Unlike the texture atlas, the glyph cache
can use multiple atlases which would be neccessary if the maximum
texture size is reached and we need to create a second
texture. Whenever a display list is created it now has to register a
callback with the glyph cache so that the display list can be
recreated whenever any of the atlases are reorganized. This is needed
because the display list directly stores texture coordinates within
the atlas texture and they would become invalid when the texture is
moved.

The ensure_glyphs_for_layout now works in two steps. First it reserves
space in the atlas for all of the glyphs. The atlas is created with
the DISABLE_MIGRATION flag so that it won't actually copy any textures
if any rearranging is needed. Whenever the position is updated for a
glyph then it is marked as dirty. After space for all of the glyphs
has been reserved it will iterate over all dirty glyphs and redraw
them using Cairo. The rendered glyph is then stored in the texture
with a sub texture update.

The glyphs need to all be set at the right location before starting to
create the display list because the display list stores the texture
coordinates of the glyph. If any of the glyphs were moved around then
the parts of the display list that was created already would become
invalid. To make this work, ensure_glyphs_for_layout is now always
called before rendering a layout or a layout line.
This commit is contained in:
Neil Roberts 2010-08-04 18:05:21 +01:00
parent bbac324356
commit 8123330061
3 changed files with 338 additions and 295 deletions

View File

@ -29,28 +29,27 @@
#include "cogl-pango-glyph-cache.h" #include "cogl-pango-glyph-cache.h"
#include "cogl-pango-private.h" #include "cogl-pango-private.h"
#include "cogl/cogl-atlas.h"
/* Minimum width/height for each texture */ #include "cogl/cogl-callback-list.h"
#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 _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey;
typedef struct _CoglPangoGlyphCacheTexture CoglPangoGlyphCacheTexture;
typedef struct _CoglPangoGlyphCacheBand CoglPangoGlyphCacheBand;
struct _CoglPangoGlyphCache struct _CoglPangoGlyphCache
{ {
/* Hash table to quickly check whether a particular glyph in a /* Hash table to quickly check whether a particular glyph in a
particular font is already cached */ particular font is already cached */
GHashTable *hash_table; GHashTable *hash_table;
/* List of textures */ /* List of CoglAtlases */
CoglPangoGlyphCacheTexture *textures; GSList *atlases;
/* List of horizontal bands of glyphs */ /* List of callbacks to invoke when an atlas is reorganized */
CoglPangoGlyphCacheBand *bands; CoglCallbackList reorganize_callbacks;
/* True if some of the glyphs are dirty. This is used as an
optimization in _cogl_pango_glyph_cache_set_dirty_glyphs to avoid
iterating the hash table if we know none of them are dirty */
gboolean has_dirty_glyphs;
}; };
struct _CoglPangoGlyphCacheKey struct _CoglPangoGlyphCacheKey
@ -59,48 +58,6 @@ struct _CoglPangoGlyphCacheKey
PangoGlyph glyph; 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 static void
cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value) cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value)
{ {
@ -144,34 +101,6 @@ cogl_pango_glyph_cache_equal_func (gconstpointer a,
&& key_a->glyph == key_b->glyph; && 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 * CoglPangoGlyphCache *
cogl_pango_glyph_cache_new (void) cogl_pango_glyph_cache_new (void)
{ {
@ -185,8 +114,10 @@ cogl_pango_glyph_cache_new (void)
(GDestroyNotify) cogl_pango_glyph_cache_key_free, (GDestroyNotify) cogl_pango_glyph_cache_key_free,
(GDestroyNotify) cogl_pango_glyph_cache_value_free); (GDestroyNotify) cogl_pango_glyph_cache_value_free);
cache->textures = NULL; cache->atlases = NULL;
cache->bands = NULL; _cogl_callback_list_init (&cache->reorganize_callbacks);
cache->has_dirty_glyphs = FALSE;
return cache; return cache;
} }
@ -194,10 +125,10 @@ cogl_pango_glyph_cache_new (void)
void void
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache) cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
{ {
cogl_pango_glyph_cache_free_textures (cache->textures); g_slist_foreach (cache->atlases, (GFunc) _cogl_atlas_free, NULL);
cache->textures = NULL; g_slist_free (cache->atlases);
cogl_pango_glyph_cache_free_bands (cache->bands); cache->atlases = NULL;
cache->bands = NULL; cache->has_dirty_glyphs = FALSE;
g_hash_table_remove_all (cache->hash_table); g_hash_table_remove_all (cache->hash_table);
} }
@ -209,144 +140,168 @@ cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
g_hash_table_unref (cache->hash_table); g_hash_table_unref (cache->hash_table);
_cogl_callback_list_destroy (&cache->reorganize_callbacks);
g_free (cache); g_free (cache);
} }
static void
cogl_pango_glyph_cache_update_position_cb (void *user_data,
CoglHandle new_texture,
const CoglRectangleMapEntry *rect)
{
CoglPangoGlyphCacheValue *value = user_data;
float tex_width, tex_height;
if (value->texture)
cogl_handle_unref (value->texture);
value->texture = cogl_handle_ref (new_texture);
tex_width = cogl_texture_get_width (new_texture);
tex_height = cogl_texture_get_height (new_texture);
value->tx1 = rect->x / tex_width;
value->ty1 = rect->y / tex_height;
value->tx2 = (rect->x + value->draw_width) / tex_width;
value->ty2 = (rect->y + value->draw_height) / tex_height;
value->tx_pixel = rect->x;
value->ty_pixel = rect->y;
/* The glyph has changed position so it will need to be redrawn */
value->dirty = TRUE;
}
static void
cogl_pango_glyph_cache_reorganize_cb (void *user_data)
{
CoglPangoGlyphCache *cache = user_data;
_cogl_callback_list_invoke (&cache->reorganize_callbacks);
}
CoglPangoGlyphCacheValue * CoglPangoGlyphCacheValue *
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
PangoFont *font, gboolean create,
PangoGlyph glyph) PangoFont *font,
PangoGlyph glyph)
{ {
CoglPangoGlyphCacheKey key; CoglPangoGlyphCacheKey lookup_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; CoglPangoGlyphCacheValue *value;
/* Reserve an extra pixel gap around the glyph so that it can pull lookup_key.font = font;
in blank pixels when linear filtering is enabled */ lookup_key.glyph = glyph;
width++;
height++;
/* Round the height up to the nearest multiple of value = g_hash_table_lookup (cache->hash_table, &lookup_key);
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 */ if (create && value == NULL)
for (band = cache->bands;
band && (band->height != band_height || band->space_remaining < width);
band = band->next);
if (band == NULL)
{ {
CoglPangoGlyphCacheTexture *texture; CoglPangoGlyphCacheKey *key;
PangoRectangle ink_rect;
CoglAtlas *atlas = NULL;
GSList *l;
/* Look for a texture with enough vertical space left for a band pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
with this height */ pango_extents_to_pixels (&ink_rect, NULL);
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 value = g_slice_new (CoglPangoGlyphCacheValue);
greater than the band height or the minimum size, value->texture = COGL_INVALID_HANDLE;
whichever is lower */ value->draw_x = ink_rect.x;
texture = g_slice_new (CoglPangoGlyphCacheTexture); value->draw_y = ink_rect.y;
value->draw_width = ink_rect.width;
value->draw_height = ink_rect.height;
value->dirty = TRUE;
texture->texture_size = MIN_TEXTURE_SIZE; /* Look for an atlas that can reserve the space */
while (texture->texture_size < band_height || for (l = cache->atlases; l; l = l->next)
texture->texture_size < width) if (_cogl_atlas_reserve_space (l->data,
ink_rect.width + 1, ink_rect.height + 1,
value))
{
atlas = l->data;
break;
}
/* If we couldn't find one then start a new atlas */
if (atlas == NULL)
{
atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8,
TRUE,
cogl_pango_glyph_cache_update_position_cb);
/* If we still can't reserve space then something has gone
seriously wrong so we'll just give up */
if (!_cogl_atlas_reserve_space (atlas,
ink_rect.width + 1,
ink_rect.height + 1, value))
{ {
texture->texture_size *= 2; _cogl_atlas_free (atlas);
cogl_pango_glyph_cache_value_free (value);
return NULL;
} }
/* Allocate an empty buffer to clear the texture */ _cogl_atlas_add_reorganize_callback
clear_data = (atlas, cogl_pango_glyph_cache_reorganize_cb, cache);
g_malloc0 (texture->texture_size * texture->texture_size);
texture->texture = cache->atlases = g_slist_prepend (cache->atlases, atlas);
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); key = g_slice_new (CoglPangoGlyphCacheKey);
key->font = g_object_ref (font);
key->glyph = glyph;
texture->space_remaining = texture->texture_size; g_hash_table_insert (cache->hash_table, key, value);
texture->next = cache->textures;
cache->textures = texture;
}
band = g_slice_new (CoglPangoGlyphCacheBand); cache->has_dirty_glyphs = TRUE;
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; return value;
} }
static void
_cogl_pango_glyph_cache_set_dirty_glyphs_cb (gpointer key_ptr,
gpointer value_ptr,
gpointer user_data)
{
CoglPangoGlyphCacheKey *key = key_ptr;
CoglPangoGlyphCacheValue *value = value_ptr;
CoglPangoGlyphCacheDirtyFunc func = user_data;
if (value->dirty)
{
func (key->font, key->glyph, value);
value->dirty = FALSE;
}
}
void
_cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache,
CoglPangoGlyphCacheDirtyFunc func)
{
/* If we know that there are no dirty glyphs then we can shortcut
out early */
if (!cache->has_dirty_glyphs)
return;
g_hash_table_foreach (cache->hash_table,
_cogl_pango_glyph_cache_set_dirty_glyphs_cb,
func);
cache->has_dirty_glyphs = FALSE;
}
void
_cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache,
CoglCallbackListFunc func,
void *user_data)
{
_cogl_callback_list_add (&cache->reorganize_callbacks, func, user_data);
}
void
_cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache,
CoglCallbackListFunc func,
void *user_data)
{
_cogl_callback_list_remove (&cache->reorganize_callbacks, func, user_data);
}

View File

@ -26,6 +26,7 @@
#include <glib.h> #include <glib.h>
#include <cogl/cogl.h> #include <cogl/cogl.h>
#include <cogl/cogl-callback-list.h>
#include <pango/pango-font.h> #include <pango/pango-font.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -37,17 +38,28 @@ struct _CoglPangoGlyphCacheValue
{ {
CoglHandle texture; CoglHandle texture;
float tx1; float tx1;
float ty1; float ty1;
float tx2; float tx2;
float ty2; float ty2;
int tx_pixel;
int ty_pixel;
int draw_x; int draw_x;
int draw_y; int draw_y;
int draw_width; int draw_width;
int draw_height; int draw_height;
/* This will be set to TRUE when the glyph atlas is reorganized
which means the glyph will need to be redrawn */
gboolean dirty;
}; };
typedef void (* CoglPangoGlyphCacheDirtyFunc) (PangoFont *font,
PangoGlyph glyph,
CoglPangoGlyphCacheValue *value);
CoglPangoGlyphCache * CoglPangoGlyphCache *
cogl_pango_glyph_cache_new (void); cogl_pango_glyph_cache_new (void);
@ -56,23 +68,27 @@ cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache);
CoglPangoGlyphCacheValue * CoglPangoGlyphCacheValue *
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
gboolean create,
PangoFont *font, PangoFont *font,
PangoGlyph glyph); 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 void
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache); cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache);
void
_cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache,
CoglCallbackListFunc func,
void *user_data);
void
_cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache,
CoglCallbackListFunc func,
void *user_data);
void
_cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache,
CoglPangoGlyphCacheDirtyFunc func);
G_END_DECLS G_END_DECLS
#endif /* __COGL_PANGO_GLYPH_CACHE_H__ */ #endif /* __COGL_PANGO_GLYPH_CACHE_H__ */

View File

@ -66,6 +66,7 @@ typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata;
cache the VBO and to detect changes to the layout */ cache the VBO and to detect changes to the layout */
struct _CoglPangoRendererQdata struct _CoglPangoRendererQdata
{ {
CoglPangoRenderer *renderer;
/* The cache of the geometry for the layout */ /* The cache of the geometry for the layout */
CoglPangoDisplayList *display_list; CoglPangoDisplayList *display_list;
/* A reference to the first line of the layout. This is just used to /* A reference to the first line of the layout. This is just used to
@ -73,6 +74,9 @@ struct _CoglPangoRendererQdata
PangoLayoutLine *first_line; PangoLayoutLine *first_line;
}; };
static void
_cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line);
static void static void
cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
CoglPangoGlyphCacheValue *cache_value, CoglPangoGlyphCacheValue *cache_value,
@ -202,10 +206,25 @@ cogl_pango_render_get_qdata_key (void)
} }
static void static void
cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata) cogl_pango_render_qdata_forget_display_list (CoglPangoRendererQdata *qdata)
{ {
if (qdata->display_list) if (qdata->display_list)
_cogl_pango_display_list_free (qdata->display_list); {
_cogl_pango_glyph_cache_remove_reorganize_callback
(qdata->renderer->glyph_cache,
(CoglCallbackListFunc) cogl_pango_render_qdata_forget_display_list,
qdata);
_cogl_pango_display_list_free (qdata->display_list);
qdata->display_list = NULL;
}
}
static void
cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata)
{
cogl_pango_render_qdata_forget_display_list (qdata);
if (qdata->first_line) if (qdata->first_line)
pango_layout_line_unref (qdata->first_line); pango_layout_line_unref (qdata->first_line);
g_slice_free (CoglPangoRendererQdata, qdata); g_slice_free (CoglPangoRendererQdata, qdata);
@ -245,6 +264,7 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout,
if (qdata == NULL) if (qdata == NULL)
{ {
qdata = g_slice_new0 (CoglPangoRendererQdata); qdata = g_slice_new0 (CoglPangoRendererQdata);
qdata->renderer = priv;
g_object_set_qdata_full (G_OBJECT (layout), g_object_set_qdata_full (G_OBJECT (layout),
cogl_pango_render_get_qdata_key (), cogl_pango_render_get_qdata_key (),
qdata, qdata,
@ -257,15 +277,21 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout,
http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */ http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */
if (qdata->display_list && qdata->first_line if (qdata->display_list && qdata->first_line
&& qdata->first_line->layout != layout) && qdata->first_line->layout != layout)
{ cogl_pango_render_qdata_forget_display_list (qdata);
_cogl_pango_display_list_free (qdata->display_list);
qdata->display_list = NULL;
}
if (qdata->display_list == NULL) if (qdata->display_list == NULL)
{ {
cogl_pango_ensure_glyph_cache_for_layout (layout);
qdata->display_list = _cogl_pango_display_list_new (); qdata->display_list = _cogl_pango_display_list_new ();
/* Register for notification of when the glyph cache changes so
we can rebuild the display list */
_cogl_pango_glyph_cache_add_reorganize_callback
(priv->glyph_cache,
(CoglCallbackListFunc) cogl_pango_render_qdata_forget_display_list,
qdata);
priv->display_list = qdata->display_list; priv->display_list = qdata->display_list;
pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0);
priv->display_list = NULL; priv->display_list = NULL;
@ -346,6 +372,8 @@ cogl_pango_render_layout_line (PangoLayoutLine *line,
priv->display_list = _cogl_pango_display_list_new (); priv->display_list = _cogl_pango_display_list_new ();
_cogl_pango_ensure_glyph_cache_for_layout_line (line);
pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y); pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y);
_cogl_pango_display_list_render (priv->display_list, _cogl_pango_display_list_render (priv->display_list,
@ -390,105 +418,144 @@ _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer)
static CoglPangoGlyphCacheValue * static CoglPangoGlyphCacheValue *
cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer, cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
gboolean create,
PangoFont *font, PangoFont *font,
PangoGlyph glyph) PangoGlyph glyph)
{ {
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
CoglPangoGlyphCacheValue *value;
value = cogl_pango_glyph_cache_lookup (priv->glyph_cache, font, glyph); return cogl_pango_glyph_cache_lookup (priv->glyph_cache, create, font, glyph);
if (value == NULL) }
static void
cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
PangoGlyph glyph,
CoglPangoGlyphCacheValue *value)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
cairo_glyph_t cairo_glyph;
COGL_NOTE (PANGO, "redrawing glyph %i", glyph);
surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
value->draw_width,
value->draw_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 = -value->draw_x;
cairo_glyph.y = -value->draw_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 texture */
cogl_texture_set_region (value->texture,
0, /* src_x */
0, /* src_y */
value->tx_pixel, /* dst_x */
value->ty_pixel, /* dst_y */
value->draw_width, /* dst_width */
value->draw_height, /* dst_height */
value->draw_width, /* width */
value->draw_height, /* height */
COGL_PIXEL_FORMAT_A_8,
cairo_image_surface_get_stride (surface),
cairo_image_surface_get_data (surface));
cairo_surface_destroy (surface);
}
static void
_cogl_pango_ensure_glyph_cache_for_layout_line_internal (PangoLayoutLine *line)
{
PangoContext *context;
PangoRenderer *renderer;
GSList *l;
context = pango_layout_get_context (line->layout);
renderer =
PANGO_RENDERER (cogl_pango_get_renderer_from_context (context));
for (l = line->runs; l; l = l->next)
{ {
cairo_surface_t *surface; PangoLayoutRun *run = l->data;
cairo_t *cr; PangoGlyphString *glyphs = run->glyphs;
cairo_scaled_font_t *scaled_font; int i;
PangoRectangle ink_rect;
cairo_glyph_t cairo_glyph;
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL); for (i = 0; i < glyphs->num_glyphs; i++)
pango_extents_to_pixels (&ink_rect, NULL); {
PangoGlyphInfo *gi = &glyphs->glyphs[i];
surface = cairo_image_surface_create (CAIRO_FORMAT_A8, /* If the glyph isn't cached then this will reserve
ink_rect.width, space for it now. We won't actually draw the glyph
ink_rect.height); yet because reserving space could cause all of the
cr = cairo_create (surface); other glyphs to be moved so we might as well redraw
them all later once we know that the position is
scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font)); settled */
cairo_set_scaled_font (cr, scaled_font); cogl_pango_renderer_get_cached_glyph (renderer, TRUE,
run->item->analysis.font,
cairo_glyph.x = -ink_rect.x; gi->glyph);
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; static void
_cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line)
{
PangoContext *context;
CoglPangoRenderer *priv;
context = pango_layout_get_context (line->layout);
priv = cogl_pango_get_renderer_from_context (context);
_cogl_pango_ensure_glyph_cache_for_layout_line_internal (line);
/* Now that we know all of the positions are settled we'll fill in
any dirty glyphs */
_cogl_pango_glyph_cache_set_dirty_glyphs
(priv->glyph_cache, cogl_pango_renderer_set_dirty_glyph);
} }
void void
cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout) cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout)
{ {
PangoContext *context; PangoContext *context;
PangoRenderer *renderer; CoglPangoRenderer *priv;
PangoLayoutIter *iter; PangoLayoutIter *iter;
context = pango_layout_get_context (layout);
priv = cogl_pango_get_renderer_from_context (context);
g_return_if_fail (PANGO_IS_LAYOUT (layout)); g_return_if_fail (PANGO_IS_LAYOUT (layout));
if ((iter = pango_layout_get_iter (layout)) == NULL) if ((iter = pango_layout_get_iter (layout)) == NULL)
return; return;
context = pango_layout_get_context (layout);
renderer =
PANGO_RENDERER (cogl_pango_get_renderer_from_context (context));
do do
{ {
PangoLayoutLine *line; PangoLayoutLine *line;
GSList *l;
line = pango_layout_iter_get_line_readonly (iter); line = pango_layout_iter_get_line_readonly (iter);
for (l = line->runs; l; l = l->next) _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line);
{
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)); while (pango_layout_iter_next_line (iter));
pango_layout_iter_free (iter); pango_layout_iter_free (iter);
/* Now that we know all of the positions are settled we'll fill in
any dirty glyphs */
_cogl_pango_glyph_cache_set_dirty_glyphs
(priv->glyph_cache, cogl_pango_renderer_set_dirty_glyph);
} }
static void static void
@ -668,13 +735,18 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
} }
else else
{ {
/* Get the texture containing the glyph. This will create /* Get the texture containing the glyph */
the cache entry if there isn't already one */
cache_value = cache_value =
cogl_pango_renderer_get_cached_glyph (renderer, cogl_pango_renderer_get_cached_glyph (renderer,
FALSE,
font, font,
gi->glyph); gi->glyph);
/* cogl_pango_ensure_glyph_cache_for_layout should always be
called before rendering a layout so we should never have
a dirty glyph here */
g_assert (cache_value == NULL || !cache_value->dirty);
if (cache_value == NULL) if (cache_value == NULL)
{ {
cogl_pango_renderer_draw_box (renderer, cogl_pango_renderer_draw_box (renderer,