From ed80c884137603b624964c53faa4d2d74a8ee09a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 28 May 2008 14:09:22 +0000 Subject: [PATCH] Forgot GlyphCache implementation --- clutter/pango/pangoclutter-glyph-cache.c | 360 +++++++++++++++++++++++ clutter/pango/pangoclutter-glyph-cache.h | 69 +++++ 2 files changed, 429 insertions(+) create mode 100644 clutter/pango/pangoclutter-glyph-cache.c create mode 100644 clutter/pango/pangoclutter-glyph-cache.h diff --git a/clutter/pango/pangoclutter-glyph-cache.c b/clutter/pango/pangoclutter-glyph-cache.c new file mode 100644 index 000000000..8d7fab8dd --- /dev/null +++ b/clutter/pango/pangoclutter-glyph-cache.c @@ -0,0 +1,360 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "pangoclutter-glyph-cache.h" +#include "cogl/cogl.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 _PangoClutterGlyphCacheKey PangoClutterGlyphCacheKey; +typedef struct _PangoClutterGlyphCacheTexture PangoClutterGlyphCacheTexture; +typedef struct _PangoClutterGlyphCacheBand PangoClutterGlyphCacheBand; + +struct _PangoClutterGlyphCache +{ + /* Hash table to quickly check whether a particular glyph in a + particular font is already cached */ + GHashTable *hash_table; + + /* List of textures */ + PangoClutterGlyphCacheTexture *textures; + + /* List of horizontal bands of glyphs */ + PangoClutterGlyphCacheBand *bands; + + /* If TRUE all of the textures will be created with automatic mipmap + generation enabled */ + gboolean use_mipmapping; +}; + +struct _PangoClutterGlyphCacheKey +{ + 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 _PangoClutterGlyphCacheTexture +{ + /* 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; + + PangoClutterGlyphCacheTexture *next; +}; + +/* Represents one horizontal band of a texture. Each band contains + glyphs of a similar height */ +struct _PangoClutterGlyphCacheBand +{ + /* 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; + + PangoClutterGlyphCacheBand *next; +}; + +static void +pango_clutter_glyph_cache_value_free (PangoClutterGlyphCacheValue *value) +{ + cogl_texture_unref (value->texture); + g_slice_free (PangoClutterGlyphCacheValue, value); +} + +static void +pango_clutter_glyph_cache_key_free (PangoClutterGlyphCacheKey *key) +{ + g_object_unref (key->font); + g_slice_free (PangoClutterGlyphCacheKey, key); +} + +static guint +pango_clutter_glyph_cache_hash_func (gconstpointer key) +{ + const PangoClutterGlyphCacheKey *cache_key + = (const PangoClutterGlyphCacheKey *) 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 (guint) cache_key->font ^ cache_key->glyph; +} + +static gboolean +pango_clutter_glyph_cache_equal_func (gconstpointer a, + gconstpointer b) +{ + const PangoClutterGlyphCacheKey *key_a + = (const PangoClutterGlyphCacheKey *) a; + const PangoClutterGlyphCacheKey *key_b + = (const PangoClutterGlyphCacheKey *) 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 +pango_clutter_glyph_cache_free_textures (PangoClutterGlyphCacheTexture *node) +{ + PangoClutterGlyphCacheTexture *next; + + while (node) + { + next = node->next; + cogl_texture_unref (node->texture); + g_slice_free (PangoClutterGlyphCacheTexture, node); + node = next; + } +} + +static void +pango_clutter_glyph_cache_free_bands (PangoClutterGlyphCacheBand *node) +{ + PangoClutterGlyphCacheBand *next; + + while (node) + { + next = node->next; + cogl_texture_unref (node->texture); + g_slice_free (PangoClutterGlyphCacheBand, node); + node = next; + } +} + +PangoClutterGlyphCache * +pango_clutter_glyph_cache_new (gboolean use_mipmapping) +{ + PangoClutterGlyphCache *cache; + + cache = g_malloc (sizeof (PangoClutterGlyphCache)); + + cache->hash_table = g_hash_table_new_full + (pango_clutter_glyph_cache_hash_func, + pango_clutter_glyph_cache_equal_func, + (GDestroyNotify) pango_clutter_glyph_cache_key_free, + (GDestroyNotify) pango_clutter_glyph_cache_value_free); + + cache->textures = NULL; + cache->bands = NULL; + cache->use_mipmapping = use_mipmapping; + + return cache; +} + +void +pango_clutter_glyph_cache_clear (PangoClutterGlyphCache *cache) +{ + pango_clutter_glyph_cache_free_textures (cache->textures); + cache->textures = NULL; + pango_clutter_glyph_cache_free_bands (cache->bands); + cache->bands = NULL; + + g_hash_table_remove_all (cache->hash_table); +} + +void +pango_clutter_glyph_cache_free (PangoClutterGlyphCache *cache) +{ + pango_clutter_glyph_cache_clear (cache); + + g_hash_table_unref (cache->hash_table); + + g_free (cache); +} + +PangoClutterGlyphCacheValue * +pango_clutter_glyph_cache_lookup (PangoClutterGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph) +{ + PangoClutterGlyphCacheKey key; + + key.font = font; + key.glyph = glyph; + + return (PangoClutterGlyphCacheValue *) + g_hash_table_lookup (cache->hash_table, &key); +} + +PangoClutterGlyphCacheValue * +pango_clutter_glyph_cache_set (PangoClutterGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph, + gconstpointer pixels, + int width, + int height, + int stride, + int draw_x, + int draw_y) +{ + int band_height; + PangoClutterGlyphCacheBand *band; + PangoClutterGlyphCacheKey *key; + PangoClutterGlyphCacheValue *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) + { + PangoClutterGlyphCacheTexture *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->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 (PangoClutterGlyphCacheTexture); + + texture->texture_size = MIN_TEXTURE_SIZE; + while (texture->texture_size < band_height) + 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, + 32, cache->use_mipmapping, + 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; + + if (cache->use_mipmapping) + cogl_texture_set_filters (texture->texture, + CGL_LINEAR_MIPMAP_LINEAR, + CGL_LINEAR); + else + cogl_texture_set_filters (texture->texture, + CGL_LINEAR, + CGL_LINEAR); + } + + band = g_slice_new (PangoClutterGlyphCacheBand); + band->top = texture->texture_size - texture->space_remaining; + band->height = band_height; + band->space_remaining = texture->texture_size; + band->texture = cogl_texture_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 (PangoClutterGlyphCacheKey); + key->font = g_object_ref (font); + key->glyph = glyph; + + value = g_slice_new (PangoClutterGlyphCacheValue); + value->texture = cogl_texture_ref (band->texture); + value->tx1 = CLUTTER_INT_TO_FIXED (band->space_remaining) + / band->texture_size; + value->tx2 = CLUTTER_INT_TO_FIXED (band->space_remaining + width) + / band->texture_size; + value->ty1 = CLUTTER_INT_TO_FIXED (band->top) / band->texture_size; + value->ty2 = CLUTTER_INT_TO_FIXED (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/clutter/pango/pangoclutter-glyph-cache.h b/clutter/pango/pangoclutter-glyph-cache.h new file mode 100644 index 000000000..f54dfb577 --- /dev/null +++ b/clutter/pango/pangoclutter-glyph-cache.h @@ -0,0 +1,69 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _HAVE_PANGO_CLUTTER_GLYPH_CACHE_H +#define _HAVE_PANGO_CLUTTER_GLYPH_CACHE_H + +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _PangoClutterGlyphCache PangoClutterGlyphCache; +typedef struct _PangoClutterGlyphCacheValue PangoClutterGlyphCacheValue; + +struct _PangoClutterGlyphCacheValue +{ + CoglHandle texture; + ClutterFixed tx1, ty1, tx2, ty2; + int draw_x, draw_y, draw_width, draw_height; +}; + +PangoClutterGlyphCache *pango_clutter_glyph_cache_new (gboolean use_mipmapping); + +void pango_clutter_glyph_cache_free (PangoClutterGlyphCache *cache); + +PangoClutterGlyphCacheValue * +pango_clutter_glyph_cache_lookup (PangoClutterGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph); + +PangoClutterGlyphCacheValue * +pango_clutter_glyph_cache_set (PangoClutterGlyphCache *cache, + PangoFont *font, + PangoGlyph glyph, + gconstpointer pixels, + int width, + int height, + int stride, + int draw_x, + int draw_y); + +void pango_clutter_glyph_cache_clear (PangoClutterGlyphCache *cache); + +G_END_DECLS + +#endif /* _HAVE_PANGO_CLUTTER_GLYPH_CACHE_H */