1 /***************************************************************************/ 2 /* */ 3 /* ftcmanag.c */ 4 /* */ 5 /* FreeType Cache Manager (body). */ 6 /* */ 7 /* Copyright 2000-2018 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 38 static FT_Error ftc_scaler_lookup_size(FTC_Manager manager,FTC_Scaler scaler,FT_Size * asize)39 ftc_scaler_lookup_size( FTC_Manager manager, 40 FTC_Scaler scaler, 41 FT_Size *asize ) 42 { 43 FT_Face face; 44 FT_Size size = NULL; 45 FT_Error error; 46 47 48 error = FTC_Manager_LookupFace( manager, scaler->face_id, &face ); 49 if ( error ) 50 goto Exit; 51 52 error = FT_New_Size( face, &size ); 53 if ( error ) 54 goto Exit; 55 56 FT_Activate_Size( size ); 57 58 if ( scaler->pixel ) 59 error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height ); 60 else 61 error = FT_Set_Char_Size( face, 62 (FT_F26Dot6)scaler->width, 63 (FT_F26Dot6)scaler->height, 64 scaler->x_res, 65 scaler->y_res ); 66 if ( error ) 67 { 68 FT_Done_Size( size ); 69 size = NULL; 70 } 71 72 Exit: 73 *asize = size; 74 return error; 75 } 76 77 78 typedef struct FTC_SizeNodeRec_ 79 { 80 FTC_MruNodeRec node; 81 FT_Size size; 82 FTC_ScalerRec scaler; 83 84 } FTC_SizeNodeRec, *FTC_SizeNode; 85 86 #define FTC_SIZE_NODE( x ) ( (FTC_SizeNode)( x ) ) 87 88 89 FT_CALLBACK_DEF( void ) ftc_size_node_done(FTC_MruNode ftcnode,FT_Pointer data)90 ftc_size_node_done( FTC_MruNode ftcnode, 91 FT_Pointer data ) 92 { 93 FTC_SizeNode node = (FTC_SizeNode)ftcnode; 94 FT_Size size = node->size; 95 FT_UNUSED( data ); 96 97 98 if ( size ) 99 FT_Done_Size( size ); 100 } 101 102 103 FT_CALLBACK_DEF( FT_Bool ) ftc_size_node_compare(FTC_MruNode ftcnode,FT_Pointer ftcscaler)104 ftc_size_node_compare( FTC_MruNode ftcnode, 105 FT_Pointer ftcscaler ) 106 { 107 FTC_SizeNode node = (FTC_SizeNode)ftcnode; 108 FTC_Scaler scaler = (FTC_Scaler)ftcscaler; 109 FTC_Scaler scaler0 = &node->scaler; 110 111 112 if ( FTC_SCALER_COMPARE( scaler0, scaler ) ) 113 { 114 FT_Activate_Size( node->size ); 115 return 1; 116 } 117 return 0; 118 } 119 120 121 FT_CALLBACK_DEF( FT_Error ) ftc_size_node_init(FTC_MruNode ftcnode,FT_Pointer ftcscaler,FT_Pointer ftcmanager)122 ftc_size_node_init( FTC_MruNode ftcnode, 123 FT_Pointer ftcscaler, 124 FT_Pointer ftcmanager ) 125 { 126 FTC_SizeNode node = (FTC_SizeNode)ftcnode; 127 FTC_Scaler scaler = (FTC_Scaler)ftcscaler; 128 FTC_Manager manager = (FTC_Manager)ftcmanager; 129 130 131 node->scaler = scaler[0]; 132 133 return ftc_scaler_lookup_size( manager, scaler, &node->size ); 134 } 135 136 137 FT_CALLBACK_DEF( FT_Error ) ftc_size_node_reset(FTC_MruNode ftcnode,FT_Pointer ftcscaler,FT_Pointer ftcmanager)138 ftc_size_node_reset( FTC_MruNode ftcnode, 139 FT_Pointer ftcscaler, 140 FT_Pointer ftcmanager ) 141 { 142 FTC_SizeNode node = (FTC_SizeNode)ftcnode; 143 FTC_Scaler scaler = (FTC_Scaler)ftcscaler; 144 FTC_Manager manager = (FTC_Manager)ftcmanager; 145 146 147 FT_Done_Size( node->size ); 148 149 node->scaler = scaler[0]; 150 151 return ftc_scaler_lookup_size( manager, scaler, &node->size ); 152 } 153 154 155 static 156 const FTC_MruListClassRec ftc_size_list_class = 157 { 158 sizeof ( FTC_SizeNodeRec ), 159 160 ftc_size_node_compare, /* FTC_MruNode_CompareFunc node_compare */ 161 ftc_size_node_init, /* FTC_MruNode_InitFunc node_init */ 162 ftc_size_node_reset, /* FTC_MruNode_ResetFunc node_reset */ 163 ftc_size_node_done /* FTC_MruNode_DoneFunc node_done */ 164 }; 165 166 167 /* helper function used by ftc_face_node_done */ 168 static FT_Bool ftc_size_node_compare_faceid(FTC_MruNode ftcnode,FT_Pointer ftcface_id)169 ftc_size_node_compare_faceid( FTC_MruNode ftcnode, 170 FT_Pointer ftcface_id ) 171 { 172 FTC_SizeNode node = (FTC_SizeNode)ftcnode; 173 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 174 175 176 return FT_BOOL( node->scaler.face_id == face_id ); 177 } 178 179 180 /* documentation is in ftcache.h */ 181 182 FT_EXPORT_DEF( FT_Error ) FTC_Manager_LookupSize(FTC_Manager manager,FTC_Scaler scaler,FT_Size * asize)183 FTC_Manager_LookupSize( FTC_Manager manager, 184 FTC_Scaler scaler, 185 FT_Size *asize ) 186 { 187 FT_Error error; 188 FTC_MruNode mrunode; 189 190 191 if ( !asize || !scaler ) 192 return FT_THROW( Invalid_Argument ); 193 194 *asize = NULL; 195 196 if ( !manager ) 197 return FT_THROW( Invalid_Cache_Handle ); 198 199 #ifdef FTC_INLINE 200 201 FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare, 202 mrunode, error ); 203 204 #else 205 error = FTC_MruList_Lookup( &manager->sizes, scaler, &mrunode ); 206 #endif 207 208 if ( !error ) 209 *asize = FTC_SIZE_NODE( mrunode )->size; 210 211 return error; 212 } 213 214 215 /*************************************************************************/ 216 /*************************************************************************/ 217 /***** *****/ 218 /***** FACE MRU IMPLEMENTATION *****/ 219 /***** *****/ 220 /*************************************************************************/ 221 /*************************************************************************/ 222 223 typedef struct FTC_FaceNodeRec_ 224 { 225 FTC_MruNodeRec node; 226 FTC_FaceID face_id; 227 FT_Face face; 228 229 } FTC_FaceNodeRec, *FTC_FaceNode; 230 231 #define FTC_FACE_NODE( x ) ( ( FTC_FaceNode )( x ) ) 232 233 234 FT_CALLBACK_DEF( FT_Error ) ftc_face_node_init(FTC_MruNode ftcnode,FT_Pointer ftcface_id,FT_Pointer ftcmanager)235 ftc_face_node_init( FTC_MruNode ftcnode, 236 FT_Pointer ftcface_id, 237 FT_Pointer ftcmanager ) 238 { 239 FTC_FaceNode node = (FTC_FaceNode)ftcnode; 240 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 241 FTC_Manager manager = (FTC_Manager)ftcmanager; 242 FT_Error error; 243 244 245 node->face_id = face_id; 246 247 error = manager->request_face( face_id, 248 manager->library, 249 manager->request_data, 250 &node->face ); 251 if ( !error ) 252 { 253 /* destroy initial size object; it will be re-created later */ 254 if ( node->face->size ) 255 FT_Done_Size( node->face->size ); 256 } 257 258 return error; 259 } 260 261 262 FT_CALLBACK_DEF( void ) ftc_face_node_done(FTC_MruNode ftcnode,FT_Pointer ftcmanager)263 ftc_face_node_done( FTC_MruNode ftcnode, 264 FT_Pointer ftcmanager ) 265 { 266 FTC_FaceNode node = (FTC_FaceNode)ftcnode; 267 FTC_Manager manager = (FTC_Manager)ftcmanager; 268 269 270 /* we must begin by removing all scalers for the target face */ 271 /* from the manager's list */ 272 FTC_MruList_RemoveSelection( &manager->sizes, 273 ftc_size_node_compare_faceid, 274 node->face_id ); 275 276 /* all right, we can discard the face now */ 277 FT_Done_Face( node->face ); 278 node->face = NULL; 279 node->face_id = NULL; 280 } 281 282 283 FT_CALLBACK_DEF( FT_Bool ) ftc_face_node_compare(FTC_MruNode ftcnode,FT_Pointer ftcface_id)284 ftc_face_node_compare( FTC_MruNode ftcnode, 285 FT_Pointer ftcface_id ) 286 { 287 FTC_FaceNode node = (FTC_FaceNode)ftcnode; 288 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 289 290 291 return FT_BOOL( node->face_id == face_id ); 292 } 293 294 295 static 296 const FTC_MruListClassRec ftc_face_list_class = 297 { 298 sizeof ( FTC_FaceNodeRec), 299 300 ftc_face_node_compare, /* FTC_MruNode_CompareFunc node_compare */ 301 ftc_face_node_init, /* FTC_MruNode_InitFunc node_init */ 302 NULL, /* FTC_MruNode_ResetFunc node_reset */ 303 ftc_face_node_done /* FTC_MruNode_DoneFunc node_done */ 304 }; 305 306 307 /* documentation is in ftcache.h */ 308 309 FT_EXPORT_DEF( FT_Error ) FTC_Manager_LookupFace(FTC_Manager manager,FTC_FaceID face_id,FT_Face * aface)310 FTC_Manager_LookupFace( FTC_Manager manager, 311 FTC_FaceID face_id, 312 FT_Face *aface ) 313 { 314 FT_Error error; 315 FTC_MruNode mrunode; 316 317 318 if ( !aface ) 319 return FT_THROW( Invalid_Argument ); 320 321 *aface = NULL; 322 323 if ( !manager ) 324 return FT_THROW( Invalid_Cache_Handle ); 325 326 /* we break encapsulation for the sake of speed */ 327 #ifdef FTC_INLINE 328 329 FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare, 330 mrunode, error ); 331 332 #else 333 error = FTC_MruList_Lookup( &manager->faces, face_id, &mrunode ); 334 #endif 335 336 if ( !error ) 337 *aface = FTC_FACE_NODE( mrunode )->face; 338 339 return error; 340 } 341 342 343 /*************************************************************************/ 344 /*************************************************************************/ 345 /***** *****/ 346 /***** CACHE MANAGER ROUTINES *****/ 347 /***** *****/ 348 /*************************************************************************/ 349 /*************************************************************************/ 350 351 352 /* documentation is in ftcache.h */ 353 354 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)355 FTC_Manager_New( FT_Library library, 356 FT_UInt max_faces, 357 FT_UInt max_sizes, 358 FT_ULong max_bytes, 359 FTC_Face_Requester requester, 360 FT_Pointer req_data, 361 FTC_Manager *amanager ) 362 { 363 FT_Error error; 364 FT_Memory memory; 365 FTC_Manager manager = 0; 366 367 368 if ( !library ) 369 return FT_THROW( Invalid_Library_Handle ); 370 371 if ( !amanager || !requester ) 372 return FT_THROW( Invalid_Argument ); 373 374 memory = library->memory; 375 376 if ( FT_NEW( manager ) ) 377 goto Exit; 378 379 if ( max_faces == 0 ) 380 max_faces = FTC_MAX_FACES_DEFAULT; 381 382 if ( max_sizes == 0 ) 383 max_sizes = FTC_MAX_SIZES_DEFAULT; 384 385 if ( max_bytes == 0 ) 386 max_bytes = FTC_MAX_BYTES_DEFAULT; 387 388 manager->library = library; 389 manager->memory = memory; 390 manager->max_weight = max_bytes; 391 392 manager->request_face = requester; 393 manager->request_data = req_data; 394 395 FTC_MruList_Init( &manager->faces, 396 &ftc_face_list_class, 397 max_faces, 398 manager, 399 memory ); 400 401 FTC_MruList_Init( &manager->sizes, 402 &ftc_size_list_class, 403 max_sizes, 404 manager, 405 memory ); 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 ) FTC_Manager_Done(FTC_Manager manager)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 ) FTC_Manager_Reset(FTC_Manager manager)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 FTC_Manager_Check(FTC_Manager manager)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 ( (FT_UInt)node->cache_index >= manager->num_caches ) 493 FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %ld\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 %d instead of %d\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 ) FTC_Manager_Compress(FTC_Manager manager)538 FTC_Manager_Compress( FTC_Manager manager ) 539 { 540 FTC_Node node, 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 = %d\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 node = FTC_NODE_PREV( first ); 561 do 562 { 563 FTC_Node prev; 564 565 566 prev = ( node == first ) ? NULL : FTC_NODE_PREV( node ); 567 568 if ( node->ref_count <= 0 ) 569 ftc_node_destroy( node, manager ); 570 571 node = prev; 572 573 } while ( node && manager->cur_weight > manager->max_weight ); 574 } 575 576 577 /* documentation is in ftcmanag.h */ 578 579 FT_LOCAL_DEF( FT_Error ) FTC_Manager_RegisterCache(FTC_Manager manager,FTC_CacheClass clazz,FTC_Cache * acache)580 FTC_Manager_RegisterCache( FTC_Manager manager, 581 FTC_CacheClass clazz, 582 FTC_Cache *acache ) 583 { 584 FT_Error error = FT_ERR( Invalid_Argument ); 585 FTC_Cache cache = NULL; 586 587 588 if ( manager && clazz && acache ) 589 { 590 FT_Memory memory = manager->memory; 591 592 593 if ( manager->num_caches >= FTC_MAX_CACHES ) 594 { 595 error = FT_THROW( Too_Many_Caches ); 596 FT_ERROR(( "FTC_Manager_RegisterCache:" 597 " too many registered caches\n" )); 598 goto Exit; 599 } 600 601 if ( !FT_ALLOC( cache, clazz->cache_size ) ) 602 { 603 cache->manager = manager; 604 cache->memory = memory; 605 cache->clazz = clazz[0]; 606 cache->org_class = clazz; 607 608 /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */ 609 /* IF IT IS NOT SET CORRECTLY */ 610 cache->index = manager->num_caches; 611 612 error = clazz->cache_init( cache ); 613 if ( error ) 614 { 615 clazz->cache_done( cache ); 616 FT_FREE( cache ); 617 goto Exit; 618 } 619 620 manager->caches[manager->num_caches++] = cache; 621 } 622 } 623 624 Exit: 625 if ( acache ) 626 *acache = cache; 627 return error; 628 } 629 630 631 FT_LOCAL_DEF( FT_UInt ) FTC_Manager_FlushN(FTC_Manager manager,FT_UInt count)632 FTC_Manager_FlushN( FTC_Manager manager, 633 FT_UInt count ) 634 { 635 FTC_Node first = manager->nodes_list; 636 FTC_Node node; 637 FT_UInt result; 638 639 640 /* try to remove `count' nodes from the list */ 641 if ( !first ) /* empty list! */ 642 return 0; 643 644 /* go to last node - it's a circular list */ 645 node = FTC_NODE_PREV(first); 646 for ( result = 0; result < count; ) 647 { 648 FTC_Node prev = FTC_NODE_PREV( node ); 649 650 651 /* don't touch locked nodes */ 652 if ( node->ref_count <= 0 ) 653 { 654 ftc_node_destroy( node, manager ); 655 result++; 656 } 657 658 if ( node == first ) 659 break; 660 661 node = prev; 662 } 663 return result; 664 } 665 666 667 /* documentation is in ftcache.h */ 668 669 FT_EXPORT_DEF( void ) FTC_Manager_RemoveFaceID(FTC_Manager manager,FTC_FaceID face_id)670 FTC_Manager_RemoveFaceID( FTC_Manager manager, 671 FTC_FaceID face_id ) 672 { 673 FT_UInt nn; 674 675 676 if ( !manager ) 677 return; 678 679 /* this will remove all FTC_SizeNode that correspond to 680 * the face_id as well 681 */ 682 FTC_MruList_RemoveSelection( &manager->faces, 683 ftc_face_node_compare, 684 face_id ); 685 686 for ( nn = 0; nn < manager->num_caches; nn++ ) 687 FTC_Cache_RemoveFaceID( manager->caches[nn], face_id ); 688 } 689 690 691 /* documentation is in ftcache.h */ 692 693 FT_EXPORT_DEF( void ) FTC_Node_Unref(FTC_Node node,FTC_Manager manager)694 FTC_Node_Unref( FTC_Node node, 695 FTC_Manager manager ) 696 { 697 if ( node && 698 manager && 699 (FT_UInt)node->cache_index < manager->num_caches ) 700 node->ref_count--; 701 } 702 703 704 /* END */ 705