00001 // $Id: fntcache.cpp 1282 2006-06-09 09:46:49Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 00099 // The implementation of the TextTool's FontCache 00100 00101 /* 00102 // */ 00103 00104 // To resolve: allocation of CamCache 00105 00106 // Set CACHE_STATS to 0 if you don't want loads of TRACE 00107 00108 00109 // Includes 00110 #include "camtypes.h" 00111 #include "fntcache.h" 00112 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 //#include "mario.h" 00114 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00115 //#include "pathtype.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00116 //#include "textfuns.h" 00117 #include "grndrgn.h" 00118 #include "gdraw.h" 00119 #include "fontman.h" 00120 #ifdef RALPH 00121 #include "ralphcri.h" 00122 #endif 00123 00124 #define CACHE_STATS 0 00125 #define new CAM_DEBUG_NEW 00126 00127 #if CACHE_STATS 00128 static UINT32 AveragePathSize = 0; 00129 static UINT32 MinPathSize = 0; 00130 static UINT32 MaxPathSize = 0; 00131 static UINT32 Hits = 0; 00132 static UINT32 Misses = 0; 00133 static UINT32 DisplayRate = 50; 00134 static UINT32 DisplayCnt = 0; 00135 #endif 00136 00137 00138 00139 // statics ... 00140 BOOL FontCache::InitCalled = FALSE; // Set to TRUE on entry to Init function 00141 CamCache* FontCache::pPathCache; 00142 INT32 FontCache::BoundsEntries = 0; // number of entries in char bounds cache 00143 00144 00145 /******************************************************************************************** 00146 00147 > static BOOL FontCache::Init() 00148 00149 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00150 Created: 16/1/95 00151 Inputs: - 00152 Outputs: - 00153 Returns: FALSE if we run out of memory. 00154 00155 Purpose: Initialises the FontCache. This function must be called before the cache can be 00156 used. 00157 00158 Scope: public 00159 00160 Errors: If we run out of memory during initialisation then FALSE is returned and 00161 an error is set. 00162 SeeAlso: - 00163 00164 ********************************************************************************************/ 00165 00166 00167 00168 BOOL FontCache::Init() 00169 { 00170 InitCalled = TRUE; 00171 BoundsEntries = 0; // reset number of char bounds cached 00172 00173 // Try to allocate our path cache 00174 pPathCache = new CamCache(); 00175 ERROR1IF(pPathCache == NULL, FALSE, _R(IDE_NOMORE_MEMORY)); 00176 return TRUE; 00177 } 00178 00179 00180 /******************************************************************************************** 00181 00182 > static void FontCache::DeInit() 00183 00184 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00185 Created: 16/1/95 00186 Inputs: - 00187 Outputs: - 00188 Returns: - 00189 00190 Purpose: Deinitialises the font cache 00191 00192 Scope: public 00193 00194 SeeAlso: - 00195 00196 ********************************************************************************************/ 00197 00198 void FontCache::DeInit() 00199 { 00200 BoundsEntries = 0; // reset number of char bounds cached 00201 00202 // Only DeInit if Init was called 00203 if (InitCalled) 00204 { 00205 // Delete the PathCache if one exists 00206 if (pPathCache) 00207 { 00208 delete (pPathCache); 00209 pPathCache = NULL; 00210 } 00211 } 00212 } 00213 00214 00215 00216 /******************************************************************************************** 00217 00218 > static BOOL FontCache::GetPath(CharDescription& ChDesc, INT32**Points, BYTE**Types, 00219 UINT32 *Length) 00220 00221 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00222 Created: 16/1/95 00223 Inputs: ChDesc: A description of the character you want me to find a path for 00224 00225 Outputs: Points: An array of path points 00226 Types: An array of path types 00227 Length: The path length 00228 00229 After calling GetPath you should create a 00230 00231 Returns: TRUE if we find the requested path 00232 FALSE if we run out of memory. An error is returned and the cache is left in 00233 a stable state. i.e GetPath can be called again and will succeed if the right 00234 amount of memory is available. 00235 Purpose: 00236 Errors: - 00237 Scope: public 00238 SeeAlso: - 00239 00240 ********************************************************************************************/ 00241 00242 BOOL FontCache::GetPath(CharDescription& ChDesc, INT32**Points, BYTE**Types, UINT32 *Length) 00243 { 00244 #ifdef RALPH 00245 RalphCriticalSection RalphCS; 00246 #endif 00247 00248 // Obtain the path associated with the path handle 00249 // ChDesc will only be required if the character does not exist in the cache 00250 // If the function runs out of memory then an error will be set. 00251 00252 ERROR3IF(pPathCache == NULL, "The FontCache has not been initialised"); 00253 00254 if (!pPathCache->FindPath( ChDesc.charHandle, *Points, *Types, *Length ) ) 00255 { 00256 // We need to generate the path here ! 00258 if (FONTMANAGER->GetCharPath(ChDesc, (DocCoord**)Points, (PathVerb**)Types, Length)) 00259 { 00260 #if CACHE_STATS 00261 Misses++; 00262 MinPathSize = (*Length < MinPathSize) ? *Length: MinPathSize; 00263 MaxPathSize = (*Length > MaxPathSize) ? *Length: MaxPathSize; 00264 AveragePathSize = (AveragePathSize + *Length) / 2; 00265 #endif 00266 pPathCache->AddPath( ChDesc.charHandle, *Points, *Types, *Length ); 00267 } 00268 else 00269 { 00270 return FALSE; // Probably ran out of memory 00271 } 00272 } 00273 #if CACHE_STATS 00274 else 00275 { 00276 Hits++; 00277 } 00278 00279 DisplayCnt++; 00280 00281 if (DisplayCnt == DisplayRate) 00282 { 00283 DisplayCnt = 0; 00284 // Display stats 00285 TRACE( _T("---")); 00286 TRACE( _T("Hits = %lu, Misses = %lu\n"), Hits, Misses); 00287 TRACE( _T("Average Path Size = %lu\n"), AveragePathSize*9); 00288 TRACE( _T("Minimum Path Size = %lu\n"), MinPathSize*9); 00289 TRACE( _T("Maximum Path Size = %lu\n"), MaxPathSize*9); 00290 TRACE( _T("Hit ratio = %lu\n\n"), Hits/Misses); 00291 } 00292 00293 00294 #endif 00295 00296 00297 00298 #if CACHE_STATS 00299 static INT32 Hitpc; 00300 #endif 00301 00302 00303 return TRUE; 00304 } 00305 00306 00307 /******************************************************************************************** 00308 > static BOOL FontCache::GetBounds(DocRect* pBounds, CharDescription& CharDesc) 00309 00310 Author: Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com> 00311 Created: 24/4/95 00312 Inputs: CharDesc - char description (charcode/bold/italic/fonthandle) 00313 Outputs: pBounds - bounds of the char 00314 Returns: FALSE if fails 00315 Purpose: Get the bounds of a char specified by the char descriptor at default size 00316 ********************************************************************************************/ 00317 00318 struct AttrdCharBoundsCacheEntry 00319 { 00320 CharDescription desc; 00321 DocRect bounds; 00322 }; 00323 00324 BOOL FontCache::GetBounds(DocRect* pBounds, CharDescription& CharDesc) 00325 { 00326 #ifdef RALPH 00327 RalphCriticalSection RalphCS; 00328 #endif 00329 00330 ERROR2IF(pBounds==NULL,FALSE,"FontCache::GetBounds() - pBounds==NULL"); 00331 00332 // static cache 00333 const INT32 MaxBoundsEntries = 128; 00334 static AttrdCharBoundsCacheEntry entry[MaxBoundsEntries]; 00335 00336 // search cache for desired char 00337 INT32 i=0; 00338 while (i<BoundsEntries) 00339 { 00340 if (entry[i].desc==CharDesc) 00341 break; 00342 i+=1; 00343 } 00344 00345 // if not found, either add to end of cache or replace a random entry 00346 if (i==BoundsEntries) 00347 { 00348 if (BoundsEntries<MaxBoundsEntries) 00349 i=BoundsEntries++; 00350 else 00351 i = rand() % MaxBoundsEntries; 00352 entry[i].desc=CharDesc; 00353 if (FontCache::CalcDefaultCharBounds(&(entry[i].bounds),CharDesc)==FALSE) 00354 return FALSE; 00355 } 00356 00357 // set output and return 00358 *pBounds=entry[i].bounds; 00359 return TRUE; 00360 } 00361 00362 00363 /******************************************************************************************** 00364 > static BOOL FontCache::CalcDefaultCharBounds(DocRect* pRect, CharDescription CharDesc) 00365 00366 Author: Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com> 00367 Created: 19/3/95 00368 Inputs: CharDesc - description of char (charcode/bold/italic/font handle) 00369 Outputs: pRect - bounding box of char 00370 Returns: FALSE if fails 00371 Purpose: Calculate the bounding box of the specified char at default size 00372 Note: Always uses zero line width (LineCapButt and BevelledJoin) 00373 ********************************************************************************************/ 00374 00375 BOOL FontCache::CalcDefaultCharBounds(DocRect* pRect, CharDescription& CharDesc) 00376 { 00377 #ifdef RALPH 00378 RalphCriticalSection RalphCS; 00379 #endif 00380 00381 ERROR2IF(pRect==NULL,FALSE,"FontCache::CalcCharBounds() - pRect==NULL"); 00382 00383 // create the char's default path 00384 Path* pCharPath=FontCache::CreateDefaultCharPath(CharDesc); 00385 if (pCharPath==NULL) 00386 return FALSE; 00387 00388 // calculate the path's bounds 00389 BOOL ok=TRUE; 00390 RECT DrawRect = {0, 0, 0, 0}; 00391 DocRect TempRect(0,0,0,0); 00392 INT32 points=pCharPath->GetNumCoords(); 00393 if (points!=0) 00394 { 00395 DocCoord* pCoords=pCharPath->GetCoordArray(); 00396 PathVerb* pVerbs =pCharPath->GetVerbArray(); 00397 00398 if (ok) ok=(pCoords!=NULL && pVerbs!=NULL); 00399 if (ok) 00400 { 00401 // Get hold of the default GDraw context. 00402 GDrawContext* pGDC = GRenderRegion::GetStaticDrawContext(); 00403 00404 if (pGDC == NULL) 00405 ok = FALSE; 00406 else 00407 ok = !pGDC->CalcStrokeBBox((POINT*)pCoords, pVerbs, points, &DrawRect, pCharPath->IsFilled, 0, CAPS_BUTT, JOIN_BEVEL, NULL); 00408 // TRACEUSER("wuerthne", _T("CDraw returned %d bbox from CDraw is %d, %d, %d, %d"), ok, DrawRect.left, DrawRect.top, DrawRect.right, DrawRect.bottom); 00409 00410 // if the call to GDraw failed then we fall back to calculating the bounds from the points bounding box. 00411 if (ok) 00412 { 00413 // GDraw did the job, so copy over the result from its RECT structure 00414 // NB: The following assignment *looks* wrong because top and bottom seem to be swapped over, 00415 // but it is correct! The fields in RECT are just named in a strange way - GDraw certainly 00416 // has the same view of the structure as DocRect, so we only need to copy the fields over 00417 // in the order as they are defined and things are OK. Alternatively, we could pass a pointer 00418 // to a DocRect to GDraw but this would break things if ever someone changed the representation 00419 // of DocRect. I hope noone changes the field names in RECT - the ENSURE protects us: 00420 ENSURE(&DrawRect.bottom > &DrawRect.right && &DrawRect.right > &DrawRect.top && &DrawRect.top > &DrawRect.left, 00421 "Incompatible change of RECT structure"); 00422 TempRect = DocRect(DrawRect.left, DrawRect.top, DrawRect.right, DrawRect.bottom); 00423 } 00424 else 00425 { 00426 // GDraw failed, so scan the coordinates 00427 // pCharPath->DumpPath(); 00428 TempRect = pCharPath->GetBoundingRect(); 00429 ok = TRUE; // Well not really ! 00430 } 00431 } 00432 } 00433 00434 // tidy up, set output and return 00435 delete pCharPath; 00436 if (ok) *pRect=TempRect; 00437 return ok; 00438 } 00439 00440 00441 /******************************************************************************************** 00442 > static Path* FontCache::CreateDefaultCharPath(CharDescription CharDesc) 00443 00444 Author: Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com> 00445 Created: 19/3/95 00446 Inputs: CharDesc - description of char (charcode/bold/italic/font handle) 00447 Returns: pointer to new path (else NULL if fails) 00448 Purpose: Create a path of the specified char at the default height 00449 Note: The caller is resposible for initialising the path's flags if required 00450 ********************************************************************************************/ 00451 00452 Path* FontCache::CreateDefaultCharPath(CharDescription& CharDesc) 00453 { 00454 #ifdef RALPH 00455 RalphCriticalSection RalphCS; 00456 #endif 00457 00458 // get pointers to the char's path data 00459 DocCoord* pCoords=NULL; 00460 PathVerb* pVerbs=NULL; 00461 UINT32 points=0; 00462 if (FontCache::GetPath(CharDesc, (INT32**)&pCoords, &pVerbs, &points)==FALSE) 00463 return FALSE; 00464 00465 // put path data into a Path structure 00466 Path* pPath=new Path(); 00467 ERROR2IF(pPath==NULL,NULL,"FontCache::CreateDefaultCharPath() - failed to create path"); 00468 BOOL ok=pPath->Initialise(points,12); 00469 if (ok) ok=pPath->CopyPathDataFrom(pCoords,pVerbs,points,TRUE); 00470 00471 // if not OK, delete any path that exists and set returned path pointer to NULL 00472 if (!ok) 00473 { 00474 delete pPath; 00475 pPath=NULL; 00476 } 00477 00478 return pPath; 00479 } 00480 00481 00482 // ---------------------------------------------------------------------------------------- 00483 // ---------------------------------------------------------------------------------------- 00484 // ---------------------------------------------------------------------------------------- 00485 00486 #if 0 00487 00488 00489 // 00490 // WE ARE NOT USING THE HI-TECH FONT CACHE AT THE MOMENT 00491 // 00492 // 00493 00494 00495 00496 // static data 00497 PathHandleItem* FontCache::CacheA [CACHE_LIST_SIZE]; 00498 PathHandleItem* FontCache::CacheB [CACHE_LIST_SIZE]; 00499 PathHandleItem** FontCache::PrimaryC; 00500 PathHandleItem** FontCache::SecondaryC; 00501 CamCache* FontCache::pPathCache; 00502 UINT32 FontCache::NextUniquePathHandle; 00503 UINT32 FontCache::NumItemsInPrimaryCache; 00504 00505 // For test purposes 00506 #ifdef _DEBUG 00507 UINT32 FontCache::PHits = 0; 00508 UINT32 FontCache::SHits = 0; 00509 UINT32 FontCache::Misses = 0; 00510 #endif 00511 00512 00513 BOOL FontCache::InitCalled = FALSE; // Set to TRUE on entry to Init function 00514 00515 DECLARE_SOURCE("$Revision: 1282 $") 00516 00517 // Declare nodes dynamic 00518 CC_IMPLEMENT_DYNAMIC(PathHandleItem, CCObject) 00519 CC_IMPLEMENT_DYNAMIC(CharDescription, CCObject) 00520 00521 00522 // Riks magic 00523 #define new CAM_DEBUG_NEW 00524 00525 // ------------------------------------------------------------------------------------------ 00526 // FontCache methods 00527 00528 00529 /******************************************************************************************** 00530 00531 > static BOOL FontCache::Init() 00532 00533 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00534 Created: 16/1/95 00535 Inputs: - 00536 Outputs: - 00537 Returns: FALSE if we run out of memory. 00538 00539 Purpose: Initialises the FontCache. This function must be called before the cache can be 00540 used. 00541 00542 Scope: public 00543 00544 Errors: If we run out of memory during initialisation then FALSE is returned and 00545 an error is set. 00546 SeeAlso: - 00547 00548 ********************************************************************************************/ 00549 00550 00551 00552 BOOL FontCache::Init() 00553 { 00554 InitCalled = TRUE; 00555 00556 // Nullify CacheA and CacheB arrays; 00557 for (INT32 i=0; (i < CACHE_LIST_SIZE); i++) 00558 { 00559 CacheA[i] = CacheB[i] = NULL; 00560 } 00561 00562 // initially CacheA will be used as the primary cache 00563 PrimaryC = CacheA; 00564 // and CacheB will be our secondary cache 00565 SecondaryC = CacheB; 00566 // Next PathHandle = 1. We use NULL as a failure return code 00567 NextUniquePathHandle = 1; 00568 // The primary cache is empty 00569 NumItemsInPrimaryCache = 0; 00570 00571 // Try to allocate our path cache 00572 pPathCache = new CamCache(); 00573 ERROR1IF(pPathCache == NULL, FALSE, _R(IDE_NOMORE_MEMORY)); 00574 return TRUE; 00575 } 00576 00577 00578 /******************************************************************************************** 00579 00580 > static void FontCache::DeInit() 00581 00582 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00583 Created: 16/1/95 00584 Inputs: - 00585 Outputs: - 00586 Returns: - 00587 00588 Purpose: Deinitialises the font cache 00589 00590 Scope: public 00591 00592 SeeAlso: - 00593 00594 ********************************************************************************************/ 00595 00596 void FontCache::DeInit() 00597 { 00598 // Only DeInit if Init was called 00599 if (InitCalled) 00600 { 00601 // Delete all lists 00602 00603 DeleteCacheLists(PrimaryC); 00604 DeleteCacheLists(SecondaryC); 00605 00606 // Delete the PathCache if one exists 00607 if (pPathCache) 00608 { 00609 delete (pPathCache); 00610 } 00611 } 00612 } 00613 00614 00615 00616 /******************************************************************************************** 00617 00618 > static BOOL FontCache::GetPath(CharDescription& ChDesc, INT32**Points, BYTE**Types, UINT32 *Length) 00619 00620 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00621 Created: 16/1/95 00622 Inputs: ChDesc: A description of the character you want me to find a path for 00623 00624 Outputs: Points: An array of path points 00625 Types: An array of path types 00626 Length: The path length 00627 00628 Returns: TRUE if we find the requested path 00629 FALSE if we run out of memory. An error is returned and the cache is left in 00630 a stable state. i.e GetPath can be called again and will succeed if the right 00631 amount of memory is available. 00632 Purpose: 00633 Errors: - 00634 Scope: public 00635 SeeAlso: - 00636 00637 ********************************************************************************************/ 00638 00639 BOOL FontCache::GetPath(CharDescription& ChDesc, INT32**Points, BYTE**Types, UINT32 *Length) 00640 { 00641 // Obtain a path handle 00642 UINT32 PathHandle = GetPathHandle(ChDesc); 00643 if (PathHandle == NULL) 00644 { 00645 // This situation can only arise if a new path handle had to be allocated 00646 // and we ran out of memory 00647 return FALSE; // Out of memory, the error will have been set 00648 } 00649 00650 // Obtain the path associated with the path handle 00651 // ChDesc will only be required if the character does not exist in the cache 00652 // If the function runs out of memory then an error will be set. 00653 BOOL ok = GetPathFromPathCache(PathHandle, ChDesc, Points, Types, Length); 00654 00655 return ok; 00656 } 00657 00658 00659 /******************************************************************************************** 00660 00661 > static UINT32 FontCache::GetPathHandle(CharDescription& ChDesc) 00662 00663 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00664 Created: 16/1/95 00665 Inputs: ChDesc: Specifies the character to obtain the path handle for. 00666 Outputs: - 00667 Returns: A path handle, or NULL if we are out of memory. Error is set in this situation 00668 and the cache is left in a stable state. 00669 00670 Purpose: Obtains a unique path handle for ChDesc 00671 Errors: - 00672 Scope: private 00673 SeeAlso: - 00674 00675 ********************************************************************************************/ 00676 00677 00678 UINT32 FontCache::GetPathHandle(CharDescription& ChDesc) 00679 { 00680 // Get our hash key 00681 UINT32 Key = GetHashKey(ChDesc); 00682 00683 // First let’s see if the the path handle for the char can be found in the Primary cache 00684 PathHandleItem* pPathHItem; 00685 pPathHItem = GetPathHandleItem(PrimaryC[Key], ChDesc); 00686 if (pPathHItem == NULL) 00687 { 00688 // OK the path handle is not in the primary cache 00689 // let’s try the secondary 00690 pPathHItem = GetPathHandleItem(SecondaryC[Key], ChDesc); 00691 if (pPathHItem == NULL) 00692 { 00693 //TRACE( _T("Miss\n")); 00694 //Misses++; 00695 // It’s not here either, try to create a new handle 00696 // error is set if we fail. 00697 pPathHItem = AllocatePathHandle(Key, ChDesc); 00698 // Have we run out of memory ? 00699 if (pPathHItem == NULL) 00700 { 00701 return NULL; 00702 } 00703 } 00704 else 00705 { 00706 //TRACE( _T("Hit - secondary\n")); 00707 //SHits++; 00708 00709 // We move the PathHandleItem to the primary cache, where 00710 // it will have a longer life. 00711 00712 // Unlink pPathHItem from secondary cache, otherwise TidyCache may delete it 00713 pPathHItem->RemoveItemFromList(&(SecondaryC[Key])); 00714 00715 // Tidy cache detects if the primary cache is full, if it is then we swap the primary and 00716 // secondary caches. Then clear the primary cache (the old secondary). 00717 TidyCache(); 00718 00719 pPathHItem->Next = PrimaryC[Key]; // NULL or old head 00720 PrimaryC[Key] = pPathHItem; // Move Item to head of list 00721 00722 NumItemsInPrimaryCache++; 00723 } 00724 } 00725 // else 00726 // { 00727 //TRACE( _T("Hit - primary\n")); 00728 //PHits++; 00729 // } 00730 return pPathHItem->PathHandle; 00731 } 00732 00733 /******************************************************************************************** 00734 00735 > static PathHandleItem* FontCache::GetPathHandleItem(PathHandleItem* ListHead, 00736 CharDescription& ChDesc) 00737 00738 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00739 Created: 16/1/95 00740 Inputs: ListHead: A pointer to the head of the list 00741 ChDesc: The ChDesc to find 00742 Outputs: - 00743 Returns: The PathHandleItem if found, otherwise NULL 00744 Purpose: Searches cache list for a PathHandleItem associated with ChDesc 00745 Errors: - 00746 Scope: private 00747 SeeAlso: - 00748 00749 ********************************************************************************************/ 00750 00751 00752 PathHandleItem* FontCache::GetPathHandleItem(PathHandleItem* ListHead, 00753 CharDescription& ChDesc) 00754 { 00755 PathHandleItem* pCurrent = ListHead; 00756 while (pCurrent != NULL) 00757 { 00758 if (pCurrent->CharDesc == ChDesc) 00759 { 00760 // found 00761 break; 00762 } 00763 pCurrent = pCurrent->Next; 00764 } 00765 return pCurrent; 00766 } 00767 00768 /******************************************************************************************** 00769 00770 > static PathHandleItem* FontCache::AllocatePathHandle(UINT32 Key, CharDescription& ChDesc) 00771 00772 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00773 Created: 16/1/95 00774 Inputs: Key: The position in the PrimaryC where we should insert the new PathHandleItem 00775 ChDesc: The character description we should generate the path handle for 00776 Outputs: - 00777 Returns: The new PathHandleItem or NULL, if we run out of memory. An error is set in this 00778 situation. 00779 Purpose: Allocates a new PathHandleItem for ChDesc and adds this to the primary cache. 00780 Errors: - 00781 Scope: private 00782 SeeAlso: - 00783 00784 ********************************************************************************************/ 00785 00786 00787 00788 PathHandleItem* FontCache::AllocatePathHandle(UINT32 Key, CharDescription& ChDesc) 00789 { 00790 // Have we run out of path handles, We have 4 Billion to play with ie. 00791 // if we allocate 1 every sec we will run out in about 126 years 00792 00793 if (NextUniquePathHandle == MAX_PATH_HANDLE) 00794 { 00795 // We've run out of handles, this calls for drastic action. 00796 // First let’s clear our cache lists. 00797 00798 DeleteCacheLists(PrimaryC); 00799 DeleteCacheLists(SecondaryC); 00800 00801 // We must also clear the CamCache, there is no function to do this so we will 00802 // use the destructor. 00803 00804 delete (pPathCache); 00805 00806 if (!Init()) 00807 { 00808 // We are left with no path cache. The GetPathFromPathCache fn should cope with this 00809 // Init will have set the error 00810 return FALSE; 00811 } 00812 } 00813 00814 // Try to create the path handle 00815 PathHandleItem* pPathHItem = new PathHandleItem(ChDesc, NextUniquePathHandle); 00816 ERROR1IF(pPathHItem == NULL, NULL, _R(IDE_NOMORE_MEMORY)) 00817 00818 NextUniquePathHandle++; 00819 00820 // Tidy cache detects if the primary cache is full, if it is then we swap the primary and 00821 // secondary caches. Then clear the primary cache (the old secondary). 00822 00823 TidyCache(); 00824 00825 // Add our new path handle item to the PrimaryC 00826 pPathHItem->Next = PrimaryC[Key]; // NULL or old head 00827 PrimaryC[Key] = pPathHItem; // Move Item to head of list 00828 NumItemsInPrimaryCache++; 00829 00830 return pPathHItem; 00831 } 00832 00833 /******************************************************************************************** 00834 00835 > static void FontCache::TidyCache() 00836 00837 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00838 Created: 16/1/95 00839 Inputs: - 00840 Outputs: - 00841 Returns: - 00842 Purpose: The TidyCache routine is called before a new item is added to the primary 00843 cache. 00844 00845 It checks if the PrimaryC is full and if it is performs special processing 00846 to keep cache access efficient. As a result of calling this function the 00847 contents of the secondary cache may be deleted. 00848 00849 00850 Errors: - 00851 scope: private 00852 SeeAlso: - 00853 00854 ********************************************************************************************/ 00855 00856 void FontCache::TidyCache() 00857 { 00858 // Is the primary cache full 00859 ERROR3IF(NumItemsInPrimaryCache > MAX_ITEMS_IN_CACHE_LIST, "Too many items in primary cache"); 00860 if (NumItemsInPrimaryCache == MAX_ITEMS_IN_CACHE_LIST) 00861 { 00862 //TRACE( _T("Cache full\n")); 00863 // yup ok let’s do the clever bit 00864 // The secondary cache contains the least frequently used path handles 00865 // so we delete these 00866 DeleteCacheLists(SecondaryC); 00867 00868 // Swap the primary and secondary caches 00869 PathHandleItem** Temp = PrimaryC; 00870 PrimaryC = SecondaryC; // The primary cache is now empty 00871 SecondaryC = Temp; 00872 00873 // The PrimaryC contains no items 00874 NumItemsInPrimaryCache = 0; 00875 00876 00877 } 00878 return; 00879 } 00880 00881 00882 /******************************************************************************************** 00883 00884 > BOOL FontCache::GetPathFromPathCache(UINT32 PathHandle, 00885 CharDescription& ChDesc, 00886 INT32** Points, 00887 BYTE** Types, 00888 UINT32* Length) 00889 00890 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00891 Created: 16/1/95 00892 Inputs: PathHandle: 00893 00894 Outputs: Points: An array of path points 00895 Types: An array of path types 00896 Length: The path length 00897 00898 Returns: FALSE if we run out of memory, in this situation an error is returned. 00899 00900 Purpose: Returns back a path for the character described by ChDesc. If the path is 00901 not found in the cache then the routine tries to generate it. 00902 Errors: - 00903 Scope: private 00904 SeeAlso: - 00905 00906 ********************************************************************************************/ 00907 00908 BOOL FontCache::GetPathFromPathCache(UINT32 PathHandle, 00909 CharDescription& ChDesc, 00910 INT32** Points, 00911 BYTE** Types, 00912 UINT32* Length 00913 ) 00914 00915 { 00916 // The first thing to check is that a PathCache exists 00917 if (pPathCache == NULL) 00918 { 00919 // There is no path cache. This is possible, it may have been deleted when we ran out 00920 // of memory before. 00921 00922 // try to allocate one 00923 pPathCache = new CamCache(); 00924 ERROR1IF(pPathCache == NULL, FALSE, _R(IDE_NOMORE_MEMORY)); 00925 00926 } 00927 if ( !pPathCache->FindPath( PathHandle, *Points, *Types, *Length )) 00928 { 00929 // Call the GetPath routine which will generate a path from the 00930 // ChDesc. 00931 /* 00932 // Waiting for GeneratePath ? 00933 00934 if (GeneratePath( ChDesc, Points,Types,Length )) 00935 { 00936 PathCache->AddPath( PathHandle, *Points, *Types, *Length ); 00937 } 00938 00939 else return FALSE; 00940 */ 00941 //TRACE( _T("Path not found")); 00942 } 00943 return TRUE; 00944 } 00945 00946 00947 /******************************************************************************************** 00948 00949 > static void FontCache::DeleteCacheLists(PathHandleItem** pCacheArray) 00950 00951 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00952 Created: 17/1/95 00953 Inputs: pCacheArray: The array to delete lists for 00954 Outputs: - 00955 Returns: - 00956 Purpose: Deletes all lists for the specified cache array. All Items in the array 00957 are left nullified. 00958 Errors: - 00959 Scope: private 00960 SeeAlso: - 00961 00962 ********************************************************************************************/ 00963 00964 00965 00966 void FontCache::DeleteCacheLists(PathHandleItem** pCacheList) 00967 { 00968 PathHandleItem* p; 00969 PathHandleItem* pNxt; 00970 00971 for (INT32 i = 0; i< CACHE_LIST_SIZE; i++) 00972 { 00973 p = pCacheList[i]; // Get head of list 00974 while ( p != NULL) // while list still contains item 00975 { 00976 pNxt = p->Next; // hand over hand 00977 delete p; 00978 p = pNxt; 00979 } 00980 pCacheList[i] = NULL; 00981 } 00982 } 00983 00984 00985 /******************************************************************************************** 00986 00987 > UINT32 FontCache::GetHashKey(CharDescription& ChDesc) 00988 00989 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00990 Created: 16/1/95 00991 Inputs: ChDesc: Used to generate hash key 00992 Outputs: - 00993 Returns: - 00994 Purpose: A hashing function which generates a key from ChDesc. The key is in the range 00995 0..CACHE_LIST_SIZE-1. Taken from compiler dragon book. 00996 Errors: - 00997 SeeAlso: - 00998 00999 ********************************************************************************************/ 01000 01001 01002 UINT32 FontCache::GetHashKey(CharDescription& ChDesc) 01003 { 01004 BYTE* p; 01005 01006 // Obtain the size of the CharDescription structure 01007 INT32 ChDescSize = sizeof(CharDescription); 01008 01009 // perform some magic 01010 // for each byte, shift the bits of h left 4 positions and add in the byte.If any of the 01011 // four high-order bits of h is 1, shift the four bits right 24 positions, exclusive-or them 01012 // into h, and reset to 0 any of the four bits right 24 positions, exclusive or them into h, and 01013 // reset to 0 any of the four high-order bits that was 1. 01014 01015 unsigned h = 0; 01016 unsigned g; 01017 01018 INT32 i = 0; 01019 01020 for (p =(BYTE*)(&ChDesc); i < ChDescSize; p++) 01021 { 01022 h = (h << 4) + (*p); 01023 if (g = h & 0xf0000000) 01024 { 01025 h = h^ (g >> 24); 01026 h = h ^ g; 01027 } 01028 i++; 01029 } 01030 01031 01032 return (h % CACHE_LIST_SIZE); 01033 } 01034 01035 // ------------------------------------------------------------------------------------------ 01036 // CharDescription methods 01037 01038 /******************************************************************************************** 01039 01040 > INT32 CharDescription::operator==(const CharDescription& ChDesc) 01041 01042 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01043 Created: 18/1/95 01044 Inputs: ChDesc: The CharDescription to compare with 01045 Outputs: - 01046 Returns: TRUE if ChDesc == this 01047 Purpose: CharDescription == operator 01048 Errors: - 01049 SeeAlso: - 01050 01051 ********************************************************************************************/ 01052 01053 01054 01055 INT32 CharDescription::operator==(const CharDescription& ChDesc) 01056 { 01057 return ((Bold == ChDesc.Bold) && 01058 (Italic == ChDesc.Italic) && 01059 (HTypeface == ChDesc.HTypeface) && 01060 (CharCode == ChDesc.CharCode)); 01061 } 01062 01063 /******************************************************************************************** 01064 01065 > CharDescription& CharDescription::operator=(CharDescription& ChDesc) 01066 01067 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01068 Created: 18/1/95 01069 Inputs: ChDesc: The CharDescription to assign to this 01070 Outputs: - 01071 Returns: *this 01072 Purpose: CharDescription = operator 01073 Errors: - 01074 SeeAlso: - 01075 01076 ********************************************************************************************/ 01077 01078 CharDescription& CharDescription::operator=(CharDescription& ChDesc) 01079 { 01080 Bold = ChDesc.Bold; 01081 Italic = ChDesc.Italic; 01082 HTypeface = ChDesc.HTypeface; 01083 CharCode = ChDesc.CharCode; 01084 return *this; 01085 } 01086 01087 01088 // ---------------------------------------------------------------------------------------- 01089 // PathHandleItem methods 01090 01091 /******************************************************************************************** 01092 01093 > PathHandleItem::PathHandleItem(CharDescription& ChDesc, UINT32 PathHandle) 01094 01095 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01096 Created: 17/1/95 01097 Inputs: ChDesc: The ChDesc for the PathHandleItem to be generated 01098 PathHandle: The path handle to associate 01099 Outputs: - 01100 Returns: - 01101 Purpose: Creates a new PathHandleItem with a NULL Next pointer 01102 Errors: - 01103 SeeAlso: - 01104 01105 ********************************************************************************************/ 01106 01107 01108 PathHandleItem::PathHandleItem(CharDescription& ChDesc, UINT32 PathHandle) 01109 { 01110 CharDesc = ChDesc; 01111 PathHandleItem::PathHandle = PathHandle; 01112 Next = NULL; 01113 } 01114 01115 /******************************************************************************************** 01116 01117 > static void PathHandleItem::RemoveItemFromList(PathHandleItem** pHead) 01118 01119 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01120 Created: 17/1/95 01121 Inputs: A pointer to the head of the list on which this item lives 01122 Outputs: - 01123 Returns: - 01124 Purpose: Removes this item from the specified list. An ERROR3 will occur if the item 01125 does not live on this list. 01126 Errors: - 01127 Scope: private 01128 SeeAlso: - 01129 01130 ********************************************************************************************/ 01131 01132 void PathHandleItem::RemoveItemFromList(PathHandleItem** pHead) 01133 { 01134 // The list should not be empty 01135 ERROR3IF(*pHead == NULL, "Trying to remove an item from an empty list"); 01136 01137 // Are we at the head of the list ? 01138 if (*pHead == this) 01139 { 01140 *pHead = Next; // The head of the list points to our next sibling, which may be NULL 01141 } 01142 else 01143 { 01144 // Search for the item 01145 PathHandleItem* Prev = NULL; // Need to keep track of our previous node 01146 PathHandleItem* p = *pHead; 01147 while ( p != NULL) 01148 { 01149 if (p == this) 01150 { 01151 // weve found ourselves 01152 ERROR3IF(Prev == NULL, "We should not live at the head of the list"); 01153 Prev->Next = Next; 01154 break; 01155 } 01156 Prev = p; 01157 p = p->Next; 01158 } 01159 ERROR3IF(p == NULL, "Cannot remove an item from a list it doesn't live in"); 01160 } 01161 01162 Next = NULL; // We are no longer in the list 01163 } 01164 01165 // Test routines 01166 01167 #ifdef _DEBUG 01168 01169 // Test 1 generates random Character descriptions and tries to add them to the cache 01170 // Used to test all paths through the code. 01171 // It assumes that Init has been called on the cache 01172 01173 void FontCache::Test1() 01174 { 01175 INT32* p; 01176 BYTE* t; 01177 UINT32 l; 01178 01179 // We will only vary the ChDesc's CharCode. It is important that we hash well on this 01180 // anyway. 01181 CharDescription ChDesc; 01182 ChDesc.Height = 10; 01183 ChDesc.Width = 10; 01184 ChDesc.Italic = FALSE; 01185 01186 INT32 n; 01187 for (n=0;n<10000;n++) 01188 { 01189 if (n==255) 01190 { 01191 PHits = 0; SHits =0; Misses = 0; 01192 } 01193 TRACE( _T("n = %d "),n); 01194 // generate a random CharacterDescription to find 0..5 01195 ChDesc.CharCode = (rand() % 255) + 1; 01196 // And try to obtain path for Ch 01197 if (!FontCache::GetPath(ChDesc, &p, &t, &l)) 01198 { 01199 InformError(); 01200 } 01201 } 01202 TRACE( _T("------\n")); 01203 TRACE( _T("pHits = %lu, SHits = %lu, Miss = %lu\n"), PHits, SHits, Misses); 01204 } 01205 01206 01207 01208 #endif 01209 #endif