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