一、VC++中的DC環境及GUI有關的各種對象
在Windows中有各種圖形用戶界面GUI (Graphics User Interface)對象,當我們在進行繪圖時就需要利用這些對象。而各種對象都 擁有各種屬性,下面首先介紹幾種GUI對象和擁有的屬性。
(一)、GUI有關的各種對象
在Windows中有各種圖形用戶界面GUI(Graphics User Interface)對象,當我們在進 行繪圖時就需要利用這些對象。而各種對象都擁有各種屬性,下面首先介紹幾種GUI對象和擁 有的屬性。
字體對象CFont
字體對象CFont用於輸出文字時選用不同風格和大小的字體。可選擇的風格包括:是 否為斜體,是否為粗體,字體名稱,是否有下劃線等。
刷子CBrush對象
刷子CBrush對象決定填充區域時所采用的顏色或模板。對於一個固定色的刷子來講它 的屬性為顏色,是否采用網格和網格的類型如水平的,垂直的,交叉的等。也可以利用8*8的 位圖來創建一個自定義模板的刷子,在使用這種刷子填充時系統會利用位圖逐步填充區域。
畫筆CPen
畫筆CPen對象在畫點和畫線時有用。它的屬性包括顏色,寬度,線的風格,如虛線, 實線,點劃線等。
位圖CBitmap對象
位圖CBitmap對象可以包含一幅圖像,可以保存在資源中。
CPalette調色板
CPalette調色板是一種顏色映射接口,它允許應用程序在不影響其他應用程序的前提 下,可以充分利用輸出設備的顏色描繪能力。
此外系統中還擁有一些庫存GUI對象,你可以利用CDC::SelectStockObject (SelectStockObject( int nIndex )選入這些對象,它們包括一些固定顏色的刷子,畫筆和 一些基本字體。 如:
BLACK_BRUSH 黑色刷子
NULL_BRUSH 空刷子
WHITE_PEN 白色畫筆
DEVICE_DEFAULT_FONT 默認字體
在Windows中使用GUI對象必須遵守一定的規則。首先需要創建一個合法的對象,不同的對 象創建方法不同。然後需要將該GUI對象選入DC中,同時保存DC中原來的GUI對象。如果選入 一個非法的對象將會引起異常。在使用完後應該恢復原來的對象,這一點特別重要,如果保 存一個臨時對象在DC中,而在臨時對象被銷毀後可能引起異常。有一點必須注意,每一個對 象在重新創建前必須銷毀,下面的代碼演示了這一種安全的使用方法:
OnDraw (CDC* pDC)
{
a) CPen pen1,pen2;
b) pen1.CreatePen (PS_SOLID,2,RGB(128,128,128));//創建畫筆對象一
c) pen2.CreatePen (PS_SOLID,2,RGB(128,128,0));//創建畫筆對象二
d) CPen* pOldPen=(CPen*) pDC->SelectObject(&pen1);//選擇對象進DC
e) drawWithPen1...
f) (CPen*)pDC->SelectObject(&pen2);//選擇對象進DC
g) drawWithPen2...
h) pen1.DeleteObject();//再次創建前先銷毀
i) pen1.CreatePen(PS_SOLID,2,RGB(0,0,0));//再次創建對象
j) (CPen*)pDC- >SelectObject(&pen1);//選擇對象進DC
k) drawWithPen1...
l) pDC->SelectObject(pOldPen);//恢復
}
OnDraw(CDC* pDC) 函數是VC中最常見的圖形輸出刷新函數,參數pDC 為CDC類的一個指針,我們通過它進行畫圖 操作。
代碼a行定義CPen 類的兩個畫筆對象pen1,pen2;分別在行b,c 調用CPen 類成 員函數CreatePen 創建兩個實心畫筆, 其顏色RGB值分別為RGB(128,128,128), RGB (128,128,0)。行d 將新創建的畫筆pen1選入當前設備上下文DC環境並將舊畫筆保存在 pOldPen裡,這樣在e行輸出的圖形或文本線條將以pen1的屬性填充。f, g 行選入畫筆二並輸 出。i,j 行銷毀畫筆一並且創建RGB(0,0,0)色的畫筆,k行輸出。最後一行l行將舊畫筆選入 當前DC環境,輸出完畢。字體對象,刷子對象及位圖對象的使用方法同上,具體使用將在下 面的實例中描述。
在繪圖時都需要一個DC對象,DC(Device Context設備環境)對象 是一個抽象的作圖環境,可能是對應屏幕,也可能是對應打印機或其它。這個環境是設備無 關的,所以在對不同的設備輸出時只需要使用不同的設備環境就行了,而作圖方式可以完全 不變。
(二)、DC環境下輸出文本
在MFC裡有一個設備環境類CDC封裝了有關對 物理設備的輸出。CDC是設備環境類的基類直接由CObject派生。是圖形設備接口的關鍵元素 ,它代表了物理設備。每一個C++設備環境對象都有相對應Windows設備環境,並通過一個32 位類型的HDC句柄來標識。CDC類的虛擬性使我們可以很容易的做到編寫同時適用於多種設備 的代碼。例如OnDraw函數的pDC->TextOut(0,0,"Hello");既可以適用於顯示器 、還可以適用於打印預覽和打印,只需要在CView::OnDraw函數的pDC參數指向不同的對象類 。
CClientDC和CWindowDC是顯示設備環境類,都是由CDC派生而來,區別在於 CClientDC是窗口的客戶區不包括邊框、標題欄和菜單欄,(0,0)指客戶區域的左上角。 CWindowDC的(0,0)指整個屏幕的左上角,這意味著我們可以在顯示器的任意地方繪圖,包 括窗口邊框、標題欄和菜單欄等等。CWindowDC一般應用在框架窗口,而不是視圖窗口。
CDC對象被創建後一定要在合適的時候將它刪除掉,如果忘記了刪除設備環境對象則 會造成內存丟失。下面程序段實現在DC環境下輸出文本。
long CImg::OutImgFromText(LPCTSTR vFileName,
LPCTSTR lpText,
LPCTSTR lpBgImg,
long lCSet,
LPCTSTR lpFont,
long lWidth,
long lHeight,
long lLeft,
long lTop,
long llfHeight,
long lWeight,
long l3D)
{
i. m_nWidth = lWidth;
ii. m_nHeight = lHeight;
iii. if((m_nWidth % 8) != 0)
1. m_nWidth = ((int)(m_nWidth/8) + 1) * 8;
iv. if(m_nWidth < 3 * lLeft)
1. m_nWidth = 3 * lLeft;
v. if(m_nHeight < 3 * lTop)
1. m_nHeight = 3 * lTop;
vi. int nFHeight = llfHeight;
vii. if(0 == nFHeight)
1. nFHeight = 1;
viii. int nRealClientWidth = (m_nWidth - 2 * lLeft);
ix. HDC hDC;
x. hDC = CreateCompatibleDC(NULL);
xi. LOGFONT lf;
xii. memset (&lf,0,sizeof(lf));
xiii. lf.lfCharSet = GB2312_CHARSET;
xiv. lf.lfHeight = nFHeight;
xv. lstrcpy(lf.lfFaceName, lpFont);
xvi. lf.lfPitchAndFamily = 8;
xvii. lf.lfWeight = lWeight;
xviii. HFONT hFont = CreateFontIndirect(&lf);
1. HFONT hOldFont = (HFONT) SelectObject(hDC, hFont); //選入字體
xix. CComBSTR bstrText(lpText);
xx. RECT rectClient = {lLeft, lTop, m_nWidth - lLeft, m_nHeight - lTop};
xxi. ::DrawText(
1. hDC,
2. bstrText.m_str,
3. bstrText.Length (),
4. &rectClient,
5. DT_WORDBREAK|DT_LEFT|DT_CALCRECT
6. ); //計算輸出距形
xxii. int nRealHeight = rectClient.bottom + lTop;
xxiii. if(m_nHeight < nRealHeight)
1. m_nHeight = nRealHeight;
xxiv. else
1. rectClient.bottom = m_nHeight - lTop;
xxv. HBITMAP hBitmap;
xxvi. hBitmap = CreateDiscardableBitmap(hDC, m_nWidth, m_nHeight);
xxvii. SelectObject(hDC, hBitmap);
xxviii. //---------------------------------
xxix. HBRUSH hBBg = CreateSolidBrush(RGB(255,255,255));
xxx. RECT rectFull = {0, 0, m_nWidth, m_nHeight};
xxxi. FillRect(hDC, &rectFull, hBBg);
xxxii. if(l3D > 0)
xxxiii. {
1. //SetBkColor(hDC, RGB (200,193,193));
2. SetTextColor(hDC, ::GetSysColor(COLOR_3DDKSHADOW));
3. SetBkMode(hDC, OPAQUE);
xxxiv. }
xxxv. else
xxxvi. {
1. SetBkColor(hDC, RGB(255,255,255));
2. SetTextColor(hDC, RGB(0,0,0));
3. SetBkMode(hDC, TRANSPARENT);
xxxvii. }
xxxviii. ::DrawText(
1. hDC,
2. bstrText.m_str,
3. bstrText.Length(),
4. &rectClient,
5. DT_WORDBREAK
6. ); //輸出
xxxix. if(l3D > 0)
xl. {
1. SetTextColor(hDC, ::GetSysColor(COLOR_3DHILIGHT));
2. SetBkMode(hDC, TRANSPARENT);
3. rectClient.left = rectClient.left + l3D;
4. rectClient.top = rectClient.top - 1;
5. rectClient.right = rectClient.right + l3D;
6. rectClient.bottom = rectClient.bottom - 1;
7. ::DrawText(
a) hDC,
b) lpText,
c) wcslen(lpText),
d) &rectClient,
e) DT_WORDBREAK);
xli. }
xlii. SelectObject(hDC, hOldFont);
xliii. DeleteObject(hFont);
xliv. DeleteObject(hBBg);
xlv. SaveDCBmp(hDC, hBitmap, vFileName);
xlvi. //SaveDCJPG(hDC, hBitmap, vFileName);
xlvii. DeleteObject(hBitmap);
xlviii. ::ReleaseDC(NULL, hDC);
xlix. return 0;
}
此函數功能:通過輸入特定長度的 文本,輸出圖像到指定文件
參數說明:
vFileName: 圖像保存文件路徑
lpText: 圖像輸出文本
lpBgImg: 圖像背景路徑
lCSet: 字符集
lpFont: 字體名稱
lWidth: 圖像輸出寬度
lHeight: 圖像輸出高度
lLeft: 圖像輸出左邊距,與右邊距相同
lTop: 圖像輸出上邊距,與下邊距相 同
llfHeight: 文本輸出字體高度,字體寬度隨高度等比例變化
lWeight: 文 本重量
l3D: 三D效果,值為0時無三D效果,大於0時其值為字體偏移量
程序i. 至 viii. 行對輸入參數合法性進行檢查及究正。
行ix. ,x. 定義及創建與指定設備 兼容的設備上下文句柄hDC。
行xi. 至 xviii.1 行定義LOGFONT 邏輯字體結構並填充 。通過CreateFontIndirect(&lf) 創建字體並調用SelectObject(hDC, hFont)將創建字 體選入設備上下文,原字體句柄保存在hOldFont裡。
xix. 至 xxiv. 行取得輸入文本 長度,在當前字體環境下調用DrawText函數計算輸出矩形,並將其矩形保存在rectClient裡 ,以便調整DC輸出矩形大小。
行xxvi. 利用上面計算出的長寬創建位圖句柄,行 xxvii.將其選入設備上下文,准備工作完畢,繪圖工作正式開始。
在此函數中,畫筆 及刷子我們使用系統默認設置,不再重復申請。
行xxxii.判斷三D偏移量是否大於零 ,如果不為零,輸出三D效果。
行xxxviii.在新矩形下輸入文本。如果有三D輸出請求 ,將矩形偏移l3D個像素,再次輸出文本,以顯示三D效果。
xlii. 行選入舊字體。
xliii. 行以後刪除對象保存位圖及恢復現場。保存位圖功能SaveDCBmp將在下節討論 。
二、位圖文件
(一)、位圖文件結構
位圖文件由三部分組成:文件頭 + 位圖信息 + 位圖像素數據
1、位圖文件頭
位圖文件頭主要用於識別位圖文 件。以下是位圖文件頭結構的定義:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
其中的bfType 值應該是“BM”(0x4d42),標志該文件是位圖文件。bfSize的值是位圖文件的 大小。bfReserved1, bfReserved2 為保留字,值為0。bfOffBits為位圖文件大小與DIB(設 備無關的位圖 Device-indepentent bitmap)位圖數據的大小之差。如:
BITMAPFILEHEADER bmfHdr;
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof (BITMAPINFOHEADER) + dwPaletteSize;
2、位圖信息
位圖信息中所記錄的 值用於分配內存,設置調色板信息,讀取像素值等。以下是位圖信息結構的定義:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
可見位圖信息也是由兩部分組成 的:位圖信息頭 + 顏色表
2.1、位圖信息頭
位圖信息頭包含了單個像素所用 字節數以及描述顏色的格式,此外還包括位圖的寬度、高度、目標設備的位平面數、圖像的 壓縮格式。以下是位圖信息頭結構的定義:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
biSize 結 構BITMAPINFOHEADER的字節數,即sizeof(BITMAPINFOHEADER)
biWidth 以像素為單位 的圖像寬度
biHeight 以像素為單位的圖像長度
biplanes 目標設備的位平面 數
biBitCount 每個像素的位數
對於每個像素的位數,分別有一下意義:
0,用在JPEG格式中
1,單色圖,調色板中含有兩種顏色,也就是我們通常說 的黑白圖片
4,16色圖
8,256色圖,通常說的灰度圖
16,64K圖,一般 沒有調色板,圖像數據中每兩個字節表示一個像素,5個或6個位表示一個RGB分量
24 ,16M真彩色圖,一般沒有調色板,圖像數據中每3個字節表示一個像素,每個字節表示一個 RGB分量
32,4G真彩色,一般沒有調色板,每4個字節表示一個像素,相對24位真彩圖 而言,加入了一個透明度,即RGBA模式
biCompression 圖像的壓縮格式(這個值幾乎 總是為0)
biSizeImage 以字節為單位的圖像數據的大小(對BI_RGB壓縮方式而言)
biXPelsPermeter 水平方向上的每米的像素個數
biYpelsPerMeter 垂直方向 上的每米的像素個數
biClrused 調色板中實際使用的顏色數,這個值通常為 0
biClrImportant 現實位圖時必須的顏色數, 這個值通常為0,表示所有的顏色都是 必需的
2.2、顏色表
顏色表一般是針對16位以下的圖像而設置的,對於16位和 16位以上的圖像,由於其位圖像素數據中直接對對應像素的RGB(A)顏色進行描述,因而省卻 了調色板。而對於16位以下的圖像,由於其位圖像素數據中記錄的只是調色板索引值,因而 需要根據這個索引到調色板去取得相應的RGB(A)顏色。顏色表的作用就是創建調色板。顏色 表是由顏色表項組成的,顏色表項結構的定義如下:
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
rgbBlue 藍色的強度
rgbGreen 綠色的強 度
rgbRed 紅色的強度
rgbReserved 保留字,為0
其中需要注意的問題 是,RGBQUAD結構中的顏色順序是BGR,而不是平常的RGB。
3、位圖數據
最後 ,在位圖文件頭、位圖信息頭、位圖顏色表之後,便是位圖的主體部分:位圖數據。根據不 同的位圖,位圖數據所占據的字節數也是不同的,比如,對於8位位圖,每個字節代表了一個 像素,對於16位位圖,每兩個字節代表了一個像素,對於24位位圖,每三個字節代表了一個 像素,對於32位位圖,每四個字節代表了一個像素。
(二)、存儲區域DC到位圖文件
認識了位圖文件的結構以後,對特定位圖文件進行操作就顯得簡單了。我們通過創建 特定的畫筆,刷子及位圖對象,在DC 環境下進行繪圖後,就要將保存在DC 裡的圖像存儲到 位圖文件中,以便使用及輸出到其他媒體。下面代碼實現將設圖上下文圖形保存為位圖文件 。
BOOL CImg::SaveDCBmp(HDC hDC, HBITMAP hBitmap, LPCTSTR lpFileName)
{
//當前分辨率下每象素所占字節數
int iBits;
//位圖中每象素所占字節數
WORD wBitCount;
//定義調色板大小 , 位圖中像素字節大小 ,位圖文件大小 , 寫入文件字節數
DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
//位圖屬性結 構
BITMAP Bitmap;
//位圖文件頭結構
BITMAPFILEHEADER bmfHdr;
//位圖信息頭結構
BITMAPINFOHEADER bi;
//指向位 圖信息頭結構
LPBITMAPINFOHEADER lpbi;
//定義文件,分配內存句柄 ,調色板句柄
HANDLE fh, hDib, hPal,hOldPal=NULL;
//計算位 圖文件每個像素所占字節數
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
if (iBits <= 1) wBitCount = 1;
else if (iBits <= 4) wBitCount = 4;
else if (iBits <= 8) wBitCount = 8;
else wBitCount = 24;
//wBitCount = 4;
GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrImportant = 0;
bi.biClrUsed = 0;
dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;
//為位圖內容分配內存
hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER) GlobalLock(hDib);
*lpbi = bi;
// 處理調色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);
}
// 獲取該調色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
+dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);
//恢復調色板
if (hOldPal)
{
::SelectPalette(hDC, (HPALETTE) hOldPal, TRUE);
RealizePalette(hDC);
}
//創 建位圖文件
fh = CreateFile(lpFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh == INVALID_HANDLE_VALUE) return FALSE;
// 設置位圖文件頭
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof (BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;
// 寫入位圖文件頭
WriteFile(fh, (LPSTR)&bmfHdr, sizeof (BITMAPFILEHEADER), &dwWritten, NULL);
// 寫入位圖文件其余內容
WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle (fh);
return TRUE;
}
保存位圖文件前通過 GetObject函數取得位圖長度, 通過GetDIBits取得位圖圖像掃描數據,填充 BITMAPFILEHEADER(位圖文件頭結構); BITMAPINFOHEADER (位圖信息頭結構); 然後寫入位圖 文件頭:
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
寫入位圖文件其余內容:
WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
以 文件頭 + 位圖信息 + 位 圖像素數據 的順序進行存儲。
下載源代碼:http://www.vckbase.com/code/downcode.asp?id=2362