Como desenhar contornos de glifo TrueType

Traduções deste artigo Traduções deste artigo
ID do artigo: 243285 - Exibir os produtos aos quais esse artigo se aplica.
Expandir tudo | Recolher tudo

Sumário

A função de API do Win32 GetGlyphOutline pode retornar dados de estrutura de tópicos de glifo nativo a partir de uma fonte TrueType. Para desenhar um contorno de glifo TrueType, os dados devem ser convertidos de sua definição B-spline nativa como uma seqüência de definições de linha de Bézier. Em seguida, a função de API do Win32 PolyBezier pode ser usada para desenhar o contorno.

Mais Informações

A função GetGlyphOutline o Interface(API) de programação de aplicativo Win32 pode recuperar um contorno TrueType. A opção de formato GGO_NATIVE preenche um buffer com curvas de B-spline quadrática para uma estrutura de tópicos de TrueType. B-splines quadráticas são usados por TrueType para descrever a estrutura de glifo em um arquivo de fonte TrueType. Para desenhar curvas esses tópicos um pode implementar uma função de desenho B-spline ou use a função PolyBezier da API Win32.

Curvas de B-spline quadráticas são uma classe de curvas paramétricas que definem o caminho de vários segmentos de curva por meio de alguns pontos de controle. Uma curva quadrática é uma segunda curva paramétrica de ordem. Não há nenhuma função na API para desenhar uma spline quadrática diretamente, mas se o quadrática é convertida em um cúbicos pode ser obtido com a API do Win32 funciona para desenhar uma curva de Bezier; chamado PolyBezier .

B-spline quadráticas curvas em determinado e paramétricas curvas em geral são um tópico well-researched de elementos gráficos em ciência da computação. Eles também podem ser bem complexos. Algoritmos foram publicados em vários textos que podem ser usados para implementar uma função para desenhar uma spline quadrática, mas que descrevem como um algoritmo está além do escopo deste artigo.

A função PolyBezier pode desenhar uma spline quadrática porque uma curva de Bezier é uma curva paramétrica cúbicos ou terceira ordem. Como um spline quadrática é uma segunda equação de ordem, pode ser expresso em termos da equação cúbicos de ordem superior. Embora uma equação para expressar uma quadrática como um cúbicos for fornecida no código de exemplo, sua derivação não é discutida neste artigo.

O código de exemplo neste artigo é uma demonstração de como analisar um buffer de glifo GGO_NATIVE retornado pela função GetGlyphOutline . O buffer retornado pelo sinalizador de formato GGO_NATIVE segue a estrutura TTPOLYGONHEADER . A estrutura TTPOLYGONHEADER e os dados imediatamente a seguir constituem um contorno de um glifo de TrueType. Uma delimitação é um caminho completo de curvas que está implicitamente fechado se não for retornado explicitamente dessa maneira.

Consulte o SDK de plataforma para documentação sobre as estruturas TTPOLYGONHEADER e TTPOLYCURVE .

Uma delimitação de glifo consiste em vários segmentos de curva representados por TTPOLYCURVE estruturas. Em uma delimitação TTPOLYGONHEADER é seguido por um ou mais estruturas TTPOLYCURVE e dados de ponto de coordenada. O membro pfxStart fornece o ponto de coordenada inicial da delimitação. A contagem de registros de curva que siga TTPOLYGONHEADER é fornecida pelo membro cb . O ponto inicial é em relação à origem o glifo. A origem de um glifo é o canto inferior esquerdo do caractere na linha de base do caractere.

Cada registro de curva TTPOLYCURVE (um segmento de curva) consiste em ou pontos de controle de B-spline quadrática pontos Polyline simples. A contagem de pontos é fornecida pelo membro cpfx TTPOLYCURVE . O tipo de curva de spline ou polilinha é fornecido pelo membro wType . A matriz de pontos de coordenadas imediatamente seguem a estrutura. O ponto inicial da curva é fornecido pelo membro apfx .

Como um glifo pode conter mais de uma delimitação, o buffer contém um ou mais grupos de TTPOLYGONHEADER estruturas e os dados. Delimitações subseqüentes são compactadas imediatamente após a anterior no buffer nativo. A delimitação próxima começa com um TTPOLYGONHEADER no próximo byte além do último ponto do Registro curva anterior.

Este código de exemplo desenha o contorno de um glifo TrueType ao analisar o buffer GGO_NATIVE para criar uma lista de segmentos de linha de Bezier cada delimitação de formulário. A lista de segmentos de linha de Bezier é desenhada em seguida, usando PolyBezier . O código que analisa o buffer está na função DrawT2Outline .

A primeira etapa para criar uma lista de linhas de Bezier é determinar o tamanho do buffer para a lista. Quatro pontos definem linhas de Bézier. A função PolyBezier interpreta uma matriz de pontos como sendo uma linha contínua de segmentos de Bezier onde o início da próxima linha é coincidentes com o ponto de extremidade da linha anterior. Assim, apenas três pontos são necessárias para descrever um segmento de linha de Bezier adicional.

O buffer GGO_NATIVE pode conter uma curva de polilinha ou uma curva quadrática B-spline. Dois pontos de definem um segmento de linha enquanto três pontos definem um B-spline. Como cada tipo é ser desenhado com uma linha de Bezier usando PolyBezier, pior das hipóteses ocorre quando uma delimitação que é totalmente composta por segmentos de linha se expande para vários Beziers.

Observe que, para expressar uma curva adicional segmento em uma representação de curva B-spline requer apenas um ponto adicional. Esse ponto define explicitamente "B" ou desativar curva aponte e define implicitamente a adicionais no ponto da curva. O ponto de curva na é o ponto médio para o próximo ponto de "B". Assim, em um segmento de linha adicionais ou um segmento de curva adicionais se expandirá para três pontos adicionais de uma linha de Bezier.

O código pressupõe que o buffer nativo todo consistirá POINTFX estruturas para que ele possa determinar o maior número possível de pontos representado pelo buffer. O número real é um pouco menor como não há sobrecarga de estrutura em que descrevem as delimitações e curvas. O tamanho do buffer para a lista de Bezier é fornecido pelo número máximo possível de pontos multiplicado pelo tamanho de dados de uma estrutura de ponto e multiplicado por três. Três é o número de pontos adicionais que são necessários para cada segmento de linha adicionais ou segmento de spline quadrática quando convertido em um Bézier.

Depois que o buffer de Bezier foi alocado o código analisa o buffer nativo iniciando no primeiro TTPOLYGONHEADER . Durante uma iteração do loop, o código processa uma delimitação e desenha-lo. Um segundo loop interno analisa cada tipo de curva.

No caso de uma curva de polilinha (TT_PRIM_LINE), cada segmento de linha da polilinha é convertido e acrescentado à lista de Beziers com a função AppendPolyLineToBezier . Se a curva é uma B-spline (TT_PRIM_QSPLINE quadrática), a função AppendQuadBSplineToBezier converte e acrescenta o B-spline quadrática. Cada uma dessas funções usa um loop para analisar saída cada segmento da curva em seus pontos de controle independente. Em seguida, a linha ou o spline é convertido em um Bezier e acrescentado à lista de Beziers.

Uma linha de Bezier pode desenhar uma linha reta facilmente. O início e término vetores simplesmente apontam para a extremidade oposta do segmento de linha. Isso é feito na função MakeBezierFromLine .

Para converter uma spline quadrática uma spline de Bézier cúbica requer expressar pontos de controle do cúbicos em termos de pontos de controle do quadrática. A equação para converter os pontos de controle está localizada na função MakeBezierFromQBSpline .

Antes da delimitação de desenho, o código garante que o caminho é fechado ao chamar a função CloseContour . A função é chamada somente se o início e pontos de extremidade da seqüência de Beziers não são coincidentes. Em seguida, a função PolyBezier é chamada.

Após a delimitação é desenhada, TTPOLYGONHEADER da delimitação próxima for encontrado por aprimorando o ponteiro lpHeader além do final dos registros de delimitação atual. Se isso resultar em um valor do ponteiro além do fim do buffer nativo, o código processou todas as delimitações e sai.
/****************************************************************************
 *  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 );
}
				

Referências

Para obter mais informações sobre a especificação de TrueType consulte:
Microsoft TrueType Specifications (http://www.microsoft.com/typography/tt/tt.htm)

Também disponível do CD do Microsoft Developer Network Library em especificações.

Propriedades

ID do artigo: 243285 - Última revisão: segunda-feira, 12 de fevereiro de 2007 - Revisão: 1.5
A informação contida neste artigo aplica-se a:
  • Interface de Programação de Aplicativos do Microsoft Win32
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition
Palavras-chave: 
kbmt kbdswgdi2003swept kbdraw kbfont kbgdi kbhowto KB243285 KbMtpt
Tradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine Translation ou MT), não tendo sido portanto traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 243285

Submeter comentários

 

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