/******************************Module*Header*******************************\ * Module Name: cache.cxx * * * * Non-inline methods for font cache objects. * * * * Created: 11-Apr-1991 16:54:54 * * Author: Gilman Wong [gilmanw] * * * * Copyright (c) 1991-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" extern "C" VOID vInitFontCache(); #pragma alloc_text(INIT, vInitFontCache) #define COPYSMALLMETRICS(pgdn, pgd) \ { \ RtlCopyMemory((PVOID)(pgdn), (PVOID)(pgd), offsetof(GLYPHDATA,fxInkTop)); \ } #define ROUND_TO_PAGE(x) (((x)+PAGE_SIZE-1)&~(PAGE_SIZE-1)) // binary cache search extern const BYTE acBits[16]; extern const INT aiStart[17]; /******************************Public*Routine******************************\ * vInitFontCache * * Initializes the CACHE_PARM structure from the [FontCache] section of * WIN.INI. * * Returns: * TRUE if successful, FALSE otherwise. * \**************************************************************************/ #define CJMININCREMENT 0x2000 #define CJMAX (8 * 0x2000) /******************************Public*Routine******************************\ * RFONTOBJ::bInitCache * * UNICODE GLYPHDATA CACHE: * * Reserves and commits glyphbit cache memory and allocates GLYPHDATA memory * from the heap in 1024 bytes chunks. * * ______ ______ ______ * pgdThreshold--> | | | | | | * | G D | | G D | | G D | * | l a | | l a | | l a | * pgdNext--> | y t | | y t | | y t | * | p a | | p a | | p a | * | h | | h | | h | * |------| |------| |------| * pc->pdblBase -->| link | --> | link | --> | NULL | * |______| |______| |______| * | WCPG | * |______| * * * * * * * Preloads the default glyph, in anticipation of need, and to avoid * loading it multiple times. * * Builds empty WCGP, sets RFONT mode to cache. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * * 31-Nov-1994 -by- Gerrit van Wingerden * Re-rewrote it to cache GLYPHDATA more effieciently. * * 21-Apr-1992 -by- Paul Butzi * Rewrote it. * * 15-Apr-1991 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ // we want number divisible by 8 containing about 75 glyphs, // almost an upper limit on number of glyphs in the metrics cache // when running winstone memory constrained scenario #define GD_INC (76 * offsetof(GLYPHDATA,fxInkTop)) // GD_INC amounts to 1520 == 000005f0H, far less than a page. // according to Kirk's statistics, very few realizations cache more // than 60 glyphs, so we shall start with a block which contains about // 60 glyphs #define C_GLYPHS_IN_BLOCK 64 BOOL RFONTOBJ::bInitCache(FLONG flType) { #if DBG IFIOBJ ifio(prfnt->ppfe->pifi); TRACE_CACHE(( " -- TRACE_CACHE --\n" " RFONTOBJ::bInitCache\n" " FaceName = \"%ws\"\n" " ExAllocatePoolWithTag\n", ifio.pwszFaceName() )); #endif CACHE *pc = &(prfnt->cache); // Set the pointer to null. vDeleteCache will free memory from // any non-null pointers. This simplifies cleanup, since bRealize // ensures that vDeleteCache is called if this routine fails. // metrics portion pc->pdblBase = NULL; pc->cMetrics = 0; // no metrics in the cache yet // glyphbits portion pc->cjbbl = pc->cBlocks = pc->cBlocksMax = 0; pc->pbblBase = pc->pbblCur = NULL; pc->pgbNext = pc->pgbThreshold = NULL; pc->cjbblInitial = 0; // no bits in the cache to begin with. pc->cGlyphs = 0; // no bits in the cache to begin with. pc->cjTotal = 0; // nothing used yet // aux mem portion pc->pjAuxCacheMem = NULL; pc->cjAuxCacheMem = 0; prfnt->wcgp = NULL; // First, figure out how big the max glyph will be // Default is zero - glyphdata size is not counted! pc->cjGlyphMax = 0; switch ( prfnt->ulContent ) { case FO_HGLYPHS: case FO_GLYPHBITS: pc->cjGlyphMax = prfnt->cjGlyphMax; break; case FO_PATHOBJ: // oh, yeah? Got a better guess? // Here we are putting an upper bound on glyph outline data. // Unlike the bitmap case, in the outline case the font driver // can not give us a cjGlyphMax number we can trust to be sufficient // for all glyphs. // Even if the font driver new this number, bFlatten may // alter ie. increase the number of points so much that even as huge a // number as cjMax/2 may not suffice for some glyphs. So we // had better be prepared to fail gracefully in the pgbCheckGlyphbits // routine if that is the case. pc->cjGlyphMax = CJMAX / 2; break; } // this is used in few places below, remember it: ULONG cjGlyphMaxX2 = 2 * pc->cjGlyphMax; // if we can't even get one glyph in a maximum size cache, don't cache // Note that we need room for the default glyph and one other glyph prfnt->flType = flType; if ((prfnt->ulContent != FO_HGLYPHS) && (cjGlyphMaxX2 > CJMAX)) { // // Glyph exceeds maximum cache memory size, so we will revert to // caching just the metrics. This will speed up things like // GetCharWidths, and stuff that just *has* to have the glyphs // will use the lookaside stuff (previously called BigGlyph) prfnt->flType |= RFONT_TYPE_NOCACHE; } // get the width of the break character HGLYPH hg = hgXlat(prfnt->ppfe->pifi->wcBreakChar); // do not bother to store this in the cache PDEVOBJ pdo(prfnt->hdevProducer); // Call font driver to get the metrics. ULONG ulMode = QFD_GLYPHANDBITMAP; if (prfnt->ulContent == FO_PATHOBJ) { ulMode = QFD_GLYPHANDOUTLINE; } GLYPHDATA gd; if (pdo.QueryFontData( prfnt->dhpdev, pfo(), ulMode, hg, &gd, NULL, 0) == FD_ERROR) { WARNING("bGetBreak: QueryFontData failed\n"); return FALSE; } prfnt->fxBreak = gd.fxD; prfnt->hgBreak = hg; // Now we have everything ready to fly. Handle some little details: // set up the cache semaphore. if(!(prfnt->ppfe->flPFE & PFE_EUDC)) { prfnt->hsemEUDC = GreCreateSemaphore(); if (!prfnt->hsemEUDC) return FALSE; } else prfnt->hsemEUDC = NULL; prfnt->hsemCache = GreCreateSemaphore(); if (!prfnt->hsemCache) { if (prfnt->hsemEUDC) { GreDeleteSemaphore(prfnt->hsemEUDC); prfnt->hsemEUDC = NULL; } WARNING("Semaphore creation failed in bInitCache\n"); return FALSE; } return TRUE; } BOOL RFONTOBJ::bAllocateCache(RFONTOBJ* prfoBase) { FLONG flType = prfnt->flType; WCGP *wcgp; CACHE *pc = &(prfnt->cache); ULONG cjGlyphMaxX2 = 2 * pc->cjGlyphMax; FLONG flEUDC = 0; // calculate the size of the WCGP structure GISET *pgiset; FD_GLYPHSET *pfdg; ULONG cRuns; ULONG cGlyphsTotal; if (flType & RFONT_TYPE_UNICODE) { pfdg = prfnt->pfdg; cRuns = pfdg->cRuns; cGlyphsTotal = pfdg->cGlyphsSupported; } else // RFONT_TYPE_HGLYPH { if (prfnt->ppfe->pgiset) { pgiset = prfnt->ppfe->pgiset; cRuns = pgiset->cGiRuns; cGlyphsTotal = pgiset->cgiTotal; } else { // The mapper should have prevented us from getting here. // However, in case we have a bug in the mapper we still do // not want to go down in flames: WARNING("gdi: attempting to init cache for non glyph index font\n"); return FALSE; } } ULONGSIZE_T sizeWCGP = (ULONGSIZE_T)(offsetof(WCGP, agpRun) + cRuns * sizeof(GPRUN) + cGlyphsTotal * sizeof(GLYPHDATA*)); #if DBG if (flType & RFONT_TYPE_UNICODE) { SIZE_T cGlyphs = 0; for (UINT i = 0; i < pfdg->cRuns; i += 1) { cGlyphs += pfdg->awcrun[i].cGlyphs; } ASSERTGDI(cGlyphs == pfdg->cGlyphsSupported, "cache.cxx, cGlyphs init\n"); } #endif // The distribution of metics per realized font w/ Winstone97 is: // // 43% <= 0 Metrics // 50% <= 6 Metrics // 76% <= 32 Metrics // 99% <= 216 Metrics // 100% <= 249 Metrics // // Make the allocation either be smaller than a page or be multiples // of the pages size due to the current pool allocation scheme. // // If the 32 GD's + the runs and pointers fit with plenty of space // left then use that. Otherwize, otherwize round up to the page // boundary (but watch that at least a few fit). // // We round up to a page boundary if we're going to use more than 3K // on the premise that we are more likely to be able to page it out // if we don't have multiple uses for it. // now figure out how much space we will need for at least the WCPG // and one "block" of glyphdata structures. // Insure that the DATABLOCK's following the GLYPHDATA pointers is // maximally aligned ULONGSIZE_T dpDATABLOCK = ALIGN8(sizeWCGP); ULONGSIZE_T cjInitData; int GlyphDataSize = bSmallMetrics() ? offsetof(GLYPHDATA,fxInkTop) : sizeof(GLYPHDATA); if ((dpDATABLOCK + GDI_POOL_HEADER_SIZE + (GlyphDataSize * 32)) <= (PAGE_SIZE*3/4)) { // Use a partial page cjInitData = dpDATABLOCK + (32 * GlyphDataSize); } else { // Use a full page but make sure that at least a few fit. If // there's less that 128 bytes left on the last page allocate // another. // round up to page, somewhat arbitrary cjInitData = ROUND_TO_PAGE(dpDATABLOCK); if ((cjInitData - dpDATABLOCK) < 128){ cjInitData += PAGE_SIZE; } } BYTE *pjRunAndData; // Allocate enough memory for the WCGP and one GLYPHDATA block if ((pjRunAndData = (BYTE*)PALLOCNOZ(cjInitData, 'cacG')) == NULL) { WARNING("win32k!bAllocateCache failed on first attempt \n"); // not enough memory, try to clean up all inactive RFONT lists // release the fmCache semaphores for both linked fonts if any // and the base font, otherwise it might cause deadlock if (prfoBase) { PRFONT prfntBase = prfoBase->prfntFont(); { GreAcquireSemaphore(prfntBase->hsemEUDC); if (prfntBase->flEUDCState & TT_SYSTEM_INITIALIZED) { ASSERTGDI(prfoBase->prfntSystemTT(), "bAllocate cache NULL prfntSysTT\n"); if (prfoBase->prfntSystemTT()) { flEUDC |= TT_SYSTEM_INITIALIZED; prfntBase->flEUDCState &= ~TT_SYSTEM_INITIALIZED; GreReleaseSemaphore(prfoBase->prfntSystemTT()->hsemCache); } } if (prfntBase->flEUDCState & EUDC_INITIALIZED) { flEUDC |= EUDC_INITIALIZED; prfntBase->flEUDCState &= ~EUDC_INITIALIZED; if (prfoBase->prfntSysEUDC()) { GreReleaseSemaphore(prfoBase->prfntSysEUDC()->hsemCache); } if (prfoBase->prfntDefEUDC()) { GreReleaseSemaphore(prfoBase->prfntDefEUDC()->hsemCache); } for( UINT ii = 0; ii < prfoBase->uiNumFaceNameLinks(); ii++ ) { if( prfntBase->paprfntFaceName[ii] != NULL ) { GreReleaseSemaphore(prfntBase->paprfntFaceName[ii]->hsemCache); } } } GreReleaseSemaphore(prfntBase->hsemEUDC); } GreReleaseSemaphore(prfntBase->hsemCache); } else { GreReleaseSemaphore(prfnt->hsemCache); } // // Winbug 399722: instead of using semaphores to secure the // InactiveRont list cleanup process (which caused deadlock), // we restrict the process's access to the UMPD pdev's. // PPDEV ppdev; GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL); for (ppdev=gppdevList; ppdev; ppdev=ppdev->ppdevNext) { PDEVOBJ pdo((HDEV)ppdev); if (!pdo.bFontDriver() && (!pdo.bUMPD() || pdo.pid() == (PW32PROCESS)W32GetCurrentProcess()) ) { ppdev->cPdevRefs++; break; } } GreReleaseSemaphoreEx(ghsemDriverMgmt); while(ppdev) { PDEVOBJ pdoCurrent((HDEV)ppdev); vRemoveAllInactiveRFONTs(ppdev); GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL); for (ppdev = ppdev->ppdevNext; ppdev; ppdev=ppdev->ppdevNext) { PDEVOBJ pdoNext((HDEV)ppdev); if (!pdoNext.bFontDriver() && (!pdoNext.bUMPD() || pdoNext.pid() == (PW32PROCESS)W32GetCurrentProcess()) ) { ppdev->cPdevRefs++; break; } } GreReleaseSemaphoreEx(ghsemDriverMgmt); pdoCurrent.vUnreferencePdev(); } // Grab the fmCache semaphore again if (prfoBase) { PRFONT prfntBase = prfoBase->prfntFont(); GreAcquireSemaphore(prfntBase->hsemCache); { GreAcquireSemaphore(prfntBase->hsemEUDC); if ((flEUDC & TT_SYSTEM_INITIALIZED) && !(prfntBase->flEUDCState & TT_SYSTEM_INITIALIZED) ) { prfntBase->flEUDCState |= TT_SYSTEM_INITIALIZED; GreAcquireSemaphore(prfoBase->prfntSystemTT()->hsemCache); } if ((flEUDC & EUDC_INITIALIZED) && !(prfntBase->flEUDCState & EUDC_INITIALIZED) ) { prfntBase->flEUDCState |= EUDC_INITIALIZED; if (prfoBase->prfntSysEUDC()) { GreAcquireSemaphore(prfoBase->prfntSysEUDC()->hsemCache); } if (prfoBase->prfntDefEUDC()) { GreAcquireSemaphore(prfoBase->prfntDefEUDC()->hsemCache); } for( UINT ii = 0; ii < prfoBase->uiNumFaceNameLinks(); ii++ ) { if( prfntBase->paprfntFaceName[ii] != NULL ) { GreAcquireSemaphore(prfntBase->paprfntFaceName[ii]->hsemCache); } } } GreReleaseSemaphore(prfntBase->hsemEUDC); } } else { GreAcquireSemaphore(prfnt->hsemCache); } // Check whether the cache has been allocated // while we clean up the inactive RFONTs. // Don't allocate again if it has been done. if (prfnt->wcgp) { return TRUE; } else { if ((pjRunAndData = (BYTE*)PALLOCNOZ(cjInitData, 'cacG')) == NULL) { WARNING("win32k!bAllocateCache failed to on second attempt\n"); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return (FALSE); } } } // The debug pool allocation functions do not return // page aligned memory. #if 0 #if DBG if (cjInitData >= (PAGE_SIZE - GDI_POOL_HEADER_SIZE)) { if ((((ULONG_PTR)pjRunAndData) & (PAGE_SIZE - 1)) != 0) DbgPrint("pjRunAndData alignment (0x%08lx, 0x%p)\n", cjInitData, pjRunAndData); } #endif #endif // Set up the WCPG stucture prfnt->wcgp = (WCGP *) pjRunAndData; wcgp = prfnt->wcgp; wcgp->cRuns = cRuns; wcgp->pgdDefault = (GLYPHDATA *) NULL; GLYPHDATA **ppgd = (GLYPHDATA **)&(wcgp->agpRun[wcgp->cRuns]); // init all glyphdata pointers to zero RtlZeroMemory(ppgd, sizeof(GLYPHDATA*) * cGlyphsTotal); if (flType & RFONT_TYPE_UNICODE) { for (UINT i = 0; i < cRuns; i += 1 ) { GPRUN *pRun = &wcgp->agpRun[i]; WCRUN *pWCRun = &(pfdg->awcrun[i]); pRun->apgd = ppgd; pRun->wcLow = (UINT) pWCRun->wcLow; pRun->cGlyphs = pWCRun->cGlyphs; ppgd += pRun->cGlyphs; } } else // RFONT_TYPE_HGLYPH { for (UINT i = 0; i < cRuns; i += 1 ) { GPRUN *pRun = &wcgp->agpRun[i]; GIRUN *pgiRun = &(pgiset->agirun[i]); pRun->apgd = ppgd; pRun->wcLow = pgiRun->giLow; pRun->cGlyphs = pgiRun->cgi; ppgd += pRun->cGlyphs; } } // Now we will set up the parameters for the GLYPHDATA // part of the cache. We are assured we are aligned properly. pc->pdblBase = (DATABLOCK *)(pjRunAndData + dpDATABLOCK); // init head to null. This value will stay null always. pc->pdblBase->pdblNext = (DATABLOCK*)NULL; pc->pgdNext = &pc->pdblBase->agd[0]; // end of the current block and first block, same in this case pc->pjFirstBlockEnd = pjRunAndData + cjInitData; pc->pgdThreshold = (GLYPHDATA *)pc->pjFirstBlockEnd; // Now, the GLYPHDATA portion is all set. Go ahead and set up the // space for the GLYPHBITS or PATHOBJS if needed. if ((prfnt->ulContent != FO_HGLYPHS) && !(prfnt->flType & RFONT_TYPE_NOCACHE)) { // 47% <= 0 Glyphs // 55% <= 1 Glyphs // 75% <= 16 Glyphs // 95% <= 58 Glyphs // 100% <= 217 Glyphs // // Special case the first allocation, since most realizations use // <= 16 Glyphs. Make the first allocation 16 glyphs if that size fits // well within a page boundary. // // Subsequent allocations will be in page aligned allocations w/ // enough to hold 16 max sized glyphs. This will be limited to // CJMAX KB // // If caching glyphs, at least 2 glyphs (default one and another one, // see NOCACHE above, will fit in CJMAX). if (prfnt->ulContent == FO_PATHOBJ) { // this seems to work and this is what we did before DavidFie changes // for PATHOBJ case pc->cjbbl = pc->cjbblInitial = cjGlyphMaxX2; } else { ULONG cjBytes = 16 * pc->cjGlyphMax; ULONG AllocationSize = ROUND_TO_PAGE(cjBytes); if (AllocationSize <= CJMININCREMENT) { pc->cjbbl = AllocationSize; pc->cjbblInitial = (cjBytes < (PAGE_SIZE*3/4)) ? cjBytes : AllocationSize; } else { cjBytes = 8 * pc->cjGlyphMax; if (cjBytes <= CJMININCREMENT) { pc->cjbbl = pc->cjbblInitial = CJMININCREMENT; } else { AllocationSize = ROUND_TO_PAGE(cjBytes); pc->cjbbl = pc->cjbblInitial = MIN(AllocationSize, CJMAX); ASSERTGDI(pc->cjbbl >= cjGlyphMaxX2, "two glyphs don't fit in the cache\n"); } } } // we shall re-interpret cjMax to mean the max number of bytes in // glyphbits portion of the cache per 1K of glyphs in the font. // That is for larger fonts we shall allow more glyphbits // memory per realization than for ordinary US fonts. This will be // particularly important for FE fonts. This same code will work fine // in their case too: pc->cBlocksMax = (CJMAX * ((cGlyphsTotal + 1024 - 1)/1024)) / pc->cjbbl; ASSERTGDI(pc->cjbbl <= CJMAX, "bogus cache initializaiton\n"); } // We decide whether or not to invoke the binary search based on the // number of runs in the font. For large numbers of runs it makes // sense to do a binary search. For small numbers of runs a linear // search will be better. Right now I use 200 as the cutoff for // a linear search because I am sure that a binary search will be // faster for this number of runs. We should do some experimentation // to find the OPTIMAL cutoff in the future. [gerritv]\ // Arial etc has about 90 runs, Lucida Sans Unicode 65 #define BINARY_CUTOFF 200 if(prfnt->wcgp->cRuns > BINARY_CUTOFF) { pc->iMax = prfnt->wcgp->cRuns - 1; if( pc->iMax & 0xF000 ) { pc->cBits = acBits[(pc->iMax >> 12) & 0x00FF] + 12; } else if( pc->iMax & 0x0F00 ) { pc->cBits = acBits[(pc->iMax >> 8) & 0x00FF] + 8; } else if( pc->iMax & 0x00F0 ) { pc->cBits = acBits[(pc->iMax >> 4) & 0x00FF] + 4; } else { pc->cBits = acBits[pc->iMax]; } pc->iFirst = aiStart[pc->cBits]; } else { // setting iMax to zero signifies a linear search pc->iMax = 0; } return (TRUE); } /******************************Public*Routine******************************\ * RFONTOBJ::vDeleteCache * * Destroy the font cache object (CACHE). * * Returns FALSE if the function fails. * * History: * 15-Apr-1991 -by- Gilman Wong [gilmanw] * Wrote it. * * 24-Nov-92 -by- Paul Butzi * Rewrote it. * Fri 08-Sep-1995 -by- Bodin Dresevic [BodinD] * update: Rewrote one more time \**************************************************************************/ VOID RFONTOBJ::vDeleteCache () { CACHE *pc = &prfnt->cache; // Free up glyph data portion of the cache: DATABLOCK *pdbl = pc->pdblBase; // We are counting on the while loop to free prfnt->wcpg so // ppv better always be non-NULL when prfnt->wcpg is. ASSERTGDI( ((prfnt->wcgp == NULL ) || (pdbl != (DATABLOCK*)NULL)), "vDeleteCache: prfnt->wcgp non-NULL but pc->pdblBase was NULL\n"); // walks the list of blocks of GLYPHDATA and frees all of them. while (pdbl) { DATABLOCK *pdblTmp = pdbl; pdbl = pdbl->pdblNext; if (pdbl == NULL) { // this is the first block so wcpg really points to its base. VFREEMEM(prfnt->wcgp); } else { VFREEMEM(pdblTmp); } } pc->pdblBase = NULL; prfnt->wcgp = NULL; // Free up glyphbits portion of the cache, if it was ever allocated if (pc->pbblBase != NULL) { BITBLOCK * pbbl, *pbblNext; for (pbbl = pc->pbblBase; pbbl; pbbl = pbblNext) { pbblNext = pbbl->pbblNext; VFREEMEM(pbbl); } pc->pbblBase = NULL; } // free aux memory if it was used if (prfnt->cache.pjAuxCacheMem != NULL) { VFREEMEM((PVOID) prfnt->cache.pjAuxCacheMem); prfnt->cache.pjAuxCacheMem = NULL; prfnt->cache.cjAuxCacheMem = 0; } return; } /******************************Public*Routine******************************\ * BOOL RFONTOBJ::bGetGlyphMetrics * * Translate wchars into an array of GLYPHPOS structures, filling in * the pointer to GLYPHDATA field. Only the metrics are assured to be * valid; no attempt is made to ensure that the glyph data itself is * present in the cache before the return to the caller. * * This routine is to be used primarily by GetTextExtent and GetCharWidths, * which have no need for anything except metrics. * * A zero return means that we failed to insert the metrics due to some * hard error, most likely a failure to commit memory in the glyph * insertion routine. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ #pragma optimize("t", on) BOOL RFONTOBJ::bGetGlyphMetrics ( COUNT c, GLYPHPOS *pgp, WCHAR *pwc, XDCOBJ *pdco, ESTROBJ *pto ) { if (prfnt->wcgp == NULL) { if (!bAllocateCache()) { return(FALSE); } } WCGP *pwcgp = prfnt->wcgp; WCHAR *pwcInit = pwc; // need to check for empty glyphset if (pwcgp->cRuns == 0) { WARNING("bGetGlyphMetrics - empty glyphset\n"); for (; c != 0; --c, ++pgp) { pgp->hg = hgDefault(); pgp->pgdf = (GLYPHDEF *) pgdDefault(); } return TRUE; } GPRUN *pwcRun = pwcgp->agpRun; // initialize with guess for loop below GLYPHDATA *wpgd; for (WCHAR *pwcEnd = pwc + c; pwc < pwcEnd; pwc+=1, pgp+=1) { WCHAR wc = *pwc; // Find the correct run, if any. // Try the current run first. UINT swc = (UINT)wc - pwcRun->wcLow; if ( swc >= pwcRun->cGlyphs ) { // This path should go out of line pwcRun = gprunFindRun(wc); swc = (UINT)wc - pwcRun->wcLow; if ( swc < pwcRun->cGlyphs ) { wpgd = pwcRun->apgd[swc]; } else { BOOL bAccel; if ((wpgd = wpgdGetLinkMetricsPlus(pdco,pto, pwc, pwcInit, c, &bAccel, FALSE)) == NULL) { WARNING("wpgdGetLinkMetricsPlus return NULL\n"); return FALSE; } } } else { // Look up entry in current run // This path should go in line wpgd = pwcRun->apgd[swc]; } // check to make sure in cache, insert if needed if ( wpgd == NULL ) { // This path should go out of line if ( !bInsertMetrics(&pwcRun->apgd[swc], wc) ) { // when insert fails trying to get just metrics, it is a hard // failure. Get out of here! WARNING("bGetGlyphMetrics - bInsertMetrics failed\n"); return FALSE; } wpgd = pwcRun->apgd[swc]; } // set the pgp and go on to the next wc pgp->hg = wpgd->hg; pgp->pgdf = (GLYPHDEF *) wpgd; } return TRUE; } /******************************Public*Routine******************************\ * BOOL RFONTOBJ::bGetGlyphMetricsPlus * * Translate wchars into an array of GLYPHPOS structures, filling in * the pointer to GLYPHDATA field. Although only the metrics are assured to be * valid, an attempt is made to ensure that the glyph data itself is * present in the cache before the return to the caller. Failure in this * attempt is indicated by clearing the flag *pbAccel. This allows the * text code to tell the device driver that the STROBJ_bEnum callback is * not needed. * * This routine is to be used primarily by TextOut and its kin. * * A zero return means that we failed to insert the metrics due to some * hard error, most likely a failure to commit memory in the glyph * insertion routine. * * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ BOOL RFONTOBJ::bGetGlyphMetricsPlus ( COUNT c, GLYPHPOS *pgp, WCHAR *pwc, BOOL *pbAccel, XDCOBJ *pdco, ESTROBJ *pto ) { if (prfnt->wcgp == NULL) { if (!bAllocateCache()) { return(FALSE); } } // if this realization is intended for the device that does not need bits, // let us make sure we do not rasterize these bits, unless the device driver // really wants the bits (for whatever reason), // in which case this device driver will have to call STROBJ_bEnum for the string // that it wants rasterized: PDEVOBJ po(hdevConsumer()); if ( (po.flGraphicsCapsNotDynamic() & GCAPS_FONT_RASTERIZER) && (prfnt->ulContent == FO_GLYPHBITS) ) { if (pbAccel) { *pbAccel = FALSE; } return bGetGlyphMetrics(c, pgp, pwc, pdco, pto); } WCHAR *pwcInit = pwc; *pbAccel = TRUE; // need to check for empty glyphset if (prfnt->wcgp->cRuns == 0) { WARNING("bGetGlyphMetricsPlus - empty glyphset\n"); for (; c != 0; --c, ++pgp) { pgp->hg = hgDefault(); pgp->pgdf = (GLYPHDEF *) pgdDefault(); } return TRUE; } GPRUN *pwcRun = prfnt->wcgp->agpRun; // initialize with guess for loop below for (WCHAR *pwcEnd = pwc + c; pwc < pwcEnd; pwc+=1, pgp+=1) { GLYPHDATA *wpgd; // Find the correct run, if any. // Try the current run first. UINT swc = (UINT)*pwc - pwcRun->wcLow; if ( swc >= pwcRun->cGlyphs ) { // This path should go out of line pwcRun = gprunFindRun(*pwc); swc = (UINT)*pwc - pwcRun->wcLow; if ( swc < pwcRun->cGlyphs ) { wpgd = pwcRun->apgd[swc]; } else { // wpgdGetLinkMetricsPlus should never return NULL if ((wpgd = wpgdGetLinkMetricsPlus(pdco, pto, pwc, pwcInit,c, pbAccel, TRUE)) == NULL) { WARNING("wpgdGetLinkMetricsPlus returns NULL\n"); return FALSE; } } } else { // Look up entry in current run // This path should go in line wpgd = pwcRun->apgd[swc]; } // check to make sure in cache, insert if needed if ( wpgd == NULL ) { // This path should go out of line if ( !bInsertMetricsPlus(&pwcRun->apgd[swc], *pwc) ) { // when insert fails trying to get just metrics, it is a hard // failure. Get out of here! WARNING("bGetGlyphMetricsPlus - bInsertMetrics failed\n"); return FALSE; } wpgd = pwcRun->apgd[swc]; } // Try to ensure that the glyph bits are there, too. // Don't bother if we already are going to mess with the driver if ( (wpgd->gdf.pgb == NULL) && *pbAccel ) { // this path should go out of line if ( (prfnt->ulContent != FO_HGLYPHS) && !bInsertGlyphbits(wpgd, pwc == pwcInit) ) { *pbAccel = 0; } } // set the pgp and go on to the next wc pgp->hg = wpgd->hg; pgp->pgdf = (GLYPHDEF *) wpgd; } return TRUE; } #pragma optimize("", on) /******************************Public*Routine******************************\ * COUNT RFONTOBJ::cGetGlyphDataCache * * Run along an array of GLYPHPOS structures which have been filled in * by a call to bGetGlyphMetricsPlus. Fill in any missing pointers to * the glyph data in the referenced GLYPHDATA structures, filling the * cache as needed. If the cache is full, and we are not trying to get * the very first GLYPHDATA referenced by the array passed in, just return. * * If, on the other hand, we are still dealing with the first element of * the array, we needn't be concerned about invalidating the pointers in * the already worked on portion, so we can tell the glyph insertion routine * that it can flush the cache with impunity. * * This routine is to be used exclusively by the STROBJ_bEnum callback. * * Historical Note: * In the olden days, we were not so clever, and font caches had the * metrics for glyphs and the glyphs themselves joined together. This * had the unpleasant effect of invalidating the pointers in the * GLYPHPOS array in addition to invalidating the pointers in the * (discarded) GLYPHDATA structures in the font cache. * * Now that that is no longer the case, the callback does not need to * pass the string down to this routine, and we never have to do the * wchar->GLYPHPOS* translation twice! Isn't that nice? * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ COUNT RFONTOBJ::cGetGlyphDataCache( COUNT c, GLYPHPOS *pgpStart ) { if ( prfnt->ulContent == FO_HGLYPHS ) return c; GLYPHPOS *pgpEnd = pgpStart + c; for ( GLYPHPOS *pgp = pgpStart; pgp < pgpEnd; pgp += 1 ) { GLYPHDEF *pgdf = pgp->pgdf; ASSERTGDI(pgdf != NULL, "cGetGlyphDataCache - pgdf == NULL"); // If the pointer is already valid, just move on if ( pgdf->pgb != NULL ) continue; // If the insertion attempt fails, we're full // Sundown: safe to truncate to COUNT since pgpEnd = pgpStart + c if ( !bInsertGlyphbits( (GLYPHDATA*)(pgp->pgdf), pgp == pgpStart) ) return (COUNT)(pgp - pgpStart); } // Sundown: safe to truncate to COUNT since pgpEnd = pgpStart + c return (COUNT)(pgp - pgpStart); } /******************************Public*Routine******************************\ * COUNT RFONTOBJ::cGetGlyphDataLookaside * * For now, just handle the first entry. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ COUNT RFONTOBJ::cGetGlyphDataLookaside( COUNT c, GLYPHPOS *pgp ) { if ( c == 0 ) return 0; if ( !bInsertGlyphbitsLookaside(pgp, prfnt->ulContent)) return 0; return 1; } /******************************Public*Routine******************************\ * GPRUN * RFONTOBJ::gprunFindRun * * Given a wchar, run along the GPRUN structures and find the * entry which contains the char, if any. If not found, return pointer * to last run examined. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ GPRUN * RFONTOBJ::gprunFindRun( WCHAR wc ) { ASSERTGDI(prfnt->wcgp != NULL, "xprunFindRunRFONTOBJ: wcgp is NULL\n"); WCGP *pwcgp = prfnt->wcgp; // callers handle cRuns == 0 case ASSERTGDI(pwcgp->cRuns != 0, "pwcgp->cRuns is 0\n"); if(!prfnt->cache.iMax) { GPRUN *pwcRunLow = pwcgp->agpRun; GPRUN *pwcRunHi = pwcgp->agpRun + (pwcgp->cRuns - 1); // The worst case case is pwcRunHi->wcLow == 0xffff and pwcRunHi->cGlyphs == 1 UINT swc = (UINT)(pwcRunHi->wcLow + pwcRunHi->cGlyphs - 1); if (wc > swc) { return pwcRunLow; } for ( GPRUN *pwcRun = pwcRunLow; pwcRun <= pwcRunHi; pwcRun += 1 ) { UINT nwc = wc - pwcRun->wcLow; if ( nwc < pwcRun->cGlyphs ) { return pwcRun; } } return pwcRunLow; } else { // do a binary search int iThis, iMax; GPRUN *pwcRun; GPRUN *pwcRunBase = pwcgp->agpRun; if( wc < pwcRunBase->wcLow) { return( pwcRunBase ); } iThis = prfnt->cache.iFirst; iMax = prfnt->cache.iMax; switch( prfnt->cache.cBits ) { case 16: iThis += (wc >= pwcRunBase[iThis].wcLow) ? 32768 : 0; iThis -= 16384; case 15: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 16384 : 0; iThis -= 8192; case 14: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 8192 : 0; iThis -= 4096; case 13: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 4096 : 0; iThis -= 2048; case 12: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 2048 : 0; iThis -= 1024; case 11: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 1024 : 0; iThis -= 512; case 10: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 512 : 0; iThis -= 256; case 9: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 256 : 0; iThis -= 128; case 8: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 128 : 0; iThis -= 64; case 7: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 64 : 0; iThis -= 32; case 6: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 32 : 0; iThis -= 16; case 5: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 16 : 0; iThis -= 8; case 4: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 8 : 0; iThis -= 4; case 3: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 4 : 0; iThis -= 2; case 2: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 2 : 0; iThis -= 1; case 1: iThis += ((iThis <= iMax) && (wc >= pwcRunBase[iThis].wcLow)) ? 1 : 0; iThis -= 1; case 0: break; } pwcRun = &pwcRunBase[iThis]; // This is our candidate. if( wc - pwcRun->wcLow >= (INT) pwcRun->cGlyphs ) { return( pwcRunBase ); } return( pwcRun ); } } /******************************Public*Routine******************************\ * BOOL xInsertMetricsRFONTOBJ * * Insert the requested glyph's metrics into the font cache. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ extern "C" BOOL xInsertMetricsRFONTOBJ ( PRFONTOBJ pRfont, GLYPHDATA **ppgd, WCHAR wc ) { HGLYPH hg; WCGP *pwcgp; pwcgp = pRfont->prfnt->wcgp; if (pRfont->prfnt->flType & RFONT_TYPE_UNICODE) { hg = pRfont->hgXlat(wc); } else { hg = (HGLYPH)wc; // here is the tiny speed advantage } // Make sure we don't insert the default glyph more than once. // Just return the correct answer if we know it. if ( (hg == pRfont->prfnt->hgDefault) && (pwcgp->pgdDefault != (GLYPHDATA *) NULL) ) { *ppgd = pwcgp->pgdDefault; return(TRUE); } CACHE *pc = &pRfont->prfnt->cache; // Verify enough room in metrics cache area, grow if needed. // Note that failure to fit a glyphdata is a hard error, get out now. if (!pRfont->bCheckMetricsCache()) { WARNING("xInsertMetricsRFONTOBJ - bCheckMetricsCache failed!\n"); return FALSE; } ASSERTGDI(pc->pgdNext < pc->pgdThreshold, "xInsertMetricsRFONTOBJ - no room in cache\n"); // These constructors used to be in the calling routine - cGet***** // That was the wrong place because we anticipate many calls that never // miss the cache. Better to have them here and lock on every miss. PDEVOBJ pdo(pRfont->prfnt->hdevProducer); // Call font driver to get the metrics. ULONG ulMode = QFD_GLYPHANDBITMAP; if ( pRfont->prfnt->ulContent == FO_PATHOBJ ) { ulMode = QFD_GLYPHANDOUTLINE; } GLYPHDATA gd; if (pdo.QueryFontData( pRfont->prfnt->dhpdev, pRfont->pfo(), ulMode, hg, pRfont->bSmallMetrics() ? &gd : pc->pgdNext, NULL, 0) == FD_ERROR) { WARNING("xInsertMetricsRFONTOBJ: QueryFontData failed\n"); return FALSE; } if (pRfont->bSmallMetrics()) COPYSMALLMETRICS(pc->pgdNext, &gd); ASSERTGDI(pc->pgdNext->hg == hg, "xInsertMetricsRFONTOBJ - hg not set\n"); pc->pgdNext->gdf.pgb = NULL; // Set returned value, adjust cache, indicate success *ppgd = pc->pgdNext; if( pRfont->bSmallMetrics() ) { pc->pgdNext = (GLYPHDATA*) (((BYTE*) pc->pgdNext ) + offsetof(GLYPHDATA,fxInkTop)); } else { pc->pgdNext += 1; } #if DBG pc->cMetrics += 1; #endif return TRUE; } ULONG ulClearTypeFilter(GLYPHBITS *pgb, GLYPHDATA *pgd, PRFONT prfnt); /******************************Public*Routine******************************\ * BOOL xInsertMetricsPlusRFONTOBJ * * Insert the requested glyph's metrics into the font cache. * In addition, try to get the glyph data, too, but don't flush the * cache to try to get them. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ extern "C" BOOL xInsertMetricsPlusRFONTOBJ( PRFONTOBJ pRfont, GLYPHDATA **ppgd, WCHAR wc ) { HGLYPH hg; WCGP *pwcgp; pwcgp = pRfont->prfnt->wcgp; if (pRfont->prfnt->flType & RFONT_TYPE_UNICODE) { hg = pRfont->hgXlat(wc); } else { hg = (HGLYPH)wc; // here is the tiny speed advantage } // Make sure we don't insert the default glyph more than once. // Just return the correct answer if we know it. if ( (hg == pRfont->prfnt->hgDefault) && (pwcgp->pgdDefault != (GLYPHDATA *) NULL) ) { *ppgd = pwcgp->pgdDefault; return(TRUE); } CACHE *pc = &pRfont->prfnt->cache; // If only getting hglyphs, use bInsertMetrics if (pRfont->prfnt->ulContent == FO_HGLYPHS) { return pRfont->bInsertMetrics(ppgd, wc); } // Verify enough room in metrics cache area, grow if needed. // Note that failure to fit a glyphdata is a hard error, get out now. if (!pRfont->bCheckMetricsCache()) { WARNING("bInsertMetricsPlus - bCheckMetricsCache failed!\n"); return FALSE; } ASSERTGDI(pc->pgdNext < pc->pgdThreshold, "bInsertMetricsPlus - no room in cache\n"); // Handle paths somewhere else! if ( pRfont->prfnt->ulContent == FO_PATHOBJ ) { return pRfont->bInsertMetricsPlusPath(ppgd, wc); } // These constructors used to be in the calling routine - cGet***** // That was the wrong place because we anticipate many calls that never // miss the cache. Better to have them here and lock on every miss. PDEVOBJ pdo(pRfont->prfnt->hdevProducer); // Look to see if there is room in the glyphbits cache // Grow the glyphbits cache if neccessary, but don't flush the cache ULONG cjNeeded; GLYPHDATA gd; // If mode is paths, or max glyph will fit, assume max glyph // otherwise, call up and ask how big if (pc->cjGlyphMax < (SIZE_T)(pc->pgbThreshold - pc->pgbNext)) { cjNeeded = pc->cjGlyphMax; } else { cjNeeded = pdo.QueryFontData( pRfont->prfnt->dhpdev, pRfont->pfo(), QFD_GLYPHANDBITMAP, hg, &gd, NULL, 0); if ( cjNeeded == FD_ERROR ) { WARNING("bInsertGlyphMetricsPlus - qfd for size failed\n"); return FALSE; } if (pRfont->pfo()->flFontType & FO_CLEARTYPE_X) { ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left); ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top); cjNeeded = CJ_CTGD(cx+2,cy); } } // We will try to fit the glyphbits in. If they fit, they'll go // in at pc->pgbNext, so we set that as the default. // If they won't fit, we'll set the pointer to null to avoid getting // the bits VOID *pgb = pRfont->pgbCheckGlyphCache(cjNeeded); GLYPHDATA *pgd = pRfont->bSmallMetrics() ? &gd : pc->pgdNext; // Call font driver to get the metrics. cjNeeded = pdo.QueryFontData( pRfont->prfnt->dhpdev, pRfont->pfo(), QFD_GLYPHANDBITMAP, hg, pgd, pgb, cjNeeded); if ( cjNeeded == FD_ERROR ) { WARNING("bInsertGlyphMetricsPlus - qfd for data failed\n"); return FALSE; } // need to add CLEARTYPE filtering correction again: if (pRfont->pfo()->flFontType & FO_CLEARTYPE_X) { ULONG cx = (ULONG)(pgd->rclInk.right - pgd->rclInk.left); ULONG cy = (ULONG)(pgd->rclInk.bottom - pgd->rclInk.top); cjNeeded = CJ_CTGD(cx+2,cy); if (pgb) { cjNeeded = ulClearTypeFilter((GLYPHBITS *)pgb, pgd, pRfont->prfnt); } } #if DBG if (pgb) { //TRACE_INSERT(("xInsertMetricsPlus: inserted hg = 0x%lx, cj = 0x%lx at pgbNext: 0x%lx\n", hg, cjNeeded, pgb)); } else { TRACE_INSERT(("xInsertMetricsPlus: cound not insert hg = 0x%lx, cj = 0x%lx\n", hg, cjNeeded)); } #endif if (pRfont->bSmallMetrics()) COPYSMALLMETRICS(pc->pgdNext, &gd); ASSERTGDI(pc->pgdNext->hg == hg, "bInsertMetricsPlus - hg not set\n"); ASSERTGDI(pc->pgdNext->gdf.pgb == pgb, "bInsertMetricsPlus - pgb not set\n"); // Set the returned value *ppgd = pc->pgdNext; // Adjust the cache next pointers as needed. if (pRfont->bSmallMetrics()) { pc->pgdNext = (GLYPHDATA*)(((BYTE*)pc->pgdNext) + offsetof(GLYPHDATA,fxInkTop)); } else { pc->pgdNext += 1; } #if DBG pc->cMetrics += 1; #endif if ( pgb != NULL ) { pc->pgbNext += cjNeeded; #if DBG pc->cGlyphs += 1; pc->cjTotal += cjNeeded; #endif } return TRUE; } /******************************Public*Routine******************************\ * BOOL bInsertMetricsPlusPath * * Insert the requested glyph's metrics into the font cache. * In addition, try to get the glyph data, too, but don't flush the * cache to try to get them. * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ BOOL RFONTOBJ::bInsertMetricsPlusPath( GLYPHDATA **ppgd, WCHAR wc ) { HGLYPH hg; CACHE *pc = &prfnt->cache; if (prfnt->wcgp == NULL) { if (!bAllocateCache()) { return(FALSE); } } if (prfnt->flType & RFONT_TYPE_UNICODE) { hg = hgXlat(wc); } else { hg = (HGLYPH)wc; // here is the tiny speed advantage } // These constructors used to be in the calling routine - cGet***** // That was the wrong place because we anticipate many calls that never // miss the cache. Better to have them here and lock on every miss. PDEVOBJ pdo(prfnt->hdevProducer); PATHMEMOBJ pmo; if (!pmo.bValid()) return(FALSE); // Call font driver to get the metrics. GLYPHDATA gd; ULONG cjNeeded = pdo.QueryFontData( prfnt->dhpdev, pfo(), QFD_GLYPHANDOUTLINE, hg, bSmallMetrics() ? &gd : pc->pgdNext, &pmo, 0); if ( cjNeeded == FD_ERROR ) return FALSE; if (bSmallMetrics()) COPYSMALLMETRICS(pc->pgdNext, &gd); PDEVOBJ pdoCon(prfnt->hdevConsumer); // Don't cache bezier glyphs for display drivers even if GCAPS_BEZIERS is // set because dynamic mode changes may cause this capability to change at // any time. if ( (pdo.bDisplayPDEV()) || ((pdo.flGraphicsCaps() & GCAPS_BEZIERS) == 0) ) { if (!pmo.bFlatten()) return FALSE; } ASSERTGDI(pc->pgdNext->hg == hg, "bInsertMetricsPlus - hg not set\n"); cjNeeded = offsetof(EPATHFONTOBJ, pa.apr) + pmo.cjSize(); VOID *pgb = pgbCheckGlyphCache(cjNeeded); if ( pgb != NULL ) { EPATHFONTOBJ *epfo = (EPATHFONTOBJ *)pgb; epfo->vInit(cjNeeded); epfo->bClone(pmo); pc->pgdNext->gdf.ppo = (PATHOBJ *)epfo; } else { pc->pgdNext->gdf.ppo = NULL; } // Set the returned value *ppgd = pc->pgdNext; // Adjust the cache next pointers as needed. if( prfnt->cache.bSmallMetrics ) { pc->pgdNext = (GLYPHDATA*)(((BYTE*)pc->pgdNext) + offsetof(GLYPHDATA,fxInkTop)); } else { pc->pgdNext += 1; } #if DBG pc->cMetrics += 1; #endif if ( pgb != NULL ) { pc->pgbNext += cjNeeded; #if DBG pc->cGlyphs += 1; pc->cjTotal += cjNeeded; #endif } return TRUE; } /******************************Public*Routine******************************\ * BOOL xInsertGlyphbitsRFONTOBJ * * Insert the requested glyph into the glyph cache * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ extern "C" BOOL xInsertGlyphbitsRFONTOBJ( PRFONTOBJ pRfont, GLYPHDATA *pgd, ULONG bFlushOk ) { CACHE *pc = &pRfont->prfnt->cache; if ( (pRfont->prfnt->flType & RFONT_TYPE_NOCACHE) || (pRfont->prfnt->ulContent == FO_HGLYPHS) ) { return FALSE; } if ( pRfont->prfnt->ulContent == FO_PATHOBJ ) return pRfont->bInsertGlyphbitsPath(pgd, bFlushOk); PDEVOBJ pdo(pRfont->prfnt->hdevProducer); // Look to see if there is room in the glyphbits cache // Grow the glyphbits cache if neccessary, but don't flush the cache ULONG cjNeeded; // If max glyph will fit, assume max glyph // otherwise, call up and ask how big GLYPHDATA gd; if ( (pc->cjGlyphMax < (SIZE_T)(pc->pgbThreshold - pc->pgbNext)) ) { cjNeeded = pc->cjGlyphMax; } else { cjNeeded = pdo.QueryFontData( pRfont->prfnt->dhpdev, pRfont->pfo(), QFD_GLYPHANDBITMAP, pgd->hg, &gd, NULL, 0); if ( cjNeeded == FD_ERROR ) return FALSE; if (pRfont->pfo()->flFontType & FO_CLEARTYPE_X) { ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left); ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top); cjNeeded = CJ_CTGD(cx+2,cy); } } // Now, we try to fit the bits in. If they fit, fine. // If not, and we can flush the cache, we flush it and try again. // If we couldn't flush, or we flushed and still fail, just return. GLYPHBITS *pgb; TRACE_INSERT(("InsertGlyphbits: attempting to insert bits at: 0x%lx\n", pc->pgbNext)); while ((pgb = (GLYPHBITS *)pRfont->pgbCheckGlyphCache(cjNeeded)) == NULL) { if ( !bFlushOk ) return FALSE; TRACE_INSERT(("InsertGlyphbits: Flushing the cache\n")); pRfont->vFlushCache(); bFlushOk = FALSE; } // Call font driver to get the metrics. cjNeeded = pdo.QueryFontData( pRfont->prfnt->dhpdev, pRfont->pfo(), QFD_GLYPHANDBITMAP, pgd->hg, &gd, (VOID *)pgb, cjNeeded); if ( cjNeeded == FD_ERROR ) return FALSE; if (pRfont->pfo()->flFontType & FO_CLEARTYPE_X) { ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left); ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top); cjNeeded = CJ_CTGD(cx+2,cy); if (pgb) { cjNeeded = ulClearTypeFilter(pgb, &gd, pRfont->prfnt); } } #if DBG if(cjNeeded > pc->cjGlyphMax) { DbgPrint("cjNeeded = %x cjGlyphmax = %x\n", cjNeeded, pc->cjGlyphMax); DbgBreakPoint(); } #endif TRACE_INSERT(("InsertGlyphbits: inserted hg = 0x%lx, cj = 0x%lx at pgbNext: 0x%lx\n", pgd->hg, cjNeeded, pc->pgbNext)); // Set the returned value pgd->gdf.pgb = pgb; // Adjust the cache next pointers as needed. pc->pgbNext += cjNeeded; #if DBG pc->cGlyphs += 1; pc->cjTotal += cjNeeded; #endif return TRUE; } /******************************Public*Routine******************************\ * BOOL bInsertGlyphbitsPath * * Insert the requested glyph into the glyph cache * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ BOOL RFONTOBJ::bInsertGlyphbitsPath( GLYPHDATA *pgd, ULONG bFlushOk ) { CACHE *pc = &prfnt->cache; ASSERTGDI(prfnt->wcgp != NULL, "bInsertGlyphbitsPath: wcgp is NULL\n"); ASSERTGDI(!(prfnt->flType & RFONT_TYPE_NOCACHE), "bInsertGlyphbitsPath: NOCACHE cache type\n"); // These constructors used to be in the calling routine - cGet***** // That was the wrong place because we anticipate many calls that never // miss the cache. Better to have them here and lock on every miss. PDEVOBJ pdo(prfnt->hdevProducer); PATHMEMOBJ pmo; if (!pmo.bValid()) { return FALSE; // MEMORY ALLOC FAILED, HMGR routines log error code } // Call font driver to get the path ULONG cjNeeded = pdo.QueryFontData( prfnt->dhpdev, pfo(), QFD_GLYPHANDOUTLINE, pgd->hg, (GLYPHDATA *)NULL, &pmo, 0); if ( cjNeeded == FD_ERROR ) return FALSE; PDEVOBJ pdoCon(prfnt->hdevConsumer); // Don't cache bezier glyphs for display drivers even if GCAPS_BEZIERS is // set because dynamic mode changes may cause this capability to change at // any time. if ( (pdoCon.bDisplayPDEV()) || ((pdoCon.flGraphicsCaps() & GCAPS_BEZIERS) == 0) ) { if (!pmo.bFlatten()) return FALSE; } cjNeeded = offsetof(EPATHFONTOBJ, pa.apr) + pmo.cjSize(); // Now, we try to fit the bits in. If they fit, fine. // If not, and we can flush the cache, we flush it and try again. // If we couldn't flush, or we flushed and still fail, just return. VOID *pgb; while ( (pgb = pgbCheckGlyphCache(cjNeeded)) == NULL ) { if ( !bFlushOk ) return FALSE; vFlushCache(); bFlushOk = FALSE; } EPATHFONTOBJ *epfo = (EPATHFONTOBJ *)pgb; epfo->vInit(cjNeeded); epfo->bClone(pmo); // Set the returned value pgd->gdf.ppo = (PATHOBJ *)epfo; // Adjust the cache next pointers as needed. pc->pgbNext += cjNeeded; #if DBG pc->cGlyphs += 1; pc->cjTotal += cjNeeded; #endif return TRUE; } /******************************Public*Routine******************************\ * BOOL RFONTOBJ::bInsertGlyphbitsLookaside * * Get the glyph bits into the lookaside buffer * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ BOOL RFONTOBJ::bInsertGlyphbitsLookaside( GLYPHPOS *pgp, ULONG imode ) { if ( imode == FO_PATHOBJ ) return bInsertPathLookaside(pgp); CACHE *pc = &prfnt->cache; // Make sure the lookaside buffer has enough room for the bitmap ULONGSIZE_T cjMaxBitmap = prfnt->cjGlyphMax + sizeof(GLYPHDATA); // ensure that cjGlyphMax > offsetof(GLYPHBITS,aj) and that // the cjMaxBitmap calculation didn't overflow if ( cjMaxBitmap < sizeof(GLYPHDATA)+ offsetof(GLYPHBITS,aj) ) { WARNING("bGetGlyphbitsLookaside - cjGlyphMax invalid\n"); return FALSE; } // Allocate the buffer and save its size if existing buffer isn't big enough if( prfnt->cache.cjAuxCacheMem < cjMaxBitmap ) { if ( prfnt->cache.pjAuxCacheMem != NULL ) { VFREEMEM(prfnt->cache.pjAuxCacheMem); } prfnt->cache.pjAuxCacheMem = (PBYTE)PALLOCMEM(cjMaxBitmap, 'cacG'); if ( prfnt->cache.pjAuxCacheMem == NULL ) { prfnt->cache.cjAuxCacheMem = 0; WARNING("bGetGlyphbitsLookaside - error allocating buffer\n"); return FALSE; } prfnt->cache.cjAuxCacheMem = cjMaxBitmap; } GLYPHDATA *pgd = (GLYPHDATA *)prfnt->cache.pjAuxCacheMem; GLYPHBITS *pgb = (GLYPHBITS *)(pgd + 1); // Call font driver to get the metrics. PDEVOBJ pdo(prfnt->hdevProducer); ULONG cjNeeded = pdo.QueryFontData( prfnt->dhpdev, pfo(), QFD_GLYPHANDBITMAP, pgp->hg, pgd, (VOID *)pgb, prfnt->cjGlyphMax); if ( cjNeeded == FD_ERROR ) return FALSE; if (pfo()->flFontType & FO_CLEARTYPE_X) { ULONG cx = (ULONG)(pgd->rclInk.right - pgd->rclInk.left); ULONG cy = (ULONG)(pgd->rclInk.bottom - pgd->rclInk.top); cjNeeded = CJ_CTGD(cx+2,cy); if (pgb) { cjNeeded = ulClearTypeFilter(pgb, pgd, prfnt); } } #if DBG if(cjNeeded > pc->cjGlyphMax) { KdPrint(("cjNeeded = %x cjGlyphmax = %x\n", cjNeeded, pc->cjGlyphMax)); DbgBreakPoint(); } #endif // Set the returned value pgp->pgdf = (GLYPHDEF *)pgd; pgd->gdf.pgb = pgb; return TRUE; } /******************************Public*Routine******************************\ * BOOL RFONTOBJ::bInsertPathLookaside * * Get the glyph bits into the lookaside buffer * * History: * 13-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ BOOL RFONTOBJ::bInsertPathLookaside( GLYPHPOS *pgp, BOOL bFlatten ) { CACHE *pc = &prfnt->cache; PDEVOBJ pdo(prfnt->hdevProducer); GLYPHDATA gdTemp; PATHMEMOBJ pmo; if (!pmo.bValid()) return(FALSE); // Call font driver to get the path ULONG cjNeeded = pdo.QueryFontData( prfnt->dhpdev, pfo(), QFD_GLYPHANDOUTLINE, pgp->hg, &gdTemp, &pmo, 0); if ( cjNeeded == FD_ERROR ) return FALSE; PDEVOBJ pdoCon(prfnt->hdevConsumer); // Don't cache bezier glyphs for display drivers even if GCAPS_BEZIERS is // set because dynamic mode changes may cause this capability to change at // any time. if ( ((pdoCon.bDisplayPDEV()) || ((pdoCon.flGraphicsCaps() & GCAPS_BEZIERS) == 0)) && bFlatten ) { if (!pmo.bFlatten()) return FALSE; } cjNeeded = sizeof(GLYPHDATA) + offsetof(EPATHFONTOBJ, pa.apr) + pmo.cjSize(); // Make sure the lookaside buffer is allocated if ( ( prfnt->cache.cjAuxCacheMem < cjNeeded ) && ( prfnt->cache.pjAuxCacheMem != NULL )) { #if DBG IFIOBJ ifio(prfnt->ppfe->pifi); TRACE_CACHE(( " -- TRACE_CACHE --\n" " RFONTOBJ::bInsertPathLookaside\n" " FaceName = \"%ws\"\n" " ExFreePool\n" " cache.pjAuxCacheMem = %-#x\n", ifio.pwszFaceName(), prfnt->cache.pjAuxCacheMem )) #endif VFREEMEM((PVOID) prfnt->cache.pjAuxCacheMem); prfnt->cache.pjAuxCacheMem = NULL; prfnt->cache.cjAuxCacheMem = 0; } if ( prfnt->cache.pjAuxCacheMem == NULL ) { prfnt->cache.pjAuxCacheMem = (PBYTE)PALLOCMEM(cjNeeded, 'cacG'); if ( prfnt->cache.pjAuxCacheMem == NULL ) { WARNING("bGetGlyphbitsLookaside - error allocating buffer\n"); return FALSE; } prfnt->cache.cjAuxCacheMem = cjNeeded; #if DBG IFIOBJ ifio(prfnt->ppfe->pifi); TRACE_CACHE(( " -- TRACE_CACHE --\n" " RFONTOBJ::bInsertPathLookaside\n" " FaceName = \"%ws\"\n" " ExAllocatePoolWithTag\n" " tag = Gcac\n" " size = %-#x\n" " cache.pjAuxCacheMem = %-#x\n", ifio.pwszFaceName(), cjNeeded, prfnt->cache.pjAuxCacheMem )); #endif } GLYPHDATA *pgd = (GLYPHDATA *)prfnt->cache.pjAuxCacheMem; EPATHFONTOBJ *epfo = (EPATHFONTOBJ *)(pgd + 1); epfo->vInit(cjNeeded - sizeof(GLYPHDATA)); epfo->bClone(pmo); // Set the returned value *pgd = gdTemp; pgp->pgdf = (GLYPHDEF *)pgd; pgd->gdf.ppo = epfo; return TRUE; } /******************************Public*Routine******************************\ * BOOL bCheckMetrics * * * * Make sure there's enough room for a GLYPHDATA in the metrics part of the * * cache. Return FALSE if we failed to do so. * * * * History: * * 25-Nov-92 -by- Paul Butzi * * Wrote it. * \**************************************************************************/ BOOL RFONTOBJ::bCheckMetricsCache() { CACHE *pc = &prfnt->cache; ASSERTGDI(prfnt->wcgp != NULL, "bCheckMetricsCache: wcgp is NULL\n"); // Verify enough room in metrics cache area, grow if needed. if ( ( bSmallMetrics() ? ((GLYPHDATA*) ((BYTE*)(pc->pgdNext) + offsetof(GLYPHDATA,fxInkTop))) : (pc->pgdNext + 1) ) > pc->pgdThreshold ) { DATABLOCK *pdbl; // allocate a new block of GLYPHDATA structs if ((pdbl = (DATABLOCK*)PALLOCNOZ(GD_INC, 'cacG')) == (DATABLOCK*)NULL) { return(FALSE); } #if DBG IFIOBJ ifio(prfnt->ppfe->pifi); TRACE_CACHE(( " -- TRACE_CACHE --\n" " RFONTOBJ::bCheckMetrics\n" " FaceName = \"%ws\"\n" " ExAllocatePoolWithTag\n" " tag = Gcac\n" " size = %-#x\n" " cache.pdblBase = %-#x\n", ifio.pwszFaceName(), GD_INC, pdbl )); #endif // insert this block into the chain of GLYPHDATA blocks pdbl->pdblNext = pc->pdblBase; pc->pdblBase = pdbl; pc->pgdThreshold = (GLYPHDATA*) ((BYTE *)pdbl + GD_INC); pc->pgdNext = &pdbl->agd[0]; } ASSERTGDI((( bSmallMetrics() ? (GLYPHDATA*) ((BYTE*)(pc->pgdNext) + offsetof(GLYPHDATA,fxInkTop)) : (pc->pgdNext + 1)) <= pc->pgdThreshold), "bInsertMetrics - no room in cache\n" ); return TRUE; } /******************************Public*Routine******************************\ * PVOID pgbCheckGlyphCache * * Make sure there's enough room for a glyph in the glyph part of the * cache. Return NULL if we failed to do so. * * History: * 25-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ PVOID RFONTOBJ::pgbCheckGlyphCache( SIZE_T cjNeeded ) { CACHE *pc = &prfnt->cache; ASSERTGDI(prfnt->wcgp != NULL, "pgbCheckGlyphCache: wcgp is NULL\n"); if ((pc->pgbNext + cjNeeded) > pc->pgbThreshold) { // there are two possible situations that can arise here. // One is that there already is a block allocated, following the pbblCur // in the linked list, which is presently unused. This would be the case // after the cache was flushed and then partially refilled. // The other possible situation is that the pbblCur is // the last block allocated and we have to allocate // another block if we are allowed to, i.e. if the total // number of blocks is not exceeding cBlocksMax. BITBLOCK *pbblNext; if (pc->pbblCur && (pbblNext = pc->pbblCur->pbblNext)) { TRACE_INSERT(("pgbCheckGlyphCache:Inserting into existing block at 0x%lx\n", pbblNext)); pc->pbblCur = pbblNext; pc->pgbNext = pc->pbblCur->ajBits; // we do not want to use the last 8 bytes in the BITBLOCK. Some drivers // read the last dword (or quadword) past the end of the GLYPHBITS. // If there is a GLYPHBITS at the very and of the BITBLOCK AND the // allocation happens to be at the end of the page the read will AV. pc->pgbThreshold = (PBYTE)pc->pbblCur + pc->cjbbl - sizeof(double); ASSERTGDI(pc->cBlocks == pc->cBlocksMax, "Glyphbits logic wrong, cBlocks ??? \n"); } else { ULONG cjBlockSize = (pc->cBlocks == 0) ? pc->cjbblInitial : pc->cjbbl; if ( !(prfnt->flType & RFONT_TYPE_NOCACHE) && (pc->cBlocks < pc->cBlocksMax) && ((offsetof(BITBLOCK,ajBits) + cjNeeded) <= cjBlockSize) ) { // The only reason we need the last check is the PATHOBJ case // where cjNeeded may actually not fit in the block of cjbbl bytes. // This is because we have no way of knowing how big the paths // are going to be (especailly after doing bFlatten) and our // pc->cjGlyphMax is just a good guess in this case. // We are going to append another block at the end of the list pbblNext = (BITBLOCK *) PALLOCNOZ(cjBlockSize,' bgG'); if (!pbblNext) { WARNING1("gdisrv!bInitCache(): glyphbit allocation failed\n"); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return NULL; } TRACE_CACHE(( " tag = Ggb" " size = %-#x" " pbbl = %-#x\n", cjBlockSize, pbblNext )); TRACE_INSERT(( "Block %ld, cGlyphs = %ld, tag = Ggb, cjBlockSize = 0x%lx, pbbl = %-#x\n", pc->cBlocks, // do before incrementing cBlocks pc->cGlyphs, cjBlockSize, pbblNext )); // we have just allocated another block, update cBlocks: pc->cBlocks += 1; // append this block to the end of the list if (!pc->pbblCur) // first block ever for this rfont { ASSERTGDI( (pc->pbblBase == NULL) && (pc->cBlocks == 1), "The font cache is trashed\n"); pc->pbblBase = pc->pbblCur = pbblNext; } else { ASSERTGDI( (pc->pbblCur->pbblNext == NULL), "The end of the font cache linked list is trashed\n"); pc->pbblCur->pbblNext = pbblNext; pc->pbblCur = pbblNext; } // init the header of the current block pc->pbblCur->pbblNext = NULL; // essential initialization pc->pgbNext = pc->pbblCur->ajBits; // we do not want to use the last 8 bytes in the BITBLOCK. Some drivers // read the last dword (or quadword) past the end of the GLYPHBITS. // If there is a GLYPHBITS at the very and of the BITBLOCK AND the // allocation happens to be at the end of the page the read will AV. pc->pgbThreshold = (PBYTE)pc->pbblCur + cjBlockSize - sizeof(double); } else { // tough luck, we are not allowed to add more blocks return NULL; } } } ASSERTGDI((pc->pgbNext + cjNeeded) <= pc->pgbThreshold, "pgbCheckGlyphCache, we are about to trash the font cache\n"); return pc->pgbNext; } /******************************Public*Routine******************************\ * VOID vFlushCache() * * Flush the glyph cache. * * History: * Fri 29-Sep-1995 -by- Bodin Dresevic [BodinD] * rewrote. * 25-Nov-92 -by- Paul Butzi * Wrote it. \**************************************************************************/ VOID RFONTOBJ::vFlushCache() { CACHE *pc = &prfnt->cache; // all the pointers to glyphs bits will be invalidated and we will start // filling the glyphbits cache all over again. Therefore, we set the current // block to be the same as base block and pgbN to the first available field in // in the Current block. // Note that vFlushCache is allways called after pgbCheckGlyphCache has failed. // pgbCheckGlyphCache could fail for one of the two following reasons: // // a) (pc->cBlocks == pc->cBlocksMax) && (no room in the last block) // b) (pc->cBlocks < pc->cBlocksMax) && // (failed to alloc mem for the new bitblock). // // In the latter case we do not want to flush glyphbits cache. // Instead we shall try to allocate one more time a bit later. if (pc->pbblBase && (pc->cBlocks == pc->cBlocksMax)) { pc->pbblCur = pc->pbblBase; pc->pgbNext = pc->pbblCur->ajBits; // we do not want to use the last 8 bytes in the BITBLOCK. Some drivers // read the last dword (or quadword) past the end of the GLYPHBITS. // If there is a GLYPHBITS at the very and of the BITBLOCK AND the // allocation happens to be at the end of the page the read will AV. pc->pgbThreshold = (PBYTE)pc->pbblCur + pc->cjbblInitial - sizeof(double); } // now go and invalidate the glyphbit pointers in the glyphdata cache UINT cjGD = bSmallMetrics() ? offsetof(GLYPHDATA,fxInkTop) : sizeof(GLYPHDATA); BYTE *pjBegin; BYTE *pjEnd; if ( prfnt->wcgp->pgdDefault != NULL ) prfnt->wcgp->pgdDefault->gdf.pgb = NULL; for ( DATABLOCK *pdbl = pc->pdblBase; pdbl != (DATABLOCK*)NULL; pdbl = pdbl->pdblNext ) { if (pdbl == pc->pdblBase) { // this is the current block so pjEnd is just pc->pgdNext pjEnd = (PBYTE) pc->pgdNext; } else if (pdbl->pdblNext == (DATABLOCK*)NULL) { // this is the first block and has the WCPG attached so we need to // look into pc.pjFirstBlockEnd to determine the end of it pjEnd = pc->pjFirstBlockEnd; } else { // this is a normal block so we know it must be GD_INC // bytes long pjEnd = ((BYTE*) pdbl) + GD_INC; } pjBegin = (BYTE*)&pdbl->agd[0]; for ( ; pjBegin < pjEnd ; pjBegin += cjGD) { ((GLYPHDATA*) pjBegin)->gdf.pgb = NULL; } } } // out of line method for assembler linkage extern "C" GLYPHDATA *xpgdDefault(RFONTOBJ *pRfontobj) { return pRfontobj->pgdDefault(); }