Introduction
The TrueType structure TTPOLYGONHEADER
can be utilized to generate user-defined geometric shapes. The topic here is to demonstrate how a TrueType font is formatted with this structure.
As you may already know, a glyph contour consists of multiple curve segments represented by TTPOLYCURVE
structures which are actually followed by TTPOLYGONHEADER
. Therefore, we may just simply add each curve segment behind a polygon header in sequence without using the TTPOLYCURVE
structure implicitly.
Background
The original idea is to study how TTPOLYGONHEADER
and TTPOLYCURVE
are used in TrueType font rendering. Furthermore, we can think about how to combine these geometric shapes with TrueType font characters. We will talk about this advanced topic in the next section.
TrueType Polygon Structure
Let's see the members of the TTPOLYGONHEADER
and TTPOLYCURVE
structures.
typedef struct _TTPOLYGONHEADER
{
DWORD cb;
DWORD dwType;
POINTFX pfxStart;
} TTPOLYGONHEADER, *LPTTPOLYGONHEADER;
cb
- Specifies the number of bytes required by the TTPOLYGONHEADER
structure and TTPOLYCURVE
structure or structures required to describe the contour of the character.
dwType
- Specifies the type of character outline returned. Currently, this value must be TT_POLYGON_TYPE
.
pfxStart
- Specifies the starting point of the contour in the character outline.
typedef struct tagTTPOLYCURVE
{
WORD wType;
WORD cpfx;
POINTFX apfx[1];
} TTPOLYCURVE, *LPTTPOLYCURVE;
wType
- Specifies the type of curve described by the structure.
cpfx
- Specifies the number of POINTFX
structures in the array.
apfx
- Specifies an array of POINTFX
structures that define the polyline or Bézier spline.
Getting started
We will only focus on how to generate customized geometric shapes with TTPOLYGONHEADER
. Here is a code snippet to explain the ways to create a user-defined geometric shape.
LPTTPOLYGONHEADER CTTPolygonDlg::DemoPolygon(const PPLOYCURVE pPolycurve, int nCurveCount)
{
LPTTPOLYGONHEADER lpPolygonHeader = NULL;
LPBYTE lpPolyCurve = NULL;
LPPOINTFX lpPointsFx = NULL;
LPPOINTFX lpPointsInc = NULL;
LPPOINTFX lpPointsEnd = NULL;
DWORD dwOffsetAddr = 0L;
UINT nSize = 0;
UINT nPtBufSize = 0;
UINT nPtCounts = 0;
WORD wType = 0;
WORD cpfx = 0;
LPBYTE lpBytes = NULL;
int i = 0;
double* pDblVertex = NULL;
double* pDblInc = NULL;
if (0 >= nCurveCount || NULL == pPolycurve)
{
return( NULL );
}
for (i = 0; i < nCurveCount; i++)
{
if (1 >= (*(pPolycurve + i)).nVertexCount ||
NULL == (*(pPolycurve + i)).pDblValues)
{
return( NULL );
}
if ((*(pPolycurve + i)).wCurveType != TT_PRIM_LINE &&
(*(pPolycurve + i)).wCurveType != TT_PRIM_QSPLINE &&
(*(pPolycurve + i)).wCurveType != TT_PRIM_CSPLINE)
{
return( NULL );
}
nPtBufSize += (sizeof( POINTFX ) * (*(pPolycurve + i)).nVertexCount);
}
nSize =
(
sizeof( TTPOLYGONHEADER ) +
sizeof( TTPOLYCURVE ) +
((sizeof( WORD ) * 2) + nPtBufSize)
);
lpBytes = ::new BYTE[nSize];
lpPolygonHeader = (LPTTPOLYGONHEADER)lpBytes;
lpPolygonHeader->cb = nSize;
lpPolygonHeader->dwType = TT_POLYGON_TYPE;
pDblVertex = (*pPolycurve).pDblValues;
lpPolygonHeader->pfxStart.x = FixedFromDouble(*pDblVertex);
lpPolygonHeader->pfxStart.y = FixedFromDouble(*(pDblVertex + 1));
dwOffsetAddr = (DWORD)((LPBYTE)lpPolygonHeader + sizeof( TTPOLYGONHEADER ));
for (i = 0; i < nCurveCount; i++)
{
wType = (*(pPolycurve + i)).wCurveType;
cpfx = (*(pPolycurve + i)).nVertexCount;
::CopyMemory((LPBYTE)dwOffsetAddr, &wType, sizeof( WORD ));
dwOffsetAddr += sizeof( WORD );
::CopyMemory((LPBYTE)dwOffsetAddr, &cpfx, sizeof( WORD ));
dwOffsetAddr += sizeof( WORD );
nPtBufSize = (sizeof( POINTFX ) * (*(pPolycurve + i)).nVertexCount);
lpBytes = ::new BYTE[nPtBufSize];
lpPointsFx = (LPPOINTFX)lpBytes;
lpPointsInc = lpPointsFx;
lpPointsEnd = (lpPointsInc + cpfx);
pDblInc = (*(pPolycurve + i)).pDblValues;
do
{
lpPointsInc->x = FixedFromDouble(*pDblInc++);
lpPointsInc->y = FixedFromDouble(*pDblInc++);
}
while(++lpPointsInc < lpPointsEnd);
::CopyMemory((LPBYTE)dwOffsetAddr, lpPointsFx, nPtBufSize);
dwOffsetAddr += nPtBufSize;
delete []lpBytes;
lpBytes = NULL;
}
return( lpPolygonHeader );
}
Here is the detailed explanation:
- We have to give the size to the polyline structure which depends on how many vertices the polyline has. And then, allocate a space for this structure.
for (i = 0; i < nCurveCount; i++)
{
if (1 >= (*(pPolycurve + i)).nVertexCount ||
NULL == (*(pPolycurve + i)).pDblValues)
{
return( NULL );
}
if ((*(pPolycurve + i)).wCurveType != TT_PRIM_LINE &&
(*(pPolycurve + i)).wCurveType != TT_PRIM_QSPLINE &&
(*(pPolycurve + i)).wCurveType != TT_PRIM_CSPLINE)
{
return( NULL );
}
nPtBufSize += (sizeof( POINTFX ) * (*(pPolycurve + i)).nVertexCount);
}
nSize =
(
sizeof( TTPOLYGONHEADER ) +
sizeof( TTPOLYCURVE ) +
((sizeof( WORD ) * 2) + nPtBufSize)
);
lpBytes = ::new BYTE[nSize];
lpPolygonHeader = (LPTTPOLYGONHEADER)lpBytes;
- Start the first vertex of the polyline, as shown below.
lpPolygonHeader->cb = nSize;
lpPolygonHeader->dwType = TT_POLYGON_TYPE;
pDblVertex = (*pPolycurve).pDblValues;
lpPolygonHeader->pfxStart.x = FixedFromDouble(*pDblVertex);
lpPolygonHeader->pfxStart.y = FixedFromDouble(*(pDblVertex + 1));
dwOffsetAddr = (DWORD)((LPBYTE)lpPolygonHeader + sizeof( TTPOLYGONHEADER ));
- Give each curve the vertices. To define the TTPOLYCURVE structure:
- Set the polygon type.
- Give the vertex count to the polygon.
- Give the vertices to the polygon.
for (i = 0; i < nCurveCount; i++)
{
wType = (*(pPolycurve + i)).wCurveType;
cpfx = (*(pPolycurve + i)).nVertexCount;
::CopyMemory((LPBYTE)dwOffsetAddr, &wType, sizeof( WORD ));
dwOffsetAddr += sizeof( WORD );
::CopyMemory((LPBYTE)dwOffsetAddr, &cpfx, sizeof( WORD ));
dwOffsetAddr += sizeof( WORD );
nPtBufSize = (sizeof( POINTFX ) * (*(pPolycurve + i)).nVertexCount);
lpBytes = ::new BYTE[nPtBufSize];
lpPointsFx = (LPPOINTFX)lpBytes;
lpPointsInc = lpPointsFx;
lpPointsEnd = (lpPointsInc + cpfx);
pDblInc = (*(pPolycurve + i)).pDblValues;
do
{
lpPointsInc->x = FixedFromDouble(*pDblInc++);
lpPointsInc->y = FixedFromDouble(*pDblInc++);
}
while(++lpPointsInc < lpPointsEnd);
::CopyMemory((LPBYTE)dwOffsetAddr, lpPointsFx, nPtBufSize);
dwOffsetAddr += nPtBufSize;
delete []lpBytes;
lpBytes = NULL;
}
- We now can render the geometric object with this customized polygon header.
RenderPolygon(lpPolygonHeader, nSize);
Revisions
- Version 1.0: Initial release.
- Version 1.1: Multiple-shape support.