windows-nt/Source/XPSP1/NT/windows/richedit/re41/kern.cpp
2020-09-26 16:20:57 +08:00

185 lines
3.9 KiB
C++

/*
* @doc INTERNAL
*
* @module KERN.CPP -- CCKernCache class |
*
* Class which implements a kerning pair cache. Note that these widths
* are stored in the font's design units (2048 pixel high font)
* This allows us to share the same kerning pair information for all
* sizes of a font.
*
* The kerning cache assumes you know in advance how many entries
* you will put into the cache. It does not support the expensive
* operations of growing and re-hashing all the data.
*
* Owner:<nl>
* Keith Curtis: Stolen from Quill '98, simplified and improved upon.
*
* Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
*/
#include <_common.h>
#include <_kern.h>
const int dvpDesign = 2048;
/*
* CKernCache::FetchDup(chFirst, chSecond, dvpFont)
*
* @mfunc
* Looks up the characters in the table and returns their pair
* adjustment if found.
*
* We will scale the value from the font's design units.
*
* This routine is very important for performance.
* Many optimizations (double hashing, early returns if the data
* didn't match but a collision wasn't found) actually slowed things down!
*
* Note this is very similar to the below function but having
* separate functions makes pagination 7% faster.
*/
LONG CKernCache::FetchDup(WCHAR chFirst, WCHAR chSecond, LONG dvpFont)
{
KERNHASHKEY kernhashkey = MakeHashKey(chFirst, chSecond);
int ikpe = Hash(kernhashkey);
KPE *pkpe = _pmpkpe.Elem(ikpe);
for(;;)
{
if (pkpe->chFirst == chFirst && pkpe->chSecond == chSecond)
return MulDiv(pkpe->du, dvpFont, dvpDesign);
if (pkpe->chFirst == 0) //Empty slot, so no pair
return 0;
ikpe++;
pkpe++;
if (ikpe == _pmpkpe.Count()) //Loop around if necessary
{
ikpe = 0;
pkpe = _pmpkpe.Elem(0);
}
}
}
/*
* CKernCache::Add(chFirst, chSecond, du)
*
* @mfunc
* Finds a free spot to put the kerning pair information.
* This function cannot fail because the array has been preallocated.
*
*/
void CKernCache::Add(WCHAR chFirst, WCHAR chSecond, LONG du)
{
KERNHASHKEY kernhashkey = MakeHashKey(chFirst, chSecond);
int ikpe = Hash(kernhashkey);
KPE *pkpe = _pmpkpe.Elem(ikpe);
for(;;)
{
if (pkpe->chFirst == 0)
{
pkpe->chFirst = chFirst;
pkpe->chSecond = chSecond;
pkpe->du = du;
return;
}
ikpe++;
pkpe++;
if (ikpe == _pmpkpe.Count())
{
ikpe = 0;
pkpe = _pmpkpe.Elem(0);
}
}
}
/*
* CKernCache::FInit(hfont)
*
* @mfunc
* If the kern cache is uninitialized, Init it. If there are no
* kerning pairs (or it failed) return FALSE.
*
* @rdesc
* Return TRUE if you can fetch kerning pairs for this cache
*
*/
BOOL CKernCache::FInit(HFONT hfont)
{
if (_kcis == Unitialized)
Init(hfont);
return _kcis == Initialized;
}
/*
* CKernCache::Init(hfont)
*
* @mfunc
* Fetches the kerning pairs from the OS in design units and hashes them
* all into a table. Updates _ckis with result
*
*/
void CKernCache::Init(HFONT hfont)
{
KERNINGPAIR *pkp = 0;
int prime, ikp;
HFONT hfontOld = 0;
HFONT hfontIdeal = 0;
int ckpe = 0;
HDC hdc = W32->GetScreenDC();
LOGFONT lfIdeal;
W32->GetObject(hfont, sizeof(LOGFONT), &lfIdeal);
//FUTURE (keithcu) Support kerning of Greek, Cyrillic, etc.
lfIdeal.lfHeight = -dvpDesign;
lfIdeal.lfCharSet = ANSI_CHARSET;
hfontIdeal = CreateFontIndirect(&lfIdeal);
if (!hfontIdeal)
goto LNone;
hfontOld = SelectFont(hdc, hfontIdeal);
Assert(hfontOld);
ckpe = GetKerningPairs(hdc, 0, 0);
if (ckpe == 0)
goto LNone;
prime = FindPrimeLessThan(ckpe * 5 / 2);
if (prime == 0)
goto LNone;
pkp = new KERNINGPAIR[ckpe];
if (!pkp)
goto LNone;
GetKerningPairs(hdc, ckpe, pkp);
_pmpkpe.Add(prime, 0);
PvSet(*(void**) &_pmpkpe);
for (ikp = 0; ikp < ckpe; ikp++)
Add(pkp[ikp].wFirst, pkp[ikp].wSecond, pkp[ikp].iKernAmount);
_kcis = Initialized;
goto LDone;
LNone:
_kcis = NoKerningPairs;
LDone:
delete []pkp;
if (hfontOld)
SelectObject(hdc, hfontOld);
DeleteObject(hfontIdeal);
}