DIB位图文件的格式、读取、保存和显示

转自:http://blog.csdn.net/sjiang2142/article/details/6445082

一、位图文件结构

位图文件由三部分组成:文件头 + 位图信息 + 位图像素数据

1、位图文件头。位图文件头主要用于识别位图文件。以下是位图文件头结构的定义:

view plain
  1. typedef struct tagBITMAPFILEHEADER { // bmfh   
  2.     WORD    bfType;   
  3.     DWORD   bfSize;   
  4.     WORD    bfReserved1;   
  5.     WORD    bfReserved2;   
  6.     DWORD   bfOffBits;   
  7. } BITMAPFILEHEADER;  

其中的bfType值应该是“BM”(0x4d42),标志该文件是位图文件。bfSize的值是位图文件的大小。

2、位图信息中所记录的值用于分配内存,设置调色板信息,读取像素值等。
以下是位图信息结构的定义:

view plain
  1. typedef struct tagBITMAPINFO {  
  2.     BITMAPINFOHEADER    bmiHeader;  
  3.     RGBQUAD             bmiColors[1];  
  4. } BITMAPINFO;  

可见位图信息也是由两部分组成的:位图信息头 + 颜色表

2.1位图信息头。位图信息头包含了单个像素所用字节数以及描述颜色的格式,此外还包括位图的宽度、高度、目标设备的位平面数、图像的压缩格式。以下是位图信息头结构的定义:

view plain
  1. typedef struct tagBITMAPINFOHEADER{ // bmih   
  2.     DWORD  biSize;   
  3.     LONG   biWidth;   
  4.     LONG   biHeight;   
  5.     WORD   biPlanes;   
  6.     WORD   biBitCount   
  7.     DWORD  biCompression;   
  8.     DWORD  biSizeImage;   
  9.     LONG   biXPelsPerMeter;   
  10.     LONG   biYPelsPerMeter;   
  11.     DWORD  biClrUsed;   
  12.     DWORD  biClrImportant;   
  13. } BITMAPINFOHEADER;   

下表是对结构体当中各个成员的说明: 
结构成员
 说 明 
biSize 结构BITMAPINFOHEADER的字节数,即sizeof(BITMAPINFOHEADER)* 
biWidth
 以像素为单位的图像宽度* 
biHeight
 以像素为单位的图像长度* 
biplanes
 目标设备的位平面数 
biBitCount
 每个像素的位数*(1) 
biCompression
 图像的压缩格式(这个值几乎总是为0) 
biSizeImage
 以字节为单位的图像数据的大小(对BI_RGB压缩方式而言) 
biXPelsPermeter
 水平方向上的每米的像素个数 
biYpelsPerMeter
 垂直方向上的每米的像素个数 
biClrused
 调色板中实际使用的颜色数(2) 
biClrImportant
 现实位图时必须的颜色数(3)

说明:*是需要加以注意的部分,因为它们是我们在进行位图操作时经常参考的变量
(1)对于每个像素的字节数,分别有一下意义:
0,用在JPEG格式中
1,单色图,调色板中含有两种颜色,也就是我们通常说的黑白图片
4,16色图
8,256色图,通常说的灰度图
16,64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个位表示一个RGB分量
24,16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量
32,4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式

(2)这个值通常为0,表示使用biBitCount确定的全部颜色,例外是使用的颜色数目小于制定的颜色深度的颜色数目的最大值。

(3)这个值通常为0,表示所有的颜色都是必需的

2.2颜色表。颜色表一般是针对16位一下的图像而设置的,对于16位和16位以上的图像,由于其位图像素数据中直接对对应像素的RGB(A)颜色进行描述,因而省却了调色板。而对于16位一下的

图像,由于其位图像素数据中记录的只是调色板索引值,因而需要根据这个索引到调色板去取得相应的RGB(A)颜色。颜色表的作用就是创建调色板。

下图是带调色板和不带调色板的位图的简单示意图

图1 带调色板和不带调色板位图之间的区别

颜色表是由颜色表项组成的,颜色表项结构的定义如下:

view plain
  1. typedef struct tagRGBQUAD { // rgbq   
  2.     BYTE    rgbBlue;   
  3.     BYTE    rgbGreen;   
  4.     BYTE    rgbRed;   
  5.     BYTE    rgbReserved;   
  6. } RGBQUAD;  

其中需要注意的问题是,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB。

3、位图数据。最后,在位图文件头、位图信息头、位图颜色表之后,便是位图的主体部分:位图数据。根据不同的位图,位图数据所占据的字节数也是不同的,比如,对于8位位图,每个字

节代表了一个像素,对于16位位图,每两个字节代表了一个像素,对于24位位图,每三个字节代表了一个像素,对于32位位图,每四个字节代表了一个像素

二、位图文件的读取、保存和显示

头文件中定义

view plain
  1. private:  
  2.  DWORD m_dwDibSize;  
  3.  CPalette m_Palette;  
  4.  int m_nPaletteEntries;  
  5.  RGBQUAD *m_pPalette;  
  6.  unsigned char *m_pDib, *m_pDibBits;  
  7.  BITMAPINFOHEADER *m_pBIH;  
  

1、读取位图

view plain
  1. //加载图片  
  2. void CImageDisposeDlg::OnBtnloadimage()   
  3. {  
  4.  // TODO: Add your control notification handler code here  
  5.   
  6.  //文件路径名称  
  7.  CString pszFilename;  
  8.   
  9.  //浏览文件对话框  
  10.  CFileDialog hFileDlg(TRUE,"bmp",  
  11.     NULL,  
  12.     OFN_FILEMUSTEXIST|OFN_READONLY|OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR,  
  13.     TEXT("BMP (*.bmp)|*.bmp|所有文件(*.*)|*.*|"),  
  14.     NULL);  
  15.   
  16.  if(hFileDlg.DoModal() == IDOK)  
  17.  {  
  18.     //获得文件路径名称  
  19.     pszFilename=hFileDlg.GetPathName();   
  20.  }  
  21.    
  22.  //文件类  
  23.  CFile cf;  
  24.   
  25.  //文件打开失败,程序返回  
  26.  if( !cf.Open( pszFilename, CFile::modeRead ) )  
  27.  {  
  28.   return;  
  29.  }  
  30.   
  31.  //获得位图信息文件大小  
  32.  DWORD dwDibSize;  
  33.  dwDibSize = cf.GetLength() - sizeof( BITMAPFILEHEADER );  
  34.   
  35.  //申请一块内存存放位图信息  
  36.  unsigned char *pDib;  
  37.  pDib = new unsigned char [dwDibSize];  
  38.  if( pDib == NULL )  
  39.  {  
  40.   return;  
  41.  }  
  42.   
  43.  //位图文件头  
  44.  BITMAPFILEHEADER BFH;  
  45.   
  46.  //从文件读取位图文件头和位图数据  
  47.  try{  
  48.   // 判断读取位图文件头是否成功  
  49.   if( cf.Read( &BFH, sizeof( BITMAPFILEHEADER ) )  
  50.    != sizeof( BITMAPFILEHEADER ) ||  
  51.   
  52.    // 判断是否是位图类型  
  53.    BFH.bfType != 'MB' ||  
  54.   
  55.    // 判断读取位图数据是否成功  
  56.    cf.Read( pDib, dwDibSize ) != dwDibSize ){  
  57.   
  58.    //释放位图数据指针  
  59.    delete [] pDib;  
  60.   
  61.    //读取失败,程序返回  
  62.    return;  
  63.    }  
  64.   }  
  65.  catch( CFileException *e ){  
  66.    e->Delete();  
  67.    delete [] pDib;  
  68.    return;  
  69.   }  
  70.    
  71.  //重置全局位图信息指针  
  72.  if( m_pDib != NULL )  
  73.  {  
  74.   delete m_pDib;  
  75.  }  
  76.     
  77.  //将位图信息指针和位图信息大小赋值给全局变量  
  78.  m_pDib = pDib;  
  79.  m_dwDibSize = dwDibSize;  
  80.   
  81.  //获取位图信息头指针  
  82.  m_pBIH = (BITMAPINFOHEADER *) m_pDib;  
  83.   
  84.  //获取位图调色板指针  
  85.  m_pPalette = (RGBQUAD *) &m_pDib[sizeof(BITMAPINFOHEADER)];  
  86.   
  87.  // 计算调色板中实际颜色数量  
  88.  m_nPaletteEntries = 1 << m_pBIH->biBitCount;//1左移  
  89.  if( m_pBIH->biBitCount > 8 )  
  90.  {  
  91.   m_nPaletteEntries = 0;  
  92.  }  
  93.  else if( m_pBIH->biClrUsed != 0 )  
  94.  {  
  95.   m_nPaletteEntries = m_pBIH->biClrUsed;  
  96.  }  
  97.   
  98.  // 获取位图数据指针  
  99.  m_pDibBits = &m_pDib[sizeof(BITMAPINFOHEADER)+m_nPaletteEntries*sizeof(RGBQUAD)];  
  100.   
  101.  // 重置全局调色板  
  102.  if( m_Palette.GetSafeHandle() != NULL )  
  103.  {  
  104.   m_Palette.DeleteObject();  
  105.  }  
  106.    
  107.  //如果调色板颜色数量不为零,则通过逻辑调色板创建调色板  
  108.  if( m_nPaletteEntries != 0 ){  
  109.   
  110.   //为逻辑调色板分配内存  
  111.   LOGPALETTE *pLogPal = (LOGPALETTE *) new char  
  112.     [sizeof(LOGPALETTE)+  
  113.     m_nPaletteEntries*sizeof(PALETTEENTRY)];  
  114.   
  115.   if( pLogPal != NULL ){  
  116.   
  117.    //设置逻辑调色板的版本  
  118.    pLogPal->palVersion = 0x300;  
  119.    //设置逻辑调色板的颜色数量  
  120.    pLogPal->palNumEntries = m_nPaletteEntries;  
  121.   
  122.    //为每个颜色实体赋颜色值  
  123.    forint i=0; i<m_nPaletteEntries; i++ ){  
  124.     pLogPal->palPalEntry[i].peRed =  
  125.      m_pPalette[i].rgbRed;  
  126.     pLogPal->palPalEntry[i].peGreen =  
  127.      m_pPalette[i].rgbGreen;  
  128.     pLogPal->palPalEntry[i].peBlue =  
  129.      m_pPalette[i].rgbBlue;  
  130.     }  
  131.   
  132.    //创建调色板  
  133.    m_Palette.CreatePalette( pLogPal );  
  134.    //释放内存  
  135.    delete [] pLogPal;  
  136.    }  
  137.   }  
  138.  //重绘  
  139.  Invalidate();  
  140. }  

2、保存位图

view plain
  1. //保存图片  
  2. void CImageDisposeDlg::OnBtnsave()   
  3. {  
  4.  // TODO: Add your control notification handler code here  
  5.  //文件路径名称  
  6.  CString pszFilename;  
  7.   
  8.  //浏览文件对话框  
  9.  CFileDialog hFileDlg(FALSE,"bmp",  
  10.     NULL,  
  11.     OFN_FILEMUSTEXIST|OFN_READONLY|OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR,  
  12.     TEXT("BMP (*.bmp)|*.bmp|所有文件(*.*)|*.*|"),  
  13.     NULL);   
  14.   
  15.  if(hFileDlg.DoModal() == IDOK)  
  16.  {  
  17.     //获得文件路径名称  
  18.     pszFilename=hFileDlg.GetPathName();   
  19.  }  
  20.   
  21.  // 如果位图信息为空则程序返回  
  22.  if( m_pDib == NULL )  
  23.   return;  
  24.    
  25.  //文件类  
  26.  CFile cf;  
  27.   
  28.  // 创建文件  
  29.  if( !cf.Open( pszFilename,  
  30.   CFile::modeCreate | CFile::modeWrite ) )  
  31.   return;  
  32.    
  33.  // 写入数据  
  34.  try{  
  35.     
  36.   //创建位图文件头  
  37.   BITMAPFILEHEADER BFH;  
  38.   memset( &BFH, 0, sizeof( BITMAPFILEHEADER ) );  
  39.   BFH.bfType = 'MB';  
  40.   BFH.bfSize = sizeof( BITMAPFILEHEADER ) + m_dwDibSize;  
  41.   BFH.bfOffBits = sizeof( BITMAPFILEHEADER ) +  
  42.    sizeof( BITMAPINFOHEADER ) +  
  43.    m_nPaletteEntries * sizeof( RGBQUAD );  
  44.   
  45.   //将数据写入文件  
  46.   cf.Write( &BFH, sizeof( BITMAPFILEHEADER ) );  
  47.   cf.Write( m_pDib, m_dwDibSize );  
  48.   }  
  49.  catch( CFileException *e ){  
  50.   e->Delete();  
  51.   return;  
  52.   }  
  53.   
  54. }  

3、显示位图

view plain
  1. void CImageDisposeDlg::OnPaint()   
  2. {  
  3.   
  4.  CPaintDC dc(this);   
  5.   
  6.  //如果位图信息为空,程序返回  
  7.  if( m_pDib == NULL )  
  8.   return;  
  9.   
  10.  //获得位图宽高  
  11.  int nWidth,nHeight;  
  12.  nWidth = m_pBIH->biWidth;  
  13.  nHeight = m_pBIH->biHeight;  
  14.   
  15.  //如果有调色板则使用调色板  
  16.  if( m_Palette.GetSafeHandle() != NULL )  
  17.  {  
  18.   CPalette *pOldPalette;  
  19.   pOldPalette = dc.SelectPalette( &m_Palette, FALSE );//选择调色板  
  20.   dc.RealizePalette();//实现调色板  
  21.   
  22.   //绘图  
  23.   StretchDIBits( dc.m_hDC, 0, 0,  
  24.    nWidth, nHeight,  
  25.    0, 0,  
  26.    m_pBIH->biWidth, m_pBIH->biHeight,  
  27.    m_pDibBits,  
  28.    (BITMAPINFO *) m_pBIH,  
  29.    BI_RGB, SRCCOPY );  
  30.   
  31.   //恢复调色板  
  32.   dc.SelectPalette( pOldPalette, FALSE );  
  33.  }  
  34.  else//没有调色板,直接绘制  
  35.  {  
  36.   StretchDIBits( dc.m_hDC, 0, 0,  
  37.    nWidth, nHeight,  
  38.    0, 0,  
  39.    m_pBIH->biWidth, m_pBIH->biHeight,  
  40.    m_pDibBits,  
  41.    (BITMAPINFO *) m_pBIH,  
  42.    BI_RGB, SRCCOPY );  
  43.  }  
  44. }  


你可能感兴趣的:(DIB位图文件的格式、读取、保存和显示)