1/****************************************************************************
2 *
3 * ftcmanag.c
4 *
5 * FreeType Cache Manager (body).
6 *
7 * Copyright (C) 2000-2023 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
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 )
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 )
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 )
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 )
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
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 )
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 )
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 )
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 )
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 )
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 )
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 manager->cur_weight = 0;
387
388 manager->request_face = requester;
389 manager->request_data = req_data;
390
391 FTC_MruList_Init( &manager->faces,
392 &ftc_face_list_class,
393 max_faces,
394 manager,
395 memory );
396
397 FTC_MruList_Init( &manager->sizes,
398 &ftc_size_list_class,
399 max_sizes,
400 manager,
401 memory );
402
403 manager->nodes_list = NULL;
404 manager->num_nodes = 0;
405 manager->num_caches = 0;
406
407 *amanager = manager;
408
409 Exit:
410 return error;
411 }
412
413
414 /* documentation is in ftcache.h */
415
416 FT_EXPORT_DEF( void )
417 FTC_Manager_Done( FTC_Manager manager )
418 {
419 FT_Memory memory;
420 FT_UInt idx;
421
422
423 if ( !manager || !manager->library )
424 return;
425
426 memory = manager->memory;
427
428 /* now discard all caches */
429 for ( idx = manager->num_caches; idx-- > 0; )
430 {
431 FTC_Cache cache = manager->caches[idx];
432
433
434 if ( cache )
435 {
436 cache->clazz.cache_done( cache );
437 FT_FREE( cache );
438 manager->caches[idx] = NULL;
439 }
440 }
441 manager->num_caches = 0;
442
443 /* discard faces and sizes */
444 FTC_MruList_Done( &manager->sizes );
445 FTC_MruList_Done( &manager->faces );
446
447 manager->library = NULL;
448 manager->memory = NULL;
449
450 FT_FREE( manager );
451 }
452
453
454 /* documentation is in ftcache.h */
455
456 FT_EXPORT_DEF( void )
457 FTC_Manager_Reset( FTC_Manager manager )
458 {
459 if ( !manager )
460 return;
461
462 FTC_MruList_Reset( &manager->sizes );
463 FTC_MruList_Reset( &manager->faces );
464
465 FTC_Manager_FlushN( manager, manager->num_nodes );
466 }
467
468
469#ifdef FT_DEBUG_ERROR
470
471 static void
472 FTC_Manager_Check( FTC_Manager manager )
473 {
474 FTC_Node node, first;
475
476
477 first = manager->nodes_list;
478
479 /* check node weights */
480 if ( first )
481 {
482 FT_Offset weight = 0;
483
484
485 node = first;
486
487 do
488 {
489 FTC_Cache cache = manager->caches[node->cache_index];
490
491
492 if ( node->cache_index >= manager->num_caches )
493 FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %hu\n",
494 node->cache_index ));
495 else
496 weight += cache->clazz.node_weight( node, cache );
497
498 node = FTC_NODE_NEXT( node );
499
500 } while ( node != first );
501
502 if ( weight != manager->cur_weight )
503 FT_TRACE0(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
504 manager->cur_weight, weight ));
505 }
506
507 /* check circular list */
508 if ( first )
509 {
510 FT_UFast count = 0;
511
512
513 node = first;
514 do
515 {
516 count++;
517 node = FTC_NODE_NEXT( node );
518
519 } while ( node != first );
520
521 if ( count != manager->num_nodes )
522 FT_TRACE0(( "FTC_Manager_Check:"
523 " invalid cache node count %u instead of %u\n",
524 manager->num_nodes, count ));
525 }
526 }
527
528#endif /* FT_DEBUG_ERROR */
529
530
531 /* `Compress' the manager's data, i.e., get rid of old cache nodes */
532 /* that are not referenced anymore in order to limit the total */
533 /* memory used by the cache. */
534
535 /* documentation is in ftcmanag.h */
536
537 FT_LOCAL_DEF( void )
538 FTC_Manager_Compress( FTC_Manager manager )
539 {
540 FTC_Node node, prev, first;
541
542
543 if ( !manager )
544 return;
545
546 first = manager->nodes_list;
547
548#ifdef FT_DEBUG_ERROR
549 FTC_Manager_Check( manager );
550
551 FT_TRACE0(( "compressing, weight = %ld, max = %ld, nodes = %u\n",
552 manager->cur_weight, manager->max_weight,
553 manager->num_nodes ));
554#endif
555
556 if ( manager->cur_weight < manager->max_weight || !first )
557 return;
558
559 /* go to last node -- it's a circular list */
560 prev = FTC_NODE_PREV( first );
561 do
562 {
563 node = prev;
564 prev = FTC_NODE_PREV( node );
565
566 if ( node->ref_count <= 0 )
567 ftc_node_destroy( node, manager );
568
569 } while ( node != first && manager->cur_weight > manager->max_weight );
570 }
571
572
573 /* documentation is in ftcmanag.h */
574
575 FT_LOCAL_DEF( FT_Error )
576 FTC_Manager_RegisterCache( FTC_Manager manager,
577 FTC_CacheClass clazz,
578 FTC_Cache *acache )
579 {
580 FT_Error error = FT_ERR( Invalid_Argument );
581 FTC_Cache cache = NULL;
582
583
584 if ( manager && clazz && acache )
585 {
586 FT_Memory memory = manager->memory;
587
588
589 if ( manager->num_caches >= FTC_MAX_CACHES )
590 {
591 error = FT_THROW( Too_Many_Caches );
592 FT_ERROR(( "FTC_Manager_RegisterCache:"
593 " too many registered caches\n" ));
594 goto Exit;
595 }
596
597 if ( !FT_QALLOC( cache, clazz->cache_size ) )
598 {
599 cache->manager = manager;
600 cache->memory = memory;
601 cache->clazz = clazz[0];
602 cache->org_class = clazz;
603
604 /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
605 /* IF IT IS NOT SET CORRECTLY */
606 cache->index = manager->num_caches;
607
608 error = clazz->cache_init( cache );
609 if ( error )
610 {
611 clazz->cache_done( cache );
612 FT_FREE( cache );
613 goto Exit;
614 }
615
616 manager->caches[manager->num_caches++] = cache;
617 }
618 }
619
620 Exit:
621 if ( acache )
622 *acache = cache;
623 return error;
624 }
625
626
627 FT_LOCAL_DEF( FT_UInt )
628 FTC_Manager_FlushN( FTC_Manager manager,
629 FT_UInt count )
630 {
631 FTC_Node first = manager->nodes_list;
632 FTC_Node prev, node;
633 FT_UInt result = 0;
634
635
636 /* try to remove `count' nodes from the list */
637 if ( !first || !count )
638 return result;
639
640 /* go to last node -- it's a circular list */
641 prev = FTC_NODE_PREV( first );
642 do
643 {
644 node = prev;
645 prev = FTC_NODE_PREV( node );
646
647 /* don't touch locked nodes */
648 if ( node->ref_count <= 0 )
649 {
650 ftc_node_destroy( node, manager );
651 result++;
652 }
653 } while ( node != first && result < count );
654
655 return result;
656 }
657
658
659 /* documentation is in ftcache.h */
660
661 FT_EXPORT_DEF( void )
662 FTC_Manager_RemoveFaceID( FTC_Manager manager,
663 FTC_FaceID face_id )
664 {
665 FT_UInt nn;
666
667
668 if ( !manager )
669 return;
670
671 /* this will remove all FTC_SizeNode that correspond to
672 * the face_id as well
673 */
674 FTC_MruList_RemoveSelection( &manager->faces,
675 ftc_face_node_compare,
676 face_id );
677
678 for ( nn = 0; nn < manager->num_caches; nn++ )
679 FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
680 }
681
682
683 /* documentation is in ftcache.h */
684
685 FT_EXPORT_DEF( void )
686 FTC_Node_Unref( FTC_Node node,
687 FTC_Manager manager )
688 {
689 if ( node &&
690 manager &&
691 node->cache_index < manager->num_caches )
692 node->ref_count--;
693 }
694
695
696/* END */
697