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