分类: C/C++
2008-02-21 10:24:30
一、VC++中的DC环境及GUI有关的各种对象
在Windows中有各种图形用户界面GUI(Graphics User Interface)对象,当我们在进行绘图时就需要利用这些对象。而各种对象都拥有各种属性,下面首先介绍几种GUI对象和拥有的属性。
(一)、GUI有关的各种对象
在Windows中有各种图形用户界面GUI(Graphics User Interface)对象,当我们在进行绘图时就需要利用这些对象。而各种对象都拥有各种属性,下面首先介绍几种GUI对象和拥有的属性。
此外系统中还拥有一些库存GUI对象,你可以利用CDC::SelectStockObject(SelectStockObject( int nIndex )选入这些对象,它们包括一些固定颜色的刷子,画笔和一些基本字体。 如:
在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类的一个指针,我们通过它进行画图操作。
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; }此函数功能:通过输入特定长度的文本,输出图像到指定文件
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);
以 文件头 + 位图信息 + 位图像素数据 的顺序进行存储。
作者信息: