国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

MFC-GDI繪制

發布時間:2011-3-29 20:28    發布者:1770309616
關鍵詞: MFC-GDI繪制
最好的GDI入門教程是《Window程序設計》的第五章,如果你沒有任何GDI基礎,最好精讀這一章,因為本文并不會介紹GDI的方方面面,事實上這也是不可能完成的任務。我只將以前學習GDI時遇到的幾個難點拿出來講講。
GDI對象的用法
GDI對象就是畫筆,畫刷,字體這類資源,以我的經驗,GDI對象的管理是一件麻煩的事,如果操作不當,很容易引起GDI泄漏。
Delphi用TPen,TBrush,TFont三個類來表示畫筆畫刷和字體,用Canvas表示設備描述表。以TPen為例,一個TPen并不表示一個GDI對象,真正的GDI對象被保存在“池”里面,TPen只是根據自己的屬性到“池”里面尋找對應的GDI對象,如果找不到將會創建一個新的,而這些GDI對象都有一個引用計數,代表它被多少個TPen對象引用,只要引用計數為0,這個GDI馬上被DeleteObject并從“池”中移走。
各位可以想象Delphi對WinAPI封裝到什么程度,這就是為什么你即使不會Windows編程也可以很快上手Delphi。這樣封裝的好處是非常明顯的,你不必理會什么時候需要DeleteObject,你只要向Pen設置你喜歡的顏色,寬度,風格,然后調用Canvas的函數來繪制就行了。但它的壞處也非常明顯,你設的樣式越多,表明“池”中的GDI對象也會越多,Delphi程序的GDI普遍偏高就緣于此,更可怕的是我們對這種偏高的GDI數量有些束手無策。
MFC則完全不一樣,它僅僅利用棧對象的自動消毀來簡化GDI對象的管理,其余的差不多就是一層簡單的包裝。所以你必須了解GDI的用法,有下面三條規則,這是從Windows程序設計引過來的:
1.         最后必須刪除自己創建的所有GDI對象。
2.         當GDI對象正在一個有效的DC中使用時,不要刪除它。
3.         不要刪除現有對象(StockObject)。
我們用簡單的例子來說明這三條規則,請看下例:
HPEN hPen = ::CreatePen(PS_SOLID, 2, RGB(0xFF, 00, 00));
::SelectObject(hDC, hPen);
::Rectangle(hDC, 10, 10, 100, 100);
參照上面三條規則,明顯違反了第一條規則,創建一個畫筆之后,最后沒有調用DeleteObject消毀hPen,這會發生什么事情呢,GDI對象不斷的泄漏,在XP系統下GDI達到10000時程序就死掉了。
把代碼修改如下:
HPEN hPen = ::CreatePen(PS_SOLID, 2, RGB(0xFF, 00, 00));
::SelectObject(hDC, hPen);
::Rectangle(hDC, 10, 10, 100, 100);
:eleteObject(hPen);
這樣是不是就正確了呢,如果hDC在DeleteObject之后還繼續繪制的話就違反規則2了,這會導致后面的繪制失去Pen的風格。
更加安全的做法是這樣的:
HPEN hPen = ::CreatePen(PS_SOLID, 2, RGB(0xFF, 00, 00));
HPEN hOldPen = ::SelectObject(hDC, hPen);
::Rectangle(hDC, 10, 10, 100, 100);
:eleteObject(::SelectObject(hOldPen));
SelectObject會返回原有的GDI對象,將它保存起來,最后再用SelectObject恢復回來。
不過事情總不是絕對的,如果hDC在DeleteObject之后不再繪制并且將被ReleaseDC的話,其實也可以不保存hOldPen,這樣即可以簡化代碼,也可以提高繪制效率。
有些情況要設的樣式比較多,用上面保存舊GDI對象的方式有些麻煩,可以用SaveDC和RestoreDC來簡化操作,就像下面這樣:
HPEN hPen = ::CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
HBRUSH hBrush = ::CreateSolidBrush(RGB(0xFF, 0, 0));
int nDCSave = ::SaveDC(hDC);   
::SelectObject(hDC, hPen);
::SelectObject(hDC, hBrush);
::Rectangle(hDC, 10, 10, 100, 100);
::RestoreDC(hDC, nDCSave);
:eleteObject(hPen);
:eleteObject(hBrush);
SaveDC將當前的DC樣式保存起來,直到RestoreDC時恢復回來。
規則3比較容易理解,凡是用GetStockObject取出來的GDI對象都不必手動刪除。
映射方式
       映射方式是GDI里最難理解的概念,其中涉及到設備坐標,邏輯坐標,窗口,視口這些術語。
如果不使用GDI函數,我們所要面對的只是設備坐標。設備坐標可以分為三種,屏幕,窗口,客戶區,這之間的區別僅僅是原點的位置,屏幕坐標以屏幕左上角為原點,窗口坐標以窗口左上角為原點,客戶區坐標以客戶區左上角為原點,此外,設備坐標的XY軸都向右向下增長,并且單位是像素。究竟要使用哪種坐標由所調用的函數或所處理的消息決定。比如,當我們調用GetCursorPos時,取得的點以屏幕坐標為準;而處理WM_MOUSEMOVE時,參數所指的點以窗口所在的客戶區坐標為準。需要窗口坐標的情況不是很多,常用于GetWindowDC邏輯坐標向設備坐標的映射。
如果使用GDI函數就必須理解邏輯坐標,因為GDI函數的位置參數都以邏輯坐標為準。比如這個函數:
BOOL Rectangle(
  HDC hdc,         // handle to DC
  int nLeftRect,   // x-coord of upper-left corner of rectangle
  int nTopRect,    // y-coord of upper-left corner of rectangle
  int nRightRect,  // x-coord of lower-right corner of rectangle
  int nBottomRect  // y-coord of lower-right corner of rectangle
);
后面的四個參數都是邏輯坐標。假想一個虛擬的平面,這個平面使用邏輯坐標,Rectangle先在虛擬平面上畫出矩形,然后利用“某種方式”將矩形從虛擬平面映射到屏幕上的窗口來,這個過程就是映射:

映射到何種設備坐標取決于DC是怎么取到的,簡單來說如果是通過GetDC或BeginPaint則為客戶區坐標,如果通過GetWindowDC則為窗口坐標。
問題的復雜性在于邏輯坐標并不像設備坐標那樣固定不變,它的單位是可變的,XY軸增長的方向也是可變的,甚至于邏輯坐標的原點也不一定映射為設備坐標的原點。
       邏輯坐標的XY軸的單位與增長方向由SetMapMode決定;邏輯坐標的原點映射到設備坐標什么地方由SetWindowOrgEx或SetViewportOrgEx決定。
       我們分別討論這兩個問題,為了簡化復雜性,當討論一個問題時,都假定另一個問題為默認情況,比如討論XY軸的單位和增長方向時,假定邏輯坐標的原點就映射為設備坐標的原點,反過來也一樣。
       MapMode的默認值是MM_TEXT,這和設備坐標是一樣的,即單位是像素,XY軸向右向下增長,如果hDC是客戶區的設備描述表,我們調用Rectangle(hDC, 10, 10, 100, 100),矩形就正確無誤地在客戶區的(10, 10, 100,100)處顯示出來。
       現在用SetMapMode將映射方式設為MM_LOENGLISH,再調用Rectangle:
::SetMapMode(hDC, MM_LOENGLISH);
::Rectangle(hDC, 0, 0, 100, 100);
這時客戶區并沒有出現矩形,因為MM_LOENGLISH的XY軸是向右向上增長的,且邏輯單位是0.01in,映射方式就像下圖所示:


矩形被映射到客戶區上邊了,要想屏幕顯示矩形,須作如下修改:
::SetMapMode(hDC, MM_LOENGLISH);
::Rectangle(hDC, 0, 0, 100, -100);
但最終結果仍然不是一個100×100像素的矩形,因為MM_LOENGLISH的邏輯單位是0.01in,相當于0.96像素,最終結果是96×96像素的矩形,正確的代碼是這樣的:
::SetMapMode(hDC, MM_LOENGLISH);
int npx_X = ::GetDeviceCaps(hDC, LOGPIXELSX);
int npx_Y = ::GetDeviceCaps(hDC, LOGPIXELSY);
::Rectangle(hDC, 0, 0, 100/(npx_X*0.01), -100/(npx_Y*0.01));
這個轉換僅對于MM_LOENGLISH有效,其他的映射方式要作不同的轉換,為了簡化這種轉換,Windows提供了DPtoLP和LPtoDP,用于在邏輯坐標和設備坐標之間進行點的轉換。現在,代碼變成這樣:
::SetMapMode(hDC, MM_LOENGLISH);
RECT rcBound;
::SetRect(&rcBound, 0, 0, 100, 100);
toLP(hDC, (LPPOINT)&rcBound, 2);
::Rectangle(hDC, rcBound.left, rcBound.top, rcBound.right, rcBound.bottom);
注意rcBound指定的是設備坐標,但Rectangle需要邏輯坐標,所以用DPtoLP轉換一下。
DPToLP是非常有用的函數,MiniDraw在繪圖時用到這個函數,用于處理滾動條出現的情況。
絕大多數情況下,我們都用MM_TEXT作為映射方式,這種方式下邏輯坐標單位與方向都與設備坐標一樣,理解起來比較容易。

第二個問題是邏輯坐標的原點映射到設備坐標什么地方?默認都是原點映射到原點,即邏輯坐標的(0,0)對應設備坐標的(0,0)。
什么情況下需要改變這種映射方式,我想最常見的是客戶區出現滾動條的時候。比如一個圖形的位置是(100, 100),我們將滾動條往下拉一點,此時圖形的實際位置可能變成(0,0),但我們仍然會認為圖形就在(100,100)處,只是整個客戶區往上滾動了一點。
SetWindowOrgEx和SetViewportOrgEx指定了原點的映射方式。我用這樣的方式來理解原點的映射。比如:
::SetWindowOrgEx(hDC, 100, 100, NULL);
理解為:邏輯坐標的(100,100)映射為設備坐標的(0,0),其他的點以此為依據映射,用下圖表示:


       又比如:
       ::SetViewportOrgEx(hDC, 100, 100, NULL);
理解為邏輯坐標的(0,0)映射為設備坐標的(100,100),其他的點以此為依據映射,用下圖表示:

       又比如:
::SetWindowOrgEx(hDC, 100, 100, NULL);
       ::SetViewportOrgEx(hDC, 200, 200, NULL);
       理解為:邏輯坐標的(100,100)映射為設備坐標的(200,200),其他的點以此為依據映射。
       這樣的描述是否有助于對窗口和視口的理解呢?更加詳細的介紹請看Windows程序設計的第五章。
       映射方式僅僅是某個DC的屬性,如果這個DC釋放,則映射方式也沒有什么用了,即使存在兩個DC,他們都對應同一個窗口,映射方式仍然是獨立的。

       這篇文章并沒有分析MFC如何封裝GDI,相比之下,我覺得理解GDI的一些重點知識顯得更加重要,理解了這些知識,再看MFC的CDC,那不過就是將hDC和GDI函數組織起來的數據結構。
本文地址:http://www.qingdxww.cn/thread-60399-1-1.html     【打印本頁】

本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
您需要登錄后才可以發表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區
  • 想要避免發生災難,就用MPLAB SiC電源仿真器!
  • 更佳設計的解決方案——Microchip模擬開發生態系統
  • 深度體驗Microchip自動輔助駕駛應用方案——2025巡展開啟報名!
  • 我們是Microchip
  • 貿澤電子(Mouser)專區
關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 四虎网址最新 | 在线麻豆国产传媒60在线观看 | 久久久久综合给合狠狠狠 | 日日免费视频 | 交欧美 | 天堂在线亚洲 | 99re8在线这里只有精品 | 成人精品h高辣长篇小说 | 91精品综合国产在线观看 | 啪啪午夜视频 | 四虎影视在线永久免费看黄 | 日本a黄 | 免费日本视频 | 日韩一区二区免费看 | 日产精品卡2卡三卡乱码网站 | 欧美日韩视频在线播放 | 日韩视频在线一区 | 老师的小兔子好大好软水好多视频 | 欧美在线精品永久免费播放 | 国内精品久久久久久麻豆 | 国产网红在线 | 久久久综合结合狠狠狠97色 | 91视频国内| 四虎永久在线精品国产免费 | 亚洲欧洲日产v特级毛片 | 亚洲青草视频 | 亚洲欧美另类在线观看 | 欧美三级不卡在线观看视频 | 欧美成人性色大片在线观看 | 日韩欧美综合视频 | 久久久全国免费视频 | 色妞妞www精品视频 色妞www精品一级视频 | 亚洲人成网站色7799在线播放 | 又粗又硬女人免费视频 | 日本精品一区二区三区四区 | 拍拍叫痛的无挡视频免费 | 视频二区 国产精品 职场同事 | 中文字幕成人免费视频 | 日韩城人免费 | 亚洲激情在线播放 | 在线视频精品免费 |