WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/facerealization.cpp
2020-09-30 16:53:49 +02:00

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;
}