Como desenhar contornos de imagens TrueType

Traduções de Artigos Traduções de Artigos
Artigo: 243285 - Ver produtos para os quais este artigo se aplica.
Expandir tudo | Reduzir tudo

Sumário

A função de API do Win32 GetGlyphOutline pode devolver glifo nativo destacar dados a partir de um tipo de letra TrueType. Para desenhar um contorno de glifo TrueType, os dados tem de ser convertidos da definição de B-spline nativa numa sequência de Bezier linha definições. Em seguida, a função de API do Win32 PolyBezier pode ser utilizada para desenhar o destaque.

Mais Informação

A função GetGlyphOutline Interface(API) de programação de aplicações para Win32 pode obter um destaque do TrueType. A opção de formato GGO_NATIVE preenche uma memória intermédia com curvas quadrático B-spline para um destaque de TrueType. Curvas polinomiais B quadrático são utilizados por TrueType para descrever o destaque da imagem num ficheiro de tipo de letra TrueType. Para desenhar estas curvas de destaque um pode implementar uma função de desenho B-spline ou utilize a função PolyBezier da API do Win32.

Curvas de B-spline quadrático são uma classe de curvas parametric que definem o caminho de vários segmentos de curva através de alguns pontos de controlo. Uma curva quadrático é uma curva de parametric ordem segunda. Não existe nenhuma função na API para desenhar um Spline quadrático directamente mas se o Quadratic é convertido para um metros podem ser desenhada com a API do Win32 funciona para desenhar uma curva de Bezier; chamado PolyBezier .

Em geral quadrático curvas de B-spline específicos e parametric curvas são um tópico well-researched de gráficos no computador Ciência. Também podem ser bastante complexas. Algoritmos foram publicados em vários textos que pode ser utilizado para implementar uma função para desenhar um Spline quadrático, mas descrever tal um algoritmo está para além do âmbito deste artigo.

A função PolyBezier pode desenhar um Spline quadrático porque uma curva de Bezier é uma curva parametric metros cúbicos ou terceira. Uma vez que um Spline quadrático é uma equação ordem segunda, pode ser expressa em termos da equação metros cúbicos de ordem superior. Apesar de uma equação para expressar um quadratic como um metros for especificada no código de exemplo, a derivação não é abordada neste artigo.

O código de exemplo neste artigo é uma demonstração de como analisar uma glifo GGO_NATIVE memória intermédia devolvido pela função GetGlyphOutline . A memória intermédia devolvida pelo sinalizador de formato GGO_NATIVE está em conformidade com a estrutura TTPOLYGONHEADER . A estrutura TTPOLYGONHEADER e os dados imediatamente a seguir constituem uma curva de nível de um glifo TrueType. Uma curva de nível é um caminho completo das curvas implicitamente fechado se não for devolvido explicitamente dessa forma.

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

Curva de nível uma glifo é composta por vários segmentos de curva representados por estruturas TTPOLYCURVE . De uma curva de nível TTPOLYGONHEADER é seguido por um ou mais estruturas TTPOLYCURVE e coordenadas do ponto de dados. O membro pfxStart fornece o ponto inicial coordenado da curva de nível. A contagem de registos de curva que se seguem TTPOLYGONHEADER é fornecida pelo membro cb . O ponto de partida é relativo a origem da imagem de. A origem de um glifo é o canto inferior esquerdo do carácter no plano base o carácter.

Cada registo de curva TTPOLYCURVE (um segmento de curva) consiste em B-spline quadrático pontos de controlo ou pontos de polilinha simples. A contagem de pontos é fornecida pelo membro cpfx TTPOLYCURVE . O tipo de curva de curva polinomial ou de polilinha é fornecido pelo membro wType . A matriz de pontos de coordenadas siga imediatamente a estrutura. O ponto de partida da curva é fornecido pelo membro apfx .

Uma vez que um glifo pode conter mais do que uma curva de nível, a memória intermédia contém um ou mais grupos de TTPOLYGONHEADER estruturas e dados. Curvas de nível subsequentes são compactadas imediatamente a seguir anterior na memória intermédia nativa. Curva de nível seguinte começa um TTPOLYGONHEADER no byte seguinte para além do último ponto de curva de registo anterior.

Este código de exemplo desenha o destaque de um glifo TrueType por analisar a memória intermédia GGO_NATIVE para criar uma lista de Bezier segmentos de linha que constituem cada curva de nível. A lista de Bezier segmentos de linha, em seguida, é desenhada utilizando PolyBezier . O código que analisa a memória intermédia é na função DrawT2Outline .

O primeiro passo para criar uma lista das linhas de Bezier é para determinar o tamanho da memória intermédia para a lista. Quatro pontos definem Bezier linhas. A função PolyBezier interpreta uma matriz de pontos como sendo uma linha contínua dos segmentos de Bezier onde o início da linha seguinte é coincident com o ponto final da linha anterior. Deste modo, apenas três pontos são necessários para descrever um segmento de linha Bezier adicional.

A memória intermédia GGO_NATIVE pode conter uma curva de polilinha ou uma curva de B-spline quadrático. Dois pontos de definem um segmento de linha enquanto três pontos definem B-spline. Uma vez que cada tipo de ser desenhado com uma linha de Bezier utilizando PolyBezier, pior caso prático ocorre quando expande uma curva de nível é totalmente composta por segmentos de linha para vários Beziers.

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

O código assume que a memória intermédia nativa inteira consistirá POINTFX estruturas, para que possa determinar o maior número possível de pontos representado pela memória intermédia. O número real é ligeiramente mais pequeno porque existe estrutura sobrecarga na descrição curvas de nível e curvas. Tamanho da memória intermédia para a lista de Bezier é dado pelo número máximo possível de pontos multiplicado pelo tamanho de uma estrutura de ponto de dados e multiplicado por três. Três é o número de pontos adicionais necessários para cada segmento de linha adicionais ou segmento de curva polinomial quadrático quando convertido para um Bezier.

Depois da memória intermédia Bezier foi atribuída o código analisa a memória intermédia nativa, iniciando com a primeira TTPOLYGONHEADER . Durante uma iteração do ciclo o código processa uma curva de nível e desenha-lo. Um segundo ciclo interno analisa cada tipo de curva.

No caso de uma curva de polilinha (TT_PRIM_LINE), cada segmento de linha da polilinha é convertido e anexado à lista de Beziers com a função AppendPolyLineToBezier . Se a curva um B-spline quadrático (TT_PRIM_QSPLINE), a função AppendQuadBSplineToBezier converte e acrescenta a B-spline quadrático. Cada uma destas funções utiliza um ciclo para analisar fora cada segmento de curva para os pontos de controlo independente. Em seguida, a linha ou curva polinomial é convertido para um Bezier e anexada à lista de Beziers.

Uma linha de Bezier pode desenhar uma linha recta facilmente. O início e finais vectores aponte simplesmente no fim do segmento de linha oposto. Isto é feito na função MakeBezierFromLine .

Para converter uma curva polinomial quadrático uma curva de Bezier cúbica requer expressar pontos de controlo o metros em termos de pontos de controlo de quadratic. A equação para converter os pontos de controlo encontra-se na função MakeBezierFromQBSpline .

Antes da curva de nível de desenho, o código garante que o caminho é fechado chamando a função CloseContour . A função é chamada apenas se o início e pontos finais da sequência de Beziers não forem coincident. Em seguida, chama a função PolyBezier .

Depois da curva de nível é retirada, TTPOLYGONHEADER o contorno seguinte encontra-se por avançar o ponteiro lpHeader para além do fim dos registos numa curva de nível actual. Se isto resulta num valor ponteiro para além do fim da memória intermédia nativo, o código tem processados todas as curvas de nível 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 do 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

Artigo: 243285 - Última revisão: 12 de fevereiro de 2007 - Revisão: 1.5
A informação contida neste artigo aplica-se a:
  • Microsoft Win32 Application Programming Interface
  • Microsoft Windows XP Professional Edition
  • 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 revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes 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