📄 font.cpp
字号:
* CCcs::Compare (pcf, lfHeight, fMetafile)
*
* @mfunc
* Compares this font cache with the font properties of a
* given CHARFORMAT
*
* @rdesc
* FALSE iff did not match exactly.
*/
BOOL CCcs::Compare (
const CCharFormat * const pcf, //@parm Description of desired logical font
LONG lfHeight) //@parm lfHeight as calculated with the given HDC
{
BOOL result;
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Compare");
result =
_yCfHeight == pcf->yHeight && // because different mapping modes
_lf.lfHeight == lfHeight && // have diff logical coords
_lf.lfWeight == ((pcf->dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL) &&
_lf.lfItalic == ((pcf->dwEffects & CFE_ITALIC) != 0) &&
_fStrikeOut == ((pcf->dwEffects & CFE_STRIKEOUT) != 0) &&
((((pcf->dwEffects & CFE_UNDERLINE) == 0)
&& (_bUnderlineType == CFU_UNDERLINENONE))
|| (((pcf->dwEffects & CFE_UNDERLINE) != 0)
&& (_bUnderlineType == pcf->bUnderlineType))) &&
_lf.lfCharSet == pcf->bCharSet &&
_lf.lfPitchAndFamily == pcf->bPitchAndFamily &&
((pcf->bInternalEffects & CFEI_RUNISDBCS)
? _bConvertMode == CM_LOWBYTE : 1);
// The following call has been known to cause troubles on Win CE
result = result && !lstrcmp( _lf.lfFaceName, pcf->szFaceName );
return result;
}
// ========================= WidthCache by jonmat =========================
/*
* CWidthCache::CheckWidth(ch, rlWidth)
*
* @mfunc
* check to see if we have a width for a TCHAR character.
*
* @comm
* Used prior to calling FillWidth(). Since FillWidth
* may require selecting the map mode and font in the HDC,
* checking here first saves time.
*
* @comm
* Statistics are maintained to determine when to
* expand the cache. The determination is made after a constant
* number of calls in order to make calculations faster.
*
* @rdesc
* returns TRUE if we have the width of the given TCHAR.
*/
BOOL CWidthCache::CheckWidth (
const TCHAR ch, //@parm char, can be Unicode, to check width for.
LONG &rlWidth ) //@parm the width of the character
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckWidth");
BOOL exist;
INT i;
const CacheEntry * pWidthData = GetEntry ( ch );
exist = ( ch == pWidthData->ch // Have we fetched the width?
&& pWidthData->width ); // only because we may have ch == 0.
if( exist )
{
rlWidth = pWidthData->width;
}
else
{
rlWidth = 0;
}
i = CACHE_SWITCH(ch); // Update statistics
if ( !_fMaxPerformance[i] ) // if we have not grown to the max...
{
_accesses[i]++;
if ( !exist ) // Only interesting on collision.
{
if ( 0 == pWidthData->width )// Test width not ch, 0 is valid ch.
{
_cacheUsed[i]++; // Used another entry.
AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
}
else
_collisions[i]++; // We had a collision.
if ( _accesses[i] >= PERFCHECKEPOCH )
CheckPerformance(i); // After some history, tune cache.
}
}
#ifdef DEBUG // Continue to monitor performance
else
{
_accesses[i]++;
if ( !exist ) // Only interesting on collision.
{
if ( 0 == pWidthData->width )// Test width not ch, 0 is valid ch.
{
_cacheUsed[i]++; // Used another entry.
AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
}
else
_collisions[i]++; // We had a collision.
}
if ( _accesses[i] > PERFCHECKEPOCH )
{
_accesses[i] = 0;
_collisions[i] = 0;
}
}
#endif
return exist;
}
/*
* CWidthCache::CheckPerformance(i)
*
* @mfunc
* check performance and increase cache size if deemed necessary.
*
* @devnote
* To calculate 25% collision rate, we make use of the fact that
* we are only called once every 64 accesses. The inequality is
* 100 * collisions / accesses >= 25. By converting from 100ths to
* 8ths, the ineqaulity becomes (collisions << 3) / accesses >= 2.
* Substituting 64 for accesses, this becomes (collisions >> 3) >= 2.
*/
void CWidthCache::CheckPerformance(
INT i ) //@parm which cache to check.
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckPerformance");
if ( _fMaxPerformance[i] ) // Exit if already grown to our max.
return;
if ( // Grow the cache when
// cacheSize > 0 && 75% utilized,
( _cacheSize[i] > DEFAULTCACHESIZE &&
( (_cacheSize[i] >> 1) + (_cacheSize[i] >> 2)) < _cacheUsed[i])
// or approx 25% collision rate.
|| ( _collisions[i] >> COLLISION_SHIFT ) >= 2 )
{
GrowCache( &_pWidthCache[i], &_cacheSize[i], &_cacheUsed[i] );
}
_collisions[i] = 0; // This prevents wraps but makes
_accesses[i] = 0; // calc a local rate, not global.
// Note if we've max'ed out.
if ( _cacheSize[i] >= maxCacheSize[i] )
_fMaxPerformance[i] = TRUE;
AssertSz( _cacheSize[i] <= maxCacheSize[i], "max must be 2^n-1");
AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
}
/*
* CWidthCache::GrowCache(ppWidthCache, pCacheSize, pCacheUsed)
*
* @mfunc
* Exponentially expand the size of the cache.
*
* @comm
* The cache size must be of the form 2^n as we use a
* logical & to get the hash MOD by storing 2^n-1 as
* the size and using this as the modulo.
*
* @rdesc
* Returns TRUE if we were able to allocate the new cache.
* All in params are also out params.
*
*/
BOOL CWidthCache::GrowCache(
CacheEntry **ppWidthCache, //@parm cache
INT * pCacheSize, //@parm cache's respective size.
INT * pCacheUsed) //@parm cache's respective utilization.
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GrowCache");
CacheEntry *pNewWidthCache, *pOldWidthCache, *pWidthData;
INT j, newCacheSize, newCacheUsed;
TCHAR ch;
UINT uT;
j = *pCacheSize; // Allocate cache of 2^n.
newCacheSize = max ( INITIALCACHESIZE, (j << 1) + 1);
if (newCacheSize + 1 < newCacheSize || FAILED(UIntMult(sizeof(CacheEntry), newCacheSize + 1, &uT)))
return FALSE;
pNewWidthCache = (CacheEntry *)
PvAlloc( uT, GMEM_ZEROINIT);
if ( pNewWidthCache )
{
newCacheUsed = 0;
*pCacheSize = newCacheSize; // Update out params.
pOldWidthCache = *ppWidthCache;
*ppWidthCache = pNewWidthCache;
for (; j >= 0; j--) // Move old cache info to new.
{
ch = pOldWidthCache[j].ch;
if ( ch )
{
pWidthData = &pNewWidthCache [ch & newCacheSize];
if ( 0 == pWidthData->ch )
newCacheUsed++; // Used another entry.
pWidthData->ch = ch;
pWidthData->width = pOldWidthCache[j].width;
}
}
*pCacheUsed = newCacheUsed; // Update out param.
// Free old cache.
if ( pOldWidthCache < &_defaultWidthCache[0][0]
|| pOldWidthCache > &_defaultWidthCache[TOTALCACHES-1][(DEFAULTCACHESIZE+1)-1])
//-1 on _defaultWidthCache indexes because of 0 based array.
{
FreePv(pOldWidthCache);
}
}
return NULL != pNewWidthCache;
}
/*
* CWidthCache::FillWidth(hdc, ch, xOverhang, rlWidth)
*
* @mfunc
* Call GetCharWidth() to obtain the width of the given char.
*
* @comm
* The HDC must be setup with the mapping mode and proper font
* selected *before* calling this routine.
*
* @rdesc
* Returns TRUE if we were able to obtain the widths.
*
*/
BOOL CWidthCache::FillWidth (
HDC hdc, //@parm Current HDC we want font info for.
const TCHAR ch, //@parm Char to obtain width for.
const SHORT xOverhang, //@parm Equivalent to GetTextMetrics() tmOverhang.
LONG & rlWidth, //@parm Width of character
UINT uiCodePage, //@parm code page for text
BOOL fANSI, //@parm indicates font needs to use ANSI call.
INT iDefWidth, //@parm Default width to use if font calc's zero
//width. (Handles Win95 problem).
INT iDBCDefWidth, //@parm Default width for DBC to use
// Handles Win95 Trad Chinese problem.
BOOL fFixPitch) //@parm fix pitch font for DBC to use
// Handles Win95 Trad Chinese problem.
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::FillWidth");
char ansiChar[2] = {0};
INT numOfDBCS = 0;
CacheEntry * pWidthData = GetEntry ( ch );
(void) W32->REGetCharWidth( hdc, ch, &pWidthData->width, uiCodePage );
#if 0
// REVIEW May not need codepage.
// TODO
if (!fANSI && uiCodePage != SYMBOL_CODEPAGE)
(void) W32->REGetCharWidth( hdc, ch, &pWidthData->width, uiCodePage );
else
{
WORD wDBCS = ch;
(void) GetCharWidthA( hdc, wDBCS, wDBCS, &pWidthData->width );
}
// Is this needed ????
// fAnsi case, SYMBOL_CHARSET, or GetCharWidthW failed: try GetCharWidthA
if (!fRes || 0 == pWidthData->width)
{
if(uiCodePage != SYMBOL_CODEPAGE)
{
// Try to convert string
numOfDBCS = WideCharToMultiByte( uiCodePage, 0, &ch, 1,
ansiChar, 2, NULL, NULL);
if (2 == numOfDBCS)
wDBCS = (BYTE)ansiChar[0] << 8 | (BYTE)ansiChar[1];
else if (numOfDBCS)
wDBCS = (BYTE)ansiChar[0];
}
fRes = GetCharWidthA( hdc, wDBCS, wDBCS, &pWidthData->width );
}
#endif
pWidthData->ch = ch;
// pWidthData->width -= xOverhang; // Don't need since we use
// GetTextExtentPoint32()
if (0 >= pWidthData->width)
{
// Sometimes GetCharWidth will return a zero length for small
// characters. When this happens we will use the default width
// for the font if that is non-zero otherwise we just us 1 because
// this is the smallest valid value.
// This code can also be triggered if the overhang is bugger than the
// width returned by the OS call to get the character width.
// under Win95 Trad. Chinese, there is a bug in the font.
// It is returning a width of 0 for a few characters (Eg 0x09F8D, 0x81E8)
// In such case, we need to use 2 * iDefWidth since these are DBCS
if (0 == iDefWidth)
pWidthData->width = 1;
else
pWidthData->width = (numOfDBCS == 2) ?
(iDBCDefWidth ? iDBCDefWidth : 2 * iDefWidth) : iDefWidth;
}
rlWidth = pWidthData->width;
return TRUE;
}
/*
* CWidthCache::GetWidth(ch)
*
* @mfunc
* get the width (A+B+C) for the given character.
* @comm
* we've already called GetCharWidth() at this point.
* @rdesc
* the width.
*/
INT CWidthCache::GetWidth (
const TCHAR ch )//@parm char, can be Unicode, to check width for.
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GetWidth");
const CacheEntry * pWidthData = GetEntry ( ch );
AssertSz( pWidthData->ch == ch, "Table not filled in?" );
return pWidthData->width;
}
/*
* CWidthCache::Free()
*
* @mfunc
* Free any dynamic memory allocated by the width cache and prepare
* it to be recycled.
*
*/
VOID CWidthCache::Free()
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::Free");
INT i;
for (i = 0; i < TOTALCACHES; i++ )
{
_fMaxPerformance[i] = FALSE;
_cacheSize[i] = DEFAULTCACHESIZE;
_cacheUsed[i] = 0;
_collisions[i] = 0;
_accesses[i] = 0;
if ( _pWidthCache[i] != &_defaultWidthCache[i][0] )
{
FreePv(_pWidthCache[i]);
_pWidthCache[i] = &_defaultWidthCache[i][0];
}
ZeroMemory(_pWidthCache[i], sizeof(CacheEntry)*(DEFAULTCACHESIZE + 1));
}
}
/*
* CWidthCache::CWidthCache()
*
* @mfunc
* Point the caches to the defaults.
*
*/
CWidthCache::CWidthCache()
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CWidthCache");
INT i;
for (i = 0; i < TOTALCACHES; i++ )
{
_pWidthCache[i] = &_defaultWidthCache[i][0];
}
}
/*
* CWidthCache::~CWidthCache()
*
* @mfunc
* Free any allocated caches.
*
*/
CWidthCache::~CWidthCache()
{
TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::~CWidthCache");
INT i;
for (i = 0; i < TOTALCACHES; i++ )
{
if (_pWidthCache[i] != &_defaultWidthCache[i][0])
FreePv(_pWidthCache[i]);
}
}
#if 0 // unused
/*
* INT CheckDBChar ( UINT uiCodePage, WCHAR wChar, char *pAnsiChar)
*
* @func
* Determine if the input wChar is converted to a DBC or SBC
*
* @comm
* Called when we need to determine the char width and when we render
* the char. This is mainly used for determining if the Bullet char (0xb7)
* has a DBC equivalent.
*
* @rdesc
* return 2 if DBCS, otherwise return 0. Also, pAnsiChar contains the DBCS.
*
*/
INT CheckDBChar ( UINT uiCodePage, WCHAR wChar, char *pAnsiChar)
{
WORD wDBCS=0;
// Convert string
if ( uiCodePage )
// check if we have a DBC bullet for this codepage
wDBCS = WideCharToMultiByte( uiCodePage, 0, &wChar, 1,
pAnsiChar, 2, NULL, NULL);
if ( 2 != wDBCS )
*pAnsiChar = (CHAR)wChar;
return wDBCS;
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -