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