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