如何使用 TrueType 结构渲染用户定义的几何形状
学习 TrueType 结构、TTPOLYGONHEADER 和 TTPOLYCURVE 的用法。
引言
TrueType 结构 TTPOLYGONHEADER
可用于生成用户自定义的几何形状。本文档旨在演示如何使用此结构格式化 TrueType 字体。
如你所知,一个字形轮廓由多个曲线段组成,这些曲线段由 TTPOLYCURVE
结构表示,而这些结构紧随 TTPOLYGONHEADER
之后。因此,我们可以简单地将每个曲线段按顺序添加到多边形头部之后,而无需显式使用 TTPOLYCURVE
结构。
背景
最初的想法是研究 TTPOLYGONHEADER
和 TTPOLYCURVE
在 TrueType 字体渲染中的使用方式。此外,我们可以思考如何将这些几何形状与 TrueType 字体字符结合起来。我们将在下一节中讨论这个高级主题。
TrueType 多边形结构
让我们看看 TTPOLYGONHEADER
和 TTPOLYCURVE
结构的成员。
typedef struct _TTPOLYGONHEADER
{
DWORD cb;
DWORD dwType;
POINTFX pfxStart;
} TTPOLYGONHEADER, *LPTTPOLYGONHEADER;
cb
- 指定TTPOLYGONHEADER
结构和TTPOLYCURVE
结构或描述字符轮廓所需的结构所需的字节数。dwType
- 指定返回的字符轮廓的类型。当前,此值必须为TT_POLYGON_TYPE
。pfxStart
- 指定字符轮廓中轮廓的起始点。
typedef struct tagTTPOLYCURVE
{
WORD wType;
WORD cpfx;
POINTFX apfx[1];
} TTPOLYCURVE, *LPTTPOLYCURVE;
wType
- 指定结构描述的曲线类型。cpfx
- 指定数组中的POINTFX
结构的个数。apfx
- 指定定义多边形或贝塞尔样条曲线的POINTFX
结构数组。
入门
我们将只关注如何使用 TTPOLYGONHEADER
生成自定义几何形状。以下是一个代码片段,用于解释创建用户自定义几何形状的方法。
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);
}
//--- allocate buffer for polygon header ---
nSize =
(
sizeof( TTPOLYGONHEADER ) +
sizeof( TTPOLYCURVE ) +
((sizeof( WORD ) * 2) + nPtBufSize)
);
lpBytes = ::new BYTE[nSize];
lpPolygonHeader = (LPTTPOLYGONHEADER)lpBytes;
//--- setup header of polygon and its starting point ---
lpPolygonHeader->cb = nSize;
lpPolygonHeader->dwType = TT_POLYGON_TYPE;
pDblVertex = (*pPolycurve).pDblValues;
lpPolygonHeader->pfxStart.x = FixedFromDouble(*pDblVertex);
lpPolygonHeader->pfxStart.y = FixedFromDouble(*(pDblVertex + 1));
//--- get poly curve start address ---
dwOffsetAddr = (DWORD)((LPBYTE)lpPolygonHeader + sizeof( TTPOLYGONHEADER ));
//--- traverse all polycurves ---
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 );
//--- allocate buffer for current curve ---
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);
//--- copy points to original structure ---
::CopyMemory((LPBYTE)dwOffsetAddr, lpPointsFx, nPtBufSize);
dwOffsetAddr += nPtBufSize;
//--- free temporary points ---
delete []lpBytes;
lpBytes = NULL;
}
//--- return polygon header ---
return( lpPolygonHeader );
}
以下是详细的解释
- 我们需要根据多边形有多少个顶点来为多边形结构指定大小。然后,为该结构分配空间。
- 从多边形的第一个顶点开始,如下所示。
- 为每条曲线提供顶点。定义 TTPOLYCURVE 结构
- 设置多边形类型。
- 为多边形提供顶点数。
- 为多边形提供顶点。
- 现在我们可以使用这个自定义的多边形头部渲染几何对象。
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);
}
//--- allocate buffer for polygon header ---
nSize =
(
sizeof( TTPOLYGONHEADER ) +
sizeof( TTPOLYCURVE ) +
((sizeof( WORD ) * 2) + nPtBufSize)
);
lpBytes = ::new BYTE[nSize];
lpPolygonHeader = (LPTTPOLYGONHEADER)lpBytes;
//--- setup header of polygon and its starting point ---
lpPolygonHeader->cb = nSize;
lpPolygonHeader->dwType = TT_POLYGON_TYPE;
pDblVertex = (*pPolycurve).pDblValues;
lpPolygonHeader->pfxStart.x = FixedFromDouble(*pDblVertex);
lpPolygonHeader->pfxStart.y = FixedFromDouble(*(pDblVertex + 1));
//--- get poly curve start address ---
dwOffsetAddr = (DWORD)((LPBYTE)lpPolygonHeader + sizeof( TTPOLYGONHEADER ));
//--- traverse all polycurves ---
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 );
//--- allocate buffer for current curve ---
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);
//--- copy points to original structure ---
::CopyMemory((LPBYTE)dwOffsetAddr, lpPointsFx, nPtBufSize);
dwOffsetAddr += nPtBufSize;
//--- free temporary points ---
delete []lpBytes;
lpBytes = NULL;
}
//--- render geometry shape ---
RenderPolygon(lpPolygonHeader, nSize);
修订
- 版本 1.0:初始发布。
- 版本 1.1:支持多种形状。