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

时间:2022-03-10 09:46:15

一、位图文件结构

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

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

typedef struct tagBITMAPFILEHEADER { // bmfh

WORD    bfType;                   //bfType指定文件类型。其值必须是0x4d42,即字符串“MB”,也就是说所有“.bmp”文件的头两个字节都是”MB“,标志该文件是位图文件。

DWORD   bfSize;                  //bfSize的值是位图文件的大小,包括4个字节。

WORD    bfReserved1;

WORD    bfReserved2;         //bfReserved1,bfReserved2为保留字,不用考虑。

DWORD   bfOffBits;        //为从文件头到实际的位图数据的偏移字节数,

} BITMAPFILEHEADER

该结构的长度是固定的,为14个字节(WORD 为无符号16位整数,DWORD为无符号32位整数)。

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

  1. typedef struct tagBITMAPINFO {
  2. BITMAPINFOHEADER    bmiHeader;     //位图信息头
  3. RGBQUAD             bmiColors[1];       //颜色表
  4. } BITMAPINFO;

可见位图信息也是由两部分组成的:位图信息头 + 颜色表/调色板(Palette)

2.1 、位图信息头:BitMapInfoHeader

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

  1. typedef struct tagBITMAPINFOHEADER{ // bmih
  2. DWORD  biSize;              //指定结构BITMAPINFOHEADER的字节数,为40个字节,即sizeof(BITMAPINFOHEADER)*
  3. LONG   biWidth;               //以像素为单位的图像宽度*
  4. LONG   biHeight;              // 以像素为单位的图像长度*
  5. WORD   biPlanes;             //目标设备的位平面数,必须是1,不用考虑。
  6. WORD   biBitCount           //指定表示颜色时每个像素的位数,常用的值为1(黑白二色图)、4(16色图)、8(256色)、24(真彩色)、*(1)
  7. DWORD  biCompression;   //图像的压缩格式(这个值几乎总是为0),有效地值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(这些都是Windows定义好的常量)
  8. DWORD  biSizeImage;       //以字节为单位的图像数据的大小(对BI_RGB压缩方式而言)
  9. LONG   biXPelsPerMeter;    //指定目标设备的水平分辨率,单位是像素/米。
  10. LONG   biYPelsPerMeter;    //指定目标设备的垂直分辨率,单位是像素/米。
  11. DWORD  biClrUsed;          //指定本图像实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次幂。(2)
  12. DWORD  biClrImportant;   //指定本图像中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。(3)
  13. } BITMAPINFOHEADER;

说明:*是需要加以注意的部分,因为它们是我们在进行位图操作时经常参考的变量
(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,表示所有的颜色都是必需的。

位图信息头结构的长度也是固定的,为40个字节(LONG为32位整数)。

2.2、颜色表/调色板:RGBQuad/Palette。

调色板一般是针对16位以下的图像而设置的,对于16位和16位以上的图像,由于其位图像素数据中直接对对应像素的RGB(A)颜色进行描述,因而省却了调色板,他们的BITMAPINFOHEADER后面直接是位图数据。而对于16位以下的图像,由于其位图像素数据中记录的只是调色板索引值,因而需要根据这个索引到调色板去取得相应的RGB(A)颜色。颜色表的作用就是创建调色板。调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2的biBitCount次幂个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节。

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

  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位位图,每四个字节代表了一个像素

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

头文件中定义

  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、读取位图

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

2、保存位图

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

3、显示位图

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