4
* An OpenGL based 'interactive canvas' library.
6
* Authored By Matthew Allum <mallum@openedhand.com>
8
* Copyright (C) 2008 OpenedHand
10
* This library is free software; you can redistribute it and/or
11
* modify it under the terms of the GNU Lesser General Public
12
* License as published by the Free Software Foundation; either
13
* version 2 of the License, or (at your option) any later version.
15
* This library is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* Lesser General Public License for more details.
20
* You should have received a copy of the GNU Lesser General Public
21
* License along with this library. If not, see <http://www.gnu.org/licenses>.
30
#include "cogl-pango-glyph-cache.h"
31
#include "cogl-pango-private.h"
33
/* Minimum width/height for each texture */
34
#define MIN_TEXTURE_SIZE 256
35
/* All glyph with heights within this margin from each other can be
36
put in the same band */
37
#define BAND_HEIGHT_ROUND 4
39
typedef struct _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey;
40
typedef struct _CoglPangoGlyphCacheTexture CoglPangoGlyphCacheTexture;
41
typedef struct _CoglPangoGlyphCacheBand CoglPangoGlyphCacheBand;
43
struct _CoglPangoGlyphCache
45
/* Hash table to quickly check whether a particular glyph in a
46
particular font is already cached */
47
GHashTable *hash_table;
49
/* List of textures */
50
CoglPangoGlyphCacheTexture *textures;
52
/* List of horizontal bands of glyphs */
53
CoglPangoGlyphCacheBand *bands;
56
struct _CoglPangoGlyphCacheKey
62
/* Represents one texture that will be used to store glyphs. The
63
texture is divided into horizontal bands which all contain glyphs
64
of approximatly the same height */
65
struct _CoglPangoGlyphCacheTexture
67
/* The width and height of the texture which should always be a
68
power of two. This can vary so that glyphs larger than
69
MIN_TEXTURE_SIZE can use a bigger texture */
72
/* The remaining vertical space not taken up by any bands */
75
/* The actual texture */
78
CoglPangoGlyphCacheTexture *next;
81
/* Represents one horizontal band of a texture. Each band contains
82
glyphs of a similar height */
83
struct _CoglPangoGlyphCacheBand
85
/* The y position of the top of the band */
88
/* The height of the band */
91
/* The remaining horizontal space not taken up by any glyphs */
94
/* The size of the texture. Needed to calculate texture
98
/* The texture containing this band */
101
CoglPangoGlyphCacheBand *next;
105
cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value)
107
cogl_handle_unref (value->texture);
108
g_slice_free (CoglPangoGlyphCacheValue, value);
112
cogl_pango_glyph_cache_key_free (CoglPangoGlyphCacheKey *key)
114
g_object_unref (key->font);
115
g_slice_free (CoglPangoGlyphCacheKey, key);
119
cogl_pango_glyph_cache_hash_func (gconstpointer key)
121
const CoglPangoGlyphCacheKey *cache_key
122
= (const CoglPangoGlyphCacheKey *) key;
124
/* Generate a number affected by both the font and the glyph
125
number. We can safely directly compare the pointers because the
126
key holds a reference to the font so it is not possible that a
127
different font will have the same memory address */
128
return GPOINTER_TO_UINT (cache_key->font) ^ cache_key->glyph;
132
cogl_pango_glyph_cache_equal_func (gconstpointer a,
135
const CoglPangoGlyphCacheKey *key_a
136
= (const CoglPangoGlyphCacheKey *) a;
137
const CoglPangoGlyphCacheKey *key_b
138
= (const CoglPangoGlyphCacheKey *) b;
140
/* We can safely directly compare the pointers for the fonts because
141
the key holds a reference to the font so it is not possible that
142
a different font will have the same memory address */
143
return key_a->font == key_b->font
144
&& key_a->glyph == key_b->glyph;
148
cogl_pango_glyph_cache_free_textures (CoglPangoGlyphCacheTexture *node)
150
CoglPangoGlyphCacheTexture *next;
155
cogl_handle_unref (node->texture);
156
g_slice_free (CoglPangoGlyphCacheTexture, node);
162
cogl_pango_glyph_cache_free_bands (CoglPangoGlyphCacheBand *node)
164
CoglPangoGlyphCacheBand *next;
169
cogl_handle_unref (node->texture);
170
g_slice_free (CoglPangoGlyphCacheBand, node);
175
CoglPangoGlyphCache *
176
cogl_pango_glyph_cache_new (void)
178
CoglPangoGlyphCache *cache;
180
cache = g_malloc (sizeof (CoglPangoGlyphCache));
182
cache->hash_table = g_hash_table_new_full
183
(cogl_pango_glyph_cache_hash_func,
184
cogl_pango_glyph_cache_equal_func,
185
(GDestroyNotify) cogl_pango_glyph_cache_key_free,
186
(GDestroyNotify) cogl_pango_glyph_cache_value_free);
188
cache->textures = NULL;
195
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
197
cogl_pango_glyph_cache_free_textures (cache->textures);
198
cache->textures = NULL;
199
cogl_pango_glyph_cache_free_bands (cache->bands);
202
g_hash_table_remove_all (cache->hash_table);
206
cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
208
cogl_pango_glyph_cache_clear (cache);
210
g_hash_table_unref (cache->hash_table);
215
CoglPangoGlyphCacheValue *
216
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
220
CoglPangoGlyphCacheKey key;
225
return (CoglPangoGlyphCacheValue *)
226
g_hash_table_lookup (cache->hash_table, &key);
229
CoglPangoGlyphCacheValue *
230
cogl_pango_glyph_cache_set (CoglPangoGlyphCache *cache,
233
gconstpointer pixels,
241
CoglPangoGlyphCacheBand *band;
242
CoglPangoGlyphCacheKey *key;
243
CoglPangoGlyphCacheValue *value;
245
/* Reserve an extra pixel gap around the glyph so that it can pull
246
in blank pixels when linear filtering is enabled */
250
/* Round the height up to the nearest multiple of
252
band_height = (height + BAND_HEIGHT_ROUND - 1) & ~(BAND_HEIGHT_ROUND - 1);
254
/* Look for a band with the same height and enough width available */
255
for (band = cache->bands;
256
band && (band->height != band_height || band->space_remaining < width);
260
CoglPangoGlyphCacheTexture *texture;
262
/* Look for a texture with enough vertical space left for a band
264
for (texture = cache->textures;
265
texture && (texture->space_remaining < band_height
266
|| texture->texture_size < width);
267
texture = texture->next);
272
/* Allocate a new texture that is the nearest power of two
273
greater than the band height or the minimum size,
274
whichever is lower */
275
texture = g_slice_new (CoglPangoGlyphCacheTexture);
277
texture->texture_size = MIN_TEXTURE_SIZE;
278
while (texture->texture_size < band_height ||
279
texture->texture_size < width)
281
texture->texture_size *= 2;
284
/* Allocate an empty buffer to clear the texture */
286
g_malloc0 (texture->texture_size * texture->texture_size);
289
cogl_texture_new_from_data (texture->texture_size,
290
texture->texture_size,
292
COGL_PIXEL_FORMAT_A_8,
293
COGL_PIXEL_FORMAT_A_8,
294
texture->texture_size,
299
texture->space_remaining = texture->texture_size;
300
texture->next = cache->textures;
301
cache->textures = texture;
304
band = g_slice_new (CoglPangoGlyphCacheBand);
305
band->top = texture->texture_size - texture->space_remaining;
306
band->height = band_height;
307
band->space_remaining = texture->texture_size;
308
band->texture = cogl_handle_ref (texture->texture);
309
band->texture_size = texture->texture_size;
310
band->next = cache->bands;
312
texture->space_remaining -= band_height;
315
band->space_remaining -= width;
320
cogl_texture_set_region (band->texture,
322
band->space_remaining,
326
COGL_PIXEL_FORMAT_A_8,
330
key = g_slice_new (CoglPangoGlyphCacheKey);
331
key->font = g_object_ref (font);
334
value = g_slice_new (CoglPangoGlyphCacheValue);
335
value->texture = cogl_handle_ref (band->texture);
336
value->tx1 = (float)(band->space_remaining)
337
/ band->texture_size;
338
value->tx2 = (float)(band->space_remaining + width)
339
/ band->texture_size;
340
value->ty1 = (float)(band->top)
341
/ band->texture_size;
342
value->ty2 = (float)(band->top + height)
343
/ band->texture_size;
344
value->draw_x = draw_x;
345
value->draw_y = draw_y;
346
value->draw_width = width;
347
value->draw_height = height;
349
g_hash_table_insert (cache->hash_table, key, value);