fntcache.cpp

Go to the documentation of this file.
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

Generated on Sat Nov 10 03:45:20 2007 for Camelot by  doxygen 1.4.4