The BIG graph update
[rrdtool.git] / libraries / freetype-2.0.5 / ftcglyph.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftcglyph.c                                                             */
4 /*                                                                         */
5 /*    FreeType Glyph Image (FT_Glyph) cache (body).                        */
6 /*                                                                         */
7 /*  Copyright 2000-2001 by                                                 */
8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
17
18
19 #include <ft2build.h>
20 #include FT_CACHE_H
21 #include FT_CACHE_INTERNAL_GLYPH_H
22 #include FT_ERRORS_H
23 #include FT_LIST_H
24 #include FT_INTERNAL_OBJECTS_H
25 #include FT_INTERNAL_DEBUG_H
26
27 #include "ftcerror.h"
28
29
30   /*************************************************************************/
31   /*************************************************************************/
32   /*****                                                               *****/
33   /*****                      GLYPH NODES                              *****/
34   /*****                                                               *****/
35   /*************************************************************************/
36   /*************************************************************************/
37
38
39   /* create a new glyph node, setting its cache index and ref count */
40   FT_EXPORT_DEF( void )
41   FTC_GlyphNode_Init( FTC_GlyphNode  node,
42                       FTC_GlyphSet   gset,
43                       FT_UInt        gindex )
44   {
45     FTC_Glyph_Cache      cache = gset->cache;
46     FTC_CacheNode_Data*  data  = FTC_CACHENODE_TO_DATA_P( &node->root );
47
48
49     data->cache_index = (FT_UShort)cache->root.cache_index;
50     data->ref_count   = (FT_Short) 0;
51     node->gset_index  = (FT_UShort)gset->gset_index;
52     node->glyph_index = (FT_UShort)gindex;
53   }
54
55
56   /* Important: This function is called from the cache manager to */
57   /* destroy a given cache node during `cache compression'.  The  */
58   /* second argument is always `cache.cache_data'.  Thus be       */
59   /* certain that the function FTC_Glyph_Cache_New() does indeed  */
60   /* set its `cache_data' field correctly, otherwise bad things   */
61   /* will happen!                                                 */
62
63   FT_EXPORT_DEF( void )
64   FTC_GlyphNode_Destroy( FTC_GlyphNode    node,
65                          FTC_Glyph_Cache  cache )
66   {
67     FT_LruNode    gset_lru = cache->gsets_lru->nodes + node->gset_index;
68     FTC_GlyphSet  gset     = (FTC_GlyphSet)gset_lru->root.data;
69     FT_UInt       hash     = node->glyph_index % gset->hash_size;
70
71
72     /* remove the node from its gset's bucket list */
73     {
74       FTC_GlyphNode*  pnode = gset->buckets + hash;
75       FTC_GlyphNode   cur;
76
77
78       for (;;)
79       {
80         cur = *pnode;
81         if ( !cur )
82         {
83           /* this should never happen */
84           FT_ERROR(( "FTC_GlyphNode_Destroy:"
85                      " trying to delete an unlisted node!" ));
86           return;
87         }
88
89         if ( cur == node )
90         {
91           *pnode = cur->gset_next;
92           break;
93         }
94         pnode = &cur->gset_next;
95       }
96     }
97
98     /* destroy the node */
99     gset->clazz->destroy_node( node, gset );
100   }
101
102
103   /* Important: This function is called from the cache manager to */
104   /* size a given cache node during `cache compression'.  The     */
105   /* second argument is always `cache.user_data'.  Thus be        */
106   /* certain that the function FTC_Glyph_Cache_New() does indeed  */
107   /* set its `user_data' field correctly, otherwise bad things    */
108   /* will happen!                                                 */
109
110   FT_EXPORT_DEF( FT_ULong )
111   FTC_GlyphNode_Size( FTC_GlyphNode    node,
112                       FTC_Glyph_Cache  cache )
113   {
114     FT_LruNode    gset_lru = cache->gsets_lru->nodes + node->gset_index;
115     FTC_GlyphSet  gset     = (FTC_GlyphSet)gset_lru->root.data;
116
117
118     return gset->clazz->size_node( node, gset );
119   }
120
121
122   FT_CALLBACK_TABLE_DEF
123   const FTC_CacheNode_Class  ftc_glyph_cache_node_class =
124   {
125     (FTC_CacheNode_SizeFunc)   FTC_GlyphNode_Size,
126     (FTC_CacheNode_DestroyFunc)FTC_GlyphNode_Destroy
127   };
128
129
130   /*************************************************************************/
131   /*************************************************************************/
132   /*****                                                               *****/
133   /*****                      GLYPH SETS                               *****/
134   /*****                                                               *****/
135   /*************************************************************************/
136   /*************************************************************************/
137
138
139   FT_EXPORT_DEF( FT_Error )
140   FTC_GlyphSet_New( FTC_Glyph_Cache  cache,
141                     FT_Pointer       type,
142                     FTC_GlyphSet    *aset )
143   {
144     FT_Error                error;
145     FT_Memory               memory  = cache->root.memory;
146     FTC_Manager             manager = cache->root.manager;
147     FTC_GlyphSet            gset    = 0;
148
149     FTC_Glyph_Cache_Class*  gcache_class;
150     FTC_GlyphSet_Class*     clazz;
151
152
153     gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz;
154     clazz        = gcache_class->gset_class;
155
156     *aset = 0;
157
158     if ( ALLOC( gset, clazz->gset_byte_size ) )
159       goto Exit;
160
161     gset->cache     = cache;
162     gset->manager   = manager;
163     gset->memory    = memory;
164     gset->hash_size = FTC_GSET_HASH_SIZE_DEFAULT;
165     gset->clazz     = clazz;
166
167     /* allocate buckets table */
168     if ( ALLOC_ARRAY( gset->buckets, gset->hash_size, FTC_GlyphNode ) )
169       goto Exit;
170
171     /* initialize gset by type if needed */
172     if ( clazz->init )
173     {
174       error = clazz->init( gset, type );
175       if ( error )
176         goto Exit;
177     }
178
179     *aset = gset;
180
181   Exit:
182     if ( error && gset )
183     {
184       FREE( gset->buckets );
185       FREE( gset );
186     }
187
188     return error;
189   }
190
191
192   FT_EXPORT_DEF( void )
193   FTC_GlyphSet_Destroy( FTC_GlyphSet  gset )
194   {
195     FTC_Glyph_Cache      cache        = gset->cache;
196     FTC_Manager          manager      = cache->root.manager;
197     FT_List              glyphs_lru   = &manager->global_lru;
198     FTC_GlyphNode*       bucket       = gset->buckets;
199     FTC_GlyphNode*       bucket_limit = bucket + gset->hash_size;
200     FT_Memory            memory       = cache->root.memory;
201
202     FTC_GlyphSet_Class*  clazz        = gset->clazz;
203
204
205     /* for each bucket, free the list of glyph nodes */
206     for ( ; bucket < bucket_limit; bucket++ )
207     {
208       FTC_GlyphNode   node = bucket[0];
209       FTC_GlyphNode   next = 0;
210       FT_ListNode     lrunode;
211
212
213       for ( ; node; node = next )
214       {
215         next    = node->gset_next;
216         lrunode = FTC_GLYPHNODE_TO_LRUNODE( node );
217
218         manager->num_bytes -= clazz->size_node( node, gset );
219         manager->num_nodes--;
220
221         FT_List_Remove( glyphs_lru, lrunode );
222
223         clazz->destroy_node( node, gset );
224       }
225
226       bucket[0] = 0;
227     }
228
229     if ( clazz->done )
230       clazz->done( gset );
231
232     FREE( gset->buckets );
233     FREE( gset );
234   }
235
236
237   FT_EXPORT_DEF( FT_Error )
238   FTC_GlyphSet_Lookup_Node( FTC_GlyphSet    gset,
239                             FT_UInt         glyph_index,
240                             FTC_GlyphNode  *anode )
241   {
242     FTC_Glyph_Cache      cache      = gset->cache;
243     FTC_Manager          manager    = cache->root.manager;
244     FT_UInt              hash_index = glyph_index % gset->hash_size;
245     FTC_GlyphNode*       bucket     = gset->buckets + hash_index;
246     FTC_GlyphNode*       pnode      = bucket;
247     FTC_GlyphNode        node;
248     FT_Error             error;
249
250     FTC_GlyphSet_Class*  clazz      = gset->clazz;
251
252
253     *anode = 0;
254
255     for ( ;; )
256     {
257       node = *pnode;
258       if ( !node )
259         break;
260
261       if ( (FT_UInt)node->glyph_index == glyph_index )
262       {
263         /* we found it! -- move glyph to start of the lists */
264         *pnode          = node->gset_next;
265         node->gset_next = bucket[0];
266         bucket[0]       = node;
267
268         FT_List_Up( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
269         *anode = node;
270         return 0;
271       }
272       /* go to next node in bucket */
273       pnode = &node->gset_next;
274     }
275
276     /* we didn't found the glyph image, we will now create a new one */
277     error = clazz->new_node( gset, glyph_index, &node );
278     if ( error )
279       goto Exit;
280
281     /* insert the node at the start of our bucket list */
282     node->gset_next = bucket[0];
283     bucket[0]       = node;
284
285     /* insert the node at the start the global LRU glyph list */
286     FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
287
288     manager->num_bytes += clazz->size_node( node, gset );
289     manager->num_nodes++;
290
291     if ( manager->num_bytes > manager->max_bytes )
292     {
293       FTC_GlyphNode_Ref   ( node );
294       FTC_Manager_Compress( manager );
295       FTC_GlyphNode_Unref ( node );
296     }
297
298     *anode = node;
299
300   Exit:
301     return error;
302   }
303
304
305   /*************************************************************************/
306   /*************************************************************************/
307   /*****                                                               *****/
308   /*****                   GLYPH SETS LRU CALLBACKS                    *****/
309   /*****                                                               *****/
310   /*************************************************************************/
311   /*************************************************************************/
312
313
314 #define FTC_GSET_LRU_GET_CACHE( lru )   \
315           ( (FTC_Glyph_Cache)(lru)->user_data )
316
317 #define FTC_GSET_LRU_GET_MANAGER( lru ) \
318           FTC_GSET_LRU_GET_CACHE( lru )->manager
319
320 #define FTC_LRUNODE_GSET( node )        \
321           ( (FTC_GlyphSet)(node)->root.data )
322
323
324   FT_CALLBACK_DEF( FT_Error )
325   ftc_glyph_set_lru_init( FT_Lru      lru,
326                           FT_LruNode  node )
327   {
328     FTC_Glyph_Cache  cache = FTC_GSET_LRU_GET_CACHE( lru );
329     FT_Error         error;
330     FTC_GlyphSet     gset;
331
332
333     error = FTC_GlyphSet_New( cache, (FT_Pointer)node->key, &gset );
334     if ( !error )
335     {
336       /* good, now set the gset index within the gset object */
337       gset->gset_index = (FT_UInt)( node - lru->nodes );
338       node->root.data  = gset;
339     }
340
341     return error;
342   }
343
344
345   FT_CALLBACK_DEF( void )
346   ftc_glyph_set_lru_done( FT_Lru      lru,
347                           FT_LruNode  node )
348   {
349     FTC_GlyphSet  gset = FTC_LRUNODE_GSET( node );
350
351     FT_UNUSED( lru );
352
353
354     FTC_GlyphSet_Destroy( gset );
355   }
356
357
358   FT_CALLBACK_DEF( FT_Bool )
359   ftc_glyph_set_lru_compare( FT_LruNode  node,
360                              FT_LruKey   key )
361   {
362     FTC_GlyphSet  gset = FTC_LRUNODE_GSET( node );
363
364
365     return gset->clazz->compare( gset, (FT_Pointer)key );
366   }
367
368
369   FT_CALLBACK_TABLE_DEF
370   const FT_Lru_Class  ftc_glyph_set_lru_class =
371   {
372     sizeof( FT_LruRec ),
373     ftc_glyph_set_lru_init,
374     ftc_glyph_set_lru_done,
375     0,  /* no flush */
376     ftc_glyph_set_lru_compare
377   };
378
379
380   /*************************************************************************/
381   /*************************************************************************/
382   /*****                                                               *****/
383   /*****                      GLYPH CACHE OBJECTS                      *****/
384   /*****                                                               *****/
385   /*************************************************************************/
386   /*************************************************************************/
387
388
389   FT_EXPORT_DEF( FT_Error )
390   FTC_Glyph_Cache_Init( FTC_Glyph_Cache  cache )
391   {
392     FT_Memory  memory = cache->root.memory;
393     FT_Error   error;
394
395     FTC_Glyph_Cache_Class*  gcache_clazz;
396
397
398     /* set up root node_class to be used by manager */
399     cache->root.node_clazz =
400       (FTC_CacheNode_Class*)&ftc_glyph_cache_node_class;
401
402     /* setup the `compare' shortcut */
403     gcache_clazz   = (FTC_Glyph_Cache_Class*)cache->root.clazz;
404     cache->compare = gcache_clazz->gset_class->compare;
405
406     /* The following is extremely important for ftc_destroy_glyph_image() */
407     /* to work properly, as the second parameter that is sent to it       */
408     /* through the cache manager is `cache_data' and must be set to       */
409     /* `cache' here.                                                      */
410     /*                                                                    */
411     cache->root.cache_data = cache;
412
413     error = FT_Lru_New( &ftc_glyph_set_lru_class,
414                         FTC_MAX_GLYPH_SETS,
415                         cache,
416                         memory,
417                         1, /* pre_alloc == TRUE */
418                         &cache->gsets_lru );
419     return error;
420   }
421
422
423   FT_EXPORT_DEF( void )
424   FTC_Glyph_Cache_Done( FTC_Glyph_Cache  cache )
425   {
426     /* discard glyph sets */
427     FT_Lru_Done( cache->gsets_lru );
428   }
429
430
431   FT_EXPORT_DEF( FT_Error )
432   FTC_Glyph_Cache_Lookup( FTC_Glyph_Cache  cache,
433                           FT_Pointer       type,
434                           FT_UInt          gindex,
435                           FTC_GlyphNode   *anode )
436   {
437     FT_Error       error;
438     FTC_GlyphSet   gset;
439     FTC_GlyphNode  node;
440     FTC_Manager    manager;
441
442
443     /* check for valid `desc' delayed to FT_Lru_Lookup() */
444
445     if ( !cache || !anode )
446       return FTC_Err_Invalid_Argument;
447
448     *anode = 0;
449     gset   = cache->last_gset;
450
451     if ( !gset || !cache->compare( gset, type ) )
452     {
453       error = FT_Lru_Lookup( cache->gsets_lru,
454                              (FT_LruKey)type,
455                              (FT_Pointer*)&gset );
456       cache->last_gset = gset;
457       if ( error )
458         goto Exit;
459     }
460
461     error = FTC_GlyphSet_Lookup_Node( gset, gindex, &node );
462     if ( error )
463       goto Exit;
464
465     /* now compress the manager's cache pool if needed */
466     manager = cache->root.manager;
467     if ( manager->num_bytes > manager->max_bytes )
468     {
469       FTC_GlyphNode_Ref   ( node );
470       FTC_Manager_Compress( manager );
471       FTC_GlyphNode_Unref ( node );
472     }
473
474     *anode = node;
475
476   Exit:
477     return error;
478   }
479
480
481 /* END */