1 /***************************************************************************/
2 /* */
3 /* ftcmanag.c */
4 /* */
5 /* FreeType Cache Manager (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_MANAGER_H
22 #include FT_INTERNAL_OBJECTS_H
23 #include FT_INTERNAL_DEBUG_H
24 #include FT_LIST_H
25 #include FT_SIZES_H
27 #include "ftcerror.h"
30 #undef FT_COMPONENT
31 #define FT_COMPONENT trace_cache
33 #define FTC_LRU_GET_MANAGER( lru ) (FTC_Manager)lru->user_data
36 /*************************************************************************/
37 /*************************************************************************/
38 /***** *****/
39 /***** FACE & SIZE LRU CALLBACKS *****/
40 /***** *****/
41 /*************************************************************************/
42 /*************************************************************************/
45 FT_CALLBACK_DEF( FT_Error )
46 ftc_manager_init_face( FT_Lru lru,
47 FT_LruNode node )
48 {
49 FTC_Manager manager = FTC_LRU_GET_MANAGER( lru );
50 FT_Error error;
51 FT_Face face;
54 error = manager->request_face( (FTC_FaceID)node->key,
55 manager->library,
56 manager->request_data,
57 (FT_Face*)&node->root.data );
58 if ( !error )
59 {
60 /* destroy initial size object; it will be re-created later */
61 face = (FT_Face)node->root.data;
62 if ( face->size )
63 FT_Done_Size( face->size );
64 }
66 return error;
67 }
70 /* helper function for ftc_manager_done_face() */
71 FT_CALLBACK_DEF( FT_Bool )
72 ftc_manager_size_selector( FT_Lru lru,
73 FT_LruNode node,
74 FT_Pointer data )
75 {
76 FT_UNUSED( lru );
78 return FT_BOOL( ((FT_Size)node->root.data)->face == (FT_Face)data );
79 }
82 FT_CALLBACK_DEF( void )
83 ftc_manager_done_face( FT_Lru lru,
84 FT_LruNode node )
85 {
86 FTC_Manager manager = FTC_LRU_GET_MANAGER( lru );
87 FT_Face face = (FT_Face)node->root.data;
90 /* we must begin by removing all sizes for the target face */
91 /* from the manager's list */
92 FT_Lru_Remove_Selection( manager->sizes_lru,
93 ftc_manager_size_selector,
94 face );
96 /* all right, we can discard the face now */
97 FT_Done_Face( face );
98 node->root.data = 0;
99 }
102 typedef struct FTC_FontRequest_
103 {
104 FT_Face face;
105 FT_UShort width;
106 FT_UShort height;
108 } FTC_FontRequest;
111 FT_CALLBACK_DEF( FT_Error )
112 ftc_manager_init_size( FT_Lru lru,
113 FT_LruNode node )
114 {
115 FTC_FontRequest* font_req = (FTC_FontRequest*)node->key;
116 FT_Size size;
117 FT_Error error;
118 FT_Face face = font_req->face;
120 FT_UNUSED( lru );
123 node->root.data = 0;
124 error = FT_New_Size( face, &size );
125 if ( !error )
126 {
127 FT_Activate_Size( size );
128 error = FT_Set_Pixel_Sizes( face,
129 font_req->width,
130 font_req->height );
131 if ( error )
132 FT_Done_Size( size );
133 else
134 node->root.data = size;
135 }
136 return error;
137 }
140 FT_CALLBACK_DEF( void )
141 ftc_manager_done_size( FT_Lru lru,
142 FT_LruNode node )
143 {
144 FT_UNUSED( lru );
146 FT_Done_Size( (FT_Size)node->root.data );
147 node->root.data = 0;
148 }
151 FT_CALLBACK_DEF( FT_Error )
152 ftc_manager_flush_size( FT_Lru lru,
153 FT_LruNode node,
154 FT_LruKey key )
155 {
156 FTC_FontRequest* req = (FTC_FontRequest*)key;
157 FT_Size size = (FT_Size)node->root.data;
158 FT_Error error;
161 if ( size->face == req->face )
162 {
163 FT_Activate_Size( size );
164 error = FT_Set_Pixel_Sizes( req->face, req->width, req->height );
165 if ( error )
166 FT_Done_Size( size );
167 }
168 else
169 {
170 FT_Done_Size( size );
171 node->key = key;
172 error = ftc_manager_init_size( lru, node );
173 }
174 return error;
175 }
178 FT_CALLBACK_DEF( FT_Bool )
179 ftc_manager_compare_size( FT_LruNode node,
180 FT_LruKey key )
181 {
182 FTC_FontRequest* req = (FTC_FontRequest*)key;
183 FT_Size size = (FT_Size)node->root.data;
185 FT_UNUSED( node );
188 return FT_BOOL( size->face == req->face &&
189 size->metrics.x_ppem == req->width &&
190 size->metrics.y_ppem == req->height );
191 }
194 FT_CALLBACK_TABLE_DEF
195 const FT_Lru_Class ftc_face_lru_class =
196 {
197 sizeof ( FT_LruRec ),
198 ftc_manager_init_face,
199 ftc_manager_done_face,
200 0,
201 0
202 };
205 FT_CALLBACK_TABLE_DEF
206 const FT_Lru_Class ftc_size_lru_class =
207 {
208 sizeof ( FT_LruRec ),
209 ftc_manager_init_size,
210 ftc_manager_done_size,
211 ftc_manager_flush_size,
212 ftc_manager_compare_size
213 };
216 /* documentation is in ftcache.h */
218 FT_EXPORT_DEF( FT_Error )
219 FTC_Manager_New( FT_Library library,
220 FT_UInt max_faces,
221 FT_UInt max_sizes,
222 FT_ULong max_bytes,
223 FTC_Face_Requester requester,
224 FT_Pointer req_data,
225 FTC_Manager *amanager )
226 {
227 FT_Error error;
228 FT_Memory memory;
229 FTC_Manager manager = 0;
232 if ( !library )
233 return FTC_Err_Invalid_Library_Handle;
235 memory = library->memory;
237 if ( ALLOC( manager, sizeof ( *manager ) ) )
238 goto Exit;
240 if ( max_faces == 0 )
241 max_faces = FTC_MAX_FACES_DEFAULT;
243 if ( max_sizes == 0 )
244 max_sizes = FTC_MAX_SIZES_DEFAULT;
246 if ( max_bytes == 0 )
247 max_bytes = FTC_MAX_BYTES_DEFAULT;
249 error = FT_Lru_New( &ftc_face_lru_class,
250 max_faces,
251 manager,
252 memory,
253 1, /* pre_alloc = TRUE */
254 (FT_Lru*)&manager->faces_lru );
255 if ( error )
256 goto Exit;
258 error = FT_Lru_New( &ftc_size_lru_class,
259 max_sizes,
260 manager,
261 memory,
262 1, /* pre_alloc = TRUE */
263 (FT_Lru*)&manager->sizes_lru );
264 if ( error )
265 goto Exit;
267 manager->library = library;
268 manager->max_bytes = max_bytes;
269 manager->request_face = requester;
270 manager->request_data = req_data;
272 *amanager = manager;
274 Exit:
275 if ( error && manager )
276 {
277 FT_Lru_Done( manager->faces_lru );
278 FT_Lru_Done( manager->sizes_lru );
279 FREE( manager );
280 }
282 return error;
283 }
286 /* documentation is in ftcache.h */
288 FT_EXPORT_DEF( void )
289 FTC_Manager_Done( FTC_Manager manager )
290 {
291 FT_Memory memory;
292 FT_UInt index;
295 if ( !manager || !manager->library )
296 return;
298 memory = manager->library->memory;
300 /* now discard all caches */
301 for (index = 0; index < FTC_MAX_CACHES; index++ )
302 {
303 FTC_Cache cache = manager->caches[index];
306 if ( cache )
307 {
308 cache->clazz->done_cache( cache );
309 FREE( cache );
310 manager->caches[index] = 0;
311 }
312 }
314 /* discard faces and sizes */
315 FT_Lru_Done( manager->faces_lru );
316 manager->faces_lru = 0;
318 FT_Lru_Done( manager->sizes_lru );
319 manager->sizes_lru = 0;
321 FREE( manager );
322 }
325 /* documentation is in ftcache.h */
327 FT_EXPORT_DEF( void )
328 FTC_Manager_Reset( FTC_Manager manager )
329 {
330 if (manager )
331 {
332 FT_Lru_Reset( manager->sizes_lru );
333 FT_Lru_Reset( manager->faces_lru );
334 }
335 /* XXX: FIXME: flush the caches? */
336 }
339 /* documentation is in ftcache.h */
341 FT_EXPORT_DEF( FT_Error )
342 FTC_Manager_Lookup_Face( FTC_Manager manager,
343 FTC_FaceID face_id,
344 FT_Face *aface )
345 {
346 if ( !manager )
347 return FTC_Err_Invalid_Cache_Handle;
349 return FT_Lru_Lookup( manager->faces_lru,
350 (FT_LruKey)face_id,
351 (FT_Pointer*)aface );
352 }
355 /* documentation is in ftcache.h */
357 FT_EXPORT_DEF( FT_Error )
358 FTC_Manager_Lookup_Size( FTC_Manager manager,
359 FTC_Font font,
360 FT_Face *aface,
361 FT_Size *asize )
362 {
363 FTC_FontRequest req;
364 FT_Error error;
367 /* check for valid `manager' delayed to FTC_Manager_Lookup_Face() */
369 if ( aface )
370 *aface = 0;
372 if ( asize )
373 *asize = 0;
375 error = FTC_Manager_Lookup_Face( manager, font->face_id, aface );
376 if ( !error )
377 {
378 FT_Size size;
381 req.face = *aface;
382 req.width = font->pix_width;
383 req.height = font->pix_height;
385 error = FT_Lru_Lookup( manager->sizes_lru,
386 (FT_LruKey)&req,
387 (FT_Pointer*)&size );
388 if ( !error )
389 {
390 /* select the size as the current one for this face */
391 (*aface)->size = size;
393 if ( asize )
394 *asize = size;
395 }
396 }
398 return error;
399 }
402 /* `Compress' the manager's data, i.e., get rid of old cache nodes */
403 /* that are not referenced anymore in order to limit the total */
404 /* memory used by the cache. */
406 /* documentation is in ftcmanag.h */
408 FT_EXPORT_DEF( void )
409 FTC_Manager_Compress( FTC_Manager manager )
410 {
411 FT_ListNode node;
414 node = manager->global_lru.tail;
415 while ( manager->num_bytes > manager->max_bytes && node )
416 {
417 FTC_CacheNode cache_node = FTC_LIST_TO_CACHENODE( node );
418 FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( cache_node );
419 FTC_Cache cache;
420 FT_ListNode prev = node->prev;
423 if ( data->ref_count <= 0 )
424 {
425 /* ok, we are going to remove this node */
426 FT_List_Remove( &manager->global_lru, node );
428 /* finalize cache node */
429 cache = manager->caches[data->cache_index];
430 if ( cache )
431 {
432 FTC_CacheNode_Class* clazz = cache->node_clazz;
435 manager->num_bytes -= clazz->size_node( cache_node,
436 cache->cache_data );
438 clazz->destroy_node( cache_node, cache->cache_data );
439 }
440 else
441 {
442 /* this should never happen! */
443 FT_ERROR(( "FTC_Manager_Compress: Cache Manager is corrupted!\n" ));
444 }
446 /* check, just in case of general corruption :-) */
447 if ( manager->num_nodes <= 0 )
448 FT_ERROR(( "FTC_Manager_Compress: Invalid cache node count!\n" ));
449 else
450 manager->num_nodes--;
451 }
452 node = prev;
453 }
454 }
457 FT_EXPORT_DEF( FT_Error )
458 FTC_Manager_Register_Cache( FTC_Manager manager,
459 FTC_Cache_Class* clazz,
460 FTC_Cache *acache )
461 {
462 FT_Error error = FTC_Err_Invalid_Argument;
465 if ( manager && clazz && acache )
466 {
467 FT_Memory memory = manager->library->memory;
468 FTC_Cache cache;
469 FT_UInt index = 0;
472 /* by default, return 0 */
473 *acache = 0;
475 /* check for an empty cache slot in the manager's table */
476 for ( index = 0; index < FTC_MAX_CACHES; index++ )
477 {
478 if ( manager->caches[index] == 0 )
479 break;
480 }
482 /* return an error if there are too many registered caches */
483 if ( index >= FTC_MAX_CACHES )
484 {
485 error = FTC_Err_Too_Many_Caches;
486 FT_ERROR(( "FTC_Manager_Register_Cache:" ));
487 FT_ERROR(( " too many registered caches\n" ));
488 goto Exit;
489 }
491 if ( !ALLOC( cache, clazz->cache_byte_size ) )
492 {
493 cache->manager = manager;
494 cache->memory = memory;
495 cache->clazz = clazz;
497 /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
498 /* IF IT IS NOT SET CORRECTLY */
499 cache->cache_index = index;
501 if ( clazz->init_cache )
502 error = clazz->init_cache( cache );
504 if ( error )
505 FREE( cache );
506 else
507 manager->caches[index] = *acache = cache;
508 }
509 }
511 Exit:
512 return error;
513 }
516 /* END */