Click here to Skip to main content
15,880,392 members
Articles / Multimedia / GDI

How to use a TrueType structure to render user-defined geometric shapes

Rate me:
Please Sign up or sign in to vote.
3.80/5 (5 votes)
3 Jan 2009CPOL2 min read 79.8K   458   19   16
To learn the usages of TrueType structures, TTPOLYGONHEADER and TTPOLYCURVE.

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.

C++
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.
C++
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.

C++
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 );
}

Here is the detailed explanation:

  1. 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.
  2. C++
    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;
  3. Start the first vertex of the polyline, as shown below.
  4. C++
    //--- 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 ));
  5. Give each curve the vertices. To define the TTPOLYCURVE structure:
    1. Set the polygon type.
    2. Give the vertex count to the polygon.
    3. Give the vertices to the polygon.
    C++
    //--- 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;
    }
  6. We now can render the geometric object with this customized polygon header.
  7. C++
    //--- render geometry shape ---
    RenderPolygon(lpPolygonHeader, nSize);

Revisions

  • Version 1.0: Initial release.
  • Version 1.1: Multiple-shape support.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) http://home.so-net.net.tw/lioucy
Taiwan Taiwan
I've been a coding guy for 15 years, using C/C++ and assembly. Also using database to do information presenation with graphics applications.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 21:14
professionalManoj Kumar Choubey26-Feb-12 21:14 
GeneralColor in the true type font character Pin
transoft20-Mar-09 8:48
transoft20-Mar-09 8:48 
GeneralRe: Color in the true type font character Pin
ChaoJui21-Mar-09 22:53
ChaoJui21-Mar-09 22:53 
GeneralRe: Color in the true type font character Pin
transoft22-Mar-09 3:47
transoft22-Mar-09 3:47 
GeneralRe: Color in the true type font character Pin
ChaoJui22-Mar-09 15:37
ChaoJui22-Mar-09 15:37 
GeneralRe: Color in the true type font character Pin
transoft23-Mar-09 2:06
transoft23-Mar-09 2:06 
QuestionCan any image be saved as a TTF file? Pin
transoft14-Jan-09 2:54
transoft14-Jan-09 2:54 
AnswerRe: Can any image be saved as a TTF file? Pin
ChaoJui19-Jan-09 3:23
ChaoJui19-Jan-09 3:23 
GeneralRe: Can any image be saved as a TTF file? Pin
transoft31-Jan-09 8:22
transoft31-Jan-09 8:22 
AnswerRe: Can any image be saved as a TTF file? Pin
ChaoJui1-Feb-09 17:57
ChaoJui1-Feb-09 17:57 
GeneralGo Go ... 加油 Pin
Member 440027-Jan-09 4:48
Member 440027-Jan-09 4:48 
GeneralMore details please! Pin
Jim Crafton6-Jan-09 8:07
Jim Crafton6-Jan-09 8:07 
GeneralRe: More details please! Pin
ChaoJui6-Jan-09 15:22
ChaoJui6-Jan-09 15:22 
GeneralThe rules for selecting the curves Pin
transoft4-Jan-09 4:49
transoft4-Jan-09 4:49 
AnswerRe: The rules for selecting the curves Pin
ChaoJui5-Jan-09 3:10
ChaoJui5-Jan-09 3:10 
I had programmed an utility which can add, insert and delete vertices for the curve. I may release a lightweight tool for this subject some other day.
AnswerRe: The rules for selecting the curves [modified] Pin
ChaoJui2-Oct-09 20:19
ChaoJui2-Oct-09 20:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.