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