配色: 字号:
BMP位图类的创建与MFC单文档视图类的显示
2013-05-08 | 阅:  转:  |  分享 
  
BMP位图文件的4个组成部分bmp文件大体上分成四个部分。位图文件头BITMAPFILEHEADER、

位图信息头BITMAPINFOHEADER、

调色板Palette、

实际的位图数据ImageDate

第1部分为位图文件头BITMAPFILEHEADER,是一个结构体类型,该结构的长度是固定的,为14个字节。其定义如下:

typedefstructtagBITMAPFILEHEADER

{

???WORDbfType;???????

???DWORDbfSize;?????

???WORDbfReserved1;?

???WORDbfReserved2;

????DWORDbfOffBits;

}BITMAPFILEHEADER,FARLPBITMAPFILEHEADER,PBITMAPFILEHEADER;

1.文件头信息块0000-0001:文件标识,为字母ASCII码“BM”。0002-0005:文件大小。0006-0009:保留,每字节以“00”填写。000A-000D:记录图像数据区的起始位置。各字节的信息含义依次为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。

第2部分为位图信息头BITMAPINFOHEADER,也是一个结构体类型的数据结构,该结构的长度也是固定的,为40个字节(WORD为无符号16位整数,DWORD为无符号32位整数,LONG为32位整数)。其定义如下:

typedefstructtagBITMAPINFOHEADER

{

???DWORDbiSize;?图像描述信息块的大小,常为28H。

???LONGbiWidth;??????????

???LONGbiHeight;?????????

???WORDbiPlanes;?????=1????

???WORDbiBitCount?;????????记录像素的位数

???DWORDbiCompression;数据压缩方式

???DWORDbiSizeImage;?????图像区数据的大小

???LONGbiXPelsPerMeter;??指定目标设备的水平分辨率,单位是像素/米

???LONGbiYPelsPerMeter;??

???DWORDbiClrUsed;????位图实际用到的颜色数

???DWORDbiClrImportant;??位图显示过程中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。

}BITMAPINFOHEADER,FARLPBITMAPINFOHEADER,PBITMAPINFOHEADER;

2.图像描述信息块000E-0011:图像描述信息块的大小,常为28H。0012-0015:图像宽度。0016-0019:图像高度。001A-001B:图像的plane总数(恒为1)。001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。001E-0021:数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。0022-0025:图像区数据的大小。0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。



第3部分为颜色表。颜色表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)。RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:

typedefstructtagRGBQUAD

{

???BYTErgbBlue;

???BYTErgbGreen;

???BYTErgbRed;

???BYTErgbReserved;

}RGBQUAD;

3.颜色表颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号0的颜色,接下来表示颜色号1的颜色,依此类推。

第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜色表(如果有颜色表的话)之后,记录了图像的每一个像素值。对于有颜色表的位图,位图数据就是该像素颜色在调色板中的索引值;对于真彩色图,位图数据就是实际的R、G、B值(三个分量的存储顺序是B、G、R)。下面分别就2色、16色、256色和真彩色位图的位图数据进行说明:

—?对于2色位图,用1位就可以表示该像素的颜色,所以1个字节能存储8个像素的颜色值。

—?对于16色位图,用4位可以表示一个像素的颜色。所以一个字节可以存储2个像素的颜色值。

—?对于256色位图,1个字节刚好存储1个像素的颜色值。

—?对于真彩色位图,3个字节才能表示1个像素的颜色值。

需要注意两点:

第一,Windows规定一个扫描行所占的字节数必须是4的倍数,不足4的倍数则要对其进行扩充。假设图像的宽为biWidth个像素、每像素biBitCount个比特,其一个扫描行所占的真实字节数的计算公式如下:

DataSizePerLine=(biWidthbiBitCount/8+3)/44

那么,不压缩情况下位图数据的大小(BITMAPINFOHEADER结构中的biSizeImage成员)计算如下:

biSizeImage=DataSizePerLinebiHeight

第二,一般来说,BMP文件的数据是从图像的左下角开始逐行扫描图像的,即从下到上、从左到右,将图像的像素值一一记录下来,因此图像坐标零点在图像左下角。



























CDIB类的建立

classCDib

{

public:

LPRGBQUADm_lpRgbQuad;//颜色表指针

LPBYTEm_lpData;//位图数据指针

UINTm_numberOfColors;//颜色数

BOOLm_bValid;//位图是否有效

BOOLm_bHasRgbQuad;//是否有颜色表

HPALETTEm_hPalette;//调色板句柄

LPBITMAPFILEHEADERm_lpBmpFileHeader;//位图文件头指针

LPBITMAPINFOHEADERm_lpBmpInfoHeader;//位图信息头指针

LPBITMAPINFOm_lpBmpInfo;//位图信息指针

LPBYTEm_lpDib;

//位图指针,包含除位图文件头的所有内容、需要动态分配和释放

DWORDsize;

public:

charm_fileName[256];

LPCTSTRGetFileName();

BOOLLoadFile(LPCTSTRdibFileName);

BOOLSaveFile(LPCTSTRfilename);

LPCTSTRGetFileName();

LONGGetWidth();

LONGGetHeight();

DWORDGetSize();//获取位图大小

WORDGetBitCount();//获取单个像素所占的位数

UINTGetLineByte();//获取每行像素所占字节数

UINTGetNumOfColor();//获取位图颜色数

LPRGBQUADGetRgbQuad();//获取位图颜色表

LPBYTEGetData();//获取位图数据

BOOLDraw(CDCpDC,BYTE,CPointorigin,CSizesize);//显示位图

BOOLHasRgbQuad();//判断是否有颜色表?

BITMAPINFOGetInfo();

BOOLIsGrade();//判断是否为灰度图像

WORDPaletteSize(LPBYTElpDIB);

WORDDIBNumColors(LPBYTElpDIB);

BOOLIsValid();//判断位图是否有效?

protected:

DWORDCalcRgbQuadLength();//计算位图颜色表的长度

BOOLMakePalette();//根据颜色表生成调色板

voidEmpty(BOOLbFlag=TRUE);//清理空间

public:

CDib(void);

~CDib(void);};

主要成员函数:

//////////////////////////////////////////////////////////

//函数功能:从文件加载位图

//输入参数:LPCTSTRdibFileName表示待加载位图文件路径

//返回值:加载是否成功

///////////////////////////////////////////////////////////

BOOLCDib::LoadFile(LPCTSTRdibFileName)

{

strcpy(m_fileName,dibFileName);//记录位图文件名

CFiledibFile;//选择读模式定义文件对象

if(!dibFile.Open((LPCTSTR)m_fileName,CFile::modeRead|CFile::shareDenyWrite))

{

returnFALSE;

}

Empty(FALSE);

//为位图文件头指针分配空间,并初始化为

m_lpBmpFileHeader=(LPBITMAPFILEHEADER)newBYTE[sizeof(BITMAPFILEHEADER)];

memset(m_lpBmpFileHeader,0,sizeof(BITMAPFILEHEADER));

//读取位图文件头

intnCount=

dibFile.Read(m_lpBmpFileHeader,sizeof(BITMAPFILEHEADER));

//读bmp文件头信息到指针m_lpBmpfileHeader

if(nCount!=sizeof(BITMAPFILEHEADER))returnFALSE;

if(m_lpBmpFileHeader->bfType==0x4d42)//判断是否是bmp文件

{

DWORDfileLength=dibFile.GetLength();//获取文件长度信息

size=fileLength-sizeof(BITMAPFILEHEADER);

//文件数据区大小=文件长度-文件头大小

m_lpDib=newBYTE[size];

memset(m_lpDib,0,size);

dibFile.Read((void)m_lpDib,size);//读除bmp文件头外的所有数据到指针m_lpDib

dibFile.Close();//关闭文件

m_lpBmpInfo=(LPBITMAPINFO)m_lpDib;//获取bmp文件的信息

m_lpBmpInfoHeader=(LPBITMAPINFOHEADER)m_lpDib;

//获取文件信息头地址

m_lpRgbQuad=(LPRGBQUAD)(m_lpDib+m_lpBmpInfoHeader->biSize);

//颜色表地址=pDib+m_lpBmpInfoHeader->biSize;

intm_numberOfColors=GetNumOfColor();//颜色数

if(m_lpBmpInfoHeader->biClrUsed==0)//

m_lpBmpInfoHeader->biClrUsed=m_numberOfColors;

//颜色表的大小CalcRgbQuadLength(){returnm_numberOfColorssizeof(RGBQUAD)}

DWORDcolorTableSize=CalcRgbQuadLength();

//位图数据区地址

m_lpData=m_lpDib+m_lpBmpInfoHeader->biSize+colorTableSize;

if(m_lpRgbQuad==(LPRGBQUAD)m_lpData)//Nocolortable

m_lpRgbQuad=NULL;

m_lpBmpInfoHeader->biSizeImage=GetSize();

m_bValid=TRUE;

returnTRUE;

}

else

{

m_bValid=FALSE;

AfxMessageBox("Thisisn''tabitmapfile!");

returnFALSE;

}

}





/////////////////////////////////////////////////

//将位图保存到文件

//LPCTSTRfilename表示位图文件保存路径文件名

//返回值,TRUE-表示成功

/////////////////////////////////////////////////

BOOLCDib::SaveFile(LPCTSTRfilename)

{

CFiledibFile;//以写模式打开文件

if(!dibFile.Open((LPCTSTR)filename,CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive))

{

returnFALSE;

}

strcpy(m_fileName,filename);

dibFile.Write(m_lpBmpFileHeader,sizeof(BITMAPFILEHEADER));//将位图文件头结构写进文件

dibFile.Write(m_lpBmpInfoHeader,sizeof(BITMAPINFOHEADER));//将位图信息头结构写进文件

DWORDdwRgbQuandLength=CalcRgbQuadLength();//计算颜色表长度

if(dwRgbQuandLength!=0)

dibFile.Write(m_lpRgbQuad,dwRgbQuandLength);//若存在颜色表,则将颜色表写进位图文件

DWORDdwDataSize=GetLineByte()GetHeight();

dibFile.Write(m_lpData,dwDataSize);//将位图数据写进位图文件

dibFile.Close();//关闭文件

returnTRUE;

}



//////////////////////////////////////////

//根据颜色表生成调色板

///////////////////////////////////////////

BOOLCDib::MakePalette()

{

DWORDdwRgbQuadLength=CalcRgbQuadLength();

if(dwRgbQuadLength==0)returnFALSE;

//为表示图像为真彩色图像,没有调色板

if(m_hPalette!=NULL)//删除旧的调色板对象

{

DeleteObject(m_hPalette);

m_hPalette=NULL;

}

//申请缓冲器,初始化为

/调色板编程见到这样两个结构:typedefstructtagPALETTEENTRY{??BYTEpeRed;??BYTEpeGreen;??BYTEpeBlue;??BYTEpeFlags;}PALETTEENTRY;typedefstructtagLOGPALETTE{??WORD????palVersion;??WORD????palNumEntries;??PALETTEENTRYpalPalEntry[1];//

}LOGPALETTE;

/

DWORDdwNumOfColor=GetNumOfColor();

DWORDdwSize=

2sizeof(WORD)+dwNumOfColorsizeof(PALETTEENTRY);

LPLOGPALETTElpLongPalette=(LPLOGPALETTE)newBYTE[dwSize];

memset(lpLongPalette,0,dwSize);

//生成逻辑调色板

lpLongPalette->palVersion=0x300;

lpLongPalette->palNumEntries=dwNumOfColor;

LPRGBQUADlpRgbQuad=(LPRGBQUAD)m_lpRgbQuad;

//m_lpRgbQuad位图文件颜色表地址

for(inti=0;i
{

lpLongPalette->palPalEntry[i].peRed=lpRgbQuad->rgbRed;

lpLongPalette->palPalEntry[i].peGreen=lpRgbQuad->rgbGreen;

lpLongPalette->palPalEntry[i].peBlue=lpRgbQuad->rgbBlue;

lpLongPalette->palPalEntry[i].peFlags=0;

lpRgbQuad++;

}

m_hPalette=CreatePalette(lpLongPalette);//创建逻辑调色板

delete[]lpLongPalette;

returnTRUE;

}



LPCTSTRCDib::GetFileName()

{

returnm_fileName;

}



LONGCDib::GetWidth()

{

returnm_lpBmpInfoHeader->biWidth;

}



LONGCDib::GetHeight()

{

returnm_lpBmpInfoHeader->biHeight;

}



DWORDCDib::GetSize()

{

if(m_lpBmpInfoHeader->biSizeImage!=0)

{

returnm_lpBmpInfoHeader->biSizeImage;

}

else

{

returnGetWidth()GetHeight();

}

}



WORDCDib::GetBitCount()

{

returnm_lpBmpInfoHeader->biBitCount;

}



UINTCDib::GetLineByte()

{

return(GetWidth()GetBitCount()/8+3)/44;

}

///////////////////////////////////////////////

//获取图像使用的颜色数

//////////////////////////////////////////////

UINTCDib::GetNumOfColor()

{

UINTdwNumOfColor;

//m_lpBmpInfoHeader->biClrUsed图像中使用的颜色数,m_lpBmpInfoHeader->biBitCount像素位数,,,

if((m_lpBmpInfoHeader->biClrUsed==0)&&(m_lpBmpInfoHeader->biBitCount<9))

{

switch(m_lpBmpInfoHeader->biBitCount)

{

case1:dwNumOfColor=2;break;

case4:dwNumOfColor=16;break;

case8:dwNumOfColor=256;

}

}

else

dwNumOfColor=m_lpBmpInfoHeader->biClrUsed;

returndwNumOfColor;

}



DWORDCDib::CalcRgbQuadLength()

{

DWORDdwNumOfColor=GetNumOfColor();

if(dwNumOfColor>256)

{

dwNumOfColor=0;

}

returndwNumOfColorsizeof(RGBQUAD);

}

////////////////////////////////////////

//获取颜色表

//返回颜色表指针

///////////////////////////////////////

LPRGBQUADCDib::GetRgbQuad()

{

returnm_lpRgbQuad;

}

//////////////////////////////////////

//获取位图数据区指针

////////////////////////////////////

LPBYTECDib::GetData()

{

returnm_lpData;

}





///////////////////////////////////////////////////////////

//函数功能:显示位图

//CDCpDC表示设备环境指针

//CPointorigin表示显示矩形区域的左上角

//CSizesize表示显示矩形区域的大小

////////////////////////////////////////////////////////////

BOOLCDib::Draw(CDCpDC,BYTElpData,CPointorigin,CSizesize)

{

if(!IsValid())returnFALSE;

if(m_lpDib==NULL)returnFALSE;

HPALETTEhOldPalette=NULL;

if(m_hPalette!=NULL)//如果位图有调色板则选进设备环境中 hOldPalette=SelectPalette(pDC->GetSafeHdc(),m_hPalette,TRUE);

pDC->SetStretchBltMode(COLORONCOLOR);//设置位图伸缩模式

//将位图在pDC所指向的设备上进行显示

StretchDIBits(pDC->GetSafeHdc(),origin.x,origin.y,size.cx,size.cy,0,0,GetWidth(),GetHeight(),lpData,m_lpBmpInfo,DIB_RGB_COLORS,SRCCOPY);

if(hOldPalette!=NULL)

SelectPalette(pDC->GetSafeHdc(),hOldPalette,TRUE);//恢复旧的调试板

returnTRUE;

}

BOOLCDib::HasRgbQuad()

{

returnm_bHasRgbQuad;

}

BOOLCDib::IsValid()

{

returnm_bValid;

}

BOOLCDib::IsGrade()

{

return(GetBitCount()<9&&GetBitCount()>0);

}

voidCDib::Empty(BOOLbFlag)//清理空间

{

if(bFlag)strcpy(m_fileName,"");

if(m_lpBmpFileHeader!=NULL)

{

delete[]m_lpBmpFileHeader;

m_lpBmpFileHeader=NULL;

}

if(m_lpDib!=NULL)

{

delete[]m_lpDib;

m_lpDib=NULL;

m_lpBmpInfo=NULL;

m_lpBmpInfoHeader=NULL;

m_lpRgbQuad=NULL;

m_lpData=NULL;

}

if(m_hPalette!=NULL)

{

DeleteObject(m_hPalette);

m_hPalette=NULL;

}

m_bHasRgbQuad=FALSE;

m_bValid=FALSE;

}





MFC单文档中图像的显示与操作

一、创建MFC单文档工程:DIPAX

二、在DIPAX工程添加CDib类的定义及其实现。

三、在DIPAX工程MFC单文档中创建两个视图类,左右分开

1、首先创建类:

classCDynSplitterWnd:publicCSplitterWnd{public:?CDynSplitterWnd(void);?~CDynSplitterWnd(void);};

在CMainFrame中包含上述类的头文件,重写CMainFrame类的方法virtualBOOLOnCreateClient(LPCREATESTRUCTlpcs,CCreateContextpContext);

2、在重写类之前,创建新的视图类

classCDynSplitView:publicCViewCdipaxView

以及在CMainFrame里创建上述窗口分割类的对象CDynSplitterWndm_wndSplitter;

3、现在可以重写上述OnCreateClient()方法了:

BOOLCMainFrame::OnCreateClient(LPCREATESTRUCTlpcs,CCreateContextpContext)

{

//TODO:在此添加专用代码和/或调用基类

if(!m_wndSplitter.CreateStatic(this,1,2))

{

TRACE0("FailedtoSplitterwindow\n");

returnFALSE;

}

//addthefirstsplitterpane-thedefaultviewincolumn0

if(!m_wndSplitter.CreateView(0,0,

pContext->m_pNewViewClass,CSize(275,150),pContext))

{

TRACE0("Failedtocreatefirstpane\n");

returnFALSE;

}

//addthesecondsplitterpane-aninputviewincolumn1

if(!m_wndSplitter.CreateView(0,1,

RUNTIME_CLASS(CDynSplitView),CSize(0,0),pContext))

{

TRACE0("Failedtocreatesecondpane\n");

returnFALSE;

}

//activatetheinputview

SetActiveView((CView)m_wndSplitter.GetPane(0,1));

returnTRUE;

//returnCFrameWnd::OnCreateClient(lpcs,pContext);

}

4、运行效果











四、在视图类CdipaxView中显示位图

1.在CdipaxDoc文档类中添加:

(1)添加头文件

#include"DIB.h"

(2)添加成员变量:

CDibCdib;

CStringfilename;

intstatedoc;

(3)添加OnFileOpen()事件函数:OnFileOpen()

voidCdipaxDoc::OnFileOpen()

{

//TODO:在此添加命令处理程序代码

CFileDialogdlg(TRUE,_T("BMP"),_T(".BMP"),OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,_T("位图文件(.BMP)|.BMP|"));

if(IDOK==dlg.DoModal())

filename.Format("%s",dlg.GetPathName());

Cdib.LoadFile(filename);



statedoc=1;

}

(4)在构造函数中添加:

CdipaxDoc::CdipaxDoc()

{

//TODO:在此添加一次性构造代码

statedoc=0;

}



(1)在CdipaxView.h中添加成员变量

public:

CStringfilename;

intstate1;

(2)在构造函数中添加:

CdipaxView::CdipaxView()

{

//TODO:在此处添加构造代码

state1=0;

}

(3)改写菜单项:











(4)改写工具栏:



















(5)在CdipaxView中为菜单ID_ORIGINIMAGE添加事件处理程序:

voidCdipaxView::OnOriginimage()

{

//TODO:在此添加命令处理程序代码

CdipaxDocpDoc=GetDocument();

ASSERT_VALID(pDoc);

filename=pDoc->filename;

state1=1;

Invalidate();

}



(6)重写OnDraw函数:

voidCdipaxView::OnDraw(CDCpDC)

{

CdipaxDocpDoc=GetDocument();

ASSERT_VALID(pDoc);

//TODO:在此处为本机数据添加绘制代码

if(state1==1)

{

pDoc->Cdib.Draw(pDC,pDoc->Cdib.m_lpData,CPoint(0,0),CSize(pDoc->Cdib.GetWidth(),pDoc->Cdib.GetHeight()));

}

}

(7)运行效果:













五、一个简单的二值化操作

1、在CDynSplitView中显示经过处理之后的图像

(1)在CDynSplitView.h中添加

public:

BYTEimage_out;//处理后图像数据区指针

BYTEimage_in;//处理前图像数据区指针

longm_imagex;//图像的宽度

longm_imagey;//图像的高度

intstate2;//CDynSplitView视图类是否为当前视图

CPalettehPalette;

public:

voidclearmem(void);//复制图像数据

(2)构造函数

CDynSplitView::CDynSplitView()

{

image_out=NULL;

image_in=NULL;

state2=0;

m_imagex=0;

m_imagey=0;

}

(3)成员函数的实现

voidCDynSplitView::clearmem(void)

{

CdipaxDocpDoc=(CdipaxDoc)GetDocument();

ASSERT_VALID(pDoc);

pDoc->statedoc=0;

state2=1;

m_imagex=pDoc->Cdib.GetWidth();

m_imagey=pDoc->Cdib.GetHeight();

longintsize=m_imagexm_imagey;

if(pDoc->Cdib.GetBitCount()>8)

size=size3;

image_out=newBYTE[size];

image_in=newBYTE[size];

memcpy(image_in,pDoc->Cdib.m_lpData,size);//复制原图像到处理区

}

(4)改写OnDraw()函数:

voidCDynSplitView::OnDraw(CDCpDC)

{

CdipaxDocpDoc=GetDocument();

ASSERT_VALID(pDoc);

if(!pDoc->statedoc&&state2==1)

{

pDoc->Cdib.Draw(pDC,image_out,CPoint(0,0),CSize(m_imagex,m_imagey));

}

}

2、灰度图像的二值化处理操作

(1)添加图像操作菜单:ID_BINARIZATION,标题为二值化处理,并添加事件处理程序。

voidCDynSplitView::OnBinarization()

{

//TODO:在此添加命令处理程序代码

clearmem(); //复制图像数据

for(inti=0;i
{

for(intj=0;j
{

if((image_in+im_imagey+j)>128)

(image_out+im_imagey+j)=255;

else

(image_out+im_imagey+j)=0;

}

}

Invalidate();

}

二值化处理:像素灰度值>128的为白色值255,否则为黑色值0。

























































献花(0)
+1
(本文系学海无涯GL首藏)