WinCE将HDC保存为BMP文件

HDC在MSDN中的全称为:The handle of device context。通常,我们都是用来做相应的显示操作。

熟悉WIN32的朋友对于其应该不会陌生,经常采用GetDC,GetWindowDC等等来获取其句柄。而用得最多的,可能就是BeginPaint,如:

  
  
  
  
  1. case WM_PAINT:  
  2.     HDC hdc = BeginPaint(hWnd,&ps);  
  3.     ...  
  4.     EndPaint(hdc,&ps);  
  5.     break;  

使用起来非常简单,但如果想将其内容保存为普通的图像文档,可就没那么容易。确切地说,在只知道HDC句柄的情况下,我们是无法保存其内容的;但我们可以剑走偏锋,将HDC的内容写到一个缓存中,然后我们再保存该缓存的内容即可。

听起来很简单,却又像很复杂,不是么?没关系,我们现在一步一步来。

首先,我们需要一个HDC的句柄。如同前面所说,你可以有多种方法,比如GetDC,GetWindowDC,甚至是CreateDC。反正呢,你用什么方法我不管,我只要有一个HDC的句柄就好了。

有了HDC的句柄,接下来我们所需要做的是,知道这HDC的大小,也就是宽度和长度。这个不难,我们只要简单地调用GetDeviceCaps,然后参数给予HORZRES或VERTRES即可:

  
  
  
  
  1. int iWidth = GetDeviceCaps(hdc,HORZRES);  
  2. int iHeight = GetDeviceCaps(hdc,VERTRES); 

为什么要知道大小呢?因为我们要用它来创建缓存。而这缓存,说白了,其实就是一个BMP格式的数据结构而已。

为了创建这个关键的缓存,我们必须调用CreateDIBSection函数,而该函数形参又用到BITMAPINFOHEADER,所以我们的一切,就先从填充该结构体开始。

该结构体定义如下:

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

结构体里面还有一个BITMAPINFOHEADER,其定义如下:

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

 

这么多变量,是不是有点头晕?大可不必紧张,其实我们只需要填充其中几个,其它统统置为0即可:

  
  
  
  
  1. BITMAPINFO bmpInfo = {0};  
  2. bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);  
  3. bmpInfo.bmiHeader.biWidth = iWidth;  
  4. bmpInfo.bmiHeader.biHeight = iHeight;  
  5. bmpInfo.bmiHeader.biPlanes = 1;  
  6. bmpInfo.bmiHeader.biBitCount = 24; 

一切从最简单做起,对于BMP而言,最简单的自然是24位位图,这就是为什么biPlanes和biBitCount分别设置为1和24的原因。

填充完BITMAPINFO结构,我们还是不能马上调用CreateDIBSection,因为形参中还有一个HDC。虽然我们可以直接采用已知的HDC句柄,但接下来还要将创建的HBITMAP和HDC相连接,所以我们还是先创建一个缓存DC:

  
  
  
  
  1. HDC hdcMem = CreateCompatibleDC(hdc); 

一切准备就绪之后,就调用CreateDIBSection吧:

  
  
  
  
  1. BYTE *pData = NULL;  
  2. hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,
  3. reinterpret_cast<VOID **>(&pData),NULL,0); 

pData是分配的一个内存空间,将来用来存储HDC的内容,只不过现在一切都是空的。如果你将这数据保存出来,你会发现一团漆黑。

将HBITMAP和HDC结合:

  
  
  
  
  1. hOldObj = SelectObject(hdcMem, hBmp); 

 

至此为止,我们前期工作已经准备就绪,我们只需要将HDC的内容用BitBlt绘制到缓存中即可:

  
  
  
  
  1. BitBlt(hdcMem,  
  2.        0,  
  3.        0,  
  4.        iWidth,  
  5.        iHeight,  
  6.        hdc,  
  7.        0,  
  8.        0,  
  9.        SRCCOPY); 

这里其实还有一个小技巧,如果你是想绘制HDC的某个区域,你只需要用StretchBlt替代即可:

  
  
  
  
  1. StretchBlt(hdcMem,  
  2.             0,  
  3.             0,  
  4.             iWidth,  
  5.             iHeight,  
  6.             hdc,  
  7.             rcDC.left,  
  8.             rcDC.top,  
  9.             rcDC.right - rcDC.left + 1,  
  10.             rcDC.bottom - rcDC.top + 1,  
  11.             SRCCOPY); 

喜欢追究问题的你,也许会发现,在调用该函数之后,pData所指向的内存缓冲区已经改变。是的,没错,这些改变的数据就是我们所需要的。接下来我们所需要做的仅仅是,将这数据按BMP文件的格式,保存下来即可。

BMP文件格式其实很简单,最开始是文件头信息,然后是图片信息,接下来是数据。我们只需要按照这格式,顺序将数据写入即可。

文件头信息和图片信息,微软已经为我们定义好了相应的结构体:

BMP信息:

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

文件头信息:

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

 

我们首先填充这两个结构体数值:

  
  
  
  
  1. BITMAPINFOHEADER bmInfoHeader = {0};  
  2. bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);  
  3. bmInfoHeader.biWidth = iWidth;  
  4. bmInfoHeader.biHeight = iHeight;  
  5. bmInfoHeader.biPlanes = 1;  
  6. bmInfoHeader.biBitCount = 24;  
  7.  
  8. //Bimap file header in order to write bmp file  
  9. BITMAPFILEHEADER bmFileHeader = {0};  
  10. bmFileHeader.bfType = 0x4d42;  //bmp    
  11. bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
  12. bmFileHeader.bfSize = bmFileHeader.bfOffBits + 
  13. ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8) 

接下来的事情,估计大家都轻车熟路了。创建文件,然后写入数据,保存,完毕。

  
  
  
  
  1. HANDLE hFile = CreateFile(strFile.c_str(),
  2. GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);  
  3. DWORD dwWrite = 0;  
  4. WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);  
  5. WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);  
  6. WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);  
  7. CloseHandle(hFile); 

 

文章的最后,是参考源代码:

#ifdef UNICODE
 #ifndef TSTRING
  #define TSTRING std::wstring
 #endif
#else
 #ifndef TSTRING
  #define TSTRING std::string
 #endif
#endif
BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg);
BOOL WriteBmp(const TSTRING &strFile,HDC hdc);
BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC);
BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg)
{  
 BITMAPINFOHEADER bmInfoHeader = {0};
 bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
 bmInfoHeader.biWidth = sizeImg.cx;
 bmInfoHeader.biHeight = sizeImg.cy;
 bmInfoHeader.biPlanes = 1;
 bmInfoHeader.biBitCount = 24;
 //Bimap file header in order to write bmp file
 BITMAPFILEHEADER bmFileHeader = {0};
 bmFileHeader.bfType = 0x4d42;  //bmp 
 bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)
 HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
 if(hFile == INVALID_HANDLE_VALUE)
 {
  return FALSE;
 }
 DWORD dwWrite = 0;
 WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);
 WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);
 WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);

 CloseHandle(hFile);
 return TRUE;
}

BOOL WriteBmp(const TSTRING &strFile,HDC hdc)
{
 int iWidth = GetDeviceCaps(hdc,HORZRES);
 int iHeight = GetDeviceCaps(hdc,VERTRES);
 RECT rcDC = {0,0,iWidth,iHeight};
 return WriteBmp(strFile,hdc,rcDC); 
}
BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC)
{
 BOOL bRes = FALSE;
 BITMAPINFO bmpInfo = {0};
 BYTE *pData = NULL;
 SIZE sizeImg = {0};
 HBITMAP hBmp = NULL;
 std::vector<BYTE> vtData;
 HGDIOBJ hOldObj = NULL;
 HDC hdcMem = NULL;
 //Initilaize the bitmap information 
 bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 bmpInfo.bmiHeader.biWidth = rcDC.right - rcDC.left;
 bmpInfo.bmiHeader.biHeight = rcDC.bottom - rcDC.top;
 bmpInfo.bmiHeader.biPlanes = 1;
 bmpInfo.bmiHeader.biBitCount = 24;
 //Create the compatible DC to get the data
 hdcMem = CreateCompatibleDC(hdc);
 if(hdcMem == NULL)
 {
  goto EXIT;
 }
 //Get the data from the memory DC 
 hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);
 if(hBmp == NULL)
 {
  goto EXIT;
 }
 hOldObj = SelectObject(hdcMem, hBmp);
 
 //Draw to the memory DC
 sizeImg.cx = bmpInfo.bmiHeader.biWidth;
 sizeImg.cy = bmpInfo.bmiHeader.biHeight;
 StretchBlt(hdcMem,
    0,
    0,
    sizeImg.cx,
    sizeImg.cy,
    hdc,
    rcDC.left,
    rcDC.top,
    rcDC.right - rcDC.left + 1,
    rcDC.bottom - rcDC.top + 1,
    SRCCOPY);
 
 vtData.resize(sizeImg.cx * sizeImg.cy * 3);
 memcpy(&vtData[0],pData,vtData.size());
 bRes = WriteBmp(strFile,vtData,sizeImg);
 SelectObject(hdcMem, hOldObj);
 
EXIT:
 if(hBmp != NULL)
 {
  DeleteObject(hBmp);
 }
 if(hdcMem != NULL)
 {
  DeleteDC(hdcMem);
 }
 return bRes;
}

 

 一共有三个WriteBmp函数,使用上非常简单。

比如,我想保存一个HDC,只需要简单地调用:

  
  
  
  
  1. HDC hdc = GetDC(NULL);  
  2. WriteBmp(TEXT("//NAND//DCSave.bmp"));  
  3. ReleaseDC(NULL,hdc); 

 如果想保存HDC的某一个部分,同样也很简单:

  
  
  
  
  1. HDC hdc = GetDC(NULL);  
  2. RECT rcDC = {0,0,100,100};  
  3. WriteBmp(TEXT("//NAND//DCSavePart.bmp"),rcDC);  
  4. ReleaseDC(NULL,hdc); 

这个函数还能做到一个很有意思的功能,就是截取屏幕。对于屏幕来说,也是一个HDC,我们只要获取屏幕的HDC句柄,剩下的就没有什么难度了:

  
  
  
  
  1. HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);  
  2. WriteBmp(TEXT("//NAND//ScreenCapture.BMP"),hdc);  
  3. DeleteDC(hdc); 

你可能感兴趣的:(数据结构,File,null,byte,WinCE,colors)