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 /***************************************************************************/
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
27 #include "ftcerror.h"
30 /*************************************************************************/
31 /*************************************************************************/
32 /***** *****/
33 /***** GLYPH NODES *****/
34 /***** *****/
35 /*************************************************************************/
36 /*************************************************************************/
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 );
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 }
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! */
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;
72 /* remove the node from its gset's bucket list */
73 {
74 FTC_GlyphNode* pnode = gset->buckets + hash;
75 FTC_GlyphNode cur;
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 }
89 if ( cur == node )
90 {
91 *pnode = cur->gset_next;
92 break;
93 }
94 pnode = &cur->gset_next;
95 }
96 }
98 /* destroy the node */
99 gset->clazz->destroy_node( node, gset );
100 }
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! */
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;
118 return gset->clazz->size_node( node, gset );
119 }
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 };
130 /*************************************************************************/
131 /*************************************************************************/
132 /***** *****/
133 /***** GLYPH SETS *****/
134 /***** *****/
135 /*************************************************************************/
136 /*************************************************************************/
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;
149 FTC_Glyph_Cache_Class* gcache_class;
150 FTC_GlyphSet_Class* clazz;
153 gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz;
154 clazz = gcache_class->gset_class;
156 *aset = 0;
158 if ( ALLOC( gset, clazz->gset_byte_size ) )
159 goto Exit;
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;
167 /* allocate buckets table */
168 if ( ALLOC_ARRAY( gset->buckets, gset->hash_size, FTC_GlyphNode ) )
169 goto Exit;
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 }
179 *aset = gset;
181 Exit:
182 if ( error && gset )
183 {
184 FREE( gset->buckets );
185 FREE( gset );
186 }
188 return error;
189 }
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;
202 FTC_GlyphSet_Class* clazz = gset->clazz;
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;
213 for ( ; node; node = next )
214 {
215 next = node->gset_next;
216 lrunode = FTC_GLYPHNODE_TO_LRUNODE( node );
218 manager->num_bytes -= clazz->size_node( node, gset );
219 manager->num_nodes--;
221 FT_List_Remove( glyphs_lru, lrunode );
223 clazz->destroy_node( node, gset );
224 }
226 bucket[0] = 0;
227 }
229 if ( clazz->done )
230 clazz->done( gset );
232 FREE( gset->buckets );
233 FREE( gset );
234 }
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;
250 FTC_GlyphSet_Class* clazz = gset->clazz;
253 *anode = 0;
255 for ( ;; )
256 {
257 node = *pnode;
258 if ( !node )
259 break;
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;
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 }
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;
281 /* insert the node at the start of our bucket list */
282 node->gset_next = bucket[0];
283 bucket[0] = node;
285 /* insert the node at the start the global LRU glyph list */
286 FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
288 manager->num_bytes += clazz->size_node( node, gset );
289 manager->num_nodes++;
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 }
298 *anode = node;
300 Exit:
301 return error;
302 }
305 /*************************************************************************/
306 /*************************************************************************/
307 /***** *****/
308 /***** GLYPH SETS LRU CALLBACKS *****/
309 /***** *****/
310 /*************************************************************************/
311 /*************************************************************************/
314 #define FTC_GSET_LRU_GET_CACHE( lru ) \
315 ( (FTC_Glyph_Cache)(lru)->user_data )
317 #define FTC_GSET_LRU_GET_MANAGER( lru ) \
318 FTC_GSET_LRU_GET_CACHE( lru )->manager
320 #define FTC_LRUNODE_GSET( node ) \
321 ( (FTC_GlyphSet)(node)->root.data )
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;
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 }
341 return error;
342 }
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 );
351 FT_UNUSED( lru );
354 FTC_GlyphSet_Destroy( gset );
355 }
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 );
365 return gset->clazz->compare( gset, (FT_Pointer)key );
366 }
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 };
380 /*************************************************************************/
381 /*************************************************************************/
382 /***** *****/
383 /***** GLYPH CACHE OBJECTS *****/
384 /***** *****/
385 /*************************************************************************/
386 /*************************************************************************/
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;
395 FTC_Glyph_Cache_Class* gcache_clazz;
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;
402 /* setup the `compare' shortcut */
403 gcache_clazz = (FTC_Glyph_Cache_Class*)cache->root.clazz;
404 cache->compare = gcache_clazz->gset_class->compare;
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;
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 }
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 }
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;
443 /* check for valid `desc' delayed to FT_Lru_Lookup() */
445 if ( !cache || !anode )
446 return FTC_Err_Invalid_Argument;
448 *anode = 0;
449 gset = cache->last_gset;
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 }
461 error = FTC_GlyphSet_Lookup_Node( gset, gindex, &node );
462 if ( error )
463 goto Exit;
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 }
474 *anode = node;
476 Exit:
477 return error;
478 }
481 /* END */