|
|
/*
* @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); }
|