Cómo dibujar contornos de glifo de TrueType

Seleccione idioma Seleccione idioma
Id. de artículo: 243285 - Ver los productos a los que se aplica este artículo
Expandir todo | Contraer todo

Resumen

La función de API Win32 GetGlyphOutline puede devolver datos de esquema de glifo nativo desde una fuente TrueType. Para dibujar un contorno de glifo TrueType, los datos deben convertirse de su definición de B-spline nativo a una secuencia de las definiciones de línea de Bézier. A continuación, la función de API de Win32 PolyBezier puede utilizarse para dibujar el contorno.

Más información

La función GetGlyphOutline en el Interface(API) de programación de aplicaciones de Win32 puede recuperar un esquema de TrueType. La opción de formato GGO_NATIVE llena un búfer con curvas B-spline cuadrática para un esquema de TrueType. TrueType utilizan cuadrática curvas spline-B para describir el esquema de glifo en un archivo de fuentes TrueType. Para dibujar estas curvas de esquema uno puede implementar una función de dibujo de B-spline o utilice la función PolyBezier de la API Win32.

Cuadráticas curvas B-spline son una clase de curvas paramétricos que definen la ruta de acceso de varios segmentos de curva a través de unos puntos de control. Una curva cuadrática es la segunda curva paramétrico orden. No hay ninguna función en la API para dibujar una spline cuadrática directamente, pero si el cuadrática se convierte en un cúbicos puede dibujarse con la API Win32 función para dibujar una curva Bézier; denominado PolyBezier .

En general, cuadráticas curvas B-spline en particular y paramétricos curvas son un tema well-researched de gráficos en informática. También pueden ser bastante complejos. Algoritmos se han publicado en diversos textos que se puede utilizar para implementar una función para dibujar una spline cuadrática pero describir tal algoritmo está fuera del ámbito de este artículo.

La función PolyBezier puede dibujar una spline cuadrática porque una curva de Bézier es una curva paramétrico cúbica o tercer pedido. Puesto que una spline cuadrática es una ecuación de orden de segunda, se puede expresar en términos de la ecuación cúbicos de orden superior. Aunque una ecuación para expresar un quadratic como un cúbica se expresa en el código de ejemplo, su derivación no se explica en este artículo.

El código de ejemplo en este artículo es una demostración de cómo analizar un búfer de glifo GGO_NATIVE devuelto por la función GetGlyphOutline . El búfer devuelto por el indicador de formato GGO_NATIVE se ajusta a la estructura TTPOLYGONHEADER . La estructura TTPOLYGONHEADER y los datos que sigue inmediatamente constituyen un contorno de un glifo de TrueType. Un contorno es una ruta completa de las curvas que implícitamente se cierra si no se devuelve explícitamente de este modo.

Consulte el SDK de plataforma para la documentación en las estructuras TTPOLYGONHEADER y TTPOLYCURVE .

Un contorno de glifo se compone de varios segmentos de curva representados por TTPOLYCURVE estructuras. En un perfil la TTPOLYGONHEADER va seguido de uno o más estructuras TTPOLYCURVE y datos de punto de coordenadas. El miembro pfxStart proporciona el punto de coordenadas inicial de contorno. El valor de registros de la curva que siga la TTPOLYGONHEADER viene dado por el miembro cb . El punto inicial es relativa al origen del glifo. El origen de un glifo es la esquina inferior izquierda del carácter en la línea de base del carácter.

Cada registro de curva TTPOLYCURVE (un segmento de curva) está formada por puntos de control de B-spline cuadrática o puntos de polilínea simples. El valor de puntos viene dado por el miembro de cpfx TTPOLYCURVE . El tipo de curva de spline o polilínea viene dado por el miembro wType . La matriz de puntos de coordenada seguir inmediatamente a la estructura. El punto inicial de la curva viene dado por el miembro apfx .

Puesto que un glifo puede contener más de un perfil, el búfer contiene uno o más grupos de TTPOLYGONHEADER estructuras y datos. Perfiles posteriores se empaquetan sigue inmediatamente al anterior en el búfer nativo. El siguiente perfil empieza un TTPOLYGONHEADER en el siguiente byte más allá del último punto del anterior registro de la curva.

Este ejemplo de código dibuja el contorno de un glifo TrueType analizando el búfer GGO_NATIVE para crear una lista de segmentos de línea de Bezier que forman cada perfil. La lista de segmentos de línea de Bézier se dibuja a continuación, utilizando PolyBezier . El código que analiza el búfer es en la función DrawT2Outline .

El primer paso para crear una lista de líneas de Bézier es para determinar el tamaño del búfer para la lista. Cuatro puntos definen líneas de Bézier. La función PolyBezier interpreta una matriz de puntos como una línea continua de segmentos Bezier donde el inicio de la línea siguiente es coincidentes con el extremo de la línea anterior. Por lo tanto, sólo tres puntos son necesarios para describir un segmento de línea adicional de Bézier.

El búfer GGO_NATIVE puede contener una curva de polilínea o una curva B-spline cuadrática. Dos puntos definen un segmento de línea mientras tres puntos definen una B-spline. Debido a que cada tipo es dibujarse con una línea de Bezier mediante PolyBezier, la peor se produce cuando un perfil que está completamente compuesto de segmentos de línea se expande para varias curvas y colores.

Observe que, para expresar una curva adicional segmento en una representación de curva B-spline requiere sólo un punto adicional. Ese punto define explícitamente "B" o Cerrar curva, punto y define implícitamente el adicionales en el punto de curva. El punto de curva en es el punto medio del siguiente punto de "B". Por tanto, un segmento de línea adicional o de un segmento de curva adicional se expandirá para tres puntos adicionales de una línea de Bézier.

En el código se supone que el búfer completo nativo constará de las estructuras POINTFX por lo que puede determinar el mayor número posible de puntos representado por el búfer. El número real es ligeramente menor puesto que hay sobrecarga de la estructura de describir los perfiles y curvas. El tamaño del búfer de la lista de Bézier viene dado por el máximo número posible de puntos multiplicado por el tamaño de datos de una estructura POINT y la multiplica por tres. Tres es el número de puntos adicionales que son necesarios para cada segmento de línea adicional o segmento de spline cuadrática al convertir a un Bezier.

Una vez que el búfer de Bézier se ha asignado el código analiza el búfer nativo a partir de la primera TTPOLYGONHEADER . Durante una iteración del bucle el código procesa un perfil y lo dibuja. Un segundo bucle interno analiza cada tipo de curva.

En el caso de una curva de polilínea (TT_PRIM_LINE), cada segmento de línea de la polilínea es convertir y anexado a la lista de Beziers con la función AppendPolyLineToBezier . Si la curva es un TT_PRIM_QSPLINE (B-spline cuadrática), la función AppendQuadBSplineToBezier convierte y anexa la B-spline cuadrática. Cada una de estas funciones utiliza un bucle para analizar cada segmento de la curva en sus puntos de control independiente. A continuación, la línea o curva spline se convierte en un Bézier y se anexa a la lista de Beziers.

Una línea de Bézier puede dibujar una línea recta fácilmente. El inicio y final de vectores simplemente señale el extremo opuesto del segmento de línea. Esto se realiza en la función MakeBezierFromLine .

Para convertir una spline cuadrática una spline de Bézier cúbica requiere expresar puntos de control del cúbicos en términos de puntos de control del quadratic. La ecuación para traducir los puntos de control se encuentra en la función MakeBezierFromQBSpline .

Antes de dibujar el contorno, el código garantiza que la ruta de acceso se cierra llamando a la función CloseContour . Se llama a la función sólo si los puntos inicial y final de la secuencia de Beziers no coincidentes. A continuación, se llama a la función PolyBezier .

Una vez dibujado el perfil, se encuentra TTPOLYGONHEADER del perfil siguiente avance el puntero lpHeader más allá del final de los registros en el perfil actual. Si esto da como resultado un valor de puntero más allá del final del búfer nativo, el código ha procesado todos los perfiles y sale.
/****************************************************************************
 *  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 );
}
				

Referencias

Para obtener más información sobre la especificación de TrueType consulte:
Microsoft TrueType Specifications (http://www.microsoft.com/typography/tt/tt.htm)

También disponible en del Microsoft Developer Network Library CD en especificaciones.

Propiedades

Id. de artículo: 243285 - Última revisión: lunes, 12 de febrero de 2007 - Versión: 1.5
La información de este artículo se refiere a:
  • Microsoft Win32 Application Programming Interface
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition
Palabras clave: 
kbmt kbdswgdi2003swept kbdraw kbfont kbgdi kbhowto KB243285 KbMtes
Traducción automática
IMPORTANTE: Este artículo ha sido traducido por un software de traducción automática de Microsoft (http://support.microsoft.com/gp/mtdetails) en lugar de un traductor humano. Microsoft le ofrece artículos traducidos por un traductor humano y artículos traducidos automáticamente para que tenga acceso en su propio idioma a todos los artículos de nuestra base de conocimientos (Knowledge Base). Sin embargo, los artículos traducidos automáticamente pueden contener errores en el vocabulario, la sintaxis o la gramática, como los que un extranjero podría cometer al hablar el idioma. Microsoft no se hace responsable de cualquier imprecisión, error o daño ocasionado por una mala traducción del contenido o como consecuencia de su utilización por nuestros clientes. Microsoft suele actualizar el software de traducción frecuentemente.
Haga clic aquí para ver el artículo original (en inglés): 243285

Enviar comentarios

 

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