1 /***************************************************************************/ 2 /* */ 3 /* ftcmanag.c */ 4 /* */ 5 /* FreeType Cache Manager (body). */ 6 /* */ 7 /* Copyright 2000-2001, 2002 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 FT_CACHE_MANAGER_H 22 #include FT_CACHE_INTERNAL_LRU_H 23 #include FT_INTERNAL_OBJECTS_H 24 #include FT_INTERNAL_DEBUG_H 25 #include FT_SIZES_H 26 27 #include "ftcerror.h" 28 29 30 #undef FT_COMPONENT 31 #define FT_COMPONENT trace_cache 32 33 #define FTC_LRU_GET_MANAGER( lru ) ( (FTC_Manager)(lru)->user_data ) 34 35 36 /*************************************************************************/ 37 /*************************************************************************/ 38 /***** *****/ 39 /***** FACE LRU IMPLEMENTATION *****/ 40 /***** *****/ 41 /*************************************************************************/ 42 /*************************************************************************/ 43 44 typedef struct FTC_FaceNodeRec_* FTC_FaceNode; 45 typedef struct FTC_SizeNodeRec_* FTC_SizeNode; 46 47 48 typedef struct FTC_FaceNodeRec_ 49 { 50 FT_LruNodeRec lru; 51 FT_Face face; 52 53 } FTC_FaceNodeRec; 54 55 56 typedef struct FTC_SizeNodeRec_ 57 { 58 FT_LruNodeRec lru; 59 FT_Size size; 60 61 } FTC_SizeNodeRec; 62 63 64 FT_CALLBACK_DEF( FT_Error ) ftc_face_node_init(FTC_FaceNode node,FTC_FaceID face_id,FTC_Manager manager)65 ftc_face_node_init( FTC_FaceNode node, 66 FTC_FaceID face_id, 67 FTC_Manager manager ) 68 { 69 FT_Error error; 70 71 72 error = manager->request_face( face_id, 73 manager->library, 74 manager->request_data, 75 &node->face ); 76 if ( !error ) 77 { 78 /* destroy initial size object; it will be re-created later */ 79 if ( node->face->size ) 80 FT_Done_Size( node->face->size ); 81 } 82 83 return error; 84 } 85 86 87 /* helper function for ftc_face_node_done() */ 88 FT_CALLBACK_DEF( FT_Bool ) ftc_size_node_select(FTC_SizeNode node,FT_Face face)89 ftc_size_node_select( FTC_SizeNode node, 90 FT_Face face ) 91 { 92 return FT_BOOL( node->size->face == face ); 93 } 94 95 96 FT_CALLBACK_DEF( void ) ftc_face_node_done(FTC_FaceNode node,FTC_Manager manager)97 ftc_face_node_done( FTC_FaceNode node, 98 FTC_Manager manager ) 99 { 100 FT_Face face = node->face; 101 102 103 /* we must begin by removing all sizes for the target face */ 104 /* from the manager's list */ 105 FT_LruList_Remove_Selection( manager->sizes_list, 106 (FT_LruNode_SelectFunc)ftc_size_node_select, 107 face ); 108 109 /* all right, we can discard the face now */ 110 FT_Done_Face( face ); 111 node->face = NULL; 112 } 113 114 115 FT_CALLBACK_TABLE_DEF 116 const FT_LruList_ClassRec ftc_face_list_class = 117 { 118 sizeof ( FT_LruListRec ), 119 (FT_LruList_InitFunc)0, 120 (FT_LruList_DoneFunc)0, 121 122 sizeof ( FTC_FaceNodeRec ), 123 (FT_LruNode_InitFunc) ftc_face_node_init, 124 (FT_LruNode_DoneFunc) ftc_face_node_done, 125 (FT_LruNode_FlushFunc) 0, /* no flushing needed */ 126 (FT_LruNode_CompareFunc)0, /* direct comparison of FTC_FaceID handles */ 127 }; 128 129 130 /* documentation is in ftcache.h */ 131 132 FT_EXPORT_DEF( FT_Error ) FTC_Manager_Lookup_Face(FTC_Manager manager,FTC_FaceID face_id,FT_Face * aface)133 FTC_Manager_Lookup_Face( FTC_Manager manager, 134 FTC_FaceID face_id, 135 FT_Face *aface ) 136 { 137 FT_Error error; 138 FTC_FaceNode node; 139 140 141 if ( aface == NULL ) 142 return FTC_Err_Bad_Argument; 143 144 *aface = NULL; 145 146 if ( !manager ) 147 return FTC_Err_Invalid_Cache_Handle; 148 149 error = FT_LruList_Lookup( manager->faces_list, 150 (FT_LruKey)face_id, 151 (FT_LruNode*)&node ); 152 if ( !error ) 153 *aface = node->face; 154 155 return error; 156 } 157 158 159 /*************************************************************************/ 160 /*************************************************************************/ 161 /***** *****/ 162 /***** SIZES LRU IMPLEMENTATION *****/ 163 /***** *****/ 164 /*************************************************************************/ 165 /*************************************************************************/ 166 167 168 typedef struct FTC_SizeQueryRec_ 169 { 170 FT_Face face; 171 FT_UInt width; 172 FT_UInt height; 173 174 } FTC_SizeQueryRec, *FTC_SizeQuery; 175 176 177 FT_CALLBACK_DEF( FT_Error ) ftc_size_node_init(FTC_SizeNode node,FTC_SizeQuery query)178 ftc_size_node_init( FTC_SizeNode node, 179 FTC_SizeQuery query ) 180 { 181 FT_Face face = query->face; 182 FT_Size size; 183 FT_Error error; 184 185 186 node->size = NULL; 187 error = FT_New_Size( face, &size ); 188 if ( !error ) 189 { 190 FT_Activate_Size( size ); 191 error = FT_Set_Pixel_Sizes( query->face, 192 query->width, 193 query->height ); 194 if ( error ) 195 FT_Done_Size( size ); 196 else 197 node->size = size; 198 } 199 return error; 200 } 201 202 203 FT_CALLBACK_DEF( void ) ftc_size_node_done(FTC_SizeNode node)204 ftc_size_node_done( FTC_SizeNode node ) 205 { 206 if ( node->size ) 207 { 208 FT_Done_Size( node->size ); 209 node->size = NULL; 210 } 211 } 212 213 214 FT_CALLBACK_DEF( FT_Error ) ftc_size_node_flush(FTC_SizeNode node,FTC_SizeQuery query)215 ftc_size_node_flush( FTC_SizeNode node, 216 FTC_SizeQuery query ) 217 { 218 FT_Size size = node->size; 219 FT_Error error; 220 221 222 if ( size->face == query->face ) 223 { 224 FT_Activate_Size( size ); 225 error = FT_Set_Pixel_Sizes( query->face, query->width, query->height ); 226 if ( error ) 227 { 228 FT_Done_Size( size ); 229 node->size = NULL; 230 } 231 } 232 else 233 { 234 FT_Done_Size( size ); 235 node->size = NULL; 236 237 error = ftc_size_node_init( node, query ); 238 } 239 return error; 240 } 241 242 243 FT_CALLBACK_DEF( FT_Bool ) ftc_size_node_compare(FTC_SizeNode node,FTC_SizeQuery query)244 ftc_size_node_compare( FTC_SizeNode node, 245 FTC_SizeQuery query ) 246 { 247 FT_Size size = node->size; 248 249 250 return FT_BOOL( size->face == query->face && 251 (FT_UInt)size->metrics.x_ppem == query->width && 252 (FT_UInt)size->metrics.y_ppem == query->height ); 253 } 254 255 256 FT_CALLBACK_TABLE_DEF 257 const FT_LruList_ClassRec ftc_size_list_class = 258 { 259 sizeof ( FT_LruListRec ), 260 (FT_LruList_InitFunc)0, 261 (FT_LruList_DoneFunc)0, 262 263 sizeof ( FTC_SizeNodeRec ), 264 (FT_LruNode_InitFunc) ftc_size_node_init, 265 (FT_LruNode_DoneFunc) ftc_size_node_done, 266 (FT_LruNode_FlushFunc) ftc_size_node_flush, 267 (FT_LruNode_CompareFunc)ftc_size_node_compare 268 }; 269 270 271 /* documentation is in ftcache.h */ 272 273 FT_EXPORT_DEF( FT_Error ) FTC_Manager_Lookup_Size(FTC_Manager manager,FTC_Font font,FT_Face * aface,FT_Size * asize)274 FTC_Manager_Lookup_Size( FTC_Manager manager, 275 FTC_Font font, 276 FT_Face *aface, 277 FT_Size *asize ) 278 { 279 FT_Error error; 280 281 282 /* check for valid `manager' delayed to FTC_Manager_Lookup_Face() */ 283 if ( aface ) 284 *aface = 0; 285 286 if ( asize ) 287 *asize = 0; 288 289 error = FTC_Manager_Lookup_Face( manager, font->face_id, aface ); 290 if ( !error ) 291 { 292 FTC_SizeQueryRec query; 293 FTC_SizeNode node; 294 295 296 query.face = *aface; 297 query.width = font->pix_width; 298 query.height = font->pix_height; 299 300 error = FT_LruList_Lookup( manager->sizes_list, 301 (FT_LruKey)&query, 302 (FT_LruNode*)&node ); 303 if ( !error ) 304 { 305 /* select the size as the current one for this face */ 306 FT_Activate_Size( node->size ); 307 308 if ( asize ) 309 *asize = node->size; 310 } 311 } 312 313 return error; 314 } 315 316 317 /*************************************************************************/ 318 /*************************************************************************/ 319 /***** *****/ 320 /***** SET TABLE MANAGEMENT *****/ 321 /***** *****/ 322 /*************************************************************************/ 323 /*************************************************************************/ 324 325 static void ftc_family_table_init(FTC_FamilyTable table)326 ftc_family_table_init( FTC_FamilyTable table ) 327 { 328 table->count = 0; 329 table->size = 0; 330 table->entries = NULL; 331 table->free = FTC_FAMILY_ENTRY_NONE; 332 } 333 334 335 static void ftc_family_table_done(FTC_FamilyTable table,FT_Memory memory)336 ftc_family_table_done( FTC_FamilyTable table, 337 FT_Memory memory ) 338 { 339 FT_FREE( table->entries ); 340 table->free = 0; 341 table->count = 0; 342 table->size = 0; 343 } 344 345 346 FT_EXPORT_DEF( FT_Error ) ftc_family_table_alloc(FTC_FamilyTable table,FT_Memory memory,FTC_FamilyEntry * aentry)347 ftc_family_table_alloc( FTC_FamilyTable table, 348 FT_Memory memory, 349 FTC_FamilyEntry *aentry ) 350 { 351 FTC_FamilyEntry entry; 352 FT_Error error = 0; 353 354 355 /* re-allocate table size when needed */ 356 if ( table->free == FTC_FAMILY_ENTRY_NONE && table->count >= table->size ) 357 { 358 FT_UInt old_size = table->size; 359 FT_UInt new_size, idx; 360 361 362 if ( old_size == 0 ) 363 new_size = 8; 364 else 365 { 366 new_size = old_size * 2; 367 368 /* check for (unlikely) overflow */ 369 if ( new_size < old_size ) 370 new_size = 65534; 371 } 372 373 if ( FT_RENEW_ARRAY( table->entries, old_size, new_size ) ) 374 return error; 375 376 table->size = new_size; 377 378 entry = table->entries + old_size; 379 table->free = old_size; 380 381 for ( idx = old_size; idx + 1 < new_size; idx++, entry++ ) 382 { 383 entry->link = idx + 1; 384 entry->index = idx; 385 } 386 387 entry->link = FTC_FAMILY_ENTRY_NONE; 388 entry->index = idx; 389 } 390 391 if ( table->free != FTC_FAMILY_ENTRY_NONE ) 392 { 393 entry = table->entries + table->free; 394 table->free = entry->link; 395 } 396 else if ( table->count < table->size ) 397 { 398 entry = table->entries + table->count++; 399 } 400 else 401 { 402 FT_ERROR(( "ftc_family_table_alloc: internal bug!" )); 403 return FTC_Err_Invalid_Argument; 404 } 405 406 entry->link = FTC_FAMILY_ENTRY_NONE; 407 table->count++; 408 409 *aentry = entry; 410 return error; 411 } 412 413 414 FT_EXPORT_DEF( void ) ftc_family_table_free(FTC_FamilyTable table,FT_UInt idx)415 ftc_family_table_free( FTC_FamilyTable table, 416 FT_UInt idx ) 417 { 418 /* simply add it to the linked list of free entries */ 419 if ( idx < table->count ) 420 { 421 FTC_FamilyEntry entry = table->entries + idx; 422 423 424 if ( entry->link != FTC_FAMILY_ENTRY_NONE ) 425 FT_ERROR(( "ftc_family_table_free: internal bug!\n" )); 426 else 427 { 428 entry->link = table->free; 429 table->free = entry->index; 430 table->count--; 431 } 432 } 433 } 434 435 436 /*************************************************************************/ 437 /*************************************************************************/ 438 /***** *****/ 439 /***** CACHE MANAGER ROUTINES *****/ 440 /***** *****/ 441 /*************************************************************************/ 442 /*************************************************************************/ 443 444 445 /* documentation is in ftcache.h */ 446 447 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)448 FTC_Manager_New( FT_Library library, 449 FT_UInt max_faces, 450 FT_UInt max_sizes, 451 FT_ULong max_bytes, 452 FTC_Face_Requester requester, 453 FT_Pointer req_data, 454 FTC_Manager *amanager ) 455 { 456 FT_Error error; 457 FT_Memory memory; 458 FTC_Manager manager = 0; 459 460 461 if ( !library ) 462 return FTC_Err_Invalid_Library_Handle; 463 464 memory = library->memory; 465 466 if ( FT_NEW( manager ) ) 467 goto Exit; 468 469 if ( max_faces == 0 ) 470 max_faces = FTC_MAX_FACES_DEFAULT; 471 472 if ( max_sizes == 0 ) 473 max_sizes = FTC_MAX_SIZES_DEFAULT; 474 475 if ( max_bytes == 0 ) 476 max_bytes = FTC_MAX_BYTES_DEFAULT; 477 478 error = FT_LruList_New( &ftc_face_list_class, 479 max_faces, 480 manager, 481 memory, 482 &manager->faces_list ); 483 if ( error ) 484 goto Exit; 485 486 error = FT_LruList_New( &ftc_size_list_class, 487 max_sizes, 488 manager, 489 memory, 490 &manager->sizes_list ); 491 if ( error ) 492 goto Exit; 493 494 manager->library = library; 495 manager->max_weight = max_bytes; 496 manager->cur_weight = 0; 497 498 manager->request_face = requester; 499 manager->request_data = req_data; 500 501 ftc_family_table_init( &manager->families ); 502 503 *amanager = manager; 504 505 Exit: 506 if ( error && manager ) 507 { 508 FT_LruList_Destroy( manager->faces_list ); 509 FT_LruList_Destroy( manager->sizes_list ); 510 FT_FREE( manager ); 511 } 512 513 return error; 514 } 515 516 517 /* documentation is in ftcache.h */ 518 519 FT_EXPORT_DEF( void ) FTC_Manager_Done(FTC_Manager manager)520 FTC_Manager_Done( FTC_Manager manager ) 521 { 522 FT_Memory memory; 523 FT_UInt idx; 524 525 526 if ( !manager || !manager->library ) 527 return; 528 529 memory = manager->library->memory; 530 531 /* now discard all caches */ 532 for (idx = 0; idx < FTC_MAX_CACHES; idx++ ) 533 { 534 FTC_Cache cache = manager->caches[idx]; 535 536 537 if ( cache ) 538 { 539 cache->clazz->cache_done( cache ); 540 FT_FREE( cache ); 541 manager->caches[idx] = 0; 542 } 543 } 544 545 /* discard families table */ 546 ftc_family_table_done( &manager->families, memory ); 547 548 /* discard faces and sizes */ 549 FT_LruList_Destroy( manager->faces_list ); 550 manager->faces_list = 0; 551 552 FT_LruList_Destroy( manager->sizes_list ); 553 manager->sizes_list = 0; 554 555 FT_FREE( manager ); 556 } 557 558 559 /* documentation is in ftcache.h */ 560 561 FT_EXPORT_DEF( void ) FTC_Manager_Reset(FTC_Manager manager)562 FTC_Manager_Reset( FTC_Manager manager ) 563 { 564 if ( manager ) 565 { 566 FT_LruList_Reset( manager->sizes_list ); 567 FT_LruList_Reset( manager->faces_list ); 568 } 569 /* XXX: FIXME: flush the caches? */ 570 } 571 572 573 #ifdef FT_DEBUG_ERROR 574 575 FT_EXPORT_DEF( void ) FTC_Manager_Check(FTC_Manager manager)576 FTC_Manager_Check( FTC_Manager manager ) 577 { 578 FTC_Node node, first; 579 580 581 first = manager->nodes_list; 582 583 /* check node weights */ 584 if ( first ) 585 { 586 FT_ULong weight = 0; 587 588 589 node = first; 590 591 do 592 { 593 FTC_FamilyEntry entry = manager->families.entries + node->fam_index; 594 FTC_Cache cache; 595 596 if ( (FT_UInt)node->fam_index >= manager->families.count || 597 entry->link != FTC_FAMILY_ENTRY_NONE ) 598 FT_ERROR(( "FTC_Manager_Check: invalid node (family index = %ld\n", 599 node->fam_index )); 600 else 601 { 602 cache = entry->cache; 603 weight += cache->clazz->node_weight( node, cache ); 604 } 605 606 node = node->mru_next; 607 608 } while ( node != first ); 609 610 if ( weight != manager->cur_weight ) 611 FT_ERROR(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n", 612 manager->cur_weight, weight )); 613 } 614 615 /* check circular list */ 616 if ( first ) 617 { 618 FT_UFast count = 0; 619 620 621 node = first; 622 do 623 { 624 count++; 625 node = node->mru_next; 626 627 } while ( node != first ); 628 629 if ( count != manager->num_nodes ) 630 FT_ERROR(( 631 "FTC_Manager_Check: invalid cache node count %d instead of %d\n", 632 manager->num_nodes, count )); 633 } 634 } 635 636 #endif /* FT_DEBUG_ERROR */ 637 638 639 /* `Compress' the manager's data, i.e., get rid of old cache nodes */ 640 /* that are not referenced anymore in order to limit the total */ 641 /* memory used by the cache. */ 642 643 /* documentation is in ftcmanag.h */ 644 645 FT_EXPORT_DEF( void ) FTC_Manager_Compress(FTC_Manager manager)646 FTC_Manager_Compress( FTC_Manager manager ) 647 { 648 FTC_Node node, first; 649 650 651 if ( !manager ) 652 return; 653 654 first = manager->nodes_list; 655 656 #ifdef FT_DEBUG_ERROR 657 FTC_Manager_Check( manager ); 658 659 FT_ERROR(( "compressing, weight = %ld, max = %ld, nodes = %d\n", 660 manager->cur_weight, manager->max_weight, 661 manager->num_nodes )); 662 #endif 663 664 if ( manager->cur_weight < manager->max_weight || first == NULL ) 665 return; 666 667 /* go to last node - it's a circular list */ 668 node = first->mru_prev; 669 do 670 { 671 FTC_Node prev = node->mru_prev; 672 673 674 prev = ( node == first ) ? NULL : node->mru_prev; 675 676 if ( node->ref_count <= 0 ) 677 ftc_node_destroy( node, manager ); 678 679 node = prev; 680 681 } while ( node && manager->cur_weight > manager->max_weight ); 682 } 683 684 685 /* documentation is in ftcmanag.h */ 686 687 FT_EXPORT_DEF( FT_Error ) FTC_Manager_Register_Cache(FTC_Manager manager,FTC_Cache_Class clazz,FTC_Cache * acache)688 FTC_Manager_Register_Cache( FTC_Manager manager, 689 FTC_Cache_Class clazz, 690 FTC_Cache *acache ) 691 { 692 FT_Error error = FTC_Err_Invalid_Argument; 693 FTC_Cache cache = NULL; 694 695 696 if ( manager && clazz && acache ) 697 { 698 FT_Memory memory = manager->library->memory; 699 FT_UInt idx = 0; 700 701 702 /* check for an empty cache slot in the manager's table */ 703 for ( idx = 0; idx < FTC_MAX_CACHES; idx++ ) 704 { 705 if ( manager->caches[idx] == 0 ) 706 break; 707 } 708 709 /* return an error if there are too many registered caches */ 710 if ( idx >= FTC_MAX_CACHES ) 711 { 712 error = FTC_Err_Too_Many_Caches; 713 FT_ERROR(( "FTC_Manager_Register_Cache:" )); 714 FT_ERROR(( " too many registered caches\n" )); 715 goto Exit; 716 } 717 718 if ( !FT_ALLOC( cache, clazz->cache_size ) ) 719 { 720 cache->manager = manager; 721 cache->memory = memory; 722 cache->clazz = clazz; 723 724 /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */ 725 /* IF IT IS NOT SET CORRECTLY */ 726 cache->cache_index = idx; 727 728 if ( clazz->cache_init ) 729 { 730 error = clazz->cache_init( cache ); 731 if ( error ) 732 { 733 if ( clazz->cache_done ) 734 clazz->cache_done( cache ); 735 736 FT_FREE( cache ); 737 goto Exit; 738 } 739 } 740 741 manager->caches[idx] = cache; 742 } 743 } 744 745 Exit: 746 *acache = cache; 747 return error; 748 } 749 750 751 /* documentation is in ftcmanag.h */ 752 753 FT_EXPORT_DEF( void ) FTC_Node_Unref(FTC_Node node,FTC_Manager manager)754 FTC_Node_Unref( FTC_Node node, 755 FTC_Manager manager ) 756 { 757 if ( node && (FT_UInt)node->fam_index < manager->families.count && 758 manager->families.entries[node->fam_index].cache ) 759 { 760 node->ref_count--; 761 } 762 } 763 764 765 /* END */ 766