2246 lines
63 KiB
C++
2246 lines
63 KiB
C++
#include "precomp.hpp"
|
|
|
|
#define PATH_HEIGHT_THRESHOLD 800
|
|
|
|
#define PAGE_SIZE (8*1024)
|
|
#define ROUND_TO_PAGE(x) (((x)+PAGE_SIZE-1)&~(PAGE_SIZE-1))
|
|
|
|
#define CJMININCREMENT 0x2000
|
|
#define CJMAX (16 * 0x2000)
|
|
|
|
#define FONT_GRAYSCALE_OR_CT_OR_MONOUNHINTED (FO_GRAYSCALE | FO_SUBPIXEL_4 | FO_CLEARTYPE_GRID | FO_CLEARTYPE | FO_MONO_UNHINTED | FO_COMPATIBLE_WIDTH)
|
|
|
|
// 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 * sizeof(GpGlyphData) * 2)
|
|
|
|
// 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
|
|
|
|
ULONG ulClearTypeFilter(GLYPHBITS *GlyphBits, ULONG cjBuf, CacheFaceRealization *prface);
|
|
|
|
BOOL SetFontXform(
|
|
const GpGraphics *pdc,
|
|
const GpFontFace *pfe,
|
|
REAL height,
|
|
Unit unit,
|
|
FD_XFORM *pfdx,
|
|
BOOL needPaths,
|
|
const GpMatrix *pmx
|
|
)
|
|
{
|
|
REAL EmHt, scale;
|
|
REAL DpiX, DpiY;
|
|
|
|
if (needPaths && unit != UnitWorld)
|
|
return FALSE;
|
|
|
|
EmHt = pfe->GetDesignEmHeight();
|
|
|
|
if (!needPaths)
|
|
{
|
|
DpiX = pdc->GetDpiX();
|
|
DpiY = pdc->GetDpiY();
|
|
|
|
// UnitDisplay is device dependent and cannot be used as a font unit
|
|
ASSERT(unit != UnitDisplay);
|
|
|
|
if (unit != UnitWorld && unit != UnitPixel)
|
|
{
|
|
height *= DpiY;
|
|
|
|
switch (unit)
|
|
{
|
|
case UnitPoint:
|
|
height /= 72.0;
|
|
break;
|
|
case UnitDocument:
|
|
height /= 300.0;
|
|
break;
|
|
case UnitMillimeter:
|
|
height *= 10.0;
|
|
height /= 254.0;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
scale = height / EmHt;
|
|
|
|
GpMatrix tempMatrix(scale, 0, 0, scale, 0, 0);
|
|
GpMatrix wtodMatrix;
|
|
REAL m[6];
|
|
|
|
if (pmx)
|
|
tempMatrix.Append(*pmx);
|
|
|
|
if (!needPaths)
|
|
{
|
|
pdc->GetWorldToDeviceTransform(&wtodMatrix);
|
|
tempMatrix.Append(wtodMatrix);
|
|
}
|
|
|
|
tempMatrix.GetMatrix(m);
|
|
|
|
pfdx->eXX = m[0];
|
|
pfdx->eXY = m[1];
|
|
pfdx->eYX = m[2];
|
|
pfdx->eYY = m[3];
|
|
|
|
// Adjust for the non-square resolution
|
|
|
|
if (!needPaths)
|
|
{
|
|
if (DpiY != DpiX)
|
|
{
|
|
DpiX /= DpiY;
|
|
pfdx->eXX *= DpiX;
|
|
pfdx->eYX *= DpiX;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
GpFaceRealization::GpFaceRealization(
|
|
const GpFontFace *face,
|
|
INT style,
|
|
const GpMatrix *matrix,
|
|
const SizeF dpi,
|
|
TextRenderingHint textMode,
|
|
BOOL bPath,
|
|
BOOL bCompatibleWidth, /* we want ClearType compatible width when we come from DrawDriverString */
|
|
BOOL bSideways /* for far east vertical writing, run of glyph layed out sideways,
|
|
used to do the italic simulation in the right direction */
|
|
) :
|
|
prface (NULL),
|
|
Style (style),
|
|
Status (GenericError),
|
|
LimitSubpixel (FALSE)
|
|
{
|
|
if (bInit(face, style, matrix, dpi, textMode, bPath, bCompatibleWidth, bSideways))
|
|
{
|
|
vGetCache();
|
|
Status = Ok;
|
|
}
|
|
}
|
|
|
|
void GpFaceRealization::CloneFaceRealization(
|
|
const GpFaceRealization * pfaceRealization,
|
|
BOOL bPath
|
|
)
|
|
{
|
|
BOOL bOK = FALSE;
|
|
SizeF dpi;
|
|
|
|
// Adjust for the non-square resolution
|
|
// Now we will not do it but eventually we will need to do it.
|
|
|
|
prface = NULL;
|
|
|
|
if (FindRealizedFace(pfaceRealization->pfdx(), pfaceRealization->GetFontFace(),
|
|
bPath, pfaceRealization->prface->fobj.flFontType))
|
|
bOK = TRUE;
|
|
|
|
dpi.Width = (REAL) pfaceRealization->prface->fobj.sizLogResPpi.cx;
|
|
dpi.Height = (REAL) pfaceRealization->prface->fobj.sizLogResPpi.cy;
|
|
|
|
if (!bOK && Realize(dpi, pfaceRealization->GetFontFace(), pfaceRealization->GetStyle(),
|
|
pfaceRealization->pfdx(), pfaceRealization->prface->fobj.flFontType, bPath))
|
|
{
|
|
prface->Face->pff->cRealizedFace +=1;
|
|
bOK = TRUE;
|
|
}
|
|
|
|
if (bOK)
|
|
{
|
|
vGetCache();
|
|
Status = Ok;
|
|
}
|
|
}
|
|
|
|
BOOL GpFaceRealization::bInit(
|
|
const GpFontFace * pface,
|
|
INT style,
|
|
const GpMatrix * matrix,
|
|
SizeF dpi,
|
|
TextRenderingHint textMode,
|
|
BOOL bPath,
|
|
BOOL bCompatibleWidth, /* we want ClearType compatible width when we come from DrawDriverString */
|
|
BOOL bSideways /* for far east vertical writing, run of glyph layed out sideways,
|
|
used to do the italic simulation in the right direction */
|
|
)
|
|
{
|
|
|
|
// It is a new version of face realization
|
|
|
|
REAL m[6];
|
|
FD_XFORM fdxTmp;
|
|
BOOL canSimulate = TRUE;
|
|
|
|
ULONG fl = FO_EM_HEIGHT;
|
|
ULONG flSim = 0;
|
|
|
|
matrix->GetMatrix(m);
|
|
|
|
fdxTmp.eXX = m[0];
|
|
fdxTmp.eXY = m[1];
|
|
fdxTmp.eYX = m[2];
|
|
fdxTmp.eYY = m[3];
|
|
|
|
// Adjust for the non-square resolution
|
|
// Now we will not do it but eventually we will need to do it.
|
|
|
|
prface = NULL;
|
|
|
|
// TextRenderingHintSystemDefault should already been converted through GetTextRenderingHintInternal
|
|
ASSERT (textMode != TextRenderingHintSystemDefault);
|
|
|
|
if (textMode == TextRenderingHintSingleBitPerPixel)
|
|
fl |= FO_MONO_UNHINTED;
|
|
else if (textMode == TextRenderingHintAntiAliasGridFit)
|
|
fl |= FO_GRAYSCALE;
|
|
else if (textMode == TextRenderingHintAntiAlias)
|
|
fl |= FO_GRAYSCALE | FO_SUBPIXEL_4;
|
|
else if (textMode == TextRenderingHintClearTypeGridFit)
|
|
{
|
|
fl |= FO_GRAYSCALE | FO_CLEARTYPE_GRID;
|
|
if (bCompatibleWidth)
|
|
fl |= FO_COMPATIBLE_WIDTH;
|
|
}
|
|
// version 2 :
|
|
// else if (textMode == TextRenderingHintClearType)
|
|
// fl |= FO_GRAYSCALE | FO_CLEARTYPE;
|
|
|
|
|
|
if ( style & FontStyleBold
|
|
&& !(pface->GetFaceStyle() & FontStyleBold))
|
|
{
|
|
if (pface->SimBold())
|
|
{
|
|
fl |= FO_SIM_BOLD;
|
|
}
|
|
else
|
|
{
|
|
return FALSE; // Bold required but cannot be simulated
|
|
}
|
|
}
|
|
|
|
|
|
if ( style & FontStyleItalic
|
|
&& !(pface->GetFaceStyle() & FontStyleItalic))
|
|
{
|
|
if (pface->SimItalic())
|
|
{
|
|
if (bSideways)
|
|
{
|
|
fl |= FO_SIM_ITALIC_SIDEWAYS;
|
|
}
|
|
else
|
|
{
|
|
fl |= FO_SIM_ITALIC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE; // Italic required but cannot be simulated
|
|
}
|
|
}
|
|
|
|
|
|
if (FindRealizedFace(&fdxTmp, pface, bPath, fl))
|
|
return TRUE;
|
|
|
|
if (Realize(dpi, pface, style, &fdxTmp, fl, bPath))
|
|
{
|
|
pface->pff->cRealizedFace +=1;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destructor -- Unlocks the CacheFaceRealization
|
|
|
|
GpFaceRealization::~GpFaceRealization ()
|
|
{
|
|
if (prface != (CacheFaceRealization *) NULL )
|
|
{
|
|
vReleaseCache();
|
|
}
|
|
}
|
|
|
|
|
|
GpFaceRealization::ReuseRealizedFace()
|
|
{
|
|
|
|
if (prface != (CacheFaceRealization *) NULL )
|
|
{
|
|
// free glyphbit cache
|
|
|
|
GlyphBitsBlock *pbblfree, *pbbl = prface->FirstGlyphBitsBlock;
|
|
while(pbbl)
|
|
{
|
|
pbblfree = pbbl;
|
|
pbbl = pbbl->NextGlyphBitsBlock;
|
|
GpFree((PVOID)pbblfree);
|
|
}
|
|
|
|
// free glyphdata cache
|
|
|
|
GlyphDataBlock *pdblfree, *pdbl = prface->FirstGlyphDataBlock;
|
|
while (pdbl)
|
|
{
|
|
pdblfree = pdbl;
|
|
pdbl = pdbl->NextGlyphDataBlock;
|
|
GpFree((PVOID)pdblfree);
|
|
}
|
|
|
|
GpFree(prface->GlyphDataArray);
|
|
|
|
vDestroyRealizedFace(); /* release the FontContext Memory */
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpFaceRealization::DeleteRealizedFace()
|
|
{
|
|
|
|
if (prface != (CacheFaceRealization *) NULL )
|
|
{
|
|
ReuseRealizedFace();
|
|
GpFree(prface);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline
|
|
BOOL SwitchToPath(FLONG flFontType, const FD_DEVICEMETRICS & deviceMetrics)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
INT pathThreshold = PATH_HEIGHT_THRESHOLD;
|
|
|
|
if (flFontType & FO_CLEARTYPE_GRID)
|
|
pathThreshold /= 8;
|
|
else if (flFontType & FO_GRAYSCALE)
|
|
pathThreshold /= 4;
|
|
|
|
// Note: This function need quite a bit of reworking so that it takes
|
|
// the rotation of the font into account when determining the transition
|
|
// point between bitmap/CT and path. Currently the size where we switch
|
|
// rendering modes is based on the maximum dimension of the bounding
|
|
// box of the largest rotated character. Also note that if a single
|
|
// glyph in the font is significantly offset from the rest of the
|
|
// glyphs in the font, the deviceMetrics will indicate a much larger
|
|
// required bitmap than we would normally use. This switch to path really
|
|
// needs to be strictly based on the ascent of the font above the
|
|
// baseline, independent of the rotation or the maximum bounding
|
|
// rectangle for the rendered glyphs.
|
|
|
|
if (flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
// Cleartype should not take width of the bitmap into account.
|
|
// For rotated text, we will cache larger bitmaps for ClearType
|
|
// than for other rendering modes, but this will allow us to more
|
|
// closely match the behavior of ClearType in notepad.
|
|
|
|
fResult =
|
|
(deviceMetrics.yMax - deviceMetrics.yMin) > pathThreshold;
|
|
}
|
|
else
|
|
{
|
|
fResult =
|
|
(deviceMetrics.xMax - deviceMetrics.xMin) > pathThreshold ||
|
|
(deviceMetrics.yMax - deviceMetrics.yMin) > pathThreshold;
|
|
}
|
|
|
|
return fResult;
|
|
} // SwitchToPath
|
|
|
|
BOOL GpFaceRealization::FindRealizedFace(
|
|
FD_XFORM *fdx,
|
|
const GpFontFace *fontFace,
|
|
BOOL needPaths,
|
|
FLONG fl
|
|
) const
|
|
{
|
|
|
|
FLONG fltemp1 = fl & FONT_GRAYSCALE_OR_CT_OR_MONOUNHINTED;
|
|
FLONG fltemp2 = fl & (FO_SIM_BOLD | FO_SIM_ITALIC | FO_SIM_ITALIC_SIDEWAYS);
|
|
|
|
for (prface = fontFace->pff->prfaceList;
|
|
prface != NULL;
|
|
prface = (prface->NextCacheFaceRealization == fontFace->pff->prfaceList) ? NULL : prface->NextCacheFaceRealization)
|
|
{
|
|
if (prface->Face == fontFace)
|
|
{
|
|
if ((prface->ForcedPath || (needPaths == IsPathFont())) &&
|
|
MatchFDXForm(fdx))
|
|
{
|
|
if (IsPathFont())
|
|
{
|
|
// if for given text rendering hint we can't switch to path
|
|
// skip this realization (unless someone really wants path)
|
|
if (!needPaths && !SwitchToPath(fl, prface->DeviceMetrics))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// FO_NOGRAY16 means that for this transform, grayscale was turned off following the "gasp" table
|
|
// see vSetGrayState__FONTCONTEXT in TrueType driver
|
|
FLONG fltemp = fltemp1;
|
|
if (prface->fobj.flFontType & FO_NOGRAY16)
|
|
fltemp &= ~FO_GRAYSCALE;
|
|
|
|
if (prface->fobj.flFontType & FO_NOCLEARTYPE)
|
|
{
|
|
if (fltemp & FO_CLEARTYPE_GRID)
|
|
fltemp &= ~(FO_CLEARTYPE_GRID | FO_GRAYSCALE | FO_COMPATIBLE_WIDTH);
|
|
}
|
|
|
|
if ((prface->fobj.flFontType & FONT_GRAYSCALE_OR_CT_OR_MONOUNHINTED) != fltemp)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((prface->fobj.flFontType & (FO_SIM_BOLD | FO_SIM_ITALIC | FO_SIM_ITALIC_SIDEWAYS)) != fltemp2)
|
|
continue;
|
|
|
|
// We need to update the recently used list here!
|
|
Globals::FontCacheLastRecentlyUsedList->RemoveFace(prface);
|
|
Globals::FontCacheLastRecentlyUsedList->AddMostRecent(prface);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
prface = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::bGetDEVICEMETRICS()
|
|
{
|
|
|
|
if (ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
QFD_MAXEXTENTS,
|
|
HGLYPH_INVALID,
|
|
(GLYPHDATA *) NULL,
|
|
&prface->DeviceMetrics) == FD_ERROR)
|
|
{
|
|
// The QFD_MAXEXTENTS mode is required of all drivers.
|
|
// However must allow for the possibility of this call to fail.
|
|
// This could happen if the
|
|
// font file is on the net and the net connection dies, and the font
|
|
// driver needs the font file to produce device metrics [bodind]
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
// need to compute the filtering correction for CLEAR_TYPE:
|
|
// x filtering adds a pixel on each side of the glyph
|
|
prface->MaxGlyphByteCount = CJ_CTGD(
|
|
prface->DeviceMetrics.cxMax + 2,
|
|
prface->DeviceMetrics.cyMax
|
|
);
|
|
}
|
|
else
|
|
{
|
|
prface->MaxGlyphByteCount = prface->DeviceMetrics.cjGlyphMax; // used to get via QFD_MAXGLYPHBITMAP
|
|
}
|
|
|
|
|
|
// Everythings OK.
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID GpFaceRealization::vDestroyRealizedFace()
|
|
{
|
|
ttfdSemDestroyFont(&prface->fobj);
|
|
}
|
|
|
|
BOOL GpFaceRealization::Realize(
|
|
SizeF dpi,
|
|
const GpFontFace *pfe,
|
|
INT style, // style - which may require simulation
|
|
PFD_XFORM pfdx, // font xform (Notional to Device)
|
|
FLONG fl, // these two really modify the xform
|
|
BOOL bNeedPaths
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
// prface is a member variable pointing to the embedded CacheFaceRealization
|
|
|
|
if (Globals::FontCacheLastRecentlyUsedList->GetCount() >= MAXRECENTLYUSEDCOUNT)
|
|
{
|
|
prface = Globals::FontCacheLastRecentlyUsedList->ReuseLeastRecent();
|
|
|
|
ASSERT(prface);
|
|
}
|
|
else
|
|
{
|
|
prface = (CacheFaceRealization *)GpMalloc(sizeof(CacheFaceRealization));
|
|
}
|
|
|
|
if (!prface)
|
|
return FALSE;
|
|
|
|
// Copy the font transform passed in.
|
|
|
|
prface->fobj.fdx = *pfdx;
|
|
|
|
// Initialize the DDI callback EXFORMOBJ.
|
|
|
|
GpMatrix tmpMatrix(pfdx->eXX, pfdx->eXY, pfdx->eYX, pfdx->eYY, 0, 0);
|
|
prface->mxForDDI = tmpMatrix;
|
|
|
|
// Initialize the FONTOBJ inherited by the embedded CacheFaceRealization
|
|
|
|
// Save identifiers to the source of the font (physical font).
|
|
|
|
prface->Face = pfe;
|
|
|
|
// GetDpiX() and GetDpiY() return REALs
|
|
|
|
prface->fobj.sizLogResPpi.cx = GpRound(dpi.Width);
|
|
prface->fobj.sizLogResPpi.cy = GpRound(dpi.Height);
|
|
|
|
prface->fobj.ulPointSize = 0;
|
|
prface->fobj.flFontType = fl | FO_TYPE_TRUETYPE; // fl contains simulation flag(s)
|
|
prface->fobj.pvProducer = (PVOID) NULL; // the true type driver will init this field
|
|
prface->fobj.iFace = prface->Face->iFont;
|
|
prface->fobj.iFile = prface->Face->pff->hff;
|
|
|
|
|
|
// Get the device metrics info
|
|
|
|
if (!bGetDEVICEMETRICS())
|
|
{
|
|
vDestroyRealizedFace(); // kill the driver realization
|
|
GpFree(prface);
|
|
prface = NULL;
|
|
return result; // return FALSE
|
|
}
|
|
|
|
prface->CacheType = bNeedPaths ? CachePath : CacheBits;
|
|
prface->ForcedPath = FALSE;
|
|
|
|
if (!bNeedPaths)
|
|
{
|
|
// We force drawing with a path if size is to large
|
|
if (SwitchToPath(prface->fobj.flFontType, prface->DeviceMetrics))
|
|
{
|
|
prface->CacheType = CachePath;
|
|
prface->ForcedPath = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// If you force the path mode then turn off antialiasing
|
|
|
|
if (IsPathFont())
|
|
{
|
|
prface->fobj.flFontType &= ~FONT_GRAYSCALE_OR_CT_OR_MONOUNHINTED;
|
|
prface->realizationMethod = TextRenderingHintSingleBitPerPixelGridFit;
|
|
prface->QueryFontDataMode = QFD_GLYPHANDOUTLINE;
|
|
}
|
|
else
|
|
{
|
|
if (prface->fobj.flFontType & FO_GRAYSCALE)
|
|
{
|
|
// version 2 :
|
|
// if (prface->fobj.flFontType & FO_CLEARTYPE)
|
|
// {
|
|
// prface->realizationMethod = TextRenderingHintClearType;
|
|
// prface->QueryFontDataMode = QFD_CT;
|
|
// }
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
prface->realizationMethod = TextRenderingHintClearTypeGridFit;
|
|
prface->QueryFontDataMode = QFD_CT_GRID;
|
|
}
|
|
else if (prface->fobj.flFontType & FO_SUBPIXEL_4)
|
|
{
|
|
prface->CacheType = CacheAABits;
|
|
prface->realizationMethod = TextRenderingHintAntiAlias;
|
|
prface->QueryFontDataMode = QFD_TT_GRAY4_BITMAP;
|
|
}
|
|
else
|
|
{
|
|
prface->realizationMethod = TextRenderingHintAntiAliasGridFit;
|
|
prface->QueryFontDataMode = QFD_TT_GRAY4_BITMAP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (prface->fobj.flFontType & FO_MONO_UNHINTED)
|
|
{
|
|
prface->realizationMethod = TextRenderingHintSingleBitPerPixel;
|
|
prface->QueryFontDataMode = QFD_GLYPHANDBITMAP_SUBPIXEL;
|
|
}
|
|
else
|
|
{
|
|
prface->realizationMethod = TextRenderingHintSingleBitPerPixelGridFit;
|
|
prface->QueryFontDataMode = QFD_GLYPHANDBITMAP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bInitCache())
|
|
{
|
|
vDestroyRealizedFace(); // kill the driver realization
|
|
GpFree(prface);
|
|
prface = NULL;
|
|
return result; // return FALSE
|
|
}
|
|
|
|
// Made it this far, so everything is OK
|
|
|
|
result = TRUE;
|
|
|
|
Globals::FontCacheLastRecentlyUsedList->AddMostRecent(prface);
|
|
|
|
vInsert(&prface->Face->pff->prfaceList);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID GpFaceRealization::vInsert(CacheFaceRealization **pprfaceHead)
|
|
{
|
|
|
|
if (*pprfaceHead != NULL)
|
|
{
|
|
prface->NextCacheFaceRealization = *pprfaceHead;
|
|
|
|
prface->PreviousCacheFaceRealization = (*pprfaceHead)->PreviousCacheFaceRealization;
|
|
|
|
prface->PreviousCacheFaceRealization->NextCacheFaceRealization = prface;
|
|
|
|
(*pprfaceHead)->PreviousCacheFaceRealization = prface;
|
|
}
|
|
else
|
|
{
|
|
prface->NextCacheFaceRealization = prface;
|
|
prface->PreviousCacheFaceRealization = prface;
|
|
}
|
|
|
|
*pprfaceHead = prface;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::bInitCache() const
|
|
{
|
|
BOOL result = TRUE; // unless proven otherwise
|
|
|
|
// 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
|
|
|
|
prface->FirstGlyphDataBlock = NULL;
|
|
prface->GlyphDataBlockUnderConstruction = NULL;
|
|
prface->NextFreeGlyphDataIndex = 0;
|
|
|
|
// glyphbits portion
|
|
|
|
prface->FirstGlyphBitsBlock = NULL;
|
|
prface->GlyphBitsBlockUnderConstruction = NULL;
|
|
prface->SizeGlyphBitsBlockUnderConstruction = 0;
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction = 0;
|
|
|
|
// aux mem portion
|
|
|
|
prface->LookasideGlyphData = NULL;
|
|
prface->LookasideByteCount = 0;
|
|
prface->GlyphDataArray = NULL; // to be allocated later, big allocation
|
|
|
|
|
|
prface->NextCacheFaceRealization = NULL;
|
|
prface->PreviousCacheFaceRealization = NULL;
|
|
|
|
// First, figure out how big the max glyph will be
|
|
// Default is zero - glyphdata size is not counted!
|
|
|
|
ULONG cjGlyphMaxX2;
|
|
|
|
if (IsPathFont())
|
|
{
|
|
cjGlyphMaxX2 = CJMAX;
|
|
}
|
|
else
|
|
{
|
|
cjGlyphMaxX2 = 2 * prface->MaxGlyphByteCount;
|
|
}
|
|
|
|
// 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
|
|
|
|
prface->NoCache = FALSE;
|
|
|
|
if (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)
|
|
|
|
/* we don't support NoCache and Path */
|
|
ASSERT(!IsPathFont())
|
|
prface->NoCache = TRUE;
|
|
}
|
|
|
|
|
|
// set up the cache semaphore.
|
|
// InitializeCriticalSection(&prface->FaceRealizationCritSection);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::AllocateCache() const
|
|
{
|
|
BOOL result = TRUE; // unless proven otherwise
|
|
|
|
ULONG cGlyphsTotal = 0;
|
|
|
|
cGlyphsTotal = GetGlyphsSupported();
|
|
|
|
if (!cGlyphsTotal)
|
|
return FALSE;
|
|
|
|
// 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
|
|
//
|
|
|
|
|
|
// allocate memory for the glyphDataArray :
|
|
|
|
if ((prface->GlyphDataArray = (GpGlyphData **) GpMalloc(cGlyphsTotal * sizeof(GpGlyphData*))) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// init all glyphdata pointers to zero
|
|
|
|
memset(prface->GlyphDataArray, 0, sizeof(GpGlyphData*) * cGlyphsTotal);
|
|
|
|
|
|
// Allocate memory for the first GpGlyphData block
|
|
|
|
if ((prface->GlyphDataBlockUnderConstruction = (GlyphDataBlock *)GpMalloc(sizeof(GlyphDataBlock))) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
prface->FirstGlyphDataBlock = prface->GlyphDataBlockUnderConstruction;
|
|
prface->FirstGlyphDataBlock->NextGlyphDataBlock = NULL;
|
|
prface->NextFreeGlyphDataIndex = 0;
|
|
|
|
// 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:
|
|
|
|
ULONG cjBytes = 16 * prface->MaxGlyphByteCount;
|
|
|
|
ULONG AllocationSize = ROUND_TO_PAGE(cjBytes);
|
|
|
|
if (AllocationSize == 0)
|
|
prface->cBlocksMax = 1;
|
|
else
|
|
{
|
|
prface->cBlocksMax =
|
|
(CJMAX * ((cGlyphsTotal + 1024 - 1)/1024)) /
|
|
AllocationSize;
|
|
|
|
/* at least one block */
|
|
if (prface->cBlocksMax == 0)
|
|
prface->cBlocksMax = 1;
|
|
}
|
|
prface->cBlocks = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
//// ConvertGLYPHDATAToGpGlyphMetrics
|
|
//
|
|
// Populate GpGlyphMetrics field of GpGlyphData from font driver GLYPHDATA
|
|
|
|
|
|
VOID GpFaceRealization::ConvertGLYPHDATAToGpGlyphMetrics(
|
|
IN INT glyphIndex,
|
|
IN GLYPHDATA *pgd,
|
|
OUT GpGlyphData *pgpgd
|
|
) const
|
|
{
|
|
// horizontal metrics:
|
|
|
|
pgpgd->GlyphMetrics[0].AdvanceWidth = pgd->fxD;
|
|
pgpgd->GlyphMetrics[0].LeadingSidebearing = pgd->fxA;
|
|
pgpgd->GlyphMetrics[0].TrailingSidebearing = pgd->fxD - pgd->fxAB;
|
|
pgpgd->GlyphMetrics[0].Origin = PointF(0,0);
|
|
|
|
// vertical metrics:
|
|
|
|
pgpgd->GlyphMetrics[1].AdvanceWidth = pgd->fxD_Sideways;
|
|
pgpgd->GlyphMetrics[1].LeadingSidebearing = pgd->fxA_Sideways;
|
|
pgpgd->GlyphMetrics[1].TrailingSidebearing = pgd->fxD_Sideways - pgd->fxAB_Sideways;
|
|
|
|
pgpgd->GlyphMetrics[1].Origin.X = pgd->VerticalOrigin_X / 16.0f;
|
|
pgpgd->GlyphMetrics[1].Origin.Y = pgd->VerticalOrigin_Y / 16.0f;
|
|
|
|
pgpgd->GlyphBits = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
GpStatus GpFaceRealization::IsMetricsCached
|
|
(
|
|
UINT16 glyphIndex,
|
|
ULONG *pcjNeeded
|
|
) const
|
|
{
|
|
ULONG cjNeeded = 0;
|
|
|
|
if (prface->GlyphDataArray == NULL)
|
|
if (!AllocateCache())
|
|
return OutOfMemory;
|
|
|
|
if (glyphIndex >= prface->Face->NumGlyphs)
|
|
return InvalidParameter;
|
|
|
|
if (!prface->GlyphDataArray[glyphIndex])
|
|
{
|
|
GLYPHDATA gd;
|
|
|
|
// 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 (!CheckMetricsCache())
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
// Call font driver to get the metrics.
|
|
|
|
cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
(HGLYPH)glyphIndex,
|
|
&gd,
|
|
NULL
|
|
);
|
|
if (cjNeeded == FD_ERROR)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
gd.gdf.pgb = NULL;
|
|
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left);
|
|
ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top);
|
|
ASSERT(cjNeeded <= CJ_CTGD(cx+2,cy));
|
|
cjNeeded = CJ_CTGD(cx+2,cy);
|
|
}
|
|
|
|
prface->GlyphDataArray[glyphIndex] = &prface->GlyphDataBlockUnderConstruction->GlyphDataArray[prface->NextFreeGlyphDataIndex];
|
|
|
|
// Populate GpGlyphMetrics field of GpGlyphData from font driver GLYPHDATA
|
|
|
|
ConvertGLYPHDATAToGpGlyphMetrics(glyphIndex, &gd, prface->GlyphDataArray[glyphIndex]);
|
|
|
|
prface->NextFreeGlyphDataIndex ++;
|
|
}
|
|
|
|
if (pcjNeeded)
|
|
{
|
|
*pcjNeeded = cjNeeded;
|
|
}
|
|
|
|
|
|
ASSERT(prface->GlyphDataArray[glyphIndex])
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::InsertGlyphPath(
|
|
UINT16 glyphIndex,
|
|
BOOL bFlushOk
|
|
) const
|
|
{
|
|
|
|
// Call font driver to get the metrics.
|
|
|
|
GpGlyphPath *fontPath;
|
|
|
|
GLYPHDATA gd;
|
|
GpPath path;
|
|
|
|
ASSERT(IsPathFont());
|
|
ASSERT(prface->GlyphDataArray[glyphIndex]);
|
|
|
|
if (prface->GlyphDataArray[glyphIndex]->GlyphPath)
|
|
return TRUE;
|
|
|
|
ULONG cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
(HGLYPH)glyphIndex,
|
|
&gd,
|
|
(PVOID)&path
|
|
);
|
|
|
|
if ( cjNeeded == FD_ERROR )
|
|
return FALSE;
|
|
|
|
if (!path.IsValid())
|
|
return FALSE;
|
|
|
|
cjNeeded = sizeof(GpGlyphPath) +
|
|
path.GetPointCount() * (sizeof(GpPointF) + sizeof(BYTE));
|
|
cjNeeded = ALIGN(void*, cjNeeded);
|
|
|
|
/* a GpGlyphPath* need to be aligned to the next valid pointer address */
|
|
ALIGN(void*, prface->UsedBytesGlyphBitsBlockUnderConstruction);
|
|
|
|
VOID *GlyphBits;
|
|
|
|
while ((GlyphBits = (GLYPHBITS *)pgbCheckGlyphCache(cjNeeded)) == NULL)
|
|
{
|
|
if ( !bFlushOk )
|
|
return FALSE;
|
|
|
|
//TRACE_INSERT(("InsertGlyphBits: Flushing the cache\n"));
|
|
|
|
FlushCache();
|
|
bFlushOk = FALSE;
|
|
}
|
|
|
|
fontPath = (GpGlyphPath*)GlyphBits;
|
|
|
|
if (fontPath->CopyPath(&path) != Ok)
|
|
return FALSE;
|
|
|
|
prface->GlyphDataArray[glyphIndex]->GlyphPath = fontPath;
|
|
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction += cjNeeded;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::InsertGlyphBits(
|
|
UINT16 glyphIndex,
|
|
ULONG cjNeeded,
|
|
BOOL bFlushOk
|
|
) const
|
|
{
|
|
if (prface->NoCache)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(!IsPathFont());
|
|
ASSERT(prface->GlyphDataArray[glyphIndex]);
|
|
|
|
if (prface->GlyphDataArray[glyphIndex]->GlyphBits)
|
|
return TRUE;
|
|
|
|
// Look to see if there is room in the glyphbits cache
|
|
// Grow the glyphbits cache if neccessary, but don't flush the cache
|
|
|
|
GLYPHDATA gd;
|
|
|
|
// If max glyph will fit, assume max glyph
|
|
// otherwise, call up and ask how big
|
|
|
|
if ( (prface->MaxGlyphByteCount < (SIZE_T)(prface->SizeGlyphBitsBlockUnderConstruction - prface->UsedBytesGlyphBitsBlockUnderConstruction)) )
|
|
{
|
|
cjNeeded = prface->MaxGlyphByteCount;
|
|
}
|
|
else
|
|
{
|
|
if (!cjNeeded)
|
|
{
|
|
cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
glyphIndex,
|
|
&gd,
|
|
NULL
|
|
);
|
|
|
|
if ( cjNeeded == FD_ERROR )
|
|
return FALSE;
|
|
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left);
|
|
ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top);
|
|
ASSERT(cjNeeded <= CJ_CTGD(cx+2,cy));
|
|
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 *GlyphBits;
|
|
|
|
//TRACE_INSERT(("InsertGlyphBits: attempting to insert bits at: 0x%lx\n", prface->UsedBytesGlyphBitsBlockUnderConstruction));
|
|
|
|
while ((GlyphBits = (GLYPHBITS *)pgbCheckGlyphCache(cjNeeded)) == NULL)
|
|
{
|
|
if ( !bFlushOk )
|
|
return FALSE;
|
|
|
|
//TRACE_INSERT(("InsertGlyphBits: Flushing the cache\n"));
|
|
|
|
FlushCache();
|
|
bFlushOk = FALSE;
|
|
}
|
|
|
|
// Call font driver to get glyph bits.
|
|
|
|
cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
glyphIndex,
|
|
&gd,
|
|
(VOID *)GlyphBits
|
|
);
|
|
|
|
if ( cjNeeded == FD_ERROR )
|
|
return FALSE;
|
|
|
|
ASSERT(cjNeeded <= prface->MaxGlyphByteCount);
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left);
|
|
ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top);
|
|
|
|
ASSERT(cjNeeded <= CJ_CTGD(cx+2,cy));
|
|
cjNeeded = CJ_CTGD(cx+2,cy);
|
|
|
|
ASSERT(cjNeeded <= prface->MaxGlyphByteCount);
|
|
|
|
if (GlyphBits)
|
|
{
|
|
cjNeeded = ulClearTypeFilter(GlyphBits, cjNeeded, prface);
|
|
}
|
|
}
|
|
|
|
// Only the glyph bits we need.
|
|
|
|
prface->GlyphDataArray[glyphIndex]->GlyphBits = GlyphBits;
|
|
|
|
// Adjust the cache next pointers as needed.
|
|
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction += cjNeeded;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//// GetGlyphDataLookaside
|
|
//
|
|
// Returns glyph data for a single glyph, using the lookaside buffer
|
|
// instead of the cache.
|
|
|
|
|
|
|
|
GpGlyphData *GpFaceRealization::GetGlyphDataLookaside(
|
|
UINT16 glyphIndex
|
|
) const
|
|
{
|
|
if (!IsPathFont())
|
|
{
|
|
// Make sure the lookaside buffer has enough room for the bitmap
|
|
|
|
ULONG cjMaxBitmap = prface->MaxGlyphByteCount + sizeof(GpGlyphData);
|
|
|
|
// Allocate the buffer and save its size if existing buffer isn't big enough
|
|
|
|
if (prface->LookasideByteCount < cjMaxBitmap)
|
|
{
|
|
if (prface->LookasideGlyphData != NULL)
|
|
{
|
|
GpFree(prface->LookasideGlyphData);
|
|
}
|
|
|
|
prface->LookasideGlyphData = (GpGlyphData *)GpMalloc(cjMaxBitmap);
|
|
|
|
if (prface->LookasideGlyphData == NULL)
|
|
return NULL;
|
|
|
|
prface->LookasideByteCount = cjMaxBitmap;
|
|
}
|
|
|
|
GpGlyphData *pgd = prface->LookasideGlyphData;
|
|
GLYPHBITS *glyphBits = (GLYPHBITS *)(pgd + 1);
|
|
GLYPHDATA gd;
|
|
|
|
ULONG cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
glyphIndex,
|
|
&gd,
|
|
glyphBits
|
|
);
|
|
|
|
if (cjNeeded == FD_ERROR)
|
|
return NULL;
|
|
|
|
ASSERT(cjNeeded <= prface->MaxGlyphByteCount);
|
|
|
|
if (prface->fobj.flFontType & FO_CLEARTYPE_GRID)
|
|
{
|
|
ULONG cx = (ULONG)(gd.rclInk.right - gd.rclInk.left);
|
|
ULONG cy = (ULONG)(gd.rclInk.bottom - gd.rclInk.top);
|
|
|
|
ASSERT(cjNeeded <= CJ_CTGD(cx+2,cy));
|
|
cjNeeded = CJ_CTGD(cx+2,cy);
|
|
|
|
ASSERT(cjNeeded <= prface->MaxGlyphByteCount);
|
|
|
|
if (glyphBits)
|
|
{
|
|
cjNeeded = ulClearTypeFilter(glyphBits, cjNeeded, prface);
|
|
}
|
|
}
|
|
|
|
// Populate GpGlyphMetrics field of GpGlyphData from font driver GLYPHDATA
|
|
|
|
ConvertGLYPHDATAToGpGlyphMetrics(glyphIndex, &gd, pgd);
|
|
|
|
// Set the returned value
|
|
|
|
pgd->GlyphBits = glyphBits;
|
|
|
|
return pgd;
|
|
}
|
|
else
|
|
{
|
|
// For glyph path
|
|
|
|
// Call font driver to get the metrics.
|
|
|
|
GLYPHDATA gd;
|
|
GpPath path;
|
|
|
|
// Verify enough room in metrics cache area, grow if needed.
|
|
// Note that failure to fit a glyphdata is a hard error, get out now.
|
|
|
|
|
|
ULONG cjNeeded = ttfdSemQueryFontData(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
(HGLYPH)glyphIndex,
|
|
&gd,
|
|
(PVOID)&path
|
|
);
|
|
|
|
if ( cjNeeded == FD_ERROR )
|
|
return NULL;
|
|
|
|
if (!path.IsValid())
|
|
return NULL;
|
|
|
|
cjNeeded = sizeof(GpGlyphData) + sizeof(GpGlyphPath) + path.GetPointCount() * (sizeof(GpPointF) + sizeof(BYTE));
|
|
cjNeeded = ALIGN(void*, cjNeeded);
|
|
|
|
// Make sure the lookaside buffer is allocated
|
|
|
|
if ( ( prface->LookasideByteCount < cjNeeded ) &&
|
|
( prface->LookasideGlyphData != NULL ))
|
|
{
|
|
GpFree((PVOID) prface->LookasideGlyphData);
|
|
prface->LookasideGlyphData = NULL;
|
|
prface->LookasideByteCount = 0;
|
|
}
|
|
|
|
if ( prface->LookasideGlyphData == NULL )
|
|
{
|
|
prface->LookasideGlyphData = (GpGlyphData *)GpMalloc(cjNeeded);
|
|
|
|
if ( prface->LookasideGlyphData == NULL )
|
|
return NULL;
|
|
|
|
prface->LookasideByteCount = cjNeeded;
|
|
}
|
|
|
|
GpGlyphData * pgd = prface->LookasideGlyphData;
|
|
GpGlyphPath * fontPath = (GpGlyphPath *)(pgd + 1);
|
|
|
|
// Populate GpGlyphMetrics field of GpGlyphData from font driver GLYPHDATA
|
|
ConvertGLYPHDATAToGpGlyphMetrics(glyphIndex, &gd, pgd);
|
|
|
|
if (fontPath->CopyPath(&path) != Ok)
|
|
return FALSE;
|
|
|
|
// Set the returned value
|
|
|
|
pgd->GlyphPath = fontPath;
|
|
|
|
return pgd;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GpFaceRealization::CheckMetricsCache() const
|
|
{
|
|
|
|
// Verify enough room in metrics cache area, grow if needed.
|
|
|
|
if (prface->NextFreeGlyphDataIndex >= GLYPHDATABLOCKCOUNT)
|
|
{
|
|
GlyphDataBlock *NewGlyphDataBlock;
|
|
|
|
// allocate a new block of GpGlyphData structs
|
|
|
|
if ((NewGlyphDataBlock = (GlyphDataBlock *)GpMalloc(sizeof(GlyphDataBlock))) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
NewGlyphDataBlock->NextGlyphDataBlock = NULL;
|
|
|
|
prface->GlyphDataBlockUnderConstruction->NextGlyphDataBlock = NewGlyphDataBlock;
|
|
|
|
prface->GlyphDataBlockUnderConstruction = NewGlyphDataBlock;
|
|
prface->NextFreeGlyphDataIndex = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PVOID GpFaceRealization::pgbCheckGlyphCache(SIZE_T cjNeeded) const
|
|
{
|
|
if ((prface->UsedBytesGlyphBitsBlockUnderConstruction + cjNeeded) > prface->SizeGlyphBitsBlockUnderConstruction)
|
|
{
|
|
ULONG cjBlockSize;
|
|
|
|
|
|
ASSERT (!(prface->NoCache));
|
|
|
|
if (IsPathFont())
|
|
{
|
|
// this seems to work and this is what we did before DavidFie changes
|
|
// for PATHOBJ case
|
|
|
|
cjBlockSize = CJMAX;
|
|
}
|
|
else
|
|
{
|
|
ULONG cjBytes = 16 * prface->MaxGlyphByteCount;
|
|
|
|
cjBlockSize = ROUND_TO_PAGE(cjBytes);
|
|
|
|
if (prface->FirstGlyphBitsBlock == NULL)
|
|
{
|
|
// first block designed to contain 16 glyphs
|
|
|
|
cjBlockSize = cjBytes;
|
|
}
|
|
}
|
|
|
|
if ( !(prface->NoCache)
|
|
&& (prface->cBlocks < prface->cBlocksMax)
|
|
&& ((offsetof(GlyphBitsBlock,Bits) + cjNeeded) <= cjBlockSize))
|
|
{
|
|
// The only reason we need the last check is the PATHOBJ case
|
|
// where cjNeeded may actually not fit in the block of SizeGlyphBitsBlock bytes.
|
|
// This is because we have no way of knowing how big the paths
|
|
// are going to be (especailly after doing bFlatten) and our
|
|
// prface->MaxGlyphByteCount is just a good guess in this case.
|
|
|
|
// We are going to append another block at the end of the list
|
|
|
|
GlyphBitsBlock *newGlyphBitsBlock = (GlyphBitsBlock *) GpMalloc(cjBlockSize);
|
|
|
|
if (!newGlyphBitsBlock)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// we have just allocated another block, update cBlocks:
|
|
|
|
prface->cBlocks += 1;
|
|
|
|
// append this block to the end of the list
|
|
|
|
newGlyphBitsBlock->NextGlyphBitsBlock = NULL;
|
|
|
|
if (prface->GlyphBitsBlockUnderConstruction != NULL)
|
|
prface->GlyphBitsBlockUnderConstruction->NextGlyphBitsBlock = newGlyphBitsBlock;
|
|
|
|
prface->GlyphBitsBlockUnderConstruction = newGlyphBitsBlock;
|
|
|
|
if (!prface->FirstGlyphBitsBlock) // first block ever for this rfont
|
|
{
|
|
prface->FirstGlyphBitsBlock = newGlyphBitsBlock;
|
|
}
|
|
prface->GlyphBitsBlockUnderConstruction->SizeGlyphBitsBlock = cjBlockSize;
|
|
prface->SizeGlyphBitsBlockUnderConstruction = cjBlockSize;
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction = offsetof(GlyphBitsBlock,Bits);
|
|
ALIGN(void*, prface->UsedBytesGlyphBitsBlockUnderConstruction);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// tough luck, we are not allowed to add more blocks
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (BYTE *)prface->GlyphBitsBlockUnderConstruction + prface->UsedBytesGlyphBitsBlockUnderConstruction;
|
|
}
|
|
|
|
|
|
VOID GpFaceRealization::FlushCache() const
|
|
{
|
|
|
|
// 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 (prface->FirstGlyphBitsBlock && (prface->cBlocks == prface->cBlocksMax))
|
|
{
|
|
prface->GlyphBitsBlockUnderConstruction = prface->FirstGlyphBitsBlock;
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction = offsetof(GlyphBitsBlock,Bits);
|
|
ALIGN(void*, prface->UsedBytesGlyphBitsBlockUnderConstruction);
|
|
|
|
// 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.
|
|
|
|
prface->SizeGlyphBitsBlockUnderConstruction = prface->GlyphBitsBlockUnderConstruction->SizeGlyphBitsBlock;
|
|
}
|
|
|
|
// now go and invalidate the glyphbit pointers in the glyphdata cache
|
|
|
|
for
|
|
(
|
|
GlyphDataBlock *pdbl = prface->FirstGlyphDataBlock;
|
|
pdbl != (GlyphDataBlock*)NULL;
|
|
pdbl = pdbl->NextGlyphDataBlock
|
|
)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < GLYPHDATABLOCKCOUNT; i++)
|
|
{
|
|
pdbl->GlyphDataArray[i].GlyphBits = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//// CheckGlyphStringMetricsCached
|
|
//
|
|
// Ensures that glyph metric information for all the glyphs in the
|
|
// given glyph string are already cached.
|
|
|
|
|
|
GpStatus GpFaceRealization::CheckGlyphStringMetricsCached(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount
|
|
) const
|
|
{
|
|
ASSERT(glyphCount >= 0);
|
|
|
|
// Create glyph data array if none yet exists
|
|
|
|
if (prface->GlyphDataArray == NULL)
|
|
{
|
|
if (!AllocateCache())
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
}
|
|
|
|
GpGlyphData **glyphData = prface->GlyphDataArray;
|
|
|
|
|
|
// Check each glyph
|
|
|
|
INT glyphIndexLimit = prface->Face->NumGlyphs;
|
|
|
|
INT i=0;
|
|
|
|
while (i < glyphCount)
|
|
{
|
|
// Loop quickly through glyphs that are already cached
|
|
|
|
while ( i < glyphCount
|
|
&& ( glyphs[i] == EMPTY_GLYPH_FFFF
|
|
|| ( glyphs[i] < glyphIndexLimit
|
|
&& glyphData[glyphs[i]] != NULL)))
|
|
{
|
|
i++;
|
|
}
|
|
|
|
// Use IsMetricsCached for glyphs not already cached
|
|
|
|
if (i < glyphCount)
|
|
{
|
|
GpStatus status = IsMetricsCached(glyphs[i], NULL);
|
|
if (status != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
//// GetGlyphStringIdealAdvanceVector
|
|
//
|
|
// Returns the realized advance vector scaled to ideal units.
|
|
|
|
|
|
GpStatus GpFaceRealization::GetGlyphStringIdealAdvanceVector(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount,
|
|
REAL deviceToIdeal,
|
|
BOOL vertical,
|
|
INT *idealAdvances
|
|
) const
|
|
{
|
|
GpStatus status = CheckGlyphStringMetricsCached(glyphs, glyphCount);
|
|
IF_NOT_OK_WARN_AND_RETURN(status);
|
|
|
|
vertical = vertical ? 1 : 0; // Prepare vertical flag for use as index
|
|
|
|
// Provide advance width for each glyph
|
|
|
|
for (INT i=0; i<glyphCount; i++)
|
|
{
|
|
if (glyphs[i] == EMPTY_GLYPH_FFFF)
|
|
{
|
|
idealAdvances[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
idealAdvances[i] = GpRound(
|
|
prface->GlyphDataArray[glyphs[i]]->GlyphMetrics[vertical].AdvanceWidth
|
|
* deviceToIdeal
|
|
/ 16
|
|
);
|
|
}
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
//// GetGlyphStringDeviceAdvanceVector
|
|
//
|
|
// Returns the realized advance vector in device units
|
|
|
|
|
|
GpStatus GpFaceRealization::GetGlyphStringDeviceAdvanceVector(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount,
|
|
BOOL vertical,
|
|
REAL *deviceAdvances
|
|
) const
|
|
{
|
|
GpStatus status = CheckGlyphStringMetricsCached(glyphs, glyphCount);
|
|
IF_NOT_OK_WARN_AND_RETURN(status);
|
|
|
|
vertical = vertical ? 1 : 0; // Prepare vertical flag for use as index
|
|
|
|
// Provide advance width for each glyph
|
|
|
|
for (INT i=0; i<glyphCount; i++)
|
|
{
|
|
if (glyphs[i] == EMPTY_GLYPH_FFFF)
|
|
{
|
|
deviceAdvances[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
deviceAdvances[i] = TOREAL(prface->GlyphDataArray[glyphs[i]]->GlyphMetrics[vertical].AdvanceWidth) / 16;
|
|
}
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
// INT 28.4 variant
|
|
|
|
GpStatus GpFaceRealization::GetGlyphStringDeviceAdvanceVector(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount,
|
|
BOOL vertical,
|
|
INT *deviceAdvances
|
|
) const
|
|
{
|
|
GpStatus status = CheckGlyphStringMetricsCached(glyphs, glyphCount);
|
|
IF_NOT_OK_WARN_AND_RETURN(status);
|
|
|
|
GpGlyphData **glyphDataArray = prface->GlyphDataArray;
|
|
|
|
vertical = vertical ? 1 : 0; // Prepare vertical flag for use as index
|
|
|
|
// Provide advance width for each glyph
|
|
|
|
for (INT i=0; i<glyphCount; i++)
|
|
{
|
|
if (glyphs[i] == EMPTY_GLYPH_FFFF)
|
|
{
|
|
deviceAdvances[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
deviceAdvances[i] = glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[vertical]
|
|
.AdvanceWidth;
|
|
}
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GpStatus GpFaceRealization::GetGlyphStringVerticalOriginOffsets(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount,
|
|
PointF *offsets
|
|
) const
|
|
{
|
|
|
|
GpStatus status = CheckGlyphStringMetricsCached(glyphs, glyphCount);
|
|
IF_NOT_OK_WARN_AND_RETURN(status);
|
|
|
|
GpGlyphData **glyphDataArray = prface->GlyphDataArray;
|
|
|
|
for (INT i=0; i<glyphCount; i++)
|
|
{
|
|
if (glyphs[i] == EMPTY_GLYPH_FFFF)
|
|
{
|
|
offsets[i] = PointF(0,0);
|
|
}
|
|
else
|
|
{
|
|
offsets[i] = glyphDataArray[glyphs[i]]->GlyphMetrics[1].Origin;
|
|
}
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
//// GetGlyphStringSidebearings
|
|
//
|
|
// Sidebearings - the sidebearings returned are the largest distances
|
|
// over the ends of the string. i.e. if the first glyph has no negative A
|
|
// width, but the second glyph has a negative A width large enough to reach
|
|
// back over the whole of the first glyph, we return that part of the 2nd
|
|
// glyphs A width that overhangs the left end of the line.
|
|
// This situation is common with scripts that make extensive use of
|
|
// combining characters.
|
|
|
|
|
|
GpStatus GpFaceRealization::GetGlyphStringSidebearings(
|
|
const UINT16 *glyphs,
|
|
INT glyphCount,
|
|
BOOL vertical,
|
|
BOOL reverse, // For example right-to-left
|
|
INT *leadingSidebearing, // 28.4
|
|
INT *trailingSidebearing // 28.4
|
|
) const
|
|
{
|
|
GpStatus status = CheckGlyphStringMetricsCached(glyphs, glyphCount);
|
|
|
|
IF_NOT_OK_WARN_AND_RETURN(status);
|
|
|
|
GpGlyphData **glyphDataArray = prface->GlyphDataArray;
|
|
|
|
INT orientation = vertical ? 1 : 0; // Prepare vertical flag for use as index
|
|
|
|
INT maxSupportedSidebearing28p4 = (prface->DeviceMetrics.yMax-prface->DeviceMetrics.yMin) * 2 * 16;
|
|
|
|
if (leadingSidebearing)
|
|
{
|
|
// Determine largest overhang to left of string of any glyph
|
|
// in the string.
|
|
//
|
|
// We assume that no overhang exceeds approx 2 ems.
|
|
//
|
|
// NOTE: If you make a change to for leadingsizdebeating, also fix
|
|
// trailingsidebearing below.
|
|
|
|
INT offset28p4 = 0;
|
|
INT sidebearing28p4 = maxSupportedSidebearing28p4;
|
|
|
|
INT i = 0;
|
|
|
|
while ( i < glyphCount
|
|
&& offset28p4 < maxSupportedSidebearing28p4)
|
|
{
|
|
INT glyphSidebearing28p4;
|
|
|
|
if (reverse)
|
|
{
|
|
glyphSidebearing28p4 = glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.TrailingSidebearing;
|
|
}
|
|
else
|
|
{
|
|
glyphSidebearing28p4 = glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.LeadingSidebearing;
|
|
}
|
|
|
|
|
|
if (glyphSidebearing28p4 + offset28p4 < sidebearing28p4)
|
|
{
|
|
sidebearing28p4 = glyphSidebearing28p4+offset28p4;
|
|
}
|
|
|
|
offset28p4 += glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.AdvanceWidth;
|
|
i++;
|
|
}
|
|
|
|
*leadingSidebearing = sidebearing28p4;
|
|
}
|
|
|
|
|
|
if (trailingSidebearing)
|
|
{
|
|
INT offset28p4 = 0;
|
|
INT sidebearing28p4 = maxSupportedSidebearing28p4;
|
|
|
|
INT i = glyphCount-1;
|
|
|
|
while ( i >= 0
|
|
&& offset28p4 < maxSupportedSidebearing28p4)
|
|
{
|
|
INT glyphSidebearing28p4;
|
|
|
|
if (reverse)
|
|
{
|
|
glyphSidebearing28p4 = glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.LeadingSidebearing;
|
|
}
|
|
else
|
|
{
|
|
glyphSidebearing28p4 = glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.TrailingSidebearing;
|
|
}
|
|
|
|
if (glyphSidebearing28p4 + offset28p4 < sidebearing28p4)
|
|
{
|
|
sidebearing28p4 = glyphSidebearing28p4+offset28p4;
|
|
}
|
|
|
|
offset28p4 += glyphDataArray[glyphs[i]]
|
|
->GlyphMetrics[orientation]
|
|
.AdvanceWidth;
|
|
i--;
|
|
}
|
|
|
|
*trailingSidebearing = sidebearing28p4;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
GpStatus
|
|
GpFaceRealization::GetGlyphPath(
|
|
const UINT16 glyphIndice,
|
|
GpGlyphPath **pFontPath,
|
|
PointF *sidewaysOffset
|
|
) const
|
|
{
|
|
VOID *glyphBuffer, *glyphBits;
|
|
GpStatus status;
|
|
|
|
if ((status = IsMetricsCached(glyphIndice, NULL)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if (!InsertGlyphPath(glyphIndice, TRUE))
|
|
return GenericError;
|
|
|
|
*pFontPath = prface->GlyphDataArray[glyphIndice]->GlyphPath;
|
|
|
|
if (sidewaysOffset)
|
|
{
|
|
// Return sideways offset as REAL
|
|
*sidewaysOffset = prface->GlyphDataArray[glyphIndice]->GlyphMetrics[1].Origin;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
INT GpFaceRealization::GetGlyphPos(
|
|
const INT cGlyphs, // How many glyphs Client want to request
|
|
const UINT16 *glyphs, // An array of glyph index
|
|
GpGlyphPos *pgpos, // An array of GLYPHPOS
|
|
const PointF *glyphOrigins, // X,Y positions for sub-pixel calculation
|
|
INT *cParsed, // How many glyphs we parsed
|
|
BOOL sideways // e.g. FE characters in vertical text
|
|
) const
|
|
{
|
|
INT cgpos = 0;
|
|
BOOL noCache = prface->NoCache;
|
|
BOOL pathFont = IsPathFont();
|
|
|
|
*cParsed = 0;
|
|
|
|
INT glyphLimit = noCache ? 1 : cGlyphs;
|
|
|
|
if (prface->CacheType == CacheAABits)
|
|
{
|
|
/* we could be in noCache mode with a surrogate sequence, doing one glyph at a time
|
|
and with glyphs[0] == EMPTY_GLYPH_FFFF */
|
|
for (INT i=0; (i < cGlyphs) && (cgpos < glyphLimit); i++)
|
|
{
|
|
if (glyphs[i] != EMPTY_GLYPH_FFFF)
|
|
{
|
|
INT x = GpRound(TOREAL(glyphOrigins[i].X * 16.0));
|
|
INT y = GpRound(TOREAL(glyphOrigins[i].Y * 16.0));
|
|
|
|
if (!GetAAGlyphDataCached(glyphs[i], pgpos+cgpos, i==0, x, y, sideways))
|
|
{
|
|
break;
|
|
}
|
|
cgpos++;
|
|
}
|
|
(*cParsed)++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(prface->realizationMethod != TextRenderingHintAntiAlias);
|
|
|
|
/* we could be in noCache mode with a surrogate sequence, doing one glyph at a time
|
|
and with glyphs[0] == EMPTY_GLYPH_FFFF */
|
|
for (INT i=0; (i < cGlyphs) && (cgpos < glyphLimit); i++)
|
|
{
|
|
if (glyphs[i] != EMPTY_GLYPH_FFFF)
|
|
{
|
|
INT x = GpRound(TOREAL(glyphOrigins[i].X * 16.0));
|
|
INT y = GpRound(TOREAL(glyphOrigins[i].Y * 16.0));
|
|
|
|
GpGlyphData *pgd = NULL;
|
|
|
|
if (noCache)
|
|
{
|
|
pgd = GetGlyphDataLookaside(glyphs[i]);
|
|
}
|
|
else
|
|
{
|
|
pgd = GetGlyphDataCached(glyphs[i], i==0);
|
|
}
|
|
|
|
if (!pgd || !pgd->GlyphBits)
|
|
{
|
|
// No more glyph data available. (Cache may be full)
|
|
break;
|
|
}
|
|
|
|
if (pathFont)
|
|
{
|
|
INT left = (x+8) >> 4;
|
|
INT top = (y+8) >> 4;
|
|
|
|
if (sideways)
|
|
{
|
|
left -= GpRound(pgd->GlyphMetrics[1].Origin.X);
|
|
top -= GpRound(pgd->GlyphMetrics[1].Origin.Y);
|
|
}
|
|
|
|
pgpos[cgpos].SetLeft (left);
|
|
pgpos[cgpos].SetTop (top);
|
|
pgpos[cgpos].SetWidth (1);
|
|
pgpos[cgpos].SetHeight(1);
|
|
pgpos[cgpos].SetPath(pgd->GlyphPath);
|
|
}
|
|
else
|
|
{
|
|
if (sideways)
|
|
{
|
|
pgpos[cgpos].SetLeft(pgd->GlyphBits->ptlSidewaysOrigin.x + ((x + 8)>>4));
|
|
pgpos[cgpos].SetTop (pgd->GlyphBits->ptlSidewaysOrigin.y + ((y + 8)>>4));
|
|
}
|
|
else
|
|
{
|
|
pgpos[cgpos].SetLeft(pgd->GlyphBits->ptlUprightOrigin.x + ((x + 8)>>4));
|
|
pgpos[cgpos].SetTop (pgd->GlyphBits->ptlUprightOrigin.y + ((y + 8)>>4));
|
|
}
|
|
|
|
pgpos[cgpos].SetWidth (pgd->GlyphBits->sizlBitmap.cx);
|
|
pgpos[cgpos].SetHeight(pgd->GlyphBits->sizlBitmap.cy);
|
|
pgpos[cgpos].SetBits(pgd->GlyphBits->aj);
|
|
}
|
|
|
|
cgpos++;
|
|
}
|
|
|
|
(*cParsed)++;
|
|
}
|
|
}
|
|
|
|
return cgpos;
|
|
}
|
|
|
|
|
|
// Serch the glyph data from cache array.
|
|
|
|
GpGlyphData *
|
|
GpFaceRealization::GetGlyphDataCached(
|
|
UINT16 glyphIndex,
|
|
BOOL allowFlush
|
|
) const
|
|
{
|
|
VOID *glyphBuffer, *glyphBits;
|
|
ULONG cjNeeded = 0;
|
|
|
|
GpStatus status;
|
|
|
|
if ((status = IsMetricsCached(glyphIndex, &cjNeeded)) != Ok)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (IsPathFont())
|
|
{
|
|
if (!InsertGlyphPath(glyphIndex, allowFlush))
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!InsertGlyphBits(glyphIndex, cjNeeded, allowFlush))
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(prface->GlyphDataArray[glyphIndex]);
|
|
|
|
if (!prface->GlyphDataArray[glyphIndex])
|
|
return NULL;
|
|
|
|
return prface->GlyphDataArray[glyphIndex];
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
GpFaceRealization::GetAAGlyphDataCached(
|
|
UINT16 glyphIndex,
|
|
GpGlyphPos * pgpos,
|
|
BOOL bFlushOk,
|
|
INT x,
|
|
INT y,
|
|
BOOL sideways // e.g. FE characters in vertical text
|
|
) const
|
|
{
|
|
const GpFaceRealization * pfaceRealization = this;
|
|
|
|
UINT xsubPos = ((UINT) (((x+1) & 0x0000000F) >> 1));
|
|
// we need to be carefull that y axis is downwards
|
|
UINT ysubPos = ((UINT) (7 - (((y+1) & 0x0000000F) >> 1)));
|
|
|
|
// limit the subpixel position to 1/4 of a pixel to have only a maximum of 16 different bitmaps to cache
|
|
xsubPos = xsubPos & 0x6;
|
|
ysubPos = ysubPos & 0x6;
|
|
|
|
if (LimitSubpixel)
|
|
{
|
|
// Now limit the subpixel position further so that large font sizes do
|
|
// not generate all 16 subpixel glyphs!
|
|
|
|
if ((prface->DeviceMetrics.yMax-prface->DeviceMetrics.yMin) > 50)
|
|
{
|
|
// Force to 4 possible values...
|
|
xsubPos &= 0x4;
|
|
ysubPos &= 0x4;
|
|
|
|
if ((prface->DeviceMetrics.yMax-prface->DeviceMetrics.yMin) > 100)
|
|
{
|
|
// Force to 1 possible value...
|
|
xsubPos = 0x4;
|
|
ysubPos = 0x4;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(!pfaceRealization->IsPathFont())
|
|
|
|
// Look to see if there is room in the glyphbits cache
|
|
// Grow the glyphbits cache if neccessary, but don't flush the cache
|
|
|
|
ULONG subPosX;
|
|
if (xsubPos <= 7)
|
|
subPosX = xsubPos << 13;
|
|
else
|
|
subPosX = 0;
|
|
|
|
ULONG subPosY;
|
|
if (ysubPos)
|
|
subPosY = ysubPos << 13;
|
|
else
|
|
subPosY = 0;
|
|
|
|
// If max glyph will fit, assume max glyph
|
|
// otherwise, call up and ask how big
|
|
|
|
ASSERT (pfaceRealization->QueryFontDataMode() == QFD_TT_GRAY4_BITMAP ||
|
|
pfaceRealization->QueryFontDataMode() == QFD_GLYPHANDBITMAP_SUBPIXEL);
|
|
|
|
if (IsMetricsCached(glyphIndex, 0) != Ok)
|
|
return FALSE;
|
|
|
|
GpGlyphData * glyphData = prface->GlyphDataArray[glyphIndex];
|
|
ASSERT(glyphData);
|
|
|
|
// check if we already have a bitmap for this subposition
|
|
for (GpGlyphAABits * cur = glyphData->GlyphAABits; cur != 0; cur = cur->Next)
|
|
{
|
|
if (cur->X == subPosX && cur->Y == subPosY)
|
|
break;
|
|
}
|
|
|
|
GLYPHBITS * pgb = 0;
|
|
if (cur)
|
|
pgb = reinterpret_cast<GLYPHBITS *>(&cur->Bits);
|
|
else
|
|
{
|
|
ULONG cjNeeded = ttfdSemQueryFontDataSubPos(
|
|
&prface->fobj,
|
|
prface->QueryFontDataMode,
|
|
glyphIndex,
|
|
0,
|
|
NULL,
|
|
subPosX,
|
|
subPosY
|
|
);
|
|
if (cjNeeded == FD_ERROR)
|
|
return FALSE;
|
|
|
|
ASSERT(cjNeeded != 0);
|
|
|
|
cjNeeded += offsetof(GpGlyphAABits, Bits);
|
|
|
|
|
|
if (prface->NoCache)
|
|
{
|
|
if (prface->LookasideByteCount < cjNeeded)
|
|
{
|
|
GpFree(prface->LookasideGlyphData);
|
|
|
|
prface->LookasideGlyphData = (GpGlyphData *)GpMalloc(cjNeeded);
|
|
|
|
if (!prface->LookasideGlyphData)
|
|
return FALSE;
|
|
|
|
prface->LookasideByteCount = cjNeeded;
|
|
}
|
|
|
|
cur = reinterpret_cast<GpGlyphAABits *>(prface->LookasideGlyphData);
|
|
}
|
|
else
|
|
{
|
|
// 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.
|
|
|
|
cjNeeded = ALIGN(void*, cjNeeded);
|
|
|
|
// a GpGlyphAABits * needs to be aligned to the next valid pointer address
|
|
ALIGN(void*, prface->UsedBytesGlyphBitsBlockUnderConstruction);
|
|
|
|
while ((cur = (GpGlyphAABits *)pgbCheckGlyphCache(cjNeeded)) == NULL)
|
|
{
|
|
if ( !bFlushOk )
|
|
return FALSE;
|
|
|
|
FlushCache();
|
|
bFlushOk = FALSE;
|
|
}
|
|
prface->UsedBytesGlyphBitsBlockUnderConstruction += cjNeeded;
|
|
}
|
|
|
|
pgb = reinterpret_cast<GLYPHBITS *>(&cur->Bits);
|
|
|
|
cjNeeded = ttfdSemQueryFontDataSubPos(
|
|
pfaceRealization->pfo(),
|
|
pfaceRealization->QueryFontDataMode(),
|
|
glyphIndex,
|
|
0,
|
|
pgb,
|
|
subPosX,
|
|
subPosY
|
|
);
|
|
if (cjNeeded == FD_ERROR)
|
|
return FALSE;
|
|
|
|
cur->X = subPosX;
|
|
cur->Y = subPosY;
|
|
if (!prface->NoCache)
|
|
cur->Next = glyphData->GlyphAABits, glyphData->GlyphAABits = cur;
|
|
}
|
|
|
|
// the pixel origin is computed by rouding the real origin minus the subpixel position
|
|
// to get to the placement of the origin of the bitmap, we add to that origin
|
|
// we cast (xsubPos << 1) and (ysubPos << 1) to INT to avoid
|
|
// converting possibly negative x and y to UINTs
|
|
if (sideways)
|
|
{
|
|
pgpos->SetLeft (pgb->ptlSidewaysOrigin.x + ((x - (INT)(xsubPos << 1) + 8 ) >> 4));
|
|
// we need to be careful that the y axis go downwards
|
|
pgpos->SetTop (pgb->ptlSidewaysOrigin.y + ((y + (INT)(ysubPos << 1) + 8) >> 4));
|
|
}
|
|
else
|
|
{
|
|
pgpos->SetLeft (pgb->ptlUprightOrigin.x + ((x - (INT)(xsubPos << 1) + 8 ) >> 4));
|
|
// we need to be careful that the y axis go downwards
|
|
pgpos->SetTop (pgb->ptlUprightOrigin.y + ((y + (INT)(ysubPos << 1) + 8) >> 4));
|
|
}
|
|
pgpos->SetWidth (pgb->sizlBitmap.cx);
|
|
pgpos->SetHeight(pgb->sizlBitmap.cy);
|
|
pgpos->SetBits(pgb->aj);
|
|
|
|
return TRUE;
|
|
|
|
} // GpFaceRealization::GetAAGlyphDataCached
|
|
|
|
|
|
|
|
|
|
GpCacheFaceRealizationList::~GpCacheFaceRealizationList()
|
|
{
|
|
// elements in that list get released when the font table get released
|
|
ASSERT(count == 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
void GpCacheFaceRealizationList::AddMostRecent(CacheFaceRealization *prface)
|
|
{
|
|
count ++;
|
|
|
|
if (head != NULL)
|
|
{
|
|
prface->NextRecentCacheFaceRealization = head;
|
|
|
|
prface->PreviousRecentCacheFaceRealization = head->PreviousRecentCacheFaceRealization;
|
|
|
|
prface->PreviousRecentCacheFaceRealization->NextRecentCacheFaceRealization = prface;
|
|
|
|
head->PreviousRecentCacheFaceRealization = prface;
|
|
}
|
|
else
|
|
{
|
|
prface->NextRecentCacheFaceRealization = prface;
|
|
prface->PreviousRecentCacheFaceRealization = prface;
|
|
}
|
|
|
|
head = prface;
|
|
}
|
|
|
|
|
|
|
|
|
|
void GpCacheFaceRealizationList::RemoveFace(CacheFaceRealization *prface)
|
|
{
|
|
if ((prface->PreviousRecentCacheFaceRealization != NULL) && (prface->NextRecentCacheFaceRealization != NULL))
|
|
{
|
|
if (prface->PreviousRecentCacheFaceRealization == prface)
|
|
{
|
|
head = NULL;
|
|
}
|
|
else
|
|
{
|
|
prface->PreviousRecentCacheFaceRealization->NextRecentCacheFaceRealization = prface->NextRecentCacheFaceRealization;
|
|
prface->NextRecentCacheFaceRealization->PreviousRecentCacheFaceRealization = prface->PreviousRecentCacheFaceRealization;
|
|
if (head == prface)
|
|
{
|
|
head = prface->NextRecentCacheFaceRealization;
|
|
}
|
|
}
|
|
|
|
prface->PreviousRecentCacheFaceRealization = NULL;
|
|
prface->NextRecentCacheFaceRealization = NULL;
|
|
count --;
|
|
ASSERT(count >= 0);
|
|
}
|
|
}
|
|
|
|
|
|
CacheFaceRealization *GpCacheFaceRealizationList::ReuseLeastRecent (void)
|
|
{
|
|
CacheFaceRealization *prface = NULL;
|
|
CacheFaceRealization *prfaceList;
|
|
if (head != NULL)
|
|
{
|
|
prface = head->PreviousRecentCacheFaceRealization;
|
|
}
|
|
|
|
ASSERT(prface);
|
|
|
|
// remove prface from GpCacheFaceRealizationList
|
|
|
|
if (head == prface)
|
|
{
|
|
ASSERT(count == 1);
|
|
head = NULL;
|
|
}
|
|
else
|
|
{
|
|
prface->PreviousRecentCacheFaceRealization->NextRecentCacheFaceRealization = head;
|
|
head->PreviousRecentCacheFaceRealization = prface->PreviousRecentCacheFaceRealization;
|
|
}
|
|
|
|
count--;
|
|
|
|
if (prface != NULL)
|
|
{
|
|
GpFaceRealizationTMP rface(prface);
|
|
rface.ReuseRealizedFace();
|
|
|
|
// remove the face from the face list
|
|
|
|
prfaceList = prface->Face->pff->prfaceList;
|
|
ASSERT(prfaceList);
|
|
|
|
if ((prfaceList == prface) && (prfaceList->NextCacheFaceRealization == prface))
|
|
{
|
|
// there is only oine face in the faceList for that font face
|
|
prface->Face->pff->prfaceList = NULL;
|
|
|
|
} else
|
|
{
|
|
if (prfaceList == prface)
|
|
{
|
|
// set the beginning of the list to the next one
|
|
prface->Face->pff->prfaceList = prfaceList->NextCacheFaceRealization;
|
|
}
|
|
|
|
// update the pointers in the faceList
|
|
|
|
prface->PreviousCacheFaceRealization->NextCacheFaceRealization = prface->NextCacheFaceRealization;
|
|
prface->NextCacheFaceRealization->PreviousCacheFaceRealization = prface->PreviousCacheFaceRealization;
|
|
}
|
|
|
|
}
|
|
|
|
return prface;
|
|
}
|
|
|
|
|