Create a branch for header work.
[reactos.git] / lib / 3rdparty / freetype / src / cache / ftcmanag.c
1 /***************************************************************************/
2 /* */
3 /* ftcmanag.c */
4 /* */
5 /* FreeType Cache Manager (body). */
6 /* */
7 /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006 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 "ftcmanag.h"
22 #include FT_INTERNAL_OBJECTS_H
23 #include FT_INTERNAL_DEBUG_H
24 #include FT_SIZES_H
25
26 #include "ftccback.h"
27 #include "ftcerror.h"
28
29
30 #undef FT_COMPONENT
31 #define FT_COMPONENT trace_cache
32
33 #define FTC_LRU_GET_MANAGER( lru ) ( (FTC_Manager)(lru)->user_data )
34
35
36 static FT_Error
37 ftc_scaler_lookup_size( FTC_Manager manager,
38 FTC_Scaler scaler,
39 FT_Size *asize )
40 {
41 FT_Face face;
42 FT_Size size = NULL;
43 FT_Error error;
44
45
46 error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
47 if ( error )
48 goto Exit;
49
50 error = FT_New_Size( face, &size );
51 if ( error )
52 goto Exit;
53
54 FT_Activate_Size( size );
55
56 if ( scaler->pixel )
57 error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
58 else
59 error = FT_Set_Char_Size( face, scaler->width, scaler->height,
60 scaler->x_res, scaler->y_res );
61 if ( error )
62 {
63 FT_Done_Size( size );
64 size = NULL;
65 }
66
67 Exit:
68 *asize = size;
69 return error;
70 }
71
72
73 typedef struct FTC_SizeNodeRec_
74 {
75 FTC_MruNodeRec node;
76 FT_Size size;
77 FTC_ScalerRec scaler;
78
79 } FTC_SizeNodeRec, *FTC_SizeNode;
80
81
82 FT_CALLBACK_DEF( void )
83 ftc_size_node_done( FTC_MruNode ftcnode,
84 FT_Pointer data )
85 {
86 FTC_SizeNode node = (FTC_SizeNode)ftcnode;
87 FT_Size size = node->size;
88 FT_UNUSED( data );
89
90
91 if ( size )
92 FT_Done_Size( size );
93 }
94
95
96 FT_CALLBACK_DEF( FT_Bool )
97 ftc_size_node_compare( FTC_MruNode ftcnode,
98 FT_Pointer ftcscaler )
99 {
100 FTC_SizeNode node = (FTC_SizeNode)ftcnode;
101 FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
102 FTC_Scaler scaler0 = &node->scaler;
103
104
105 if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
106 {
107 FT_Activate_Size( node->size );
108 return 1;
109 }
110 return 0;
111 }
112
113
114 FT_CALLBACK_DEF( FT_Error )
115 ftc_size_node_init( FTC_MruNode ftcnode,
116 FT_Pointer ftcscaler,
117 FT_Pointer ftcmanager )
118 {
119 FTC_SizeNode node = (FTC_SizeNode)ftcnode;
120 FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
121 FTC_Manager manager = (FTC_Manager)ftcmanager;
122
123
124 node->scaler = scaler[0];
125
126 return ftc_scaler_lookup_size( manager, scaler, &node->size );
127 }
128
129
130 FT_CALLBACK_DEF( FT_Error )
131 ftc_size_node_reset( FTC_MruNode ftcnode,
132 FT_Pointer ftcscaler,
133 FT_Pointer ftcmanager )
134 {
135 FTC_SizeNode node = (FTC_SizeNode)ftcnode;
136 FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
137 FTC_Manager manager = (FTC_Manager)ftcmanager;
138
139
140 FT_Done_Size( node->size );
141
142 node->scaler = scaler[0];
143
144 return ftc_scaler_lookup_size( manager, scaler, &node->size );
145 }
146
147
148 FT_CALLBACK_TABLE_DEF
149 const FTC_MruListClassRec ftc_size_list_class =
150 {
151 sizeof ( FTC_SizeNodeRec ),
152 ftc_size_node_compare,
153 ftc_size_node_init,
154 ftc_size_node_reset,
155 ftc_size_node_done
156 };
157
158
159 /* helper function used by ftc_face_node_done */
160 static FT_Bool
161 ftc_size_node_compare_faceid( FTC_MruNode ftcnode,
162 FT_Pointer ftcface_id )
163 {
164 FTC_SizeNode node = (FTC_SizeNode)ftcnode;
165 FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
166
167
168 return FT_BOOL( node->scaler.face_id == face_id );
169 }
170
171
172 /* documentation is in ftcache.h */
173
174 FT_EXPORT_DEF( FT_Error )
175 FTC_Manager_LookupSize( FTC_Manager manager,
176 FTC_Scaler scaler,
177 FT_Size *asize )
178 {
179 FT_Error error;
180 FTC_SizeNode node;
181
182
183 if ( asize == NULL )
184 return FTC_Err_Bad_Argument;
185
186 *asize = NULL;
187
188 if ( !manager )
189 return FTC_Err_Invalid_Cache_Handle;
190
191 #ifdef FTC_INLINE
192
193 FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
194 node, error );
195
196 #else
197 error = FTC_MruList_Lookup( &manager->sizes, scaler, (FTC_MruNode*)&node );
198 #endif
199
200 if ( !error )
201 *asize = node->size;
202
203 return error;
204 }
205
206
207 /*************************************************************************/
208 /*************************************************************************/
209 /***** *****/
210 /***** FACE MRU IMPLEMENTATION *****/
211 /***** *****/
212 /*************************************************************************/
213 /*************************************************************************/
214
215 typedef struct FTC_FaceNodeRec_
216 {
217 FTC_MruNodeRec node;
218 FTC_FaceID face_id;
219 FT_Face face;
220
221 } FTC_FaceNodeRec, *FTC_FaceNode;
222
223
224 FT_CALLBACK_DEF( FT_Error )
225 ftc_face_node_init( FTC_MruNode ftcnode,
226 FT_Pointer ftcface_id,
227 FT_Pointer ftcmanager )
228 {
229 FTC_FaceNode node = (FTC_FaceNode)ftcnode;
230 FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
231 FTC_Manager manager = (FTC_Manager)ftcmanager;
232 FT_Error error;
233
234
235 node->face_id = face_id;
236
237 error = manager->request_face( face_id,
238 manager->library,
239 manager->request_data,
240 &node->face );
241 if ( !error )
242 {
243 /* destroy initial size object; it will be re-created later */
244 if ( node->face->size )
245 FT_Done_Size( node->face->size );
246 }
247
248 return error;
249 }
250
251
252 FT_CALLBACK_DEF( void )
253 ftc_face_node_done( FTC_MruNode ftcnode,
254 FT_Pointer ftcmanager )
255 {
256 FTC_FaceNode node = (FTC_FaceNode)ftcnode;
257 FTC_Manager manager = (FTC_Manager)ftcmanager;
258
259
260 /* we must begin by removing all scalers for the target face */
261 /* from the manager's list */
262 FTC_MruList_RemoveSelection( &manager->sizes,
263 ftc_size_node_compare_faceid,
264 node->face_id );
265
266 /* all right, we can discard the face now */
267 FT_Done_Face( node->face );
268 node->face = NULL;
269 node->face_id = NULL;
270 }
271
272
273 FT_CALLBACK_DEF( FT_Bool )
274 ftc_face_node_compare( FTC_MruNode ftcnode,
275 FT_Pointer ftcface_id )
276 {
277 FTC_FaceNode node = (FTC_FaceNode)ftcnode;
278 FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
279
280
281 return FT_BOOL( node->face_id == face_id );
282 }
283
284
285 FT_CALLBACK_TABLE_DEF
286 const FTC_MruListClassRec ftc_face_list_class =
287 {
288 sizeof ( FTC_FaceNodeRec),
289
290 ftc_face_node_compare,
291 ftc_face_node_init,
292 0, /* FTC_MruNode_ResetFunc */
293 ftc_face_node_done
294 };
295
296
297 /* documentation is in ftcache.h */
298
299 FT_EXPORT_DEF( FT_Error )
300 FTC_Manager_LookupFace( FTC_Manager manager,
301 FTC_FaceID face_id,
302 FT_Face *aface )
303 {
304 FT_Error error;
305 FTC_FaceNode node;
306
307
308 if ( aface == NULL )
309 return FTC_Err_Bad_Argument;
310
311 *aface = NULL;
312
313 if ( !manager )
314 return FTC_Err_Invalid_Cache_Handle;
315
316 /* we break encapsulation for the sake of speed */
317 #ifdef FTC_INLINE
318
319 FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
320 node, error );
321
322 #else
323 error = FTC_MruList_Lookup( &manager->faces, face_id, (FTC_MruNode*)&node );
324 #endif
325
326 if ( !error )
327 *aface = node->face;
328
329 return error;
330 }
331
332
333 /*************************************************************************/
334 /*************************************************************************/
335 /***** *****/
336 /***** CACHE MANAGER ROUTINES *****/
337 /***** *****/
338 /*************************************************************************/
339 /*************************************************************************/
340
341
342 /* documentation is in ftcache.h */
343
344 FT_EXPORT_DEF( FT_Error )
345 FTC_Manager_New( FT_Library library,
346 FT_UInt max_faces,
347 FT_UInt max_sizes,
348 FT_ULong max_bytes,
349 FTC_Face_Requester requester,
350 FT_Pointer req_data,
351 FTC_Manager *amanager )
352 {
353 FT_Error error;
354 FT_Memory memory;
355 FTC_Manager manager = 0;
356
357
358 if ( !library )
359 return FTC_Err_Invalid_Library_Handle;
360
361 memory = library->memory;
362
363 if ( FT_NEW( manager ) )
364 goto Exit;
365
366 if ( max_faces == 0 )
367 max_faces = FTC_MAX_FACES_DEFAULT;
368
369 if ( max_sizes == 0 )
370 max_sizes = FTC_MAX_SIZES_DEFAULT;
371
372 if ( max_bytes == 0 )
373 max_bytes = FTC_MAX_BYTES_DEFAULT;
374
375 manager->library = library;
376 manager->memory = memory;
377 manager->max_weight = max_bytes;
378
379 manager->request_face = requester;
380 manager->request_data = req_data;
381
382 FTC_MruList_Init( &manager->faces,
383 &ftc_face_list_class,
384 max_faces,
385 manager,
386 memory );
387
388 FTC_MruList_Init( &manager->sizes,
389 &ftc_size_list_class,
390 max_sizes,
391 manager,
392 memory );
393
394 *amanager = manager;
395
396 Exit:
397 return error;
398 }
399
400
401 /* documentation is in ftcache.h */
402
403 FT_EXPORT_DEF( void )
404 FTC_Manager_Done( FTC_Manager manager )
405 {
406 FT_Memory memory;
407 FT_UInt idx;
408
409
410 if ( !manager || !manager->library )
411 return;
412
413 memory = manager->memory;
414
415 /* now discard all caches */
416 for (idx = manager->num_caches; idx-- > 0; )
417 {
418 FTC_Cache cache = manager->caches[idx];
419
420
421 if ( cache )
422 {
423 cache->clazz.cache_done( cache );
424 FT_FREE( cache );
425 manager->caches[idx] = NULL;
426 }
427 }
428 manager->num_caches = 0;
429
430 /* discard faces and sizes */
431 FTC_MruList_Done( &manager->sizes );
432 FTC_MruList_Done( &manager->faces );
433
434 manager->library = NULL;
435 manager->memory = NULL;
436
437 FT_FREE( manager );
438 }
439
440
441 /* documentation is in ftcache.h */
442
443 FT_EXPORT_DEF( void )
444 FTC_Manager_Reset( FTC_Manager manager )
445 {
446 if ( manager )
447 {
448 FTC_MruList_Reset( &manager->sizes );
449 FTC_MruList_Reset( &manager->faces );
450 }
451 /* XXX: FIXME: flush the caches? */
452 }
453
454
455 #ifdef FT_DEBUG_ERROR
456
457 static void
458 FTC_Manager_Check( FTC_Manager manager )
459 {
460 FTC_Node node, first;
461
462
463 first = manager->nodes_list;
464
465 /* check node weights */
466 if ( first )
467 {
468 FT_ULong weight = 0;
469
470
471 node = first;
472
473 do
474 {
475 FTC_Cache cache = manager->caches[node->cache_index];
476
477
478 if ( (FT_UInt)node->cache_index >= manager->num_caches )
479 FT_ERROR(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
480 node->cache_index ));
481 else
482 weight += cache->clazz.node_weight( node, cache );
483
484 node = FTC_NODE__NEXT( node );
485
486 } while ( node != first );
487
488 if ( weight != manager->cur_weight )
489 FT_ERROR(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
490 manager->cur_weight, weight ));
491 }
492
493 /* check circular list */
494 if ( first )
495 {
496 FT_UFast count = 0;
497
498
499 node = first;
500 do
501 {
502 count++;
503 node = FTC_NODE__NEXT( node );
504
505 } while ( node != first );
506
507 if ( count != manager->num_nodes )
508 FT_ERROR((
509 "FTC_Manager_Check: invalid cache node count %d instead of %d\n",
510 manager->num_nodes, count ));
511 }
512 }
513
514 #endif /* FT_DEBUG_ERROR */
515
516
517 /* `Compress' the manager's data, i.e., get rid of old cache nodes */
518 /* that are not referenced anymore in order to limit the total */
519 /* memory used by the cache. */
520
521 /* documentation is in ftcmanag.h */
522
523 FT_LOCAL_DEF( void )
524 FTC_Manager_Compress( FTC_Manager manager )
525 {
526 FTC_Node node, first;
527
528
529 if ( !manager )
530 return;
531
532 first = manager->nodes_list;
533
534 #ifdef FT_DEBUG_ERROR
535 FTC_Manager_Check( manager );
536
537 FT_ERROR(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
538 manager->cur_weight, manager->max_weight,
539 manager->num_nodes ));
540 #endif
541
542 if ( manager->cur_weight < manager->max_weight || first == NULL )
543 return;
544
545 /* go to last node -- it's a circular list */
546 node = FTC_NODE__PREV( first );
547 do
548 {
549 FTC_Node prev;
550
551
552 prev = ( node == first ) ? NULL : FTC_NODE__PREV( node );
553
554 if ( node->ref_count <= 0 )
555 ftc_node_destroy( node, manager );
556
557 node = prev;
558
559 } while ( node && manager->cur_weight > manager->max_weight );
560 }
561
562
563 /* documentation is in ftcmanag.h */
564
565 FT_LOCAL_DEF( FT_Error )
566 FTC_Manager_RegisterCache( FTC_Manager manager,
567 FTC_CacheClass clazz,
568 FTC_Cache *acache )
569 {
570 FT_Error error = FTC_Err_Invalid_Argument;
571 FTC_Cache cache = NULL;
572
573
574 if ( manager && clazz && acache )
575 {
576 FT_Memory memory = manager->memory;
577
578
579 if ( manager->num_caches >= FTC_MAX_CACHES )
580 {
581 error = FTC_Err_Too_Many_Caches;
582 FT_ERROR(( "%s: too many registered caches\n",
583 "FTC_Manager_Register_Cache" ));
584 goto Exit;
585 }
586
587 if ( !FT_ALLOC( cache, clazz->cache_size ) )
588 {
589 cache->manager = manager;
590 cache->memory = memory;
591 cache->clazz = clazz[0];
592 cache->org_class = clazz;
593
594 /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
595 /* IF IT IS NOT SET CORRECTLY */
596 cache->index = manager->num_caches;
597
598 error = clazz->cache_init( cache );
599 if ( error )
600 {
601 clazz->cache_done( cache );
602 FT_FREE( cache );
603 goto Exit;
604 }
605
606 manager->caches[manager->num_caches++] = cache;
607 }
608 }
609
610 Exit:
611 *acache = cache;
612 return error;
613 }
614
615
616 FT_LOCAL_DEF( FT_UInt )
617 FTC_Manager_FlushN( FTC_Manager manager,
618 FT_UInt count )
619 {
620 FTC_Node first = manager->nodes_list;
621 FTC_Node node;
622 FT_UInt result;
623
624
625 /* try to remove `count' nodes from the list */
626 if ( first == NULL ) /* empty list! */
627 return 0;
628
629 /* go to last node - it's a circular list */
630 node = FTC_NODE__PREV(first);
631 for ( result = 0; result < count; )
632 {
633 FTC_Node prev = FTC_NODE__PREV( node );
634
635
636 /* don't touch locked nodes */
637 if ( node->ref_count <= 0 )
638 {
639 ftc_node_destroy( node, manager );
640 result++;
641 }
642
643 if ( node == first )
644 break;
645
646 node = prev;
647 }
648 return result;
649 }
650
651
652 /* documentation is in ftcache.h */
653
654 FT_EXPORT_DEF( void )
655 FTC_Manager_RemoveFaceID( FTC_Manager manager,
656 FTC_FaceID face_id )
657 {
658 FT_UInt nn;
659
660 /* this will remove all FTC_SizeNode that correspond to
661 * the face_id as well
662 */
663 FTC_MruList_RemoveSelection( &manager->faces, NULL, face_id );
664
665 for ( nn = 0; nn < manager->num_caches; nn++ )
666 FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
667 }
668
669
670 /* documentation is in ftcache.h */
671
672 FT_EXPORT_DEF( void )
673 FTC_Node_Unref( FTC_Node node,
674 FTC_Manager manager )
675 {
676 if ( node && (FT_UInt)node->cache_index < manager->num_caches )
677 node->ref_count--;
678 }
679
680
681 #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
682
683 FT_EXPORT_DEF( FT_Error )
684 FTC_Manager_Lookup_Face( FTC_Manager manager,
685 FTC_FaceID face_id,
686 FT_Face *aface )
687 {
688 return FTC_Manager_LookupFace( manager, face_id, aface );
689 }
690
691
692 FT_EXPORT( FT_Error )
693 FTC_Manager_Lookup_Size( FTC_Manager manager,
694 FTC_Font font,
695 FT_Face *aface,
696 FT_Size *asize )
697 {
698 FTC_ScalerRec scaler;
699 FT_Error error;
700 FT_Size size;
701 FT_Face face;
702
703
704 scaler.face_id = font->face_id;
705 scaler.width = font->pix_width;
706 scaler.height = font->pix_height;
707 scaler.pixel = TRUE;
708 scaler.x_res = 0;
709 scaler.y_res = 0;
710
711 error = FTC_Manager_LookupSize( manager, &scaler, &size );
712 if ( error )
713 {
714 face = NULL;
715 size = NULL;
716 }
717 else
718 face = size->face;
719
720 if ( aface )
721 *aface = face;
722
723 if ( asize )
724 *asize = size;
725
726 return error;
727 }
728
729 #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
730
731
732 /* END */