最近接觸GDI編程比較多,就把常見的技巧和注意點整理成一個系列吧,希望對大家有幫助。
1.TextOut的基本使用
TextOut的屬于比較老的文本輸出函數,但是簡單的文本輸出和格式控制使用它非常方便,廢話不多說,基本用法如下:
[cpp] view plaincopy print?
void ?DrawArea1(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????CRect?rc(ptLeftTop,?ptRightBottom);?? ????CBrushHandle?brush;?? ????brush.CreateSolidBrush(RGB(255,0,0));?? ????FillRect(mydc,?rc,?brush);?? ????brush.DeleteObject();?? ?? ????mydc.SetBkMode(OPAQUE);?? ????mydc.TextOut(10+ptLeftTop.x,?10+ptLeftTop.y,?L"文字填充背景?非透明" );?? ?? ????mydc.SetBkMode(TRANSPARENT);?? ????mydc.TextOut(10+ptLeftTop.x,?30+ptLeftTop.y,?L"文字填充背景?透明" );?? ?? ????mydc.SetBkMode(OPAQUE);?? ????mydc.SetBkColor(RGB(0,255,0));?? ????mydc.TextOut(10+ptLeftTop.x,?50+ptLeftTop.y,?L"文字填充背景?非透明?設置背景填充色" );?? ?? ????mydc.SetBkMode(OPAQUE);?? ????mydc.SetBkColor(RGB(0,0,255));?? ????mydc.SetTextColor(RGB(0,255,0));?? ????mydc.TextOut(10+ptLeftTop.x,?70+ptLeftTop.y,?L"文字填充背景?非透明?設置背景填充色和文字顏色" );?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea1(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();CRect rc(ptLeftTop, ptRightBottom);CBrushHandle brush;brush.CreateSolidBrush(RGB(255,0,0));FillRect(mydc, rc, brush);brush.DeleteObject();mydc.SetBkMode(OPAQUE);mydc.TextOut(10+ptLeftTop.x, 10+ptLeftTop.y, L"文字填充背景 非透明");mydc.SetBkMode(TRANSPARENT);mydc.TextOut(10+ptLeftTop.x, 30+ptLeftTop.y, L"文字填充背景 透明");mydc.SetBkMode(OPAQUE);mydc.SetBkColor(RGB(0,255,0));mydc.TextOut(10+ptLeftTop.x, 50+ptLeftTop.y, L"文字填充背景 非透明 設置背景填充色");mydc.SetBkMode(OPAQUE);mydc.SetBkColor(RGB(0,0,255));mydc.SetTextColor(RGB(0,255,0));mydc.TextOut(10+ptLeftTop.x, 70+ptLeftTop.y, L"文字填充背景 非透明 設置背景填充色和文字顏色");mydc.RestoreDC(-1);}效果如下:
可以看到,
1.使用SetBkMode決定背景是否透明 ,這在一張背景圖上輸出文字時經常遇到
2.使用SetBkColor和SetTextColor設置文本和文本背景顏色
2.SetTextAlign控制TextOut輸出格式
如下:
[cpp] view plaincopy print?
void ?DrawArea2(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????mydc.SetTextAlign(TA_RIGHT);?? ????mydc.TextOut(150+ptLeftTop.x,?10+ptLeftTop.y,?L"TextOut?Right?Align" );?? ?? ????mydc.SetTextAlign(TA_LEFT);?? ????mydc.TextOut(150+ptLeftTop.x,?30+ptLeftTop.y,?L"TextOut?Left?Align" );?? ?? ????mydc.SetTextAlign(TA_TOP);?? ????mydc.TextOut(150+ptLeftTop.x,?70+ptLeftTop.y,?L"Top?Align" );?? ?? ????mydc.SetTextAlign(TA_BOTTOM);?? ????mydc.TextOut(150+ptLeftTop.x,?70+ptLeftTop.y,?L"Bottom?Align" );?? ?? ????mydc.SetTextAlign(TA_TOP?|TA_LEFT);?? ????mydc.TextOut(150+ptLeftTop.x,?90+ptLeftTop.y,?L"Line?1\nLine2\nLine3\tLine3" );?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea2(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();mydc.SetTextAlign(TA_RIGHT);mydc.TextOut(150+ptLeftTop.x, 10+ptLeftTop.y, L"TextOut Right Align");mydc.SetTextAlign(TA_LEFT);mydc.TextOut(150+ptLeftTop.x, 30+ptLeftTop.y, L"TextOut Left Align");mydc.SetTextAlign(TA_TOP);mydc.TextOut(150+ptLeftTop.x, 70+ptLeftTop.y, L"Top Align");mydc.SetTextAlign(TA_BOTTOM);mydc.TextOut(150+ptLeftTop.x, 70+ptLeftTop.y, L"Bottom Align");mydc.SetTextAlign(TA_TOP |TA_LEFT);mydc.TextOut(150+ptLeftTop.x, 90+ptLeftTop.y, L"Line 1\nLine2\nLine3\tLine3");//不識別居中mydc.RestoreDC(-1);}效果如下:
默認的是TA_LEFT和TA_TOP
注意:
1.TA_RIGHT意味著輸出文字是以當前的坐標向左輸出
2.TA_BOTTOM意味著當前字體的底部和當前輸出的坐標點對齊 ,所以可以看到同樣是距離頂部70,TA_TOP和TA_BOTTOM分別用當前字體的頂部和底部和當前輸出坐標點對齊,導致兩次輸出不在同一行。
3.TextOut不支持轉義字符\n、\t的使用
3.DrawText使用
DrawText是TextOut的升級版,支持更多的格式控制,如下:
[cpp] view plaincopy print?
void ?DrawArea3(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????CRect?rc(ptLeftTop,?ptRightBottom);?? ????CBrushHandle?brush;?? ????brush.CreateSolidBrush(RGB(120,120,0));?? ????FillRect(mydc,?rc,?brush);?? ????brush.DeleteObject();?? ?? ????CSize?sizeText;?? ????CRect?rcText(ptLeftTop,?ptRightBottom);?? ?? ?????? ????CString?csText?=?L"A?Single?Line1" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_SINGLELINE?|?DT_CENTER);?? ?????? ?????? ????csText?=?L"A?Single?Line2" ;?? ????mydc.GetTextExtent(csText,?csText.GetLength(),?&sizeText);?? ????rcText?=?CRect(ptLeftTop.x?+?(ptRightBottom.x-ptLeftTop.x-sizeText.cx)/2,??? ???????????????????20+ptLeftTop.y,?? ???????????????????ptLeftTop.x?+?(ptRightBottom.x-ptLeftTop.x-sizeText.cx)/2?+?sizeText.cx,?? ???????????????????20+ptLeftTop.y+?sizeText.cy);?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_SINGLELINE?|?DT_LEFT?|?DT_TOP);?? ?? ?????? ????csText?=?L"A?Single?Line3" ;?? ????CRect?rcCalc;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcCalc,?DT_CALCRECT?|?DT_SINGLELINE);?? ????rcText?=?CRect(ptLeftTop.x?+?(ptRightBottom.x-ptLeftTop.x-rcCalc.Width())/2,??? ????????????????????40+ptLeftTop.y,?? ????????????????????ptLeftTop.x?+?(ptRightBottom.x-ptLeftTop.x-rcCalc.Width())/2?+?sizeText.cx,?? ????????????????????40+ptLeftTop.y+?rcCalc.Height());?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_SINGLELINE?|?DT_LEFT?|?DT_TOP);?? ?? ?????? ????mydc.SetBkMode(TRANSPARENT);?? ?? ?????? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+60),?ptRightBottom);?? ????csText?=?L"Line?1\nLine2\nLine3\tLine3" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP?|?DT_EXPANDTABS);?? ?? ?????? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+120),?ptRightBottom);?? ????csText?=?L"1A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line\n" ?? ?????????????L"1A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP?|?DT_END_ELLIPSIS);?? ?? ?????? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+160),?ptRightBottom);?? ????csText?=?L"C:\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\n" ?? ?????????????L"\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP?|?DT_PATH_ELLIPSIS);?? ?? ?????? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+200),?ptRightBottom);?? ????csText?=?L"2A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line\n" ?? ?????????????L"2A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP?|?DT_WORD_ELLIPSIS);?? ?? ?????? ????mydc.SetBkMode(OPAQUE);?? ????mydc.SetBkColor(RGB(0,0,255));?? ????mydc.SetTextColor(RGB(0,255,0));?? ?? ?????? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+240),?ptRightBottom);?? ????csText?=?L"1A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line?to?change?line" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP);?? ?? ????rcText?=?CRect(CPoint(ptLeftTop.x,?ptLeftTop.y+260),?ptRightBottom);?? ????csText?=?L"2A?Very?Long?Long?Long?Long?Long?Long?Long?Long?Long?Long?Single?Line?to?change?line" ;?? ????mydc.DrawText(csText,?csText.GetLength(),?rcText,?DT_LEFT?|?DT_TOP?|?DT_WORDBREAK);?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea3(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();CRect rc(ptLeftTop, ptRightBottom);CBrushHandle brush;brush.CreateSolidBrush(RGB(120,120,0));FillRect(mydc, rc, brush);brush.DeleteObject();CSize sizeText;CRect rcText(ptLeftTop, ptRightBottom);//自動計算居中CString csText = L"A Single Line1";mydc.DrawText(csText, csText.GetLength(), rcText, DT_SINGLELINE | DT_CENTER/* | DT_VCENTER*/);//手動計算居中csText = L"A Single Line2";mydc.GetTextExtent(csText, csText.GetLength(), &sizeText);rcText = CRect(ptLeftTop.x + (ptRightBottom.x-ptLeftTop.x-sizeText.cx)/2, 20+ptLeftTop.y,ptLeftTop.x + (ptRightBottom.x-ptLeftTop.x-sizeText.cx)/2 + sizeText.cx,20+ptLeftTop.y+ sizeText.cy);mydc.DrawText(csText, csText.GetLength(), rcText, DT_SINGLELINE | DT_LEFT | DT_TOP);//換種方法計算居中csText = L"A Single Line3";CRect rcCalc;mydc.DrawText(csText, csText.GetLength(), rcCalc, DT_CALCRECT | DT_SINGLELINE);//也可計算多行rcText = CRect(ptLeftTop.x + (ptRightBottom.x-ptLeftTop.x-rcCalc.Width())/2, 40+ptLeftTop.y,ptLeftTop.x + (ptRightBottom.x-ptLeftTop.x-rcCalc.Width())/2 + sizeText.cx,40+ptLeftTop.y+ rcCalc.Height());mydc.DrawText(csText, csText.GetLength(), rcText, DT_SINGLELINE | DT_LEFT | DT_TOP);//消除背景mydc.SetBkMode(TRANSPARENT);//多行文字,識別\n,使用DT_EXPANDTABS擴展\trcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+60), ptRightBottom);csText = L"Line 1\nLine2\nLine3\tLine3";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP | DT_EXPANDTABS);//自動截斷-DT_END_ELLIPSIS-多行時只截斷最后一行rcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+120), ptRightBottom);csText = L"1A Very Long Long Long Long Long Long Long Long Long Long Single Line\n"L"1A Very Long Long Long Long Long Long Long Long Long Long Single Line";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP | DT_END_ELLIPSIS);//自動截斷-DT_PATH_ELLIPSIS-多行時只截斷路徑最后一行rcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+160), ptRightBottom);csText = L"C:\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\n"L"\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest\\testtesttest";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP | DT_PATH_ELLIPSIS);//自動截斷-DT_WORD_ELLIPSIS-多行時截斷每一行rcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+200), ptRightBottom);csText = L"2A Very Long Long Long Long Long Long Long Long Long Long Single Line\n"L"2A Very Long Long Long Long Long Long Long Long Long Long Single Line";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP | DT_WORD_ELLIPSIS);//設置前后景顏色mydc.SetBkMode(OPAQUE);mydc.SetBkColor(RGB(0,0,255));mydc.SetTextColor(RGB(0,255,0));//自動換行rcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+240), ptRightBottom);csText = L"1A Very Long Long Long Long Long Long Long Long Long Long Single Line to change line";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP);rcText = CRect(CPoint(ptLeftTop.x, ptLeftTop.y+260), ptRightBottom);csText = L"2A Very Long Long Long Long Long Long Long Long Long Long Single Line to change line";mydc.DrawText(csText, csText.GetLength(), rcText, DT_LEFT | DT_TOP | DT_WORDBREAK);mydc.RestoreDC(-1);}對應效果如下:
可以看到:
1.SetBkMode、SetBkColor、SetTextColor對于DrawTextOut仍然可用
2.可以使用格式控制符控制在指定區域居中顯示,也可以計算輸出文本的區域大小從而計算文本位置來達到自定義居中控制。這里使用兩種方法計算文本區域大小,前一種調用dc.GetTextExtent實際上是調用SDK的GetTextExtentPoint32函數,后一種傳入DT_CALCRECT參數先計算文本區域大小,這里推薦使用后一種。
3.DrawText默認是識別\n的,為了識別\t,傳入參數DT_EXPANDTABS即可。
4.DrawText支持自動截斷,注意以下幾個參數的不同。 DT_END_ELLIPSIS ? ? -多行時只截斷最后一行 DT_PATH_ELLIPSIS ? -多行時只截斷路徑最后一行 DT_WORD_ELLIPSIS -多行時截斷每一行
5.使用參數DT_WORDBREAK支持自動換行
4.路徑的使用
使用路徑可以完成復雜的自定義繪制,類似PS中的路徑功能,如下:
[cpp] view plaincopy print?
void ?DrawArea4(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????CFont?fontNew;?? ????fontNew.CreatePointFont(1200,?L"Tohama" );?? ????mydc.SelectFont(fontNew);?? ?? ?????? ????CRect?rcText(ptLeftTop,?ptRightBottom);?? ????mydc.BeginPath();?? ????mydc.TextOut(ptLeftTop.x,?ptLeftTop.y,?L"路徑" );?? ????mydc.EndPath();?? ????mydc.SelectClipPath(RGN_XOR);?? ?? ????mydc.DrawText(L"路徑" ,?-1,?rcText,?DT_SINGLELINE|DT_CALCRECT);?? ????int ?nStep?=?2;?? ????for ?(int ?i=rcText.top;?i<=rcText.bottom;?i+=?nStep)?? ????{?? ????????mydc.MoveTo(rcText.left,?i);?? ????????mydc.LineTo(rcText.right,?i);?? ????}?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea4(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();CFont fontNew;fontNew.CreatePointFont(1200, L"Tohama");//20pxmydc.SelectFont(fontNew);//創建和添加當前剪切路徑CRect rcText(ptLeftTop, ptRightBottom);mydc.BeginPath();mydc.TextOut(ptLeftTop.x, ptLeftTop.y, L"路徑");mydc.EndPath();mydc.SelectClipPath(RGN_XOR);mydc.DrawText(L"路徑", -1, rcText, DT_SINGLELINE|DT_CALCRECT);int nStep = 2;for (int i=rcText.top; i<=rcText.bottom; i+= nStep){mydc.MoveTo(rcText.left, i);mydc.LineTo(rcText.right, i);}mydc.RestoreDC(-1);}效果如下:
使用路徑的步驟基本是固定的,在BeginPath和EndPath之間繪制GDI元素,然后SelectClipPath選擇即可作為當前剪切區域使用。
5.區域的使用
區域特別是剪切區域在界面繪制中用的非常多,如下:
[cpp] view plaincopy print?
void ?DrawArea5(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????CRect?rcDraw(ptLeftTop,?ptRightBottom);?? ????rcDraw.DeflateRect(30,?30);?? ?? ?????? ????CRgn?rgnDraw;?? ????rgnDraw.CreateRectRgnIndirect(rcDraw);?? ????mydc.SelectClipRgn(rgnDraw,?RGN_AND);?? ?? ?????? ????CRect?rcExclude(ptLeftTop,?ptRightBottom);?? ????rcExclude.DeflateRect(60,60);?? ????mydc.ExcludeClipRect(rcExclude);?? ?? ????int ?nSteps?=?5;?? ????for ?(int ?i=ptLeftTop.y;?i<ptRightBottom.y;?i+=nSteps)?? ????{?? ????????mydc.MoveTo(ptLeftTop.x,?i);?? ????????mydc.LineTo(ptRightBottom.x,?i);?? ????}?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea5(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();CRect rcDraw(ptLeftTop, ptRightBottom);rcDraw.DeflateRect(30, 30);//選擇剪切區域CRgn rgnDraw;rgnDraw.CreateRectRgnIndirect(rcDraw);mydc.SelectClipRgn(rgnDraw, RGN_AND);//排除部分繪制區域CRect rcExclude(ptLeftTop, ptRightBottom);rcExclude.DeflateRect(60,60);mydc.ExcludeClipRect(rcExclude);int nSteps = 5;for (int i=ptLeftTop.y; i<ptRightBottom.y; i+=nSteps){mydc.MoveTo(ptLeftTop.x, i);mydc.LineTo(ptRightBottom.x, i);}mydc.RestoreDC(-1);}效果如下:
這里剪切區域SelectClipRgn和排除繪制區域ExcludeClipRect都是常用的技巧,前者保證只在指定區域繪制,后者保證指定的區域不重復繪制,都可以達到優化繪制效率的效果。
6.窗口和視口區域的調整
這里還是按照Petzold的定義,邏輯坐標系是視口(View),設備坐標系是窗口(Window),默認的映射關系是MM_TEXT,一般不變,如果需要深入了解的話,建議去看下《Windows程序設計》,這里只演示最常用的原點變換,如下:
[cpp] view plaincopy print?
void ?DrawArea6(CDCHandle?mydc,?POINT?ptLeftTop,?POINT?ptRightBottom)??{?? ????mydc.SaveDC();?? ?? ????mydc.SetViewportOrg(ptLeftTop.x+50,?ptLeftTop.y+50);?? ????mydc.TextOut(0,?0,?L"Text?1" );?? ?? ????mydc.SetViewportOrg(0,?0);?? ????mydc.SetWindowOrg(-ptLeftTop.x-50,?-ptLeftTop.y-50);?? ????mydc.TextOut(0,?20,?L"Text?2" );?? ?? ????mydc.RestoreDC(-1);?? }??
void DrawArea6(CDCHandle mydc, POINT ptLeftTop, POINT ptRightBottom){mydc.SaveDC();mydc.SetViewportOrg(ptLeftTop.x+50, ptLeftTop.y+50);mydc.TextOut(0, 0, L"Text 1");mydc.SetViewportOrg(0, 0);mydc.SetWindowOrg(-ptLeftTop.x-50, -ptLeftTop.y-50);mydc.TextOut(0, 20, L"Text 2");mydc.RestoreDC(-1);}顯示如下:
一般建議使用SetViewportOrg,這邏輯上比較直觀。
本文完整演示代碼下載鏈接
原創,轉載請注明來自http://blog.csdn.net/wenzhou1219
總結
以上是生活随笔 為你收集整理的GDI编程注意点-1 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。