如何绘制 TrueType 标志符号轮廓

文章翻译 文章翻译
文章编号: 243285 - 查看本文应用于的产品
展开全部 | 关闭全部

概要

Win32 API GetGlyphOutline 函数可以从 TrueType 字体返回本机的标志符号分级显示数据。若要绘制一个 TrueType 标志符号轮廓,数据必须转换从其本机 B 样条定义为贝塞尔行定义的序列。然后 PolyBezier Win32 API 函数可用于绘制轮廓。

更多信息

在 Win32 应用程序编程 Interface(API) GetGlyphOutline 函数可以检索 TrueType 大纲。GGO_NATIVE 格式选项填充使用 TrueType 大纲的二次 B 样条曲线的缓冲区。二次 B 样条由 TrueType 用来描述标志符号轮廓 TrueType 字体文件中。若要绘制这些大纲曲线一个可以实现 B 样条绘图函数,或使用从 Win32 API PolyBezier 函数。

二次 B 样条曲线是一类的参数定义的多个通过几个控件点的曲线段路径的曲线。二次曲线是第二个顺序参数化曲线。直接绘制样条二次 API 中没有任何函数,但如果该二次方程转换为可以绘制一个立方体,与 Win32 API 函数用于绘制贝塞尔曲线,称为 PolyBezier

在特定和参数化曲线的二次 B 样条曲线通常都是计算机科学中的图形的 well-researched 主题的链接。也可以是相当复杂。在文本中各个已发布的算法的可用于实现以绘制一个二次样条的函数,但描述这样一种算法是超出了本文的范围。

由于贝塞尔曲线是一条三次或第三个顺序参数化曲线 PolyBezier 函数可以绘制二次样条。二次样条是第二个的顺序方程,因为它可以表示的更高的顺序三次方程。尽管用于表达为一个三次的二次公式的代码示例中给出,本文不讨论其派生。

在这篇文章中的代码示例演示如何分析 GGO_NATIVE 标志符号缓冲区 GetGlyphOutline 函数返回。返回 GGO_NATIVE 格式标记的缓冲区符合 TTPOLYGONHEADER 结构。TTPOLYGONHEADER 结构和数据的紧随它构成了 TrueType 标志符号的一个轮廓。轮廓线是一条曲线的完整路径隐式地关闭,如果不显式返回这样的。

请参阅 TTPOLYGONHEADERTTPOLYCURVE 结构上的平台 SDK 文档。

标志符号轮廓由多个由 TTPOLYCURVE 结构表示的曲线段组成。在一个轮廓 TTPOLYGONHEADER 后跟一个或多个 TTPOLYCURVE 结构和坐标的点数据。pfxStart 成员提供了轮廓线的起始坐标的点。由 cb 成员给出的后续 TTPOLYGONHEADER 曲线记录的计数。相对于标志符号的原点是起始点。一个标志符号的原点是在该字符的基线字符的左下角。

每个 TTPOLYCURVE 曲线记录 (曲线段) 组成,二次 B 样条控制点或简单的折线点。由 TTPOLYCURVEcpfx 成员给出的点的计数。由 wType 成员给出的折线或样条曲线类型。坐标的点的数组紧跟结构。由 apfx 成员给出该曲线的起始点。

由于一个标志符号可能包含多个轮廓,在缓冲区中包含 TTPOLYGONHEADER 结构和数据的一个或多个的组。后续的分布类型被打包紧跟在前一个本机缓冲区中。在下一个字节,超过前一曲线记录的最后一个点的 TTPOLYGONHEADER 开始下一个轮廓。

此示例代码通过分析创建的窗体的每个轮廓的贝塞尔线段列表 GGO_NATIVE 缓冲区来绘制 TrueType 标志符号的大纲。然后使用 PolyBezier 绘制贝塞尔线段的列表。对缓冲区进行分析的代码是 DrawT2Outline 函数中。

若要创建的贝塞尔行的列表,第一步是缓冲区的确定列表的大小。四个点定义贝塞尔行。PolyBezier 函数将解释为一个连续的行的下一行的开始位置是与前一行的终结点重合的贝赛尔曲线段的点的数组。因此,只有三个点都需要描述其他的贝塞尔线段。

GGO_NATIVE 缓冲区可能包含折线曲线或二次 B 样条曲线。两个点定义一条线段,而三个点定义 B 样条。由于每个类型都是要绘制贝塞尔行使用 PolyBezier,最坏的情况下发生时由完全组成的直线段的一个轮廓将扩展到多个 bezier。

请注意来表示一个附加曲线段的 B 样条曲线表示要求只能有一个附加的点。该点显式地定义了"B"或关闭曲线点和隐式定义曲线点上的附加。打开的曲线点是下一个"B"点到点。因此额外的线段或一个附加曲线段将展开以贝塞尔行的三个附加点。

该代码假定整个本机缓冲区将 POINTFX 结构组成,因此它可以确定最大的可能表示被缓冲区的磅数。实际的数字,略小的因为在描述轮廓和曲线没有结构开销。由最大可能数乘以 结构的数据大小和乘以三磅为单位给出有关贝塞尔列表缓冲区的大小。三个是为每个额外的线段或二次曲线段转换为一个贝塞尔时所需的其他点的数量。

一旦贝塞尔缓冲区已被分配代码将由从开始的第一个 TTPOLYGONHEADER 分析本机的缓冲区。循环的迭代过程中代码处理一个轮廓,并将其绘制。第二的内部循环分析每种曲线类型。

在折线曲线 (TT_PRIM_LINE) 的情况下每个线段的折线转换,追加到列表中的 bezier AppendPolyLineToBezier 函数。如果该曲线是二次 B 样条 (TT_PRIM_QSPLINE),AppendQuadBSplineToBezier 函数将转换,并将附加在二次 B 样条。这些函数的每个使用循环来分析出曲线的每个段,为其独立的控制点。然后行或样条是转换为一个贝塞尔并追加到 bezier 的列表。

贝塞尔行可以很容易地绘制一条直线。开始和结束的向量只需指向条线段的另一端。这是 MakeBezierFromLine 函数。

若要将转换成三次方贝塞尔样条的二次样条要求表达的二次的控点的立方的控点。若要翻译控点公式位于 MakeBezierFromQBSpline 函数。

前绘制轮廓,该代码可确保通过调用 CloseContour 函数关闭的路径。调用该函数时才开始和 bezier 序列的结束点不重合。然后调用 PolyBezier 函数。

绘制轮廓后,通过推进 lpHeader 指针超出当前轮廓中记录的结尾找到下一个轮廓 TTPOLYGONHEADER。如果这样的指针值超出了本机的缓冲区的结尾代码已处理的所有轮廓和退出。
/****************************************************************************
 *  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;
    cTotal++;

    // 2nd Control, pt == startpoint makes Bezier a line
    pPts[cTotal].x = startpt.x;
    pPts[cTotal].y = startpt.y;
    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.
 ****************************************************************************/ 
void DrawT2Outline(HDC hDC, 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/size of(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.
    // 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
            PolyBezier( hDC, pt, cTotal );
        }
        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);
    }

    free( pt );
}
				

参考

有关 TrueType 规范的详细信息请参阅:
Microsoft TrueType Specifications (http://www.microsoft.com/typography/tt/tt.htm)

也可在 Microsoft 开发人员网络库 CD 在 $ 指标下。

属性

文章编号: 243285 - 最后修改: 2007年2月12日 - 修订: 1.5
这篇文章中的信息适用于:
  • Microsoft Win32 Application Programming Interface
  • Microsoft Windows XP Professional Edition
  • the operating system: Microsoft Windows XP 64-Bit Edition
关键字:?
kbmt kbdswgdi2003swept kbdraw kbfont kbgdi kbhowto KB243285 KbMtzh
机器翻译
注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完成。微软很高兴能同时提供给您由人工翻译的和由机器翻译的文章, 以使您能使用您的语言访问所有的知识库文章。然而由机器翻译的文章并不总是完美的。它可能存在词汇,语法或文法的问题,就像是一个外国人在说中文时总是可能犯这样的错误。虽然我们经常升级机器翻译软件以提高翻译质量,但是我们不保证机器翻译的正确度,也不对由于内容的误译或者客户对它的错误使用所引起的任何直接的, 或间接的可能的问题负责。
点击这里察看该文章的英文版: 243285
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com