一直以来,我都是用点阵写字,现在,尝试直接使用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 |