会员: 密码:  免费注册 | 忘记密码 | 会员登录 网页功能: 加入收藏 设为首页 网站搜索  
游戏开发 > 程序设计 > 系统操作
自己绘制True type font字体
发表日期:2007-01-16 17:32:06作者: 出处:  

  一直以来,我都是用点阵写字,现在,尝试直接使用True type font的内容,显示由曲线构成的字体,没想到,居然成功了,源代码如下:

#include <windows.h>
#include <vector>

/////////////////////////////////////////////////////////////////////////////
//

class CTTFOutline
{
public:
    typedef struct
    {
        std::vector<POINT> vectorPoint;
    } Contour;

public:
    CTTFOutline();
    ~CTTFOutline();

    BOOL Create( LPTTPOLYGONHEADER lpHeader, DWORD size );
    void Destroy(){ m_vectorContour.clear(); }

    int GetContourCount(){ return m_vectorContour.size(); }
    Contour* GetContour( int nContourId ){ return &m_vectorContour[nContourId]; }

protected:
    std::vector<Contour> m_vectorContour;
};

这个类用来作为ttf字体轮廓的容器,接下来这些代码用来获取TTF的曲线信息,

/****************************************************************************
* FUNCTION : IntFromFixed
* RETURNS : int value approximating the FIXED value.
****************************************************************************/

int PASCAL NEAR IntFromFixed(FIXED f)
{
    if (f.fract >= 0x8000)
        return(f.value + 1);
    else
        return(f.value);
}

/****************************************************************************
* FUNCTION : fxDiv2
* RETURNS : (val1 + val2)/2 for FIXED values
****************************************************************************/

FIXED PASCAL NEAR fxDiv2(FIXED fxVal1, FIXED fxVal2)
{
    long l; l = (*((long far *)&(fxVal1)) + *((long far *)&(fxVal2)))/2;
    return(*(FIXED *)&l);
}

/****************************************************************************
* FUNCTION : MakeBezierFromLine
*
* PURPOSE : Converts a line define by two points to a four point Bezier
* spline representation of the line in pPts.
*
*
* RETURNS : number of Bezier points placed into the pPts POINT array.
****************************************************************************/

UINT MakeBezierFromLine( POINT *pPts, POINT startpt, POINT endpt )
{
    UINT cTotal = 0; // starting point of Bezier
    pPts[cTotal] = startpt;
    cTotal++; // 1rst Control, pt == endpoint makes Bezier a line
    //pPts[cTotal].x = endpt.x;
    //pPts[cTotal].y = endpt.y;
    pPts[cTotal] = endpt;
    cTotal++; // 2nd Control, pt == startpoint makes Bezier a line
    //pPts[cTotal].x = startpt.x;
    //pPts[cTotal].y = startpt.y;

    pPts[cTotal] = startpt;
    cTotal++; // ending point of Bezier
    pPts[cTotal] = endpt;
    cTotal++;

    return cTotal;
}

/****************************************************************************
* FUNCTION : MakeBezierFromQBSpline
*
* PURPOSE : Converts a quadratic spline in pSline to a four point Bezier
* spline in pPts.
*
*
* RETURNS : number of Bezier points placed into the pPts POINT array.
****************************************************************************/

UINT MakeBezierFromQBSpline( POINT *pPts, POINTFX *pSpline )
{
    POINT P0, // Quadratic on curve start point
    P1, // Quadratic control point
    P2; // Quadratic on curve end point
    UINT cTotal = 0; // Convert the Quadratic points to integer
    P0.x = IntFromFixed( pSpline[0].x );
    P0.y = IntFromFixed( pSpline[0].y );
    P1.x = IntFromFixed( pSpline[1].x );
    P1.y = IntFromFixed( pSpline[1].y );
    P2.x = IntFromFixed( pSpline[2].x );
    P2.y = IntFromFixed( pSpline[2].y ); // conversion of a quadratic to a cubic // Cubic P0 is the on curve start point
    pPts[cTotal] = P0;
    cTotal++;

    // Cubic P1 in terms of Quadratic P0 and P1
    pPts[cTotal].x = P0.x + 2*(P1.x - P0.x)/3;
    pPts[cTotal].y = P0.y + 2*(P1.y - P0.y)/3;
    cTotal++; // Cubic P2 in terms of Qudartic P1 and P2
    pPts[cTotal].x = P1.x + 1*(P2.x - P1.x)/3;
    pPts[cTotal].y = P1.y + 1*(P2.y - P1.y)/3;
    cTotal++; // Cubic P3 is the on curve end point
    pPts[cTotal] = P2;
    cTotal++;
    return cTotal;
}

/****************************************************************************
* FUNCTION : AppendPolyLineToBezier
*
* PURPOSE : Converts line segments into their Bezier point
* representation and appends them to a list of Bezier points.
*
* WARNING - The array must have at least one valid
* start point prior to the address of the element passed.
*
* RETURNS : number of Bezier points added to the POINT array.
****************************************************************************/

UINT AppendPolyLineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
    int i;
    UINT cTotal = 0;
    POINT endpt;
    POINT startpt;
    POINT bezier[4];
    endpt.x = IntFromFixed(start.x);
    endpt.y = IntFromFixed(start.y);
    for (i = 0; i < lpCurve->cpfx; i++)
    {
        // define the line segment
        startpt = endpt;
        endpt.x = IntFromFixed(lpCurve->apfx[i].x);
        endpt.y = IntFromFixed(lpCurve->apfx[i].y); // convert a line to a bezier representation
        MakeBezierFromLine( bezier, startpt, endpt ); // append the Bezier to the existing ones
        // Point 0 is Point 3 of previous.
        pt[cTotal++] = bezier[1]; // Point 1
        pt[cTotal++] = bezier[2]; // Point 2
        pt[cTotal++] = bezier[3]; // Point 3
    }
    return cTotal;
}

/****************************************************************************
* FUNCTION : AppendQuadBSplineToBezier
*
* PURPOSE : Converts Quadratic spline segments into their Bezier point
* representation and appends them to a list of Bezier points.
*
* WARNING - The array must have at least one valid
* start point prior to the address of the element passed.
*
* RETURNS : number of Bezier points added to the POINT array.
****************************************************************************/

UINT AppendQuadBSplineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
    WORD i;
    UINT cTotal = 0;
    POINTFX spline[3]; // a Quadratic is defined by 3 points
    POINT bezier[4]; // a Cubic by 4 // The initial A point is on the curve.
    spline[0] = start;
    for (i = 0; i < lpCurve->cpfx;)
    {
        // The B point.
        spline[1] = lpCurve->apfx[i++]; // Calculate the C point.
        if (i == (lpCurve->cpfx - 1))
        {
            // The last C point is described explicitly
            // i.e. it is on the curve.
            spline[2] = lpCurve->apfx[i++];
        }
        else
        {
            // C is midpoint between B and next B point
            // because that is the on curve point of
            // a Quadratic B-Spline.
            spline[2].x = fxDiv2(
                lpCurve->apfx[i-1].x,
                lpCurve->apfx[i].x
                );
            spline[2].y = fxDiv2(
                lpCurve->apfx[i-1].y,
                lpCurve->apfx[i].y
                );
        } // convert the Q Spline to a Bezier
        MakeBezierFromQBSpline( bezier, spline );

        // append the Bezier to the existing ones
        // Point 0 is Point 3 of previous.
        pt[cTotal++] = bezier[1]; // Point 1
        pt[cTotal++] = bezier[2]; // Point 2
        pt[cTotal++] = bezier[3]; // Point 3 // New A point for next slice of spline is the
        // on curve C point of this B-Spline
        spline[0] = spline[2];
    }
    return cTotal;
}

/****************************************************************************
* FUNCTION : CloseContour
*
* PURPOSE : Adds a bezier line to close the circuit defined in pt.
*
*
* RETURNS : number of points aded to the pt POINT array.
****************************************************************************/

UINT CloseContour( LPPOINT pt, UINT cTotal )
{
    POINT endpt,
    startpt; // definition of a line
    POINT bezier[4]; // connect the first and last points by a line segment
    startpt = pt[cTotal-1];
    endpt = pt[0]; // convert a line to a bezier representation
    MakeBezierFromLine( bezier, startpt, endpt ); // append the Bezier to the existing ones
    // Point 0 is Point 3 of previous.
    pt[cTotal++] = bezier[1]; // Point 1
    pt[cTotal++] = bezier[2]; // Point 2
    pt[cTotal++] = bezier[3]; // Point 3
    return 3;
}

/****************************************************************************
* FUNCTION : DrawT2Outline
*
* PURPOSE : Decode the GGO_NATIVE outline, create a sequence of Beziers
* for each contour, draw with PolyBezier. Color and relative
* positioning provided by caller. The coordinates of hDC are
* assumed to have MM_TEXT orientation.
*
* The outline data is not scaled. To draw a glyph unhinted
* the caller should create the font at its EMSquare size
* and retrieve the outline data. Then setup a mapping mode
* prior to calling this function.
*
* RETURNS : none.
****************************************************************************/

CTTFOutline::CTTFOutline()
{
}

CTTFOutline::~CTTFOutline()
{
    assert( m_vectorContour.empty() );
}


BOOL CTTFOutline::Create( LPTTPOLYGONHEADER lpHeader, DWORD size )
{
    WORD i;
    UINT cTotal = 0; // Total points in a contour.
    LPTTPOLYGONHEADER lpStart; // the start of the buffer
    LPTTPOLYCURVE lpCurve; // the current curve of a contour
    LPPOINT pt; // the bezier buffer
    POINTFX ptStart; // The starting point of a curve
    DWORD dwMaxPts = size/sizeof(POINTFX); // max possible pts.
    DWORD dwBuffSize;
    dwBuffSize = dwMaxPts * // Maximum possible # of contour points.
            sizeof(POINT) * // sizeof buffer element
            3; // Worst case multiplier of one additional point
    // of line expanding to three points of a bezier
    lpStart = lpHeader;
    //pt = (LPPOINT)malloc( dwBuffSize ); // Loop until we have processed the entire buffer of contours.
    pt = (LPPOINT)new BYTE[dwBuffSize];
    // The buffer may contain one or more contours that begin with
    // a TTPOLYGONHEADER. We have them all when we the end of the buffer.

    while ((DWORD)lpHeader < (DWORD)(((LPSTR)lpStart) + size) && pt != NULL)
    {
        if (lpHeader->dwType == TT_POLYGON_TYPE)
        // Draw each coutour, currently this is the only valid
// type of contour.

        {
            // Convert the starting point. It is an on curve point.
            // All other points are continuous from the "last"
            // point of the contour. Thus the start point the next
            // bezier is always pt[cTotal-1] - the last point of the
            // previous bezier. See PolyBezier.
            cTotal = 1;
            pt[0].x = IntFromFixed(lpHeader->pfxStart.x);
            pt[0].y = IntFromFixed(lpHeader->pfxStart.y); // Get to first curve of contour -
            // it starts at the next byte beyond header

            lpCurve = (LPTTPOLYCURVE) (lpHeader + 1); // Walk this contour and process each curve( or line ) segment
            // and add it to the Beziers
            while ((DWORD)lpCurve < (DWORD)(((LPSTR)lpHeader) + lpHeader->cb))
            {
                //**********************************************
                // Format assumption:
                // The bytes immediately preceding a POLYCURVE
                // structure contain a valid POINTFX.
                //
                // If this is first curve, this points to the
                // pfxStart of the POLYGONHEADER.
                // Otherwise, this points to the last point of
                // the previous POLYCURVE.
                //
                // In either case, this is representative of the
                // previous curve's last point.
                //**********************************************
                ptStart = *(LPPOINTFX)((LPSTR)lpCurve - sizeof(POINTFX));
                if (lpCurve->wType == TT_PRIM_LINE)
                {
                    // convert the line segments to Bezier segments
                    cTotal += AppendPolyLineToBezier( &pt[cTotal], ptStart, lpCurve );
                    i = lpCurve->cpfx;
                }
                else if (lpCurve->wType == TT_PRIM_QSPLINE)
                {
                    // Decode each Quadratic B-Spline segment, convert to bezier,
                    // and append to the Bezier segments
                    cTotal += AppendQuadBSplineToBezier( &pt[cTotal], ptStart, lpCurve );
                    i = lpCurve->cpfx;
                }
                else
                    // Oops! A POLYCURVE format we don't understand.
                    ; // error, error, error // Move on to next curve in the contour.
                lpCurve = (LPTTPOLYCURVE)&(lpCurve->apfx[i]);
            } // Add points to close the contour.

            // All contours are implied closed by TrueType definition.
            // Depending on the specific font and glyph being used, these
            // may not always be needed.
            if ( pt[cTotal-1].x != pt[0].x || pt[cTotal-1].y != pt[0].y )
            {
                cTotal += CloseContour( pt, cTotal );
            } // flip coordinates to get glyph right side up (Windows coordinates)
            // TT native coordiantes are zero originate at lower-left.
            // Windows MM_TEXT are zero originate at upper-left.
            for (i = 0; i < cTotal; i++)
            {
                pt[i].y = 0 - pt[i].y; // Draw the contour
            }
            // RealRender added
            Contour contour;
            for( int i = 0; i < cTotal; i++ )
            {
                contour.vectorPoint.push_back( pt[i] );
            }
            m_vectorContour.push_back( contour );
        }
        else
            // Bad, bail, must have a bogus buffer.
            break; // error, error, error // Move on to next Contour.
        // Its header starts immediate after this contour

        lpHeader = (LPTTPOLYGONHEADER)(((LPSTR)lpHeader) + lpHeader->cb);
    }

    delete []pt;
    return TRUE;
}



这段代码在指定位置把ttf的轮廓画出来,

void DrawTTFOutline( CTTFOutline* pOutline, int x, int y, DWORD color )
{
    for( int nContourId = 0; nContourId < pOutline->GetContourCount(); nContourId++ )
    {
        CTTFOutline::Contour* pContour = pOutline->GetContour( nContourId );
        POINT* pt = &pContour->vectorPoint[0];
        for( int i = 0; i < pContour->vectorPoint.size()-1; i++ )
        {
            DrawLine( x+pt[i].x, y+pt[i].y, x+pt[i+1].x, y+pt[i+1].y, 0xffffffff );
        }
    }
}


根据这段代码,不仅自己绘制ttf字体的轮廓,而且,略加修改,可以生成全三维的ttf mesh,用d3d渲染,看起来感觉不错,
下次把代码提上来。


CTTFOutline* CreateOutline( const char* pChar )
{
    unsigned short nChar;
    if( strlen( pChar ) == 2 )
    {
        nChar = *(int*)pChar;
        nChar = (nChar>>8)|((nChar&0x00ff)<<8);
    }
    else
        nChar = *pChar;

    GLYPHMETRICS gmm;
    MAT2 mat;
    mat.eM11.value = 1;mat.eM11.fract = 0;
    mat.eM12.value = 0;mat.eM12.fract = 0;
    mat.eM21.value = 0;mat.eM21.fract = 0;
    mat.eM22.value = 1;mat.eM22.fract = 0;


    DWORD dwSize = GetGlyphOutline( m_hDC, nChar, GGO_NATIVE , &gmm, 0, 0, &mat );
    if( dwSize == GDI_ERROR )
        return NULL;

    BYTE* pBuffer = new BYTE[dwSize];
    if( GetGlyphOutline( m_hDC, nChar, GGO_NATIVE, &gmm, dwSize, pBuffer, &mat ) == GDI_ERROR )
    {
        delete pBuffer;
        return NULL;
    }

    TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pBuffer;
    CTTFOutline* pOutline = new CTTFOutline;
    if( pOutline->Create( pHeader, dwSize ) )
    {
        delete pBuffer;
        return pOutline;
    }
    delete pBuffer;
    delete pOutline;
    return NULL;
}


LOGFONT lf;

ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = 160;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_NORMAL;
lf.lfItalic = 0;
lf.lfUnderline = 1;
lf.lfStrikeOut = 1;
lf.lfCharSet = GB2312_CHARSET;
lf.lfOutPrecision = 1;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = 0;

lstrcpy(lf.lfFaceName, "黑体" );

CTTFOutline* outline = CreateOutline( "哪" );


通过这段代码,就创建了一个outline


有了ttf的字体轮廓,我想主要应用在3d渲染上比较有用,比如一些专门的三维字体制作工具,或者用在游戏的特殊效果中,
比如说旋转的“临兵斗者皆阵列在前”。

希望对感兴趣的朋友有用
qq:122045058
返回顶部】 【打印本页】 【关闭窗口

关于我们 / 给我留言 / 版权举报 / 意见建议 / 网站编程QQ群   
Copyright ©2003- 2024 Lihuasoft.net webmaster(at)lihuasoft.net 加载时间 0.00262