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 | |