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