mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1081 lines
32 KiB
1081 lines
32 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: fd_poly.c
|
|
*
|
|
* stolen from win31 tt code
|
|
*
|
|
* Created: 10-Feb-1992 17:10:39
|
|
* Author: Bodin Dresevic [BodinD]
|
|
*
|
|
* Copyright (c) 1990 Microsoft Corporation
|
|
*
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "fd.h"
|
|
#include "winerror.h"
|
|
|
|
STATIC VOID vQsplineToPolyBezier (
|
|
ULONG cBez, // IN count of curves to convert to beziers format
|
|
POINTFIX * pptfixStart, // IN starting point on the first curve
|
|
POINTFIX * pptfixSpline, // IN array of (cBez+1) points that, together with the starting point *pptfixStart define the spline
|
|
POINTFIX * pptfixBez // OUT buffer to be filled with 3 * cBez poly bezier control points
|
|
);
|
|
|
|
|
|
BOOL bGeneratePath (
|
|
PATHOBJ * ppo, // IN OUT pointer to the path object to be generated
|
|
TTPOLYGONHEADER * ppolyStart, // IN pointer to the buffer with outline data
|
|
ULONG cj // IN size of the buffer
|
|
);
|
|
|
|
#if DBG
|
|
|
|
// #define DBG_POLYGON
|
|
|
|
#endif
|
|
|
|
VOID vFillSingularGLYPHDATA(HGLYPH,ULONG,FONTCONTEXT*,GLYPHDATA*);
|
|
VOID vFillGLYPHDATA(HGLYPH,ULONG,FONTCONTEXT*,fs_GlyphInfoType*,GLYPHDATA*,GMC*,POINTL*,BOOL);
|
|
BOOL bGetGlyphMetrics(FONTCONTEXT*,HGLYPH,FLONG,FS_ENTRY*);
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* void Scale_16DOT16
|
|
*
|
|
*
|
|
* Effects: 26.6 -> 16.16
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* stole it from jeanp and modified for nt
|
|
\**************************************************************************/
|
|
|
|
//!!! some checks should be put in so as to verify that 26.6 -> 16.16
|
|
//!!! conversion can be done without loosing information [bodind]
|
|
|
|
void Scale_16DOT16 (POINTFX *ppfx, F26Dot6 x, F26Dot6 y, int xLsb2Org, int yLsb2Org)
|
|
{
|
|
LONG lTmp;
|
|
|
|
#ifdef DBG_POLYGON
|
|
|
|
xLsb2Org;
|
|
yLsb2Org;
|
|
|
|
lTmp = (LONG)x;
|
|
ppfx->x = * (FIXED *) &lTmp;
|
|
|
|
lTmp = (LONG)y;
|
|
ppfx->y = * (FIXED *) &lTmp;
|
|
|
|
#else // true version
|
|
|
|
// for this to work the following assert must be true:
|
|
|
|
ASSERTDD(sizeof(LONG) == sizeof(FIXED), "_Scale 16.16 \n");
|
|
|
|
lTmp = (LONG) ((x - xLsb2Org) << 10);
|
|
ppfx->x = * (FIXED *) &lTmp;
|
|
|
|
lTmp = (LONG) ((y - yLsb2Org) << 10);
|
|
ppfx->y = * (FIXED *) &lTmp;
|
|
|
|
#endif // DBG_POLYGON
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* void Scale_28Dot4
|
|
*
|
|
*
|
|
* Effects: 26.6 -> 28.4
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* wrote it
|
|
\**************************************************************************/
|
|
|
|
void Scale_28DOT4 (POINTFX *ppfx, F26Dot6 x, F26Dot6 y, int xLsb2Org, int yLsb2Org)
|
|
{
|
|
LONG lTmp;
|
|
|
|
// for this to work the following assert must be true:
|
|
|
|
ASSERTDD(sizeof(LONG) == sizeof(FIXED), "Scale, 28.4\n");
|
|
|
|
lTmp = (LONG) ((x - xLsb2Org) >> 2);
|
|
ppfx->x = * (FIXED *) &lTmp;
|
|
|
|
// note that the sign of y coordinate differs from the 16.16 case
|
|
|
|
lTmp = - (LONG) ((y - yLsb2Org) >> 2);
|
|
ppfx->y = * (FIXED *) &lTmp;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Scale_None
|
|
*
|
|
* Called when only the size of the ppoly buffer is wanted
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
void Scale_None (POINTFX *ppfx, F26Dot6 x, F26Dot6 y, int xLsb2Org, int yLsb2Org)
|
|
{
|
|
ppfx;
|
|
x;
|
|
y;
|
|
xLsb2Org;
|
|
yLsb2Org;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* cjFillPolygon
|
|
*
|
|
* Effects: fills in the array of structures that describe glyph's
|
|
* outline. There is one polygonheader stuct for every closed contour
|
|
* that composes the glyph. A polygon headed structure is followed
|
|
* by an array of polycurve structure that describe composite curves
|
|
* of a closed contour.
|
|
*
|
|
* Note: if pBuffer is NULL or cb is 0, then it is assumed that the caller
|
|
* only wants the size of the buffer required.
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it. (stole it from JeanP's win31 code and addapted for NT)
|
|
\**************************************************************************/
|
|
|
|
UINT cjFillPolygon(
|
|
PFONTCONTEXT pfc,
|
|
BOOL b16Dot16, // FORMAT of the points, 16.16 or 28.4
|
|
PBYTE pBuffer,
|
|
UINT cb
|
|
)
|
|
{
|
|
BOOL bGetLength = ( (pBuffer == (PBYTE)NULL) || (cb == 0) );
|
|
uint16 nc = pfc->pgout->numberOfContours;
|
|
uint8 *pbOnCurve = pfc->pgout->onCurve;
|
|
int16 *sp = pfc->pgout->startPtr;
|
|
int16 *ep = pfc->pgout->endPtr;
|
|
F26Dot6 *x = pfc->pgout->xPtr;
|
|
F26Dot6 *y = pfc->pgout->yPtr;
|
|
BYTE *pBuf = pBuffer;
|
|
BYTE *pStart = pBuf;
|
|
BYTE *pEnd = pStart + (bGetLength ? -1 : cb);
|
|
TTPOLYGONHEADER *pPoly;
|
|
TTPOLYCURVE *pCurve;
|
|
POINTFX *ppfxStart;
|
|
POINTFX *pptfx;
|
|
|
|
uint16 iContour; // index into a contour
|
|
int16 iptEnd, cpt;
|
|
int16 ipt = 0; // follows the points on the contour
|
|
|
|
uint8 ucMask;
|
|
void (*Scale)(POINTFX *ppfx, F26Dot6 x, F26Dot6 y, int xlsb, int ylsb);
|
|
|
|
int xLsb2Org;
|
|
int yLsb2Org;
|
|
|
|
if (!bGetLength) // we are actually filling in the information
|
|
{
|
|
|
|
#ifdef DBG_POLYGON
|
|
TtfdDbgPrint(" BEGIN NEW GLYPH \n\n");
|
|
vDbgGridFit(pfc->pgout);
|
|
#endif // DBG_POLYGON
|
|
|
|
if (b16Dot16)
|
|
{
|
|
Scale = Scale_16DOT16;
|
|
}
|
|
else // scale to 28.4 format
|
|
{
|
|
Scale = Scale_28DOT4;
|
|
}
|
|
}
|
|
else // just computing the size of the buffer needed to store the information
|
|
{
|
|
Scale = Scale_None;
|
|
}
|
|
|
|
// Compute the delta between the referencial origin and dev left bearing
|
|
|
|
cpt = (int16)(ep[nc - 1] + 1); // total number of points in a contour
|
|
|
|
xLsb2Org = x [cpt]; // LEFTSIDEBEARING == 0
|
|
yLsb2Org = y [cpt]; // LEFTSIDEBEARING == 0
|
|
|
|
for (iContour = 0; iContour < nc; iContour++)
|
|
{
|
|
// make sure that ipt points to the firts point on a contour upon entry
|
|
// to the loop
|
|
|
|
ipt = sp [iContour];
|
|
iptEnd = ep [iContour];
|
|
|
|
// skip contour made of one point
|
|
if (ipt == iptEnd)
|
|
{
|
|
continue; // go to the starting point of the next contour,
|
|
}
|
|
|
|
x = &pfc->pgout->xPtr[ipt];
|
|
y = &pfc->pgout->yPtr[ipt];
|
|
|
|
if (!bGetLength)
|
|
{
|
|
pPoly = (TTPOLYGONHEADER *) pBuf; //!!! dangerous, alignment [bodind]
|
|
pPoly->dwType = TT_POLYGON_TYPE;
|
|
ppfxStart = &pPoly->pfxStart;
|
|
|
|
#ifdef DBG_POLYGON
|
|
TtfdDbgPrint("Begin Polygon\n\n");
|
|
#endif // DBG_POLYGON
|
|
}
|
|
|
|
pBuf += sizeof (TTPOLYGONHEADER);
|
|
|
|
// The first point on the curve
|
|
if (pbOnCurve[ipt] & 1)
|
|
{
|
|
//Easy case
|
|
(*Scale) (ppfxStart, *x++, *y++, xLsb2Org, yLsb2Org); // 26.6 -> 16.16
|
|
++ipt;
|
|
}
|
|
else
|
|
{
|
|
// Is last contour point on the curve
|
|
if (pbOnCurve[iptEnd] & 1)
|
|
{
|
|
//Make the last point the first point and decrement the last point
|
|
(*Scale) (ppfxStart, x[iptEnd - ipt], y[iptEnd - ipt], xLsb2Org, yLsb2Org); // 26.6 -> 16.16
|
|
}
|
|
else
|
|
{
|
|
//First and last point are off the countour, fake a mid point
|
|
(*Scale) (ppfxStart, (x[iptEnd - ipt] + *x) >> 1, (y[iptEnd - ipt] + *y) >> 1, xLsb2Org, yLsb2Org);
|
|
}
|
|
}
|
|
|
|
while (ipt <= iptEnd)
|
|
{
|
|
pCurve = (TTPOLYCURVE *) pBuf;
|
|
pptfx = pCurve->apfx;
|
|
ucMask = (int8) (1 & (~pbOnCurve[ipt]));
|
|
if (!bGetLength)
|
|
{
|
|
// if mid point not on the curve this is qspline, this is midpoint
|
|
// because the starting point is in the previous record [bodind]
|
|
pCurve->wType = (WORD)((ucMask == 0) ? TT_PRIM_LINE : TT_PRIM_QSPLINE);
|
|
}
|
|
// Set up the POLYCURVE
|
|
while ((ipt <= iptEnd) && ((pbOnCurve[ipt] & 1) ^ ucMask))
|
|
{
|
|
// Check overflow
|
|
if (pEnd < (BYTE *)(pptfx + 1))
|
|
return FD_ERROR;
|
|
|
|
(*Scale) (pptfx++, *x++, *y++, xLsb2Org, yLsb2Org); // 26.6 -> 16.16
|
|
ipt++;
|
|
}
|
|
|
|
if (ucMask == 1) // if this curve is a qspline
|
|
{
|
|
// Check overflow
|
|
if (pEnd < (BYTE *)(pptfx + 1))
|
|
return FD_ERROR;
|
|
|
|
// Set up the end point
|
|
if (ipt <= iptEnd)
|
|
{
|
|
ASSERTDD(pbOnCurve[ipt] & 1, " end point not on the curve\n");
|
|
(*Scale) (pptfx, *x++, *y++, xLsb2Org, yLsb2Org); // 26.6 -> 16.16
|
|
ipt++;
|
|
}
|
|
else
|
|
{
|
|
// close the contour
|
|
if (!bGetLength)
|
|
*pptfx = *ppfxStart;
|
|
}
|
|
pptfx++;
|
|
}
|
|
if (!bGetLength)
|
|
{
|
|
pCurve->cpfx = (WORD)(pptfx - pCurve->apfx);
|
|
#ifdef DBG_POLYGON
|
|
vDbgCurve(pCurve);
|
|
#endif // DBG_POLYGON
|
|
}
|
|
|
|
pBuf = (BYTE *) pptfx;
|
|
}
|
|
|
|
if (!bGetLength)
|
|
{
|
|
pPoly->cb = pBuf - (BYTE *) pPoly;
|
|
#ifdef DBG_POLYGON
|
|
TtfdDbgPrint("\n end polygon, pPoly->cb = %ld\n\n", pPoly->cb);
|
|
#endif // DBG_POLYGON
|
|
}
|
|
}
|
|
#ifdef DBG_POLYGON
|
|
if (!bGetLength)
|
|
TtfdDbgPrint("\n END NEW GLYPH \n\n");
|
|
#endif // DBG_POLYGON
|
|
|
|
return (pBuf - pStart);
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* lQuerySingularTrueTypeOutline
|
|
*
|
|
* Effects:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* History:
|
|
* 22-Sep-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
|
|
|
|
|
|
LONG lQuerySingularTrueTypeOutline(
|
|
PFONTCONTEXT pfc, // IN
|
|
BOOL b16Dot16, // IN format of the points, 16.16 or 28.4
|
|
HGLYPH hglyph, // IN glyph for which info is wanted
|
|
BOOL bMetricsOnly, // IN only metrics is wanted, not the outline
|
|
GLYPHDATA * pgldt, // OUT this is where the metrics should be returned
|
|
ULONG cjBuf, // IN size in bytes of the ppoly buffer
|
|
TTPOLYGONHEADER * ppoly // OUT output buffer
|
|
)
|
|
{
|
|
FS_ENTRY iRet;
|
|
ULONG ig; // <--> hglyph
|
|
|
|
// hglyph is valid, either asking about the size for that particular
|
|
// glyph bitmap, or want the bitmap itself
|
|
|
|
vCharacterCode(pfc->pff,hglyph,pfc->pgin);
|
|
|
|
// compute the glyph index from the character code:
|
|
|
|
if ((iRet = fs_NewGlyph(pfc->pgin, pfc->pgout)) != NO_ERR)
|
|
{
|
|
V_FSERROR(iRet);
|
|
RET_FALSE("TTFD!_lQuerySingularTrueTypeOutline, fs_NewGlyph\n");
|
|
}
|
|
|
|
// return the glyph index corresponding to this hglyph:
|
|
|
|
ig = pfc->pgout->glyphIndex;
|
|
|
|
// must call cjFillPolygon now since fsFindBitmapSize messes up outline
|
|
// data in pgout
|
|
|
|
// fill all of GLYPHDATA structure
|
|
|
|
if (pgldt != (GLYPHDATA *)NULL)
|
|
{
|
|
vFillSingularGLYPHDATA(hglyph,ig,pfc,pgldt);
|
|
}
|
|
|
|
// now check whether the caller is asking about the size of the buffer
|
|
// needed to store the array of POLYGONHEADER structures:
|
|
|
|
return 0; // nothing written to the ppoly buffer
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* LONG lQueryTrueTypeOutline
|
|
*
|
|
*
|
|
* Effects:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
LONG lQueryTrueTypeOutline(
|
|
PFONTCONTEXT pfc, // IN
|
|
BOOL b16Dot16, // IN format of the points, 16.16 or 28.4
|
|
HGLYPH hglyph, // IN glyph for which info is wanted
|
|
BOOL bMetricsOnly, // IN only metrics is wanted, not the outline
|
|
GLYPHDATA * pgldt, // OUT this is where the metrics should be returned
|
|
ULONG cjBuf, // IN size in bytes of the ppoly buffer
|
|
TTPOLYGONHEADER * ppoly // OUT output buffer
|
|
)
|
|
{
|
|
FS_ENTRY iRet;
|
|
LONG cjRet;
|
|
|
|
// check if the rasterizer would behave unpolitely for this xform:
|
|
|
|
if (pfc->flXform & XFORM_SINGULAR)
|
|
return lQuerySingularTrueTypeOutline(
|
|
pfc,
|
|
b16Dot16,
|
|
hglyph,
|
|
bMetricsOnly,
|
|
pgldt,
|
|
cjBuf,
|
|
ppoly);
|
|
|
|
// check the last glyph processed to determine
|
|
// whether we have to register the glyph as new and compute its size
|
|
|
|
if (pfc->gstat.hgLast != hglyph)
|
|
{
|
|
ULONG ig;
|
|
extern BOOL bGetGlyphOutline(FONTCONTEXT*,HGLYPH,ULONG*,FLONG,FS_ENTRY*);
|
|
|
|
// DO NOT skip grid fitting even if embedded bitmpas are found,
|
|
// for we will be interested in outlines -+
|
|
// |
|
|
// |
|
|
if ( !bGetGlyphOutline(pfc, hglyph , &ig, 0, &iRet) )
|
|
{
|
|
V_FSERROR(iRet);
|
|
RETURN("lQueryTrueTypeOutline: bGetGlyphOutline failed\n", FD_ERROR);
|
|
}
|
|
|
|
// in order to be compatible with older applications we must
|
|
// call the monochrome version we do not call fs_FindGraySize
|
|
// even if the FONTOBJ suggests that it be anti-aliased
|
|
|
|
if ((iRet = fs_FindBitMapSize(pfc->pgin, pfc->pgout)) != NO_ERR)
|
|
{
|
|
EngSetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
V_FSERROR(iRet);
|
|
RETURN("lQueryTrueTypeOutline: fs_FindBitMapSize failed\n", FD_ERROR);
|
|
}
|
|
|
|
// now that everything is computed sucessfully, we can update
|
|
// glyphstate (hg data stored in pj3) and return
|
|
|
|
pfc->gstat.hgLast = hglyph;
|
|
pfc->gstat.igLast = ig;
|
|
}
|
|
|
|
// must call cjFillPolygon now since fsFindBitmapSize messes up outline
|
|
// data in pgout
|
|
|
|
if (!bMetricsOnly)
|
|
{
|
|
if ((cjRet = cjFillPolygon(pfc, b16Dot16, (PBYTE)ppoly, cjBuf)) == FD_ERROR)
|
|
RETURN("TTFD!_cjFillPolygon failed\n", FD_ERROR);
|
|
|
|
if (cjRet && ppoly && pfc->bVertical && (pfc->ulControl & VERTICAL_MODE))
|
|
vShiftOutlineInfo(pfc, b16Dot16, (PBYTE)ppoly, cjRet);
|
|
}
|
|
else // nothing will be written to ppoly buffer
|
|
{
|
|
cjRet = 0;
|
|
}
|
|
|
|
// fill all of GLYPHDATA structure
|
|
|
|
if (pgldt != (GLYPHDATA *)NULL)
|
|
{
|
|
if ( pfc->bVertical && pfc->ulControl & VERTICAL_MODE )
|
|
{
|
|
// Vertical case
|
|
fs_GlyphInfoType my_gout;
|
|
|
|
vShiftBitmapInfo( pfc, &my_gout, pfc->pgout );
|
|
vFillGLYPHDATA(
|
|
pfc->hgSave, // this is a little bit tricky. we wouldn't like to
|
|
pfc->gstat.igLast, // tell GDI about vertical glyph index.
|
|
pfc,
|
|
&my_gout,
|
|
pgldt,
|
|
(PGMC)NULL, NULL, FALSE);
|
|
}
|
|
else
|
|
{
|
|
// Normal case
|
|
vFillGLYPHDATA(
|
|
hglyph,
|
|
pfc->gstat.igLast,
|
|
pfc,
|
|
pfc->pgout,
|
|
pgldt,
|
|
(PGMC)NULL, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
// now check whether the caller is asking about the size of the buffer
|
|
// needed to store the array of POLYGONHEADER structures:
|
|
|
|
return cjRet;
|
|
}
|
|
|
|
|
|
LONG lQueryTrueTypeOutlineVertical(
|
|
PFONTCONTEXT pfc, // IN
|
|
BOOL b16Dot16, // IN format of the points, 16.16 or 28.4
|
|
HGLYPH hglyph, // IN glyph for which info is wanted
|
|
BOOL bMetricsOnly, // IN only metrics is wanted, not the outline
|
|
GLYPHDATA *pgd, // OUT this is where the metrics should be returned
|
|
ULONG cjBuf, // IN size in bytes of the ppoly buffer
|
|
TTPOLYGONHEADER * ppoly // OUT output buffer
|
|
)
|
|
{
|
|
LONG cjGlyphData;
|
|
WCHAR wc;
|
|
|
|
bIndexToWchar( pfc->pff, &wc, (uint16)hglyph );
|
|
|
|
if ( !IsFullWidthCharacter( pfc->pff->uiFontCodePage, wc ) )
|
|
{
|
|
return (lQueryTrueTypeOutline(pfc,
|
|
b16Dot16,
|
|
hglyph,
|
|
bMetricsOnly,
|
|
pgd, cjBuf, ppoly ));
|
|
}
|
|
|
|
//
|
|
// change the transformation
|
|
//
|
|
if (! bChangeXform( pfc, TRUE ) )
|
|
{
|
|
WARNING("TTFD!bChangeXform(TRUE) failed\n");
|
|
return FD_ERROR;
|
|
}
|
|
|
|
//
|
|
// set vertical mode
|
|
//
|
|
pfc->ulControl |= VERTICAL_MODE;
|
|
|
|
//
|
|
// if font file has alternate glyph index, use it.
|
|
//
|
|
pfc->hgSave = hglyph;
|
|
|
|
if ( pfc->pff->hgSearchVerticalGlyph )
|
|
hglyph = (*pfc->pff->hgSearchVerticalGlyph)( pfc, hglyph );
|
|
|
|
//
|
|
// call ordinary function
|
|
//
|
|
cjGlyphData = lQueryTrueTypeOutline(pfc,
|
|
b16Dot16,
|
|
hglyph,
|
|
bMetricsOnly, pgd, cjBuf, ppoly );
|
|
|
|
//
|
|
// restore the transformation and return
|
|
//
|
|
if ( ! bChangeXform( pfc, FALSE ) )
|
|
{
|
|
WARNING("TTFD!bChangeXform(FALSE) failed\n");
|
|
}
|
|
pfc->ulControl &= ~VERTICAL_MODE;
|
|
return(cjGlyphData);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* LONG ttfdQueryTrueTypeOutline
|
|
*
|
|
*
|
|
* Effects:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* History:
|
|
* 12-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
LONG ttfdQueryTrueTypeOutline (
|
|
FONTOBJ *pfo,
|
|
HGLYPH hglyph, // IN glyph for which info is wanted
|
|
BOOL bMetricsOnly, // IN only metrics is wanted, not the outline
|
|
GLYPHDATA *pgldt, // OUT this is where the metrics should be returned
|
|
ULONG cjBuf, // IN size in bytes of the ppoly buffer
|
|
TTPOLYGONHEADER * ppoly // IN OUT output buffer
|
|
)
|
|
{
|
|
FONTCONTEXT *pfc;
|
|
|
|
ASSERTDD(pfo->iFile, "ttfdQueryTrueTypeOutline, pfo->iFile\n");
|
|
|
|
#ifdef FE_SB
|
|
if (((TTC_FONTFILE *)pfo->iFile)->fl & FF_EXCEPTION_IN_PAGE_ERROR)
|
|
#else
|
|
if (((FONTFILE *)pfo->iFile)->fl & FF_EXCEPTION_IN_PAGE_ERROR)
|
|
#endif
|
|
{
|
|
WARNING("ttfd, ttfdQueryTrueTypeOutline: file is gone\n");
|
|
return FD_ERROR;
|
|
}
|
|
//
|
|
// If pfo->pvProducer is NULL, then we need to open a font context.
|
|
//
|
|
if ( pfo->pvProducer == (PVOID) NULL )
|
|
{
|
|
pfo->pvProducer = pfc = ttfdOpenFontContext(pfo);
|
|
}
|
|
else
|
|
{
|
|
pfc = (FONTCONTEXT*) pfo->pvProducer;
|
|
pfc->flFontType = (pfc->flFontType & FO_CHOSE_DEPTH) | pfo->flFontType;
|
|
}
|
|
|
|
if ( pfc == (FONTCONTEXT *) NULL )
|
|
{
|
|
WARNING("gdisrv!ttfdQueryTrueTypeOutline(): cannot create font context\n");
|
|
return FD_ERROR;
|
|
}
|
|
pfc->pfo = pfo;
|
|
|
|
// call fs_NewTransformation if needed:
|
|
|
|
if (!bGrabXform(pfc))
|
|
RETURN("gdisrv!ttfd bGrabXform failed\n", FD_ERROR);
|
|
|
|
if( pfc->bVertical )
|
|
{
|
|
return lQueryTrueTypeOutlineVertical(pfc,
|
|
TRUE, // b16Dot16 is true, this is the
|
|
// desired format
|
|
hglyph,
|
|
bMetricsOnly,
|
|
pgldt,
|
|
cjBuf,
|
|
ppoly);
|
|
}
|
|
else
|
|
{
|
|
return lQueryTrueTypeOutline(pfc,
|
|
TRUE, // b16Dot16 is true, this is the desired
|
|
// format
|
|
hglyph,
|
|
bMetricsOnly,
|
|
pgldt,
|
|
cjBuf,
|
|
ppoly);
|
|
}
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* ttfdQueryGlyphOutline
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* 12-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL ttfdQueryGlyphOutline (
|
|
FONTCONTEXT *pfc,
|
|
HGLYPH hglyph,
|
|
GLYPHDATA *pgldt,
|
|
PATHOBJ *ppo // pointer to path to be built
|
|
)
|
|
{
|
|
LONG cjAllPolygons, cjAllPolygons2;
|
|
BOOL bOk;
|
|
|
|
if (ppo == NULL)
|
|
{
|
|
// if ppo == NULL, the caller wants metrics only:
|
|
|
|
ASSERTDD(pgldt, "ttfdQueryGlyphOutline, pgldt NULL\n");
|
|
|
|
if(pfc->bVertical)
|
|
{
|
|
cjAllPolygons =
|
|
lQueryTrueTypeOutlineVertical
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
TRUE, // DO just metrics, do NOT do outline
|
|
pgldt, // STORE the result here
|
|
0, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)NULL // do not need it
|
|
);
|
|
}
|
|
else
|
|
{
|
|
cjAllPolygons =
|
|
lQueryTrueTypeOutline
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
TRUE, // DO just metrics, do NOT do outline
|
|
pgldt, // STORE the result here
|
|
0, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)NULL // do not need it
|
|
);
|
|
}
|
|
|
|
// interpret the result, if zero for polygons, we succeded
|
|
// glyph data was filled in and no polygon computation has been
|
|
// performed.
|
|
// if FD_ERROR we did not, no other result should be possible
|
|
|
|
if (cjAllPolygons == 0)
|
|
return TRUE;
|
|
else
|
|
{
|
|
ASSERTDD(cjAllPolygons == FD_ERROR,
|
|
"ttfdQueryGlyphOutline, pgldt == NULL\n");
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
// first learn how big a buffer we need for all polygons:
|
|
|
|
if( pfc->bVertical )
|
|
{
|
|
cjAllPolygons =
|
|
lQueryTrueTypeOutlineVertical
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
FALSE, // DO more than just metrics
|
|
(GLYPHDATA *)NULL,// do not need glyphdata
|
|
0, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
cjAllPolygons = lQueryTrueTypeOutline
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
FALSE, // DO more than just metrics
|
|
(GLYPHDATA *)NULL,// do not need glyphdata
|
|
0, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)NULL
|
|
);
|
|
}
|
|
|
|
if (cjAllPolygons == FD_ERROR)
|
|
RET_FALSE("TTFD! cjAllPolygons\n");
|
|
|
|
if (cjAllPolygons != 0)
|
|
{
|
|
if ((pfc->gstat.pv = PV_ALLOC(cjAllPolygons)) == NULL)
|
|
{
|
|
RET_FALSE("TTFD_cjAllPolygons or ppoly\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pfc->gstat.pv = NULL;
|
|
}
|
|
|
|
// get all the polygons in the buffer we just allocated:
|
|
|
|
if( pfc->bVertical )
|
|
{
|
|
cjAllPolygons2 = lQueryTrueTypeOutlineVertical
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
FALSE, // DO more than just metrics
|
|
pgldt, // this is where the metrics should be returned
|
|
cjAllPolygons, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)pfc->gstat.pv
|
|
);
|
|
}
|
|
else
|
|
{
|
|
cjAllPolygons2 = lQueryTrueTypeOutline
|
|
(
|
|
pfc, // lpMat2 is incorporated into this fc
|
|
FALSE, // NOT 16.16 i.e. 28.4
|
|
hglyph, // glyph for which info is wanted
|
|
FALSE, // DO more than just metrics
|
|
pgldt, // this is where the metrics should be returned
|
|
cjAllPolygons, // size in bytes of the ppoly buffer
|
|
(TTPOLYGONHEADER *)pfc->gstat.pv
|
|
);
|
|
}
|
|
|
|
if (cjAllPolygons2 == FD_ERROR)
|
|
{
|
|
if (pfc->gstat.pv)
|
|
{
|
|
V_FREE(pfc->gstat.pv);
|
|
pfc->gstat.pv = NULL;
|
|
}
|
|
RET_FALSE("TTFD_ QueryTrueTypeOutline failed\n");
|
|
}
|
|
|
|
ASSERTDD(cjAllPolygons == cjAllPolygons2,
|
|
"cjAllPolygons PROBLEM\n");
|
|
|
|
// now that we have all the info in ppoly buffer we can generate the path
|
|
|
|
bOk = bGeneratePath(
|
|
(PATHOBJ *)ppo,
|
|
(TTPOLYGONHEADER *)pfc->gstat.pv,
|
|
cjAllPolygons
|
|
);
|
|
|
|
if (pfc->gstat.pv)
|
|
{
|
|
V_FREE(pfc->gstat.pv);
|
|
pfc->gstat.pv = NULL;
|
|
}
|
|
|
|
return (bOk);
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* bGeneratePath
|
|
*
|
|
* Effects: Adds control points of the glyph to the gluph path
|
|
*
|
|
*
|
|
* History:
|
|
* 18-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
// macro that computes the size of the polycurve record:
|
|
|
|
#define CJ_CRV(pcrv) \
|
|
( \
|
|
offsetof(TTPOLYCURVE,apfx) + (pcrv)->cpfx * sizeof(POINTFX) \
|
|
)
|
|
|
|
|
|
// reasonable guess that in most cases a contour will not consist of more
|
|
// that this many beziers
|
|
|
|
#define C_BEZIER 6
|
|
|
|
|
|
BOOL bGeneratePath(
|
|
PATHOBJ * ppo, // IN OUT pointer to the path object to be generated
|
|
TTPOLYGONHEADER * ppolyStart, // IN OUT pointer to the buffer with outline data
|
|
ULONG cjTotal // IN size of the buffer
|
|
)
|
|
{
|
|
TTPOLYGONHEADER * ppoly, * ppolyEnd;
|
|
TTPOLYCURVE * pcrv, * pcrvEnd;
|
|
POINTFIX aptfixBez[3 * C_BEZIER]; // 3 points per bezier
|
|
POINTFIX * pptfixBez;
|
|
ULONG cBez;
|
|
POINTFIX * pptfixStart;
|
|
|
|
for (
|
|
ppoly = ppolyStart, ppolyEnd = (TTPOLYGONHEADER *)((PBYTE)ppolyStart + cjTotal);
|
|
ppoly < ppolyEnd;
|
|
ppoly = (TTPOLYGONHEADER *)((PBYTE)ppoly + ppoly->cb)
|
|
)
|
|
{
|
|
ASSERTDD(ppoly->dwType == TT_POLYGON_TYPE, "TT_POLYGON_TYPE\n");
|
|
|
|
// begin new closed contour
|
|
|
|
if (!PATHOBJ_bMoveTo(ppo, *(POINTFIX *)&ppoly->pfxStart))
|
|
RET_FALSE("TTFD!_PATHOBJ_bMoveTo failed\n");
|
|
|
|
// init a loop over curves
|
|
|
|
pptfixStart = (POINTFIX *)&ppoly->pfxStart;
|
|
pcrvEnd = (TTPOLYCURVE *)((PBYTE)ppoly + ppoly->cb);
|
|
|
|
for (
|
|
pcrv = (TTPOLYCURVE *)(ppoly + 1);
|
|
pcrv < pcrvEnd;
|
|
pcrv = (TTPOLYCURVE *)((PBYTE)pcrv + CJ_CRV(pcrv))
|
|
)
|
|
{
|
|
if (pcrv->wType == TT_PRIM_LINE)
|
|
{
|
|
if (!PATHOBJ_bPolyLineTo(ppo,(POINTFIX *)pcrv->apfx, pcrv->cpfx))
|
|
RET_FALSE("TTFD!_bPolyLineTo()\n");
|
|
}
|
|
else // qspline
|
|
{
|
|
BOOL bOk;
|
|
|
|
ASSERTDD(pcrv->wType == TT_PRIM_QSPLINE, "TT_PRIM_QSPLINE\n");
|
|
ASSERTDD(pcrv->cpfx > 1, "_TT_PRIM_QSPLINE, cpfx <= 1\n");
|
|
cBez = pcrv->cpfx - 1;
|
|
|
|
if (cBez > C_BEZIER) // must allocate buffer for the bezier points
|
|
{
|
|
if ((pptfixBez = (POINTFIX *)PV_ALLOC((3 * cBez) * sizeof(POINTFIX))) == (POINTFIX *)NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else // enough memory on the stack
|
|
{
|
|
pptfixBez = aptfixBez;
|
|
}
|
|
|
|
vQsplineToPolyBezier (
|
|
cBez, // count of curves to convert to beziers format
|
|
pptfixStart, // starting point on the first curve
|
|
(POINTFIX *)pcrv->apfx, // array of (cBez+1) points that, together with the starting point *pptfixStart define the spline
|
|
pptfixBez); // buffer to be filled with 3 * cBez poly bezier control points
|
|
|
|
bOk = PATHOBJ_bPolyBezierTo(ppo, pptfixBez, 3 * cBez);
|
|
|
|
if (cBez > C_BEZIER)
|
|
V_FREE(pptfixBez);
|
|
|
|
if (!bOk)
|
|
RET_FALSE("TTFD!_bPolyBezierTo() failed\n");
|
|
}
|
|
|
|
// get to the next curve in this polygon
|
|
|
|
pptfixStart = (POINTFIX *) &pcrv->apfx[pcrv->cpfx - 1];
|
|
}
|
|
ASSERTDD(pcrv == pcrvEnd, "pcrv problem\n");
|
|
|
|
// close the path
|
|
|
|
if (!PATHOBJ_bPolyLineTo(ppo, (POINTFIX *)&ppoly->pfxStart, 1) ||
|
|
!PATHOBJ_bCloseFigure(ppo))
|
|
RET_FALSE("TTFD!_bPolyLineTo()\n");
|
|
} // loop over polygons
|
|
|
|
ASSERTDD(ppoly == ppolyEnd, "poly problem\n");
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* vQsplineToPolyBezier
|
|
*
|
|
* Effects:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* History:
|
|
* 20-Feb-1992 -by- Bodin Dresevic [BodinD]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
#define DIV_BY_2(x) (((x) + 0x00000001) / 2)
|
|
#define DIV_BY_3(x) (((x) + 0x00000002) / 3)
|
|
|
|
STATIC VOID vQsplineToPolyBezier(
|
|
ULONG cBez, //IN count of curves to convert to beziers format
|
|
POINTFIX * pptfixStart, //IN starting point on the first curve
|
|
POINTFIX * pptfixSpline, //IN array of (cBez+1) points that, together with the starting point *pptfixStart define the spline
|
|
POINTFIX * pptfixBez //OUT buffer to be filled with 3 * cBez poly bezier control points
|
|
)
|
|
{
|
|
ULONG iBez,cMidBez;
|
|
POINTFIX ptfixA;
|
|
|
|
// cMidBez == # of beziers for whom the last point on the bezier is computed
|
|
// as a mid point of the two consecutive points in the input array. Only the
|
|
// last bezier is not a mid bezier, the last point for that bezier is equal
|
|
// to the last point in the input array
|
|
|
|
ASSERTDD(cBez > 0, "cBez == 0\n");
|
|
|
|
cMidBez = cBez - 1;
|
|
ptfixA = *pptfixStart;
|
|
|
|
for (iBez = 0; iBez < cMidBez; iBez++, pptfixSpline++)
|
|
{
|
|
// let us call the three spline points
|
|
// A,B,C;
|
|
// B = *pptfix;
|
|
// C = (pptfix[0] + pptfix[1]) / 2; // mid point, unless at the end
|
|
//
|
|
// if we decide to call the two intermediate control points for the
|
|
// bezier M,N (i.e. full set of control points for the bezier is
|
|
// A,M,N,C), the points M,N are determined by following formulas:
|
|
//
|
|
// M = (2*B + A) / 3 ; two thirds along the segment AB
|
|
// N = (2*B + C) / 3 ; two thirds along the segment CB
|
|
//
|
|
// this is the computation we are doing in this loop:
|
|
|
|
// M point for this bezier
|
|
|
|
pptfixBez->x = DIV_BY_3((pptfixSpline->x * 2) + ptfixA.x);
|
|
pptfixBez->y = DIV_BY_3((pptfixSpline->y * 2) + ptfixA.y);
|
|
pptfixBez++;
|
|
|
|
// compute C point for this bezier, which is also the A point for the next
|
|
// bezier
|
|
|
|
ptfixA.x = DIV_BY_2(pptfixSpline[0].x + pptfixSpline[1].x);
|
|
ptfixA.y = DIV_BY_2(pptfixSpline[0].y + pptfixSpline[1].y);
|
|
|
|
// now compute N point for this bezier:
|
|
|
|
pptfixBez->x = DIV_BY_3((pptfixSpline->x * 2) + ptfixA.x);
|
|
pptfixBez->y = DIV_BY_3((pptfixSpline->y * 2) + ptfixA.y);
|
|
pptfixBez++;
|
|
|
|
// finally record the C point for this curve
|
|
|
|
*pptfixBez++ = ptfixA;
|
|
}
|
|
|
|
// finally do the last bezier. If the last bezier is the only one, the loop
|
|
// above has been skipped
|
|
|
|
// M point for this bezier
|
|
|
|
pptfixBez->x = DIV_BY_3((pptfixSpline->x * 2) + ptfixA.x);
|
|
pptfixBez->y = DIV_BY_3((pptfixSpline->y * 2) + ptfixA.y);
|
|
pptfixBez++;
|
|
|
|
// compute C point for this bezier, its end point is the last point
|
|
// in the input array
|
|
|
|
ptfixA = pptfixSpline[1];
|
|
|
|
// now compute N point for this bezier:
|
|
|
|
pptfixBez->x = DIV_BY_3((pptfixSpline->x * 2) + ptfixA.x);
|
|
pptfixBez->y = DIV_BY_3((pptfixSpline->y * 2) + ptfixA.y);
|
|
pptfixBez++;
|
|
|
|
// finally record the C point for this curve, no need to increment pptfixBez
|
|
|
|
*pptfixBez = ptfixA;
|
|
}
|