~ubuntu-branches/ubuntu/vivid/clutter-1.0/vivid-proposed

« back to all changes in this revision

Viewing changes to clutter/cogl/pango/cogl-pango-glyph-cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2010-07-18 17:21:49 UTC
  • mfrom: (1.2.1 upstream) (4.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20100718172149-j6s9u4chocaoykme
Tags: 1.2.12-1
* New upstream release.
* debian/libclutter-1.0-0.symbols,
  debian/rules:
  - Add a symbols file.
* debian/rules,
  debian/source/format:
  - Switch to source format 3.0 (quilt).
* debian/control.in:
  - Standards-Version is 3.9.0, no changes needed.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Clutter.
 
3
 *
 
4
 * An OpenGL based 'interactive canvas' library.
 
5
 *
 
6
 * Authored By Matthew Allum  <mallum@openedhand.com>
 
7
 *
 
8
 * Copyright (C) 2008 OpenedHand
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 *
 
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>.
 
22
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include "config.h"
 
26
#endif
 
27
 
 
28
#include <glib.h>
 
29
 
 
30
#include "cogl-pango-glyph-cache.h"
 
31
#include "cogl-pango-private.h"
 
32
 
 
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
 
38
 
 
39
typedef struct _CoglPangoGlyphCacheKey     CoglPangoGlyphCacheKey;
 
40
typedef struct _CoglPangoGlyphCacheTexture CoglPangoGlyphCacheTexture;
 
41
typedef struct _CoglPangoGlyphCacheBand    CoglPangoGlyphCacheBand;
 
42
 
 
43
struct _CoglPangoGlyphCache
 
44
{
 
45
  /* Hash table to quickly check whether a particular glyph in a
 
46
     particular font is already cached */
 
47
  GHashTable                    *hash_table;
 
48
 
 
49
  /* List of textures */
 
50
  CoglPangoGlyphCacheTexture *textures;
 
51
 
 
52
  /* List of horizontal bands of glyphs */
 
53
  CoglPangoGlyphCacheBand    *bands;
 
54
};
 
55
 
 
56
struct _CoglPangoGlyphCacheKey
 
57
{
 
58
  PangoFont  *font;
 
59
  PangoGlyph  glyph;
 
60
};
 
61
 
 
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
 
66
{
 
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 */
 
70
  int        texture_size;
 
71
 
 
72
  /* The remaining vertical space not taken up by any bands */
 
73
  int        space_remaining;
 
74
 
 
75
  /* The actual texture */
 
76
  CoglHandle texture;
 
77
 
 
78
  CoglPangoGlyphCacheTexture *next;
 
79
};
 
80
 
 
81
/* Represents one horizontal band of a texture. Each band contains
 
82
   glyphs of a similar height */
 
83
struct _CoglPangoGlyphCacheBand
 
84
{
 
85
  /* The y position of the top of the band */
 
86
  int        top;
 
87
 
 
88
  /* The height of the band */
 
89
  int        height;
 
90
 
 
91
  /* The remaining horizontal space not taken up by any glyphs */
 
92
  int        space_remaining;
 
93
 
 
94
  /* The size of the texture. Needed to calculate texture
 
95
     coordinates */
 
96
  int        texture_size;
 
97
 
 
98
  /* The texture containing this band */
 
99
  CoglHandle texture;
 
100
 
 
101
  CoglPangoGlyphCacheBand *next;
 
102
};
 
103
 
 
104
static void
 
105
cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value)
 
106
{
 
107
  cogl_handle_unref (value->texture);
 
108
  g_slice_free (CoglPangoGlyphCacheValue, value);
 
109
}
 
110
 
 
111
static void
 
112
cogl_pango_glyph_cache_key_free (CoglPangoGlyphCacheKey *key)
 
113
{
 
114
  g_object_unref (key->font);
 
115
  g_slice_free (CoglPangoGlyphCacheKey, key);
 
116
}
 
117
 
 
118
static guint
 
119
cogl_pango_glyph_cache_hash_func (gconstpointer key)
 
120
{
 
121
  const CoglPangoGlyphCacheKey *cache_key
 
122
    = (const CoglPangoGlyphCacheKey *) key;
 
123
 
 
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;
 
129
}
 
130
 
 
131
static gboolean
 
132
cogl_pango_glyph_cache_equal_func (gconstpointer a,
 
133
                                      gconstpointer b)
 
134
{
 
135
  const CoglPangoGlyphCacheKey *key_a
 
136
    = (const CoglPangoGlyphCacheKey *) a;
 
137
  const CoglPangoGlyphCacheKey *key_b
 
138
    = (const CoglPangoGlyphCacheKey *) b;
 
139
 
 
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;
 
145
}
 
146
 
 
147
static void
 
148
cogl_pango_glyph_cache_free_textures (CoglPangoGlyphCacheTexture *node)
 
149
{
 
150
  CoglPangoGlyphCacheTexture *next;
 
151
 
 
152
  while (node)
 
153
    {
 
154
      next = node->next;
 
155
      cogl_handle_unref (node->texture);
 
156
      g_slice_free (CoglPangoGlyphCacheTexture, node);
 
157
      node = next;
 
158
    }
 
159
}
 
160
 
 
161
static void
 
162
cogl_pango_glyph_cache_free_bands (CoglPangoGlyphCacheBand *node)
 
163
{
 
164
  CoglPangoGlyphCacheBand *next;
 
165
 
 
166
  while (node)
 
167
    {
 
168
      next = node->next;
 
169
      cogl_handle_unref (node->texture);
 
170
      g_slice_free (CoglPangoGlyphCacheBand, node);
 
171
      node = next;
 
172
    }
 
173
}
 
174
 
 
175
CoglPangoGlyphCache *
 
176
cogl_pango_glyph_cache_new (void)
 
177
{
 
178
  CoglPangoGlyphCache *cache;
 
179
 
 
180
  cache = g_malloc (sizeof (CoglPangoGlyphCache));
 
181
 
 
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);
 
187
 
 
188
  cache->textures = NULL;
 
189
  cache->bands = NULL;
 
190
 
 
191
  return cache;
 
192
}
 
193
 
 
194
void
 
195
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
 
196
{
 
197
  cogl_pango_glyph_cache_free_textures (cache->textures);
 
198
  cache->textures = NULL;
 
199
  cogl_pango_glyph_cache_free_bands (cache->bands);
 
200
  cache->bands = NULL;
 
201
 
 
202
  g_hash_table_remove_all (cache->hash_table);
 
203
}
 
204
 
 
205
void
 
206
cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
 
207
{
 
208
  cogl_pango_glyph_cache_clear (cache);
 
209
 
 
210
  g_hash_table_unref (cache->hash_table);
 
211
 
 
212
  g_free (cache);
 
213
}
 
214
 
 
215
CoglPangoGlyphCacheValue *
 
216
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
 
217
                                  PangoFont              *font,
 
218
                                  PangoGlyph              glyph)
 
219
{
 
220
  CoglPangoGlyphCacheKey key;
 
221
 
 
222
  key.font = font;
 
223
  key.glyph = glyph;
 
224
 
 
225
  return (CoglPangoGlyphCacheValue *)
 
226
    g_hash_table_lookup (cache->hash_table, &key);
 
227
}
 
228
 
 
229
CoglPangoGlyphCacheValue *
 
230
cogl_pango_glyph_cache_set (CoglPangoGlyphCache *cache,
 
231
                            PangoFont           *font,
 
232
                            PangoGlyph           glyph,
 
233
                            gconstpointer        pixels,
 
234
                            int                  width,
 
235
                            int                  height,
 
236
                            int                  stride,
 
237
                            int                  draw_x,
 
238
                            int                  draw_y)
 
239
{
 
240
  int                       band_height;
 
241
  CoglPangoGlyphCacheBand  *band;
 
242
  CoglPangoGlyphCacheKey   *key;
 
243
  CoglPangoGlyphCacheValue *value;
 
244
 
 
245
  /* Reserve an extra pixel gap around the glyph so that it can pull
 
246
     in blank pixels when linear filtering is enabled */
 
247
  width++;
 
248
  height++;
 
249
 
 
250
  /* Round the height up to the nearest multiple of
 
251
     BAND_HEIGHT_ROUND */
 
252
  band_height = (height + BAND_HEIGHT_ROUND - 1) & ~(BAND_HEIGHT_ROUND - 1);
 
253
 
 
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);
 
257
       band = band->next);
 
258
  if (band == NULL)
 
259
    {
 
260
      CoglPangoGlyphCacheTexture *texture;
 
261
 
 
262
      /* Look for a texture with enough vertical space left for a band
 
263
         with this height */
 
264
      for (texture = cache->textures;
 
265
           texture && (texture->space_remaining < band_height
 
266
                       || texture->texture_size < width);
 
267
           texture = texture->next);
 
268
      if (texture == NULL)
 
269
        {
 
270
          guchar *clear_data;
 
271
 
 
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);
 
276
 
 
277
          texture->texture_size = MIN_TEXTURE_SIZE;
 
278
          while (texture->texture_size < band_height ||
 
279
                 texture->texture_size < width)
 
280
            {
 
281
              texture->texture_size *= 2;
 
282
            }
 
283
 
 
284
          /* Allocate an empty buffer to clear the texture */
 
285
          clear_data =
 
286
            g_malloc0 (texture->texture_size * texture->texture_size);
 
287
 
 
288
          texture->texture =
 
289
            cogl_texture_new_from_data (texture->texture_size,
 
290
                                        texture->texture_size,
 
291
                                        COGL_TEXTURE_NONE,
 
292
                                        COGL_PIXEL_FORMAT_A_8,
 
293
                                        COGL_PIXEL_FORMAT_A_8,
 
294
                                        texture->texture_size,
 
295
                                        clear_data);
 
296
 
 
297
          g_free (clear_data);
 
298
 
 
299
          texture->space_remaining = texture->texture_size;
 
300
          texture->next = cache->textures;
 
301
          cache->textures = texture;
 
302
        }
 
303
 
 
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;
 
311
      cache->bands = band;
 
312
      texture->space_remaining -= band_height;
 
313
    }
 
314
 
 
315
  band->space_remaining -= width;
 
316
 
 
317
  width--;
 
318
  height--;
 
319
 
 
320
  cogl_texture_set_region (band->texture,
 
321
                           0, 0,
 
322
                           band->space_remaining,
 
323
                           band->top,
 
324
                           width, height,
 
325
                           width, height,
 
326
                           COGL_PIXEL_FORMAT_A_8,
 
327
                           stride,
 
328
                           pixels);
 
329
 
 
330
  key = g_slice_new (CoglPangoGlyphCacheKey);
 
331
  key->font = g_object_ref (font);
 
332
  key->glyph = glyph;
 
333
 
 
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;
 
348
 
 
349
  g_hash_table_insert (cache->hash_table, key, value);
 
350
 
 
351
  return value;
 
352
}