diff --git a/clutter/cogl/pango/cogl-pango-glyph-cache.c b/clutter/cogl/pango/cogl-pango-glyph-cache.c index df26f672f..0bd81a1a9 100644 --- a/clutter/cogl/pango/cogl-pango-glyph-cache.c +++ b/clutter/cogl/pango/cogl-pango-glyph-cache.c @@ -29,28 +29,27 @@ #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 +#include "cogl/cogl-atlas.h" +#include "cogl/cogl-callback-list.h" 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; + GHashTable *hash_table; - /* List of textures */ - CoglPangoGlyphCacheTexture *textures; + /* List of CoglAtlases */ + GSList *atlases; - /* List of horizontal bands of glyphs */ - CoglPangoGlyphCacheBand *bands; + /* List of callbacks to invoke when an atlas is reorganized */ + 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 @@ -59,48 +58,6 @@ struct _CoglPangoGlyphCacheKey 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) { @@ -144,34 +101,6 @@ cogl_pango_glyph_cache_equal_func (gconstpointer a, && 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) { @@ -185,8 +114,10 @@ cogl_pango_glyph_cache_new (void) (GDestroyNotify) cogl_pango_glyph_cache_key_free, (GDestroyNotify) cogl_pango_glyph_cache_value_free); - cache->textures = NULL; - cache->bands = NULL; + cache->atlases = NULL; + _cogl_callback_list_init (&cache->reorganize_callbacks); + + cache->has_dirty_glyphs = FALSE; return cache; } @@ -194,10 +125,10 @@ cogl_pango_glyph_cache_new (void) 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_slist_foreach (cache->atlases, (GFunc) _cogl_atlas_free, NULL); + g_slist_free (cache->atlases); + cache->atlases = NULL; + cache->has_dirty_glyphs = FALSE; 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); + _cogl_callback_list_destroy (&cache->reorganize_callbacks); + 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 * cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, - PangoFont *font, - PangoGlyph glyph) + gboolean create, + 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; + CoglPangoGlyphCacheKey lookup_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++; + lookup_key.font = font; + lookup_key.glyph = glyph; - /* Round the height up to the nearest multiple of - BAND_HEIGHT_ROUND */ - band_height = (height + BAND_HEIGHT_ROUND - 1) & ~(BAND_HEIGHT_ROUND - 1); + value = g_hash_table_lookup (cache->hash_table, &lookup_key); - /* 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) + if (create && value == 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 - 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; + pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL); + pango_extents_to_pixels (&ink_rect, NULL); - /* 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); + value = g_slice_new (CoglPangoGlyphCacheValue); + value->texture = COGL_INVALID_HANDLE; + value->draw_x = ink_rect.x; + 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; - while (texture->texture_size < band_height || - texture->texture_size < width) + /* Look for an atlas that can reserve the space */ + for (l = cache->atlases; l; l = l->next) + 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 */ - clear_data = - g_malloc0 (texture->texture_size * texture->texture_size); + _cogl_atlas_add_reorganize_callback + (atlas, cogl_pango_glyph_cache_reorganize_cb, cache); - 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); + cache->atlases = g_slist_prepend (cache->atlases, atlas); + } - g_free (clear_data); + key = g_slice_new (CoglPangoGlyphCacheKey); + key->font = g_object_ref (font); + key->glyph = glyph; - texture->space_remaining = texture->texture_size; - texture->next = cache->textures; - cache->textures = texture; - } + g_hash_table_insert (cache->hash_table, key, value); - 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; + cache->has_dirty_glyphs = TRUE; } - 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; } + +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); +} diff --git a/clutter/cogl/pango/cogl-pango-glyph-cache.h b/clutter/cogl/pango/cogl-pango-glyph-cache.h index ab5265afa..134239cf5 100644 --- a/clutter/cogl/pango/cogl-pango-glyph-cache.h +++ b/clutter/cogl/pango/cogl-pango-glyph-cache.h @@ -26,6 +26,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -37,17 +38,28 @@ struct _CoglPangoGlyphCacheValue { CoglHandle texture; - float tx1; - float ty1; - float tx2; - float ty2; + float tx1; + float ty1; + float tx2; + float ty2; + + int tx_pixel; + int ty_pixel; int draw_x; int draw_y; int draw_width; 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 * cogl_pango_glyph_cache_new (void); @@ -56,23 +68,27 @@ cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache); CoglPangoGlyphCacheValue * cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, + gboolean create, 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); +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 #endif /* __COGL_PANGO_GLYPH_CACHE_H__ */ diff --git a/clutter/cogl/pango/cogl-pango-render.c b/clutter/cogl/pango/cogl-pango-render.c index fc48c40ad..5a554a268 100644 --- a/clutter/cogl/pango/cogl-pango-render.c +++ b/clutter/cogl/pango/cogl-pango-render.c @@ -66,6 +66,7 @@ typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata; cache the VBO and to detect changes to the layout */ struct _CoglPangoRendererQdata { + CoglPangoRenderer *renderer; /* 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 @@ -73,6 +74,9 @@ struct _CoglPangoRendererQdata PangoLayoutLine *first_line; }; +static void +_cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line); + static void cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, CoglPangoGlyphCacheValue *cache_value, @@ -202,10 +206,25 @@ cogl_pango_render_get_qdata_key (void) } static void -cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata) +cogl_pango_render_qdata_forget_display_list (CoglPangoRendererQdata *qdata) { 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) pango_layout_line_unref (qdata->first_line); g_slice_free (CoglPangoRendererQdata, qdata); @@ -245,6 +264,7 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, if (qdata == NULL) { qdata = g_slice_new0 (CoglPangoRendererQdata); + qdata->renderer = priv; g_object_set_qdata_full (G_OBJECT (layout), cogl_pango_render_get_qdata_key (), qdata, @@ -257,15 +277,21 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, 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; - } + cogl_pango_render_qdata_forget_display_list (qdata); if (qdata->display_list == NULL) { + cogl_pango_ensure_glyph_cache_for_layout (layout); + 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; pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); priv->display_list = NULL; @@ -346,6 +372,8 @@ cogl_pango_render_layout_line (PangoLayoutLine *line, 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); _cogl_pango_display_list_render (priv->display_list, @@ -390,105 +418,144 @@ _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer) static CoglPangoGlyphCacheValue * cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer, + gboolean create, 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) + return cogl_pango_glyph_cache_lookup (priv->glyph_cache, create, font, glyph); +} + +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; - cairo_t *cr; - cairo_scaled_font_t *scaled_font; - PangoRectangle ink_rect; - cairo_glyph_t cairo_glyph; + PangoLayoutRun *run = l->data; + PangoGlyphString *glyphs = run->glyphs; + int i; - pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL); - pango_extents_to_pixels (&ink_rect, NULL); + for (i = 0; i < glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = &glyphs->glyphs[i]; - 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); + /* If the glyph isn't cached then this will reserve + space for it now. We won't actually draw the glyph + yet because reserving space could cause all of the + other glyphs to be moved so we might as well redraw + them all later once we know that the position is + settled */ + cogl_pango_renderer_get_cached_glyph (renderer, TRUE, + run->item->analysis.font, + gi->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 cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout) { - PangoContext *context; - PangoRenderer *renderer; + PangoContext *context; + CoglPangoRenderer *priv; PangoLayoutIter *iter; + context = pango_layout_get_context (layout); + priv = cogl_pango_get_renderer_from_context (context); + 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); - } - } + _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line); } while (pango_layout_iter_next_line (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 @@ -668,13 +735,18 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, } else { - /* Get the texture containing the glyph. This will create - the cache entry if there isn't already one */ + /* Get the texture containing the glyph */ cache_value = cogl_pango_renderer_get_cached_glyph (renderer, + FALSE, font, 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) { cogl_pango_renderer_draw_box (renderer,