一般情況下,程序開發人員都希望自己的程序美觀大方,為了達到這一個要求,想盡辦法美化自己的程序,另一方面,文字是程序界面上不可或缺的一種傳遞信息的途徑,如果在界面上適當的地方添加并顯示各種特殊效果的文字,將不失為一個好的選擇,可以達到事半功倍的效果,本例針對Visual C++中編程實現文字的特殊顯示效果這一問題,通過編寫一個簡單的小程序,介紹如何實現這一目標。讀者朋友可以稍加改動,將該方法應用到自己的程序中去。
一、實現方法
在講述文字的各種處理效果之前,我們首先來介紹Visual C++中的MFC提供的用來創建字體的CFont類。對于Windows程序開發人員來說,可以創建的字體有兩種:庫存字體(該字體直接使用字體的索引選入設備上下文就可以了,例如:CDC::CreateStockObject(int nIndex ))和自定義字體。
CFONT類從CgdiObject派生而來,該類的對象可以通過Createfont()、CreateFontIndirect()等函數創建自定義的字體。兩個函數原型分別如下:
CFont::CreateFontIndirect(const LOGFONT* lpLogFont ); CFont::CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename )。 |
其中第二個函數的參數和第一個函數中的LOGFONT類型的參數的的分量有一一對應的關系。LOGFONT結構主要定義了字體的屬性,其定義可通過MSDN幫助查到:
typedef struct tagLOGFONT { LONG lfHeight; //字符字體高度 ; LONG lfWidth; //字符平均寬度 ; LONG lfEscapement; //文本行逆時針旋轉角度; LONG lfOrientation; //字體角度 ; LONG lfWeight; //字體粗細程度 ; BYTE lfItalic; //傾斜 ; BYTE lfUnderline; //下劃線 ; BYTE lfStrikeOut; //刪除線 ; …… } LOGFONT; |
在對LOGFONT結構進行設置時,lfOutPrecision、lfClipPrecision、lfQuality 以及lfPitchAndFamily等幾個成員變量一般可如下設置而無需改動:
LOGFONT lf; //lf定義字體屬性 lf.lfOutPrecision= OUT_STROKE_PRECIS; lf.lfClipPrecision= CLIP_STROKE_PRECIS; lf.lfQuality = DRAFT_QUALITY; lf.lfPitchAndFamily= VARIABLE_ PITCH|FF_MODERN; |
對于其他的成員變量則要根據實際需求進行具體的設定。例如,對于沒有下劃線和刪除線并且沒有傾斜處理的粗體幼圓漢字,可以采取如下設置:
lf.lfHeight = 50; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = FW_HEAVY; lf.lfItalic = FALSE; lf.lfUnderline = FALSE; lf.lfStrikeOut = FALSE; lf.lfCharSet = GB2312_CHARSET; strcpy(lf.lfFaceName,"幼圓"); |
(一)顯示傾斜的文字
上文中的圖一顯示了通過顯示一系列的傾斜字符串來實現環繞顯示的效果,下面來詳細說明如何實現這個效果。
實現上述的效果其實很容易,只要使用CFONT類和LOGFONT結構來靈活的創建字體,設置字體的屬性,就可以實現傾斜文字的效果。LOGFONT結構中包含了所要創建的字體中的全部信息,其中的ifEscapement 成員制定了所創建的字體與水平方向所傾斜的角度,需要讀者注意的是該成員變量角度的單位是十分之一度而不是度,例如,如果ifEscapement定義為450,它表示字體的傾斜角度為45度。為了保證所有的字體按照一個方向旋轉,一定要設置ifEscapenent的CLIP_LH_ANGLES位,否則字體有可能向反方向旋轉。
如同使用其它GDI(圖形用戶界面接口)對象一樣,在使用你定義的字體以前,必須要將創建的字體選入DC中(設備上下文)。
(二)實現3D文字
計算機屏幕是平面二維的,我們之所以能欣賞到真如實物般的三維圖像,是因為顯示在計算機屏幕上時色彩灰度的不同而使人眼產生視覺上的錯覺,而將二維的計算機屏幕感知為三維圖像。基于色彩學的有關知識,三維物體邊緣的凸出部分一般顯高亮度色,而凹下去的部分由于受光線的遮擋而顯暗色。這一認識被廣泛應用于網頁或其他應用中對按鈕、3D線條的繪制。對于本文所要繪制的3D文字同樣也適用,即在原始位置顯示高亮度顏色,而在左下或右上等位置用低亮度顏色勾勒出其輪廓,這樣在視覺上便會產生3D文字的效果。具體實現時,可用完全一樣的字體在不同的位置分別繪制兩個不同顏色的2D文字,只要使兩個文字的坐標合適,就完全可以在視覺上產生出不同效果的3D文字。
具體實現的思想是通過CDC::SetTextColor()分別設置文字的顏色為高亮(3DHILIGHT)和陰影(3DSHADOW)的狀態下顯示文字;同時注意在兩次顯示文字時要錯開幾個像素,這樣才能達到預期的效果。實現的效果上文的圖二所示。
(三)文字的漸變效果
為了實現文字的漸變效果,需要設置一個定時器(使用SetTimer()函數),在定時器響應函數處理過程中,通過調用CDC:SetTextColor()函數不斷改變設備上下文中文本的顯示顏色,從而實現文字的漸變效果。文本的顏色是通過RGB(紅、綠、蘭)三種基本顏色的混合所形成的最終結果,RGB三基色的變化范圍都是(0,255),R=G=B時,顏色的效果是灰色的,所謂灰色,就是在純白和純黑之間的一種過渡色,當R=G=B=0時,顏色為黑色,當R=G=B=255時,顏色為純白色,可以定義一個修正變量,不斷的對該三基色進行遞增或遞減,從而實現文字的漸變顯示。這里僅僅給出實現的思路,讀者自己可以很容易的實現代碼。
(四)其它
另外,還可以通過設置路徑對象來對普通的文字進行輪廓勾勒,使之具備特殊的效果。路徑是Win32中新增的一個GDI對象,下面先從概念上談起。
1)路徑的概念
在Windows 95/NT 這樣的Win32操作系統中,除了已有的位圖,畫筆,畫刷,字體,調色板和區域之外,還增加了一個新的GDI對象:路徑。路徑是可以被填充,畫出輪廓或同時被畫出輪廓并填充的一個或多個圖形。路徑的引入,大大地豐富了Windows的圖形功能,使得應用程序可以方便地建立復雜區域,繪制和填充不規則圖形。這里說的不規則圖形是指由直線和貝塞爾曲線組成的圖形(相對于矩形,多邊形,橢圓等規則圖形)。
2)路徑的使用
與其它原有的GDI對象不同的是,MFC類庫沒有專門用一個C++類來封裝路徑對象(或許在以后的版本中會得到支持)。有關路徑的定義和使用等各種操作都必須通過調用API函數(或CDC類中對應的成員函數)來實現。路徑的使用過程大致如下:
(1)調用BeginPath()函數開始路徑定義;
(2)調用GDI繪圖函數來定義路徑;在Win32中,可以用于定義路徑的GDI繪圖函數包括:AngleArc()、 Arc()、 ArcToChord()、Ellipse ()、LineTo()、TextOut()等函數;
(3)調用EndPath()函數結束路徑定義;
完成路徑定義后,所定義的路徑即被同時選進設備描述表,設備描述表中原有的路徑對象在調用BeginPath()函數開始路徑定義時即被廢棄。
(4)使用路徑對象。
完成路徑定義工作之后,應用程序便可以利用有關GDI函數來使用路徑,這些函數包括繪制路徑輪廓StrokePath(),填充路徑FillPath(),繪制輪廓并填充StrokeAndFillPath(),把路徑轉換成區域PathToRegion(),把路徑直線化FlattenPath(),提取路徑數據GetPath(),加寬路徑WidenPath()和設置裁剪路徑SelectClipPath()等。這些函數的具體使用方法可參閱有關的SDK文檔。
下面的代碼演示了如何實現字體的空心效果
////////////////////////////////////// 應用程序主窗口的重繪函數 void CMyWnd::OnPaint() { // 獲得窗口的客戶區設備上下文句柄 CPaintDC dc(this); // 更改當前字體 LOGFONT lf; dc.GetCurrentFont()->GetLogFont(&lf); CFont font; CFont *pOldFont; // 保存設備上下文最初使用的字體對象 lf.lfCharSet=134; lf.lfHeight=-150; lf.lfHeight=-150; lf.lfWidth=0; strcpy(lf.lfFaceName, "隸書"); font.CreateFontIndirect(&lf); pOldFont=dc.SelectObject(&font); dc.SetBkMode(TRANSPARENT); // 更改當前畫筆 CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); CPen *pOldPen; pOldPen=dc.SelectObject(&pen); // 開始一個路徑 dc.BeginPath(); dc.TextOut(10, 10, "空心字"); dc.EndPath(); // 繪制路徑 dc.StrokePath(); //可以用dc.StrokeAndFillPath()函數來代替,不過該函數會使用當前刷子填充路徑的內部。 dc.SelectObject(pOldFont); dc.SelectObject(pOldPen); } |
二、編程步驟
1、啟動Visual C++6.0,生成一個單文檔視圖結構的程序(對于旋轉字體的程序:項目名為ViewFont;對于3D效果的程序:項目名為 3DText);
2、重載應用程序的OnDraw(CDC* pDC)函數;
3、添加代碼,編譯運行程序;
三、程序代碼
////////////////////////////////////旋轉字體 void CViewFontView::OnDraw(CDC* pDC) { CViewFontDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here CString str(_T("Visua C++6.0編程實例"));//定義要顯示的字符串; CRect rect; GetClientRect(&rect); pDC->SetBkMode(TRANSPARENT);//設置背景為透明效果; pDC->SetTextColor(RGB(100,100,255));//設置顯示的文本顏色; CFont font; LOGFONT stFont;//定義字體結構; memset(&stFont,0,sizeof(stFont));//設置字體結構的屬性; stFont.lfHeight=30; stFont.lfWeight=FW_NORMAL; stFont.lfClipPrecision=CLIP_LH_ANGLES; strcpy(stFont.lfFaceName,"Arial"); //下面的代碼以視圖中心為圓點,在半徑100的圓周上每隔30度顯示字符串; for(double i=0;i<3600;i+=300) { stFont.lfEscapement=i;//設置字體的傾斜角度; font.CreateFontIndirect(&stFont);//根據字體結構創建字體對象; CFont *oldFont; oldFont=pDC->SelectObject(&font);//將創建的字體選入設備上下文; if(i<900) pDC->TextOut(rect.left+rect.Width )/2+100*cos(i/1800*3.14),rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str); if(i>=900&&i<1800) pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14), rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str); if(i>=1800&&i<2700) pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14), rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str); if(i>=2700&&i<=3600) pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14), rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str); pDC->SelectObject(oldFont);//恢復設備上下文的字體; font.DeleteObject ();//刪除創建的字體; } } //////////////////////////////////////////顯示3D效果的程序 void CMy3DTextView::OnDraw(CDC* pDC) |
四、小結
讀者只要掌握了上面的內容,靈活運用Cfont和LOGFONT結構創建不同的字體,再結合顯示顏色、位置、時間的設置,相信一定還可以實現更多的動態效果來。