VC BitmapWord格式.docx
《VC BitmapWord格式.docx》由会员分享,可在线阅读,更多相关《VC BitmapWord格式.docx(10页珍藏版)》请在冰豆网上搜索。
![VC BitmapWord格式.docx](https://file1.bdocx.com/fileroot1/2022-11/30/590fdc96-dd8d-4a5a-9b70-9086f25eb1fb/590fdc96-dd8d-4a5a-9b70-9086f25eb1fb1.gif)
CPalette*m_palDIB;
//指向调色板Cpalette类的指针;
CSizem_sizeDoc;
//初始化视图的尺寸,该尺寸为位图的尺寸;
最后将程序的字符串表中的字符串资源IDR_DibTYPE修改为:
“\nDib\nDib\nDibFiles(*.bmp;
*.dib)\n.bmp\nDib.Document\nDibDocument”。
这样作的目的是为了在程序文件对话框中可以选择BMP或DIB格式的位图文件。
1、读取灰度BMP位图
可以根据BMP位图文件的结构,操作BMP位图文件并读入图像数据,为此我们充分利用了VC的文档视图结构,重载了文挡类的OnOpenDocument()函数,这样用户就可以在自动生成程序的打开文件对话框中选择所要打开的位图文件,然后程序将自动调用该函数执行读取数据的操作。
该函数的实现代码如下所示:
BOOLCDibDoc:
:
OnOpenDocument(LPCTSTRlpszPathName)
{
LOGPALETTE*pPal;
//定义逻辑调色板指针;
pPal=newLOGPALETTE;
//初始化该指针;
CFilefile;
CFileExceptionfe;
if(!
file.Open(lpszPathName,CFile:
modeRead|CFile:
shareDenyWrite,&
fe))
{//以“读”的方式打开文件;
AfxMessageBox("
图像文件打不开!
"
);
returnFALSE;
}
DeleteContents();
//删除文挡;
BeginWaitCursor();
BITMAPFILEHEADERbmfHeader;
//定义位图文件头结构;
LPBITMAPINFOlpbmi;
DWORDdwBitsSize;
HANDLEhDIB;
LPSTRpDIB;
//指向位图数据的指针;
BITMAPINFOHEADER*bmhdr;
//指向位图信息头结构的指针
dwBitsSize=file.GetLength();
//得到文件长度
if(file.Read((LPSTR)&
bmfHeader,sizeof(bmfHeader))!
=sizeof(bmfHeader))
//读取位图文件的文件头结构信息;
if(bmfHeader.bfType!
=0x4d42)//检查该文件是否为BMP格式的文件;
hDIB=(HANDLE):
GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,dwBitsSize);
//为读取图像文件数据申请缓冲区
if(hDIB==0)
pDIB=(LPSTR):
GlobalLock((HGLOBAL)hDIB);
//得到申请的缓冲区的指针;
if(file.ReadHuge(pDIB,dwBitsSize-sizeof(BITMAPFILEHEADER))!
=
dwBitsSize-sizeof(BITMAPFILEHEADER))
GlobalUnlock((HGLOBAL)hDIB);
hDIB=NULL;
}//此时pDIB数据块中读取的数据包括位图头信息、位图颜色表、图像像素的灰度值;
bmhdr=(BITMAPINFOHEADER*)pDIB;
//为指向位图信息头结构的指针赋值;
if((*bmhdr).biBitCount!
=8)//验证是否为8bit位图
该文件不是灰度位图格式!
m_hDIB=hDIB;
//将内部变量数据赋于全局变量;
//下面是记录位图的尺寸;
m_sizeDoc.x=bmhdr->
biWidth;
m_sizeDoc.y=bmhdr->
biHeight;
//下面是根据颜色表生成调色板;
m_palDIB=newCpalette;
pPal->
palVersion=0x300;
//填充逻辑颜色表
palNumEntries=256;
lpbmi=(LPBITMAPINFO)bmhdr;
for(inti=0;
i<
256;
i++)
{//每个颜色表项的R、G、B值相等,并且各个值从“0”到“255”序列展开;
Pal->
palPalentry[i].peRed=lpbmi->
bmiColors[i].rgbRed;
palPalentry[i].peGreen=lpbmi->
bmiColors[i].rgbGreen;
palPalentry[i].peBlue=lpbmi->
bmiColors[i].rgbBlue;
;
palPalentry[i].peFlags=0;
m_palDIB->
CreatePalette(pPal);
//根据读入的数据得到位图的宽、高、颜色表;
if(pPal)
deletepPal;
EndWaitCursor();
SetPathName(lpszPathName);
//设置存储路径
SetModifiedFlag(FALSE);
//设置文件修改标志为FALSE
returnTRUE;
上面的方法是通过CFile类对象的操作来读取位图文件的,它需要分析位图中的文件头信息,从而确定需要读取的图像长度。
这种方法相对来说有些繁琐,其实还可以以一种相对简单的方法读取位图数据,首先在程序的资源中定义DIB类型资源,然后添加位图到该类型中,将图像数据以资源的形式读取出来,这时候就可以根据所获取的数据中的位图信息结构来获取、显示图像数据了。
下面的函数实现了以资源形式装载图像文件数据,该函数的实现代码如下所示:
/////////////////////////////////////////////////////////////////
HANDLELoadDIB(UINTuIDS,LPCSTRlpszDibType)
LPCSTRlpszDibRes=MAKEINTRESOURCE(uIDS);
//根据资源标志符确定资源的名字;
HINSTANCEhInst=AfxGetInstanceHandle();
//得到应用程序的句柄;
HRSRChRes=:
:
FindResource(hInst,lpszDibRes,lpszDibType);
//获取资源的句柄,这里lpszDibType为资源的名字“DIB”;
If(hRes==NULL)
returnNULL
HGLOBALhData=:
LoadResource(hInst,hRes);
//转载资源数据并返回该句柄;
returnhData;
2、灰度位图数据的存储
为了将图像处理后所得到的像素值保存起来,我们重载了文档类的OnSaveDocument()函数,这样用户在点击Save或SaveAs子菜单后程序自动调用该函数,实现图像数据的存储。
该函数的具体实现如下:
///////////////////////////////////////////////////////////////////
OnSaveDocument(LPCTSTRlpszPathName)
BITMAPFILEHEADERbmfHdr;
//位图文件头结构;
LPBITMAPINFOHEADERlpBI;
//指向位图头信息结构的指针;
DWORDdwDIBSize;
;
modeCreate|CFile:
modeReadWrite|CFile:
shareExclusive,&
文件打不开"
);
}//以读写的方式打开文件;
BOOLbSuccess=FALSE;
lpBI=(LPBITMAPINFOHEADER):
GlobalLock((HGLOBAL)m_hDIB);
if(lpBI==NULL)
dwDIBSize=*(LPDWORD)lpBI+256*sizeof(RGBQUAD);
//图像的文件信息所占用的字节数;
DWORDdwBmBitsSize;
//BMP文件中位图的像素所占的字节数
dwBmBitsSize=WIDTHBYTES((lpBI->
biWidth)*((DWORD)lpBI->
biBitCount))
*lpBI->
//存储时位图所有像素所占的总字节数
dwDIBSize+=dwBmBitsSize;
//BMP文件除文件信息结构外的所有数据占用的总字节数;
lpBI->
biSizeImage=dwBmBitsSize;
//位图所有像素所占的总字节数
//以下五句为文件头结构填充值
bmfHdr.bfType=0x4d42;
//文件为"
BMP"
类型
bmfHdr.bfSize=dwDIBSize+sizeof(BITMAPFILEHEADER);
//文件总长度
bmfHdr.bfReserved1=0;
bmfHdr.bfReserved2=0;
bmfHdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+lpBI->
biSize
+256*sizeof(RGBQUAD);
//位图数据距离文件头的偏移量;
file.Write((LPSTR)&
bmfHdr,sizeof(BITMAPFILEHEADER));
//向文件中写文件头信息;
file.WriteHuge(lpBI,dwDIBSize);
//将位图信息(信息头结构、颜色表、像素数据)写入文件;
GlobalUnlock((HGLOBAL)m_hDIB);
EndWaitCursor();
SetModifiedFlag(FALSE);
//将文档设为“干净”标志,表示此后文档不需要存盘提示;
returnTRUE;
二、调色板的操作
通过上面的操作,我们已经可以获取图像中的数据了,现在的又一个问题是如何在窗口中显示出图像数据。
灰度图像要正确显示,必须实现逻辑调色板和系统调色板。
首先我们介绍一下逻辑调色板结构LOGPALETTE,该结构定义如下:
typedefstructtagLOGPALETTE
WORDpalVersion;
//调色板的板本号,应该指定该值为0x300;
WORDpalNumEntries;
//调色板中的表项数,对于灰度图像该值为256;
PALETEENTRYpalPalEntry[1];
//调色板中的颜色表项,由于该表项的数目不一定,所以这里数组长度定义为1,灰度图像对应的该数组的长度为256;
}LOGPALETTE;
颜色表项结构PALETTEENTRY定义了调色板中的每一个颜色表项的颜色和使用方式,定义如下:
typedefstructtagPALETTEENTRY
BYTEpeRed;
//R分量值;
BYTEpeGreen;
//G分量值;
BYTEpeBlue;
//B分量值;
BYTEpeFlags;
//该颜色被使用的方式,一般情况下设为“0”;
}PALETTEENTRY;
Windows系统使用调色板管理器来管理与调色板有关的操作,通常活动窗口的调色板即是当前系统调色板,所有的非活动窗口都必须按照此系统调色板来显示自己的颜色,此时调色板管理器将自动的用系统调色板中的最近似颜色来映射相应的显示颜色。
如果窗口或应用程序按自己的调色板显示颜色,就必须将自己的调色板载入到系统调色板中,这种操作叫作实现调色板,实现调色板包括两个步骤,既首先将调色板选择到设备上下文中,然后在设备上下文中实现它。
可以通过CDC:
SelectPalette()、CDC:
RealizePalette()或相应的API函数来实现上述的两个步骤。
在实现调色板的过程中,通过在主框架类中处理Windows定义的消息WM_QUERYNEWPALETTE、WM_PALETTECHANGED及视图类中处理自定义消息WM_DOREALIZE(该消息在主框架窗口定义如下:
#defineWM_REALIZEPAL(WM_USER+101))来实现调色板的操作。
当系统需要处理调色板的变化时,将向程序的主窗口发送WM_QUERYNEWPALETTE、WM_PALETTECHANGED,例如当某一窗口即将激活时,主框架窗口将收到WM_QUERYNEWPALETTE消息,通知该窗口将要收到输入焦点,给它一次机会实现其自身的逻辑调色板;
当系统调色板改变后,主框架窗口将收到WM_PALETTECHANGED消息,通知其它窗口系统调色板已经改变,此时每一窗口都应该实现其逻辑调色板,重画客户区。
由于上述的调色板变更消息是发往主框架窗口的,所以我们只能在主窗口中响应这两个消息,然后由主框架窗口通知各个视窗,使得程序激活时能自动装载自己的调色板。
我们定义的用户消息WM_REALIZEPAL用于主框架窗口通知视窗它已经收到调色板变更消息,视窗应该协调其调色板。
下面我们给出了各个消息的响应处理函数的具体实现代码和注释:
//////////////////////////////////////////////////////////
voidCMainFrame:
OnPaletteChanged(CWnd*pFocusWnd)
{//总实现活动视的调色板
CMDIFrameWnd:
OnPaletteChanged(pFocusWnd);
CMDIChildWnd*pMDIChildWnd=MDIGetActive();
//得到活动的子窗口指针;
if(pMDIChildWnd==NULL)
return
CView*pView=pMDIChildWnd->
GetActiveView();
//得到视图的指针;
ASSERT(pView!
=NULL);
SendMessageToDescendants(WM_DOREALIZE,(WPARAM)pView->
m_hWnd);
//通知所有子窗口系统调色板已改变
////////////////////////////////////////////////
BOOLCMainFrame:
OnQueryNewPalette()//提供实现系统调色板的机会
//实现活动视的调色板
//noactiveMDIchildframe(nonewpalette)
//得到活动子窗口的视图指针;
//通知活动视图实现系统调色板
pView->
SendMessage(WM_DOREALIZE,(WPARAM)pView->
/////////////////////////////////////////////////
BOOLCDibView:
OnDoRealize(WPARAMwParam,LPARAM)//实现系统调色板
ASSERT(wParam!
CDibDoc*pDoc=GetDocument();
if(pDoc->
m_hDIB==NULL)
//mustbeanewdocument
CPalette*pPal=pDoc->
m_palDIB;
//调色板的颜色表数据在InitDIBData()函数中实现
if(pPal!
=NULL)
CMainFrame*pAppFrame=(CMainFrame*)AfxGetApp()->
m_pMainWnd;
//得到程序的主框架指针;
ASSERT_KINDOF(CMainFrame,pAppFrame);
CClientDCappDC(pAppFrame);
//获取主框架的设备上下文;
CPalette*oldPalette=appDC.SelectPalette(pPal,((HWND)wParam)!
=m_hWnd);
//只有活动视才可以设为"
FALSE"
,即根据活动视的调色板设为"
前景"
调色板;
if(oldPalette!
UINTnColorsChanged=appDC.RealizePalette();
//实现系统调色板
if(nColorsChanged>
0)
pDoc->
UpdateAllViews(NULL);
//更新视图
appDC.SelectPalette(oldPalette,TRUE);
//将原系统调色板置为背景调色板
else
TRACE0(“\tSelectPalettefailedin”);
注:
在调用API函数显示位图时,不要忘记设置逻辑调色板,即"
背景"
调色板,否则位图将无法正确显示,读者可以从后面的显示部分的实现看出我们在显示时实现了逻辑调色板。
上述的处理相对来说比较繁琐复杂,可能对于初学者来说也比较难于理解,所以如果我们的程序仅仅限于处理灰度图象,可以采用另外一种相对简单的办法,即在文档类的初始化阶段定义一个灰度调色板,然后在设备上下文中实现它,这样作的好处是在度取灰度位图时可以不再考虑文件中的颜色表信息,提高了文件读取速度,笔者在开发一个基于机器视觉的项目时采用的就是这种方法,取的了比较满意的效果。
首先定义一个指向逻辑颜色表结构LOGPALETTE的指针pPal,填充该指针,然后将该指针与调色板指针联系起来,该方法的具体实现如下:
////////////////////////////////////////////