位图文件的读取显示

本文从位图文件的格式入手,给出详细的格式说明,并有源码分析。
一、位图文件结构

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

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

typedef struct tagBITMAPFILEHEADER { // bmfh 
    WORD    bfType; 
    DWORD   bfSize; 
    WORD    bfReserved1; 
    WORD    bfReserved2; 
    DWORD   bfOffBits; 
} BITMAPFILEHEADER;

其中的bfType值应该是“BM”(0x4d42),标志该文件是位图文件。bfSize的值是位图文件的大小。
2、位图信息中所记录的值用于分配内存,设置调色板信息,读取像素值等。
以下是位图信息结构的定义:

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO;

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



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

typedef struct tagBITMAPINFOHEADER{ // bmih 
    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
每个像素的位数*(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 带调色板和不带调色板位图之间的区别

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

typedef struct tagRGBQUAD { // rgbq 
    BYTE    rgbBlue; 
    BYTE    rgbGreen; 
    BYTE    rgbRed; 
    BYTE    rgbReserved; 
} RGBQUAD;

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

3、位图数据。最后,在位图文件头、位图信息头、位图颜色表之后,便是位图的主体部分:位图数据。根据不同的位图,位图数据所占据的字节数也是不同的,比如,对于8位位图,每个字节代表了一个像素,对于16位位图,每两个字节代表了一个像素,对于24位位图,每三个字节代表了一个像素,对于32位位图,每四个字节代表了一个像素。

二、源码分析
//代码只给出了最后显示的部分,这里是显示的关键
//m_nState表示不同打开显示bmp文件的方式
void CPictureView::OnDraw(CDC* pDC)
{
 // TODO: add draw code for native data here
 CPictureDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 if (m_nState == 1)   //第一种方式,通过最基本的读取方式来完成
 {
  CFile file;               //表示欲打开的bmp文件
  char *pColor;
  int i,j;
  file.Open(m_pName,CFile::modeRead,NULL);
  ULONGLONG ulSize = file.GetLength();
  char *pBuf = new char[ulSize];                         //定义一个同位图文件相同大小的缓冲区
  UINT uiTemp = file.Read(pBuf,ulSize);            //读取位图数据到缓冲区
  file.Close();

  LONG cx,cy;

  DWORD bfOffBits;
  WORD biBitCount;
  bfOffBits = *(DWORD*)(pBuf+10);                //移动10位处,取出DWORD   bfOffBits;
  
  cx = *(LONG*)(pBuf+18);                              //分别取出x、y方向象素值,移动18和22(实际 
  cy = *(LONG*)(pBuf+22);                              //上距离bfOffBits 40)
  biBitCount = *(WORD*)(pBuf+28);                //继续取biBitCount
  BYTE byBlue,byGreen,byRed;
  if (biBitCount == 24)//如果是真彩色24位图片
  {
   char *pTemp;

   pTemp = pBuf+54;//54位后处,是每个象素的具体颜色值
   for(j=cy-1;j>=0;j--)
   {
    for(i=0;i<cx;i++)
    {
     byBlue = *pTemp;   //因为是真彩色24位,所以刚好8位是一种颜色,++就可以取下一颜色
                                    //值了
     pTemp++;
     byGreen = *pTemp;
     pTemp++;
     byRed = *pTemp;
     pTemp++;
     SetPixel(pDC->m_hDC,i,j,RGB(byRed,byGreen,byBlue));  //在屏幕上相应位置画上颜色
    }
    if ((cx*3)%4 != 0)
    {
     pTemp += (4-(cx*3)%4);
    }
   }
  }
  else if(biBitCount == 8) //256色图片
  {
   unsigned char *pTemp = (unsigned char*)(void*)(pBuf+bfOffBits);
   pColor = pBuf+54;
   int nIndex;
   for(j=cy-1;j>=0;j--)
   {
    for(i=0;i<cx;i++)
    {
     nIndex = (UINT)*pTemp;//注意现在是256色图片,颜色值取法有所不同
     byBlue = *(pColor+nIndex*4);
     byGreen = *(pColor+nIndex*4+1);
     byRed = *(pColor+nIndex*4+2);
     SetPixel(pDC->m_hDC,i,j,RGB(byRed,byGreen,byBlue));    
     pTemp++;
    }
    pTemp += (4-cx%4);
   }
  }
  else//剩下的16色图片和单色图片,读者自己可以自行试验
   AfxMessageBox("单色与16色图片打开功能暂未提供!");
  delete []pBuf;
  pBuf = NULL;
 }
 if (m_nState == 2)//第二种方法使用SetDIBitsToDevice函数来完成
 {
  CFile file;
  file.Open(m_pName,CFile::modeRead,NULL);
  ULONGLONG ulSize = file.GetLength();
  char *pBuf = new char[ulSize];
  file.Read(pBuf,ulSize);
  file.Close();
  LONG cx,cy;
  DWORD bfOffBits;
  bfOffBits = *(DWORD*)(pBuf+10);
  cx = *(LONG*)(pBuf+18);
  cy = *(LONG*)(pBuf+22);
  void *pBmpInfo,*lpvBufBmp;
  lpvBufBmp = pBuf+bfOffBits;
  pBmpInfo = pBuf+14;
  BITMAPINFO bmpInfo;
  memcpy(&bmpInfo.bmiHeader,pBmpInfo,40);
  SetDIBitsToDevice(pDC->m_hDC,0,0,cx,cy,0,0,0,cy,lpvBufBmp,
   &bmpInfo,DIB_PAL_COLORS);  
  delete [] pBuf;
  pBuf = NULL;
 }
 if (m_nState == 3)//第三种方法使用BitBlt函数来完成,这种方法最为简便实用。
 {
   CDC dcImage;
  if(!dcImage.CreateCompatibleDC(pDC))
   return;
  BITMAP bm;
  m_bBitmap.GetBitmap(&bm);
  dcImage.SelectObject(&m_bBitmap);
  pDC->BitBlt(0,0,bm.bmWidth,bm.bmHeight,&dcImage,0,0,SRCCOPY);
 }
}
通过上述分析,可知,使用位图文件本身的格式来操作位图,过于复杂,而且还需要考虑效率优化的问题,所以在普通使用过程中,我们可以使用后两种方法来实现显示位图。但是技术本身使得我们可以更好的了解位图的详细信息!
                                                                             --风小云原创,转贴请注明出处!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值