Click here to Skip to main content
Click here to Skip to main content

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

, 3 Jan 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
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.

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

    //--- 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. 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. //--- 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.
    //--- 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. //--- 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)

Share

About the Author

ChaoJui
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

 
QuestionCan any image be saved as a TTF file? Pinmemberli000014-Jan-09 3:54 
AnswerRe: Can any image be saved as a TTF file? PinmemberChaoJui19-Jan-09 4:23 
GeneralRe: Can any image be saved as a TTF file? Pinmemberli000031-Jan-09 9:22 
AnswerRe: Can any image be saved as a TTF file? PinmemberChaoJui1-Feb-09 18:57 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 3 Jan 2009
Article Copyright 2009 by ChaoJui
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid