如何显示位图

来源:互联网 发布:数据恢复大师好用么 编辑:程序博客网 时间:2024/06/15 04:43

   //原著:joes Tomas
  //译者:重庆大学光电工程学院 贾旭滨
  //欢迎批评指教,谢谢!
   位图文件是分成4部分的。第一部分是位图文件头,它包括位图文件名,位图的大小和位图数据离文件头的偏移量。接下去的是位图信息头,它包括了位图的许多信息,比如位图的宽度,高度和位图使用的颜色数。再后面是颜色表,它可能包含了2个或更多的RGBQUAD结构。最后面是位图图象的数据。
  一.位图结构如下:
  ---- 一、BMP文件结构
  ---- 1. BMP文件组成
  ---- BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
  ---- 2. BMP文件头
  ---- BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
  ---- 其结构定义如下:
  typedef struct tagBITMAPFILEHEADER
  {
  WORDbfType; // 位图文件的类型,必须为BM
  DWORD bfSize; // 位图文件的大小,以字节为单位
  WORDbfReserved1; // 位图文件保留字,必须为0
  WORDbfReserved2; // 位图文件保留字,必须为0
  DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
  // 文件头的偏移量表示,以字节为单位
  } BITMAPFILEHEADER;
  ---- 3. 位图信息头
  BMP位图信息头数据用于说明位图的尺寸等信息。
  typedef struct tagBITMAPINFOHEADER{
   DWORD biSize; // 本结构所占用字节数
   LONGbiWidth; // 位图的宽度,以像素为单位
   LONGbiHeight; // 位图的高度,以像素为单位
   WORD biPlanes; // 目标设备的级别,必须为1
   WORD biBitCount// 每个像素所需的位数,必须是1(双色),
   // 4(16色),8(256色)或24(真彩色)之一
   DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
   // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
   DWORD biSizeImage; // 位图的大小,以字节为单位
   LONGbiXPelsPerMeter; // 位图水平分辨率,每米像素数
   LONGbiYPelsPerMeter; // 位图垂直分辨率,每米像素数
   DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
   DWORD biClrImportant;// 位图显示过程中重要的颜色数
  } BITMAPINFOHEADER;
  ---- 4. 颜色表
   颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
  typedef struct tagRGBQUAD {
  BYTErgbBlue;// 蓝色的亮度(值范围为0-255)
  BYTErgbGreen; // 绿色的亮度(值范围为0-255)
  BYTErgbRed; // 红色的亮度(值范围为0-255)
  BYTErgbReserved;// 保留,必须为0
  } RGBQUAD;
  颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
  当biBitCount=1,4,8时,分别有2,16,256个表项;
  当biBitCount=24时,没有颜色表项。
   位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
  typedef struct tagBITMAPINFO {
   BITMAPINFOHEADER bmiHeader; // 位图信息头
   RGBQUAD bmiColors[1]; // 颜色表
  } BITMAPINFO;
  ---- 5. 位图数据
   位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
  当biBitCount=1时,8个像素占1个字节;
  当biBitCount=4时,2个像素占1个字节;
  当biBitCount=8时,1个像素占1个字节;
  当biBitCount=24时,1个像素占3个字节;
   Windows规定一个扫描行所占的字节数必须是 4的倍数(即以long为单位),不足的以0填充,一个扫描行所占的字节数计算方法: DataSizePerLine= (biWidth* biBitCount+31)/8; 一个扫描行所占的字节数 DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数位图数据的大小(不压缩情况下): DataSize= DataSizePerLine* biHeight;
   随便说一句,位图是设备无关图象,所以它的文件的扩展名就dib
   下面说的函数在win95/winNT都行。在win95上,你可以使用函数loadimage()来加载一个位图文件,但是在winNT上,你也可以使用这个函数,但是其中的lr_loadfromfile标志位却不能用了。
  第1步:载入位图
   为了正确的画出位图来,我们就需要位图结构中后面3部分的信息了。首先要用到的是位图信息头,从中我们可以知道得分配多少内存给位图,然后就可以把位图读进来了,而且调色板也可以创建起来。
  //loadbmp -载入位图文件,并且给它创建调色板
  //return -成功的标志
  //sbmpfile -位图文件的路径
  //phdib -指向分配给位图文件的内存空间的指针,会被释放掉
  //ppal -控制逻辑调色板
  //
  bool loadbmp( lpctstr sbmpfile, hglobal *phdib, cpalette *ppal )
  {
   cfile file;
   if( !file.open( sbmpfile, cfile::moderead) )
   return false;
   bitmapfileheader bmfheader;
   long nfilelen;
   nfilelen = file.getlength();
   // read file header
   if (file.read((lpstr)&bmfheader, sizeof(bmfheader)) != sizeof(bmfheader))
   return false;
   // file type should be 'bm'
   if (bmfheader.bftype != ((word) ('m' << 8) | 'b')) return false;
  hglobal hdib="::globalalloc(gmem_fixed," nfilelen);
  if (hdib="=" 0) return false;
   //read the remainder of the bitmap file.
  if (file.readhuge((lpstr)hdib, nfilelen sizeof(bitmapfileheader)) !="nfilelen" sizeof(bitmapfileheader) )
  { ::globalfree(hdib);
  return false; }
  bitmapinfo &bminfo="*(lpbitmapinfo)hdib" ;
  int ncolors="bminfo.bmiheader.biclrused" ? bminfo.bmiheader.biclrused : 1 << bminfo.bmiheader.bibitcount;
  // create the palette
  if(ncolors <="256" ) { uint nsize="sizeof(logpalette)" + (sizeof(paletteentry) * ncolors); logpalette *plp="(logpalette" *) new byte[nsize]; plp->palversion = 0x300;
   plp->palnumentries = ncolors;
   for( int i=0; i palpalentry[i].pered = bminfo.bmicolors[i].rgbred;
   plp->palpalentry[i].pegreen = bminfo.bmicolors[i].rgbgreen;
   plp->palpalentry[i].peblue = bminfo.bmicolors[i].rgbblue;
   plp->palpalentry[i].peflags = 0;
   }
   ppal->createpalette( plp );
   delete[] plp;
   }
   *phdib = hdib;
   return true;
  }
  //第2步:画位图
   下面的函数只是个例子来说明怎么样画位图,它使用了函数setdibitstodevice()来达到目的,你应该知道位图文件存储是从最后面一行开始的,所以,你应该特别注意!其实你也可以使用函数stretchdibits()来显示位图,它的功能更强,不但可以伸展和压缩位图,还可以不同的光栅操作来产生位图。
  void drawdib( cdc* pdc, hglobal hdib, cpalette *ppal )
  {
   lpvoid lpdibbits; // pointer to dib bits
   bool bsuccess=false; // success/fail flag
   bitmapinfo &bminfo = *(lpbitmapinfo)hdib ;
   int ncolors = bminfo.bmiheader.biclrused ? bminfo.bmiheader.biclrused :
   1 << bminfo.bmiheader.bibitcount; if( bminfo.bmiheader.bibitcount> 8 )
   lpdibbits = (lpvoid)((lpdword)(bminfo.bmicolors +
   bminfo.bmiheader.biclrused) +
   ((bminfo.bmiheader.bicompression == bi_bitfields) ? 3 : 0));
   else
   lpdibbits = (lpvoid)(bminfo.bmicolors + ncolors);
  
   if( ppal && (pdc->getdevicecaps(rastercaps) & rc_palette) )
   {
   pdc->selectpalette(ppal, false);
   pdc->realizepalette();
   }
   ::setdibitstodevice(pdc->m_hdc, // hdc
   0, // destx
   0, // desty
   bminfo.bmiheader.biwidth, // ndestwidth
   bminfo.bmiheader.biheight, // ndestheight
   0, // srcx
   0, // srcy
   0, // nstartscan
   bminfo.bmiheader.biheight, // nnumscans
   lpdibbits, // lpbits
   (lpbitmapinfo)hdib, // lpbitsinfo
   dib_rgb_colors); // wusage
  }

 

      From: http://www.wangchao.net.cn/bbsdetail_16721.html

 

=========================================================================

DDB与DIB

vc 位图操作 BITMAPINFO 赋值

BMP 文件结构分成以下几个部分:
1 BITMAP FILEHEADER (BMP 文件头)
2 BITMAP INFOHEADER (BMP 文件信息头)
3 RGBQUAD (BMP 文件调色板)
4 BITMAP DATA (BMP 文件数据)

Windows 中位图有两种格式:
设备相关位图 Device Depend Bitmap DDB
设备无关位图 Device Independ Bitmap DIB

DDB 位图格式 ------------ BITMAP(结构体) CBitmap(MFC类) HBITMAP(HANDLE)
由 BITMAP 数据类型的结构 + 图像数据构成。
因为DDB没有保存位图的调色板,在不同类设备显示时可能造成完全失真。

DIB 位图格式 ----------- BITMAPINFO
BITMAP INFOHEADER (BMP 文件信息头) + RGBQUAD (BMP 文件调色板) + BITMAP DATA (BMP 文件数据) 三部分构成
它实际就是BMP文件去掉BITMAP FILEHEADER (BMP 文件头),即一个BITMAPINFO结构后面接上调色板再加上图像数据。

BMP文件的显示 ---- DIB
首先将BMP读成DIB格式,当显示时直接DIB显示,只要读入BITMAPINFO结构和图像数据即可。

BMP文件的显示 ---- DDB
首先要先将DIB位图转化为DDB位图,再由MFC的CBitmap类显示。

总结: 统一使用DIB显示,即 BITMAPINFO;//个人意见
不要使用以下:BITMAP(结构体) CBitmap(MFC类) HBITMAP(HANDLE)//个人意见

//**********************************************
重点说说 BITMAPINFO MFC中的定义如下:
typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;

这个结构体定义很奇怪,大概的意思是兼容没有调色板的情况,不看它的写法,
依据BITMAPINFO的大小为 sizeof(BITMAPINFOHEADER) + numQuad * sizeof(RGBQUAD)       采用如下赋值方法

   CFile file;
   BITMAPFILEHEADER FILE_HEADER; //文件头
BITMAPINFOHEADER INFO_HEADER; //信息头
   file.Open("d://demo.bmp", CFile::modeRead);

   file.Read(&FILE_HEADER, sizeof(BITMAPFILEHEADER));
   if(FILE_HEADER.bfType != 0x4d42)
   {
    file.Close();
    AfxMessageBox("原图象不为BMP图象!");
    return;
   }

   file.Read(&INFO_HEADER,sizeof(BITMAPINFOHEADER));

   // 调色板数目
   int numQuad = 0;
   if(INFO_HEADER.biBitCount < 24)
   {
    numQuad = 1 << INFO_HEADER.biBitCount; //1右移?位 = 2的?次方
   }

   BITMAPINFO *pBMP_INFO = (BITMAPINFO*)HeapAlloc(GetProcessHeap(),0,
    sizeof(BITMAPINFOHEADER) + numQuad * sizeof(RGBQUAD));
   memcpy(pBMP_INFO, &INFO_HEADER, sizeof(BITMAPINFOHEADER));
   RGBQUAD *quad = (RGBQUAD*)((BYTE*)pBMP_INFO + sizeof(BITMAPINFOHEADER));
   if(numQuad != 0)
   {
    file.Read(quad, sizeof(RGBQUAD) * numQuad);
   }

   int sizeBuf = FILE_HEADER.bfSize - FILE_HEADER.bfOffBits;
   BYTE *bmpBuf = new BYTE[sizeBuf];
   file.Read(bmpBuf, sizeBuf);
   file.Close();

   CDC *pDC = GetDC();
   Graphics gdiDC(pDC->GetSafeHdc());
   Bitmap *pBmp = Bitmap::FromBITMAPINFO(pBMP_INFO, bmpBuf);//GDI+从BITMAPINFO生成bmp的方法
   gdiDC.DrawImage(pBmp, 0, 0);
   ReleaseDC(pDC);

//====================BITMAPINFO 说明=====================================

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;

在一个结构体定义
RGBQUAD bmiColors[1];
是很危险也非常巧妙的不得已的办法,它的存在只为照顾bmp文件而存在;
因此使用BITMAPINFO有很多限制:
限制一 RGBQUAD bmiColors[1];此类设置只能存在1个,且必在最后
限制二 RGBQUAD类型必为4字节的整倍数
限制三 虽然sizeof(BITMAPINFO) = 40 + 4 = 44;
       但为BITMAPINFO申请内存时必须是40 + 4 * 调色板个数,否则内存越界
       也就是在使用BITMAPINFO时申请的字节数可能为40或大于44的某个数,总之一定不是44,如果为44会报错
bmp文件的字节是连续的(当然任何文件都是连续的),在文件中,紧随BITMAPINFOHEADER之后是调色板,如果没有调色板,紧随BITMAPINFOHEADER之后是数据,注意如果没有调色板,在BITMAPINFOHEADER与数据之间没有任何字节存在,为了照顾bmp文件BITMAPINFO被设计成这样,bmiColors[1]是个数组,它存放了数组的第1个元素,也就是bmiColors[0],接下来一定是bmiColors[1](此bmiColors[1]表示RGBQUAD数组的第2个元素,而BITMAPINFO中RGBQUAD bmiColors[1];则表示定义一个类型,是一个数组,因为数组是连续存储的,这样设计才能保证与bmp文件结构吻合),好乱~

申请内存也可这样写:
BITMAPINFO *pBMP_INFO = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER) + numQuad * sizeof(RGBQUAD));

BITMAPINFO *pBMP_INFO = new BITMAPINFO[sizeof(BITMAPINFOHEADER) + numQuad * sizeof(RGBQUAD)];

 

From: http://xinxzhao.blog.163.com/blog/static/27707400200982441252458/

=========================================================================

DDB与DIB的区别

DDB(设备相关位图)

DDB依赖于具体设备:
DDB的颜色模式必需与输出设备相一致。例如,如果当前的显示设备是256色模式,那么DDB必然也是256色的。
在256色以下的位图中存储的像素值是系统调色板的索引,其颜色依赖于系统调色板。
由于DDB高度依赖输出设备,所以DDB只能存在于内存中,它要么在视频内存中,要么在系统内存中

DIB的与设备无关性主要体现在以下两个方面:
DIB的颜色模式与设备无关。例如,一个256色的DIB即可以在真彩色显示模式下使用,也可以在16色模式下使用。
256色以下(包括256色)的DIB拥有自己的颜色表,像素的颜色独立于系统调色板。
由于DIB不依赖于具体设备,因此可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存在磁盘中的,有时也会保存在*.DIB文件中。运行在不同输出设备下的应用程序可以通过DIB来交换图象

DDB的创建
MFC的CBitmap类封装了DDB。该类提供了几个函数用来创建DDB:
BOOL LoadBitmap( LPCTSTR lpszResourceName );
BOOL LoadBitmap( UINT nIDResource );
该函数从资源中载入一幅位图,若载入成功则返回TRUE。资源位图实际上是一个DIB,该函数在载入时把它转换成了DDB。

BOOL CreateBitmap( int nWidth, int nHeight, UINT nPlanes, UINT nBitcount, const void* lpBits );
该函数用来创建一幅空白的DDB。参数nWidth和nHeight以像素为单位说明了位图的宽度和高度。nPlanes是DDB的色平面数,nBitcount是每个色平面的颜色位数。一般来说,nPlanes为1,而nBitcount代表DDB中每个像素值所占的位数,但在创建16色DDB时,nPlanes为4,而nBitcount为1。参数lpBits指向存储像素阵列的数组,该数组应该逐行存储位图的每个像素值。注意,数组中每行像素的数目必需是偶数个字节,如果是奇数,则应该用0补足。若创建成功函数返回TRUE。

BOOL CreateCompatibleBitmap( CDC* pDC, int nWidth, int nHeight );
该函数创建一个与指定设备上下文兼容的DDB。参数pDC指向一个设备上下文,nWidth和nHeight是DDB的尺寸。若创建成功函数返回TRUE

DIB(设备无关位图)

至于DIB,MFC未提供现成的类来封装DIB。要想使用DIB,首先应该了解DIB的结构。这个你可以
到任何一本图像处理资料中找到详细说明。

DIB的颜色信息储存在自己的颜色表中,程序一般要根据颜色表为DIB创建逻辑调色板。在输出一幅DIB之前,程序应该将其逻辑调色板选入到相关的设备上下文中并实现到系统调色板中,然后再调用相关的GDI函数(如::SetDIBitsToDevice或::StretchDIBits)输出DIB。在输出过程中,GDI函数会把DIB转换成DDB,这项工作主要包括以下两步:

将DIB的颜色格式转换成与输出设备相同的颜色格式。例如,在真彩色的显示模式下要显示一个256色的DIB,则应该将其转换成24位的颜色格式。

将DIB像素的逻辑颜色索引转换成系统调色板索引。

From: http://blog.myspace.cn/e/402718065.htm

=========================================================================

DIB转DDB,DDB转DIB

首先,你可以不必把DIB转换成DDB就可以让它显示在设备上。但是那样的话,你都会看见会程序运行得有多慢了,而且你也不
能使用一些有关DDB操作的函数功能了,诸如BitBlt()....

以下是从DIB创建一个DDB的基本步骤:

1.从DIB颜色表信息中的信息可以创建一个逻辑调色板,如果设备支持的话,你只需要做这一步。为了创建一个调色板,就需要
分配给逻辑调色板结构。初始化palversion和palnumentries,从DIB中的颜色表把颜色数拷贝过来。然后我们就可以使用createpalett
e()函数来使用我们初始化的逻辑调色板了。

2.把逻辑调色板选到设备中,然后实现它。

3.创建DDB,可以使用createdibbitmap()函数。

4.最后不要忘记释放掉分给逻辑调色板结构的内存。

虽然这样就可以创建一个逻辑调色板了,但它没有返回信息给所调用的程序代码。如果DIB描述的是一个256色的位图的话,而
且设备也只支持256色,那么DDB可能就不能正确的显示在设备上。那是因为系统使用的颜色数与位图的颜色数不相同,因此,
你要修改一下,在我们画位图之前,就让它返回逻辑调色板,然后我们选择和实现它到设备的上下文。

//以下是程序代码

HBITMAP CProcess::DIBtoDDB(HANDLE hdib )
{
      LPBITMAPINFOHEADER lpbi;
      HBITMAP hbm;
      CPalette Pal;
      CPalette* pOldPal;
      CClientDC dc(NULL);

      if (hdib == NULL)
{
          return NULL;
}
      lpbi = (LPBITMAPINFOHEADER)hdib;

      int nColors = lpbi->biClrUsed ? lpbi->biClrUsed : 1 << lpbi->biBitCount; //颜色表项的个数

      BITMAPINFO &bmInfo = *(LPBITMAPINFO)hdib;
      LPVOID lpDIBits;
      if(bmInfo.bmiHeader.biBitCount > 8 )
{
          lpDIBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors
                      + bmInfo.bmiHeader.biClrUsed)
                      + ((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
}
      else{
          lpDIBits = (LPVOID)(bmInfo.bmiColors + nColors);
}

      // create and select a logical palette if needed
      if( nColors <= 256 && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
{
          UINT nSize=sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors);
          LOGPALETTE *plp=(LOGPALETTE *) new BYTE[nSize];
          plp->palVersion = 0x300;
          plp->palNumEntries = nColors;

          for( int i=0; i<nColors;i++)
    {
              plp->palPalEntry[i].peRed = bmInfo.bmiColors[i].rgbRed;
              plp->palPalEntry[i].peGreen = bmInfo.bmiColors[i].rgbGreen;
     plp->palPalEntry[i].peBlue = bmInfo.bmiColors[i].rgbBlue;
     plp->palPalEntry[i].peFlags = 0;
    }

    Pal.CreatePalette(plp);

    delete[] plp; //要及时处理

    // select and realize the palette
    pOldPal = dc.SelectPalette(&Pal, false);
    dc.RealizePalette();
}


hbm = CreateDIBitmap(dc.GetSafeHdc(), //设备上下文的句柄
(LPBITMAPINFOHEADER)lpbi, //位图信息头指针
(long)CBM_INIT, //初始化标志
lpDIBits, //初始化数据指针
(LPBITMAPINFO)lpbi, //位图信息指针
DIB_RGB_COLORS); //颜色数据的使用方式

if (Pal.GetSafeHandle())
{
    dc.SelectPalette(pOldPal,false);
}

return hbm;
} --------------------------

DDB转为DIB

把DDB转换成DIB  
    设备相关位图(DDB)显示方式是尽可能与显示设备驱动程序相匹配,这样,DDB不可能与其他显示设备兼容;而设备无关位图(DIB)能与所有显示设备兼容,但是,其缺点在于显示速度比较慢。  
    
    我们需要把DDB转换为DIB的一种情况是,需要将位图保存到一个文件中。下面是其实现的代码。  
    
//    DDBToDIB    -    Creates    a    DIB    from    a    DDB  
//    bitmap    -    Device    dependent    bitmap(DDB)  
//    pPal     -    Logical    palette
/*
HANDLE    DDBToDIB(CBitmap&    bitmap, DWORD    dwCompression, CPalette*    pPal)    
{  
BITMAP bm;  
BITMAPINFOHEADER bi;  
LPBITMAPINFOHEADER lpbi;  
DWORD dwLen;    //DIB长度
HANDLE hDIB;  
HANDLE handle;  
HDC hDC;  
HPALETTE hPal;  
    
ASSERT(bitmap.GetSafeHandle());  
    
//    The    function    has    no    arg    for    bitfields
if(    dwCompression    ==    BI_BITFIELDS    )  
      {
          return    NULL;  
      }
    
//    If    a    palette    has    not    been    supplied    use    defaul    palette  
hPal    =    (HPALETTE)    pPal->GetSafeHandle();  
if    (hPal==NULL)
{
    hPal    =    (HPALETTE)    GetStockObject(DEFAULT_PALETTE);  
}
    
//    Get    bitmap    information  
bitmap.GetObject(sizeof(bm),(LPSTR)&bm);  
    
//    Initialize    the    bitmapinfoheader  
bi.biSize    =    sizeof(BITMAPINFOHEADER);  
bi.biWidth    =    bm.bmWidth;  
bi.biHeight      =    bm.bmHeight;  
bi.biPlanes      =    1;  
bi.biBitCount    =    bm.bmPlanes    *    bm.bmBitsPixel;  
bi.biCompression =    dwCompression;  
bi.biSizeImage    =    0;  
bi.biXPelsPerMeter =    0;  
bi.biYPelsPerMeter =    0;  
bi.biClrUsed    =    0;  
bi.biClrImportant =    0;  
    
//    Compute    the    size    of    the      infoheader    and    the    color    table  
int    nColors    =    (1    <<    bi.biBitCount);  
if(    nColors>    256    )
{    
          nColors    =    0;  
}
dwLen      =    bi.biSize    +    nColors    *    sizeof(RGBQUAD);  
    
//    We    need    a    device    context    to    get    the    DIB    from  
hDC    =    GetDC(NULL);  
hPal    =    SelectPalette(hDC,hPal,FALSE);  
RealizePalette(hDC);  
    
//    Allocate    enough    memory    to    hold    bitmapinfoheader    and    color    table  
hDIB    =    GlobalAlloc(GMEM_FIXED,dwLen);  
    
if (!hDIB)//如果内存申请失败
{  
    SelectPalette(hDC,hPal,FALSE);  
    ReleaseDC(NULL,hDC);  
    return    NULL;  
}  
    
lpbi    =    (LPBITMAPINFOHEADER)hDIB;    //使用hDIB申请的内存
    
*lpbi    =    bi;  
    
//    Call    GetDIBits    with    a    NULL    lpBits    param,    so    the    device    driver    
//    will    calculate    the    biSizeImage    field    
GetDIBits(hDC,    (HBITMAP)bitmap.GetSafeHandle(),    0L,    (DWORD)bi.biHeight,  
       (LPBYTE)NULL,    (LPBITMAPINFO)lpbi,    (DWORD)DIB_RGB_COLORS);  
//GetDIBits函数用来计算所需的内存空间的大小和得到DIB参数
   
bi    =    *lpbi;  
    
//    If    the    driver    did    not    fill    in    the    biSizeImage    field,    then    compute    it  
//    Each    scan    line    of    the    image    is    aligned    on    a    DWORD    (32bit)    boundary  
if (bi.biSizeImage    ==    0)
{  
    bi.biSizeImage    =    ((((bi.biWidth    *    bi.biBitCount)    +    31)    &    ~31)    /    8)    *    bi.biHeight;
}
    
      //    If    a    compression    scheme    is    used    the    result    may    infact    be    larger  
      //    Increase    the    size    to    account    for    this.  
      if (dwCompression    !=    BI_RGB)
{
    bi.biSizeImage    =    (bi.biSizeImage    *    3)    /    2;  
}  
    
//    Realloc    the    buffer    so    that    it    can    hold    all    the    bits  
dwLen    +=    bi.biSizeImage;  
if    (handle    =    GlobalReAlloc(hDIB,    dwLen,    GMEM_MOVEABLE))  
{
    hDIB    =    handle;  
}
else
{  
    GlobalFree(hDIB);  
    
    //    Reselect    the    original    palette  
    SelectPalette(hDC,hPal,FALSE);  
    ReleaseDC(NULL,hDC);  
    return    NULL;  
}  
    
//    Get    the    bitmap    bits    获取内存
lpbi    =    (LPBITMAPINFOHEADER)hDIB;  
    
//    FINALLY    get    the    DIB  
BOOL    bGotBits    =    GetDIBits(    hDC,    (HBITMAP)bitmap.GetSafeHandle(),  
                                     0L,      //    Start    scan    line  
                                     (DWORD)bi.biHeight,    //    #    of    scan    lines  
                                     (LPBYTE)lpbi       //    address    for    bitmap    bits  
                                      +    (bi.biSize    +    nColors    *    sizeof(RGBQUAD)),  
                                     (LPBITMAPINFO)lpbi,    //    address    of    bitmapinfo  
                                     (DWORD)
                                     DIB_RGB_COLORS    );    //    Use    RGB    for    color    table  
    
if(    !bGotBits    )  
{    
    GlobalFree(hDIB);  
      
    SelectPalette(hDC,hPal,FALSE);  
    ReleaseDC(NULL,hDC);  
    return    NULL;  
}  
    
SelectPalette(hDC,hPal,FALSE);  
ReleaseDC(NULL,hDC);  
return    hDIB;  
}

2006-8-21
今天又写了一个DDB转DIB的函数,它是一个象素一个象素点的读取的,相对较慢!而且只支持24位的!
HANDLE CProcess::DDBtoDIB(CBitmap &bitmap, CDC *pDC)
{
LPBITMAPINFO lpbmi;
DWORD dwHeader;
DWORD dwLen;
HANDLE hDIB;
LPSTR pDIB;
CDC memDC;

dwHeader = m_image->GetHeaderSize();
dwLen = dwHeader + m_image->GetImageSize();//获取文件大小

memDC.CreateCompatibleDC(pDC);
memDC.SelectObject(bitmap);

pDIB = (LPSTR)new char[dwLen];//该内存在SaveLayer()函数中释放
if (pDIB == NULL)//如果内存申请失败
{  
    return    NULL;  
}  
    
lpbmi    =    (LPBITMAPINFO)pDIB;    //使用pDIB申请的内存

int width = m_image->GetWidth();
int height = m_image->GetHeight();
if (m_image->m_lpbmi->bmiHeader.biBitCount < 24)
{
    m_image->ComputePaletteSize(24);
    if (m_image->m_lpbmi != NULL)
    {
     m_image->m_lpbmi = NULL;
     delete m_image->m_lpbmi;
    }
    m_image->m_lpbmi = (LPBITMAPINFO)new char[sizeof(BITMAPINFOHEADER)];
    if (NULL == m_image->m_lpbmi)
    {
     AfxMessageBox("m_lpbmi内存申请失败");
    }
    m_image->m_lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_image->m_lpbmi->bmiHeader.biWidth = width;
    m_image->m_lpbmi->bmiHeader.biHeight = height;
    m_image->m_lpbmi->bmiHeader.biPlanes = 1;
    m_image->m_lpbmi->bmiHeader.biBitCount = 24;
    m_image->m_lpbmi->bmiHeader.biCompression = BI_RGB;
    m_image->m_lpbmi->bmiHeader.biSizeImage = 0;
    m_image->m_lpbmi->bmiHeader.biXPelsPerMeter = 0;
    m_image->m_lpbmi->bmiHeader.biXPelsPerMeter = 0;
    m_image->m_lpbmi->bmiHeader.biClrUsed = 0;
    m_image->m_lpbmi->bmiHeader.biClrImportant = 0;
    m_image->ComputeImage();//重新计算图像大小
    m_image->m_lpbmi->bmiHeader.biSizeImage = m_image->GetImageSize();
}
memcpy(lpbmi, m_image->m_lpbmi, dwHeader);//获取信息头

int x = 0 ,y = 0;
DWORD i = 0;
COLORREF color;
for (y = height - 1; y >= 0; y--)
{
      for (x = 0; x < width; x++)
      {
      i = (height - 1 - y)*width + x;
      color = memDC.GetPixel(x, y);
      pDIB[dwHeader + i*3] = (BYTE)((color&0x00ff0000)>>16);
      pDIB[dwHeader + i*3 +1] = (BYTE)((color&0x0000ff00)>>8);
      pDIB[dwHeader + i*3 +2] = (BYTE)(color&0x000000ff);
     }
}

hDIB = (HANDLE)pDIB;
return hDIB;
}

From: http://hi.baidu.com/lufa2014/blog/item/98c9ff1103bc4c11b9127bc4.html

原创粉丝点击