比较与整理GDI与GDIPlus抓取、合并和保存位图的方法

来源:互联网 发布:淘宝上卖的红酒真吗 编辑:程序博客网 时间:2024/06/05 10:41

在stdafx.h文件中定义
#ifndef _UNICODE
#define _UNICODE
#endif

#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib,"gdiplus.lib")
#include <gdiplus.h>
using namespace Gdiplus;

//如果原项目为非UNICODE编码环境,那么修改项目为UNICODE编码环境的方法为:

//在项目属性对话框的链接器->高级->入口点中设置值为wWinMainCRTStartup。 

 

在*.cpp文件中
#include "stdafx.h"


//通过GDI方法进行屏幕抓图,用GDI+方法保存Bitmap文件
#define USE_GDI_GDIPLUS_STORE_BMP

 

////通过GDI方法进行屏幕抓图和保存Bitmap文件
//#define USE_GDI_STORE_BMP

 

//将Bitmap文件保存在Windows的剪贴版中
#define STORE_IN_CLIPBOARD


//@brief 用GDI+的方法将Bitmap图片保存为文件
//@date   1-13-2009  
//@param [in]  hBitmap hBitmap的句柄(handle)
//@param [in]  lpszFileName 保存图片的完整路径
//@return  成功返回TRUE,失败返回FALSE
BOOL C**Dlg::GDIPlus_SaveBitmapToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
{
 CLSID pngClsid;

 Bitmap bmp(hBitmap,NULL);

 //获取BMP文件的编码方式(如果希望获取JPEG的编码方式,

 //那么参数一要设置为:_TEXT("image/jpeg"),其他支持的图片格式类似)
 int nResult = GetEncoderClsid(_TEXT("image/bmp"),&pngClsid);

 if(nResult >= 0)
 {
     //保存所截取的屏幕图片
     bmp.Save(lpszFileName,&pngClsid);
 }
 else
 {
     return FALSE;
 }
   
 return TRUE;
}

 
//@brief 获取图片文件的编码方式,支持bmp、jpg、jpeg、gif、tiff和png等格式图片
//@date   1-13-2009  
//@param [in]  format 图片格式 值可以为以下几种
//@"image/bmp"
//@"image/jpeg"
//@"image/gif"
//@"image/tiff"
//@"image/png"
//@param [in]  pClsid
//@return  成功则返回值 >= 0,失败则返回值为-1

int C**Dlg::GetEncoderClsid(const WCHAR* format, CLSID *pClsid)
{

 int nRet = -1;

 ImageCodecInfo* pCodecInfo = NULL;

 UINT nNum = 0,nSize = 0;

 GetImageEncodersSize(&nNum,&nSize);

 if (nSize<0)
 {
         return nRet;
 }

 pCodecInfo= new ImageCodecInfo[nSize];

 if (pCodecInfo==NULL)
 {
  return nRet;
 }

 GetImageEncoders(nNum,nSize,pCodecInfo);

 for (UINT i=0; i<nNum;i++)
 {

  if (wcscmp(pCodecInfo[i].MimeType,format)==0)
  {

   *pClsid= pCodecInfo[i].Clsid;

   nRet = i;

   delete[] pCodecInfo;

   return nRet;
  }
  else
  {
   continue;
  }

 }

 delete[] pCodecInfo;

 return nRet;

}

 
//@brief 用GDI+的方法将两个图片(strSrcImage1,strSrcImage2)组合成一个strDestImage
//@date   1-13-2009  
//@param [in]  format 图片格式 值可以为以下几种
//@"image/bmp"
//@"image/jpeg"
//@"image/gif"
//@"image/tiff"
//@"image/png"
//@param [in] strDestImage表示目的图片路径
//@param [in] strSrcImage1表示源图片路径1
//@param [in] strSrcImage2表示源图片路径2
//@return  TRUE表示合成图片成功,FALSE表示合成图片失败

BOOL C**Dlg::GDIPlus_CombineImage(const WCHAR *format, WCHAR* strDestImage,\
      WCHAR* strSrcImage1,WCHAR* strSrcImage2)

{

 BOOL bCombine = FALSE;

 int nRet = 0;

 CLSID clsid;

 nRet= GetEncoderClsid(format,&clsid);

 if (nRet >= 0)
 {

  Bitmap srcbmp1(strSrcImage1);

  Bitmap srcbmp2(strSrcImage2);


  int nWidth = 0, nHeight = 0;

  nWidth = srcbmp1.GetWidth(); 

  nHeight= srcbmp1.GetHeight();

  //高不变,宽*2,水平合并
  Bitmap bmpCombine(2*nWidth,nHeight);

  Graphics * pG = NULL;

  pGraphics = Graphics::FromImage(&bmpCombine);

  if (pGraphics!=NULL)
  {
          //将srcbmp1画到画布pGraphics上
          pGraphics->DrawImage(&srcbmp1,0,0);

          //将srcbmp2画到画布pGraphics上
          pGraphics->DrawImage(&srcbmp2,nWidth,0);


          //保存图片bmpCombine到路径strDestImage
          bmpCombine.Save(strDestImage,&clsid,NULL);

          bCombine = TRUE;
  }

 }

 return bCombine;

}


 
//@brief 用GDI的方法将Bitmap图片保存为文件
//@date   1-13-2009  
//@param [in] hDC DC设备的句柄
//@param [in] bitmap 被保存的Bitmap图片
//@param [in] lpszFileName 被保存Bitmap图片的完整路径
//@return  返回TRUE表示保存图片成功,FALSE表示保存图片失败

BOOL C**Dlg::GDI_SaveBitmapToFile(HDC hDC, CBitmap &bitmap, LPCTSTR lpszFileName)
{

 BOOL ret = TRUE;

 BITMAP btm;

 bitmap.GetBitmap(&btm);

 DWORD size = btm.bmWidthBytes * btm.bmHeight;

//接下来要使用GlobalAlloc()函数从堆中分配指定大小的内存,并返回指向这块内存的指针,

//分配内存是将内存的内容初始化为全零。

 HGLOBAL hMem = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,size);

 if(hMem == NULL)
 {
     return FALSE;
 }

 //为了保护我们分配的这块内存,我们要调用GlobalLock来锁定这块内存,

 //避免被其他的进程占用,这个函数返回指向内存锁的第一个指针

 LPSTR lpData = (LPSTR)GlobalLock(hMem);

 //定义位图头,并对这个结构体变量初始化,这个结构之后初始化之后才能使用,

 //尤其要指定位图的大小和位图的高度宽度

 BITMAPINFOHEADER bih;

 bih.biSize = sizeof(BITMAPINFOHEADER);

 bih.biWidth = btm.bmWidth;

 bih.biHeight = btm.bmHeight;

 bih.biPlanes = 1;

 bih.biBitCount = btm.bmBitsPixel;

 bih.biCompression = 0;

 bih.biSizeImage = size;

 bih.biXPelsPerMeter = 0;

 bih.biYPelsPerMeter = 0;

 bih.biClrUsed = 0;

 bih.biClrImportant = 0;

 //将位图中的数据以bits的形式放入lpData指向的位图数组中,这里用到了GetDIBits()函数。

 if(GetDIBits(hDC,bitmap,0,bih.biHeight,lpData,(BITMAPINFO*)&bih,DIB_RGB_COLORS) == 0)
 {
  

  GlobalFree(hMem);

  return FALSE;
 }

 int iRes      GetDeviceCaps(hDC,   SIZEPALETTE);   
 long plSize    iRes * sizeof(RGBQUAD);

 //位图文件和普通的文件使用起来有区别,为了保存位图,我们这里定义了一个位图文件,

 //用它来专门保存位图文件,在使用之前,也要填充位图文件的头部信息。Bfh的bfType成员没有选择,

 //必须赋值为BM,bfReserved1和bfReserved2都必须指定为0。

 BITMAPFILEHEADER bfh;

 bfh.bfType = ((WORD)('M'<< 8)|'B') ;//BM

 bfh.bfReserved1 = 0;

 bfh.bfReserved2 = 0;

 bfh.bfSize = sizeof(bfh)+size;

 bfh.bfOffBits = plSize + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);

 CFile bf;

 //对于位图文件的写入,我们可以使用下面的方法

 if(bf.Open(lpszFileName, CFile::modeCreate|CFile::modeWrite))

 {

  bf.Write(&bfh,sizeof(BITMAPFILEHEADER));

  bf.Write(&bih,sizeof(BITMAPINFOHEADER));

  bf.Write(lpData,size);

  bf.Close();

 }

 else
 {
  ret = FALSE;
 }

 //释放掉从堆中分配的内存,避免内存泄露

 GlobalFree(hMem);

 return ret;

 


//@brief 用GDI的方法将两张Bitmap图片合并起来
//@date   1-13-2009  
//@param [in] pDC DC设备
//@param [in] pBitmap1 被合并的Bitmap图片1
//@param [in] pBitmap2 被合并的Bitmap图片2
//@return  void

void C**Dlg::GDI_CombineBitmap(CDC *pDC, CBitmap *pBitmap1, CBitmap *pBitmap2)
{
 CDC m_dcBk;    ///<绘制背景图设备
 CDC m_dcFore;  ///<绘制前景图设备

 CClientDC dc(this);     //获得当前客户区设备环境
 m_dcBk.CreateCompatibleDC(&dc);   //创建与当前设备相兼容的设备
 m_dcFore.CreateCompatibleDC(&dc); 

 CBitmap *poldBk = m_dcBk.SelectObject(pBitmap1);   //选入背景图
 CBitmap *poldFore = m_dcFore.SelectObject(pBitmap2);

 CRect rect;
 CListCtrl   m_NavigationList; ///<列表型控件变量
 m_NavigationList.GetClientRect(&rect);  //得到客户区矩形

 CDC maskDc;        //创建设备环境maskDc
 maskDc.CreateCompatibleDC(pDC);  //创建与当前设备相兼容的设备

 CBitmap maskBitmap;
 maskBitmap.CreateBitmap(rect.Width(), rect.Height(), 1, 1, NULL ); //创建一个单色图

 CBitmap* pOldMaskDCBitmap = maskDc.SelectObject( &maskBitmap ); //选入单色图

 CBrush brush(RGB(255,255,255));
 CBrush * oldbrush = NULL;
 oldbrush = maskDc.SelectObject(&brush);
 maskDc.FillRect(&rect,&brush);

 //取得要消除的背景色值
 COLORREF clrTrans = m_dcFore.GetPixel(2, 2);

 //设置前景图的背景色
 COLORREF clrSaveBk  = m_dcFore.SetBkColor(clrTrans);

 //将前景图拷贝到maskDc
 maskDc.BitBlt(0,0,rect.Width(),rect.Height(), &m_dcFore, 0,0,SRCCOPY);


 //前景图与mask做‘与’运算 
 m_dcFore.SetBkColor(RGB(0,0,0));
 m_dcFore.SetTextColor(RGB(255,255,255));
 m_dcFore.BitBlt(0,0,rect.Width(), rect.Height(),&maskDc,0,0,SRCAND);

 //背景图与mask做‘与’运算
 m_dcBk.SetBkColor(RGB(255,255,255));
 m_dcBk.SetTextColor(RGB(0,0,0));
 m_dcBk.BitBlt(0,0,rect.Width(),rect.Height(),&maskDc,0,0,SRCAND);

 //背景图与前景图做‘或’运算 
 m_dcBk.BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&m_dcFore,0,0,SRCPAINT);

 ////将合成后的图像显示出来
 //pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcBk,0,0,SRCCOPY);

 pDC->SelectObject(oldbrush);

 m_dcBk.SelectObject(poldBk);
 m_dcFore.SelectObject(poldFore);
}


//@brief 实现屏幕抓图功能,并将抓取的图片保存为Bitmap文件
//@date   1-12-2009  
//@return  返回TRUE表示操作成功

BOOL C**Dlg::GetScreenBmpImage()
{

#ifdef USE_GDI_STORE_BMP
 //定义源图片(屏幕抓图)的Left、Top、Width和Height
 int nSrcImageLeft = 200;
 int nSrcImageTop = 200;
 int nSrcImageWidth = 0;
 int nSrcImageHeight = 0; 

 //获取源图片(屏幕抓图)Bitmap的宽高
 nSrcImageWidth = GetSystemMetrics(SM_CXSCREEN);
 nSrcImageHeight= GetSystemMetrics(SM_CYSCREEN);

 //定义被保存的图片的Left、Top、Width和Height
 int nDestImageLeft = 0;
 int nDestImageTop = 0;
 int nDestImageWidth = 500;
 int nDestImageHeight = 500; 


 CDC dc;

 //获取整个屏幕的dc(设备场景)

 dc.CreateDC(_TEXT("DISPLAY"),NULL,NULL,NULL);

 //为屏幕dc创建兼容的内存dcMem

 CDC dcMem;

 dcMem.CreateCompatibleDC(&dc);

 //创建一个与屏幕dc兼容的位图

 CBitmap bitmap;

 bitmap.CreateCompatibleBitmap(&dc, nSrcImageWidth, nSrcImageHeight);


 //把新位图选到内存dc中,并保存原位图

 CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);

 //将屏幕图像从屏幕dc拷到内存dc,其实拷到到bitmap中

 dcMem.BitBlt(nDestImageLeft,nDestImageTop,nDestImageWidth,nDestImageHeight,&dc,

              nSrcImageLeft,nSrcImageTop,SRCCOPY);

 //恢复内存dc中的原位图

 dcMem.SelectObject(pOldBitmap);

 WCHAR strFileFullPath[MAX_PATH] = {0};
 wcscpy(strFileFullPath,_TEXT("c:\\GDI_Screen.bmp"));

 this->GDI_SaveBitmapToFile(dc.GetSafeHdc(), bitmap, strFileFullPath);

 dc.DeleteDC(); 
#endif
 
#ifdef USE_GDI_GDIPLUS_STORE_BMP
 //定义源图片(屏幕抓图)的Left、Top、Width和Height
 int nSrcImageLeft = 200;
 int nSrcImageTop = 200;
 int nSrcImageWidth = 0;
 int nSrcImageHeight = 0; 

 //获取源图片(屏幕抓图)Bitmap的宽高
 nSrcImageWidth = GetSystemMetrics(SM_CXSCREEN);
 nSrcImageHeight= GetSystemMetrics(SM_CYSCREEN);

 //定义被保存的图片的Left、Top、Width和Height
 int nDestImageLeft = 0;
 int nDestImageTop = 0;
 int nDestImageWidth = 400;
 int nDestImageHeight = 500; 

 HDC hdcScreen,hMemDC;

 HBITMAP hBitmap,hOldBitmap;

 //建立一个屏幕设备环境句柄

 hdcScreen = CreateDC(_TEXT("DISPLAY"),NULL,NULL,NULL);

 //建立一个与屏幕设备环境句柄兼容的内存DC
 hMemDC = CreateCompatibleDC(hdcScreen);

 //建立一个与屏幕设备环境句柄兼容、宽高为nSrcImageWidth,nSrcImageHeight的画布,用于将各个图像添加上去
 hBitmap = CreateCompatibleBitmap(hdcScreen,nSrcImageWidth,nSrcImageHeight);

 //把新位图选到内存设备描述表中
 hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap);

 //把屏幕设备描述表拷贝到内存设备描述表中
 BitBlt(hMemDC,nDestImageLeft,nDestImageTop,nDestImageWidth,nDestImageHeight,

        hdcScreen,nSrcImageLeft,nSrcImageTop,SRCCOPY);

 nDestImageLeft = 500;
 nDestImageTop = 500;
 nDestImageWidth = 600;
 nDestImageHeight = 700; 
 nSrcImageLeft = 500;
 nSrcImageTop = 500;
 //把屏幕设备描述表拷贝到内存设备描述表中
 BitBlt(hMemDC,nDestImageLeft,nDestImageTop,nDestImageWidth,

        nDestImageHeight,hdcScreen,nSrcImageLeft,nSrcImageTop,SRCCOPY);

 DeleteDC(hdcScreen);

 DeleteDC(hMemDC);

 WCHAR strFileFullPath[MAX_PATH] = {0};
 wcscpy(strFileFullPath,_TEXT("c:\\GDIPlus_Screen.bmp"));

 //保存的图片大小由hBitmap的nSrcImageWidth和nSrcImageHeight决定
 this->GDIPlus_SaveBitmapToFile(hBitmap, strFileFullPath);

 

#else
 //定义源图片(屏幕抓图)的Left、Top、Width和Height
 int nSrcImageLeft = 0;
 int nSrcImageTop = 0;
 int nSrcImageWidth = 0;
 int nSrcImageHeight = 0; 

 //获取源图片(屏幕抓图)Bitmap的宽高
 nSrcImageWidth = GetSystemMetrics(SM_CXSCREEN);
 nSrcImageHeight= GetSystemMetrics(SM_CYSCREEN);

 //定义被保存的图片的Left、Top、Width和Height
 int nDestImageLeft = 0;
 int nDestImageTop = 0;
 int nDestImageWidth = GetSystemMetrics(SM_CXSCREEN);
 int nDestImageHeight = GetSystemMetrics(SM_CYSCREEN); 
 
CDC dc;
 HDC hScreenBmpDC;
//获取整个屏幕的dc(设备场景)
 dc.CreateDC(_TEXT("DISPLAY"),NULL,NULL,NULL);
 Graphics ScreenGraghics(dc.m_hDC) ;
 

 //创建以屏幕大小为标准的位图
 Bitmap SrcBitmap1(nSrcImageWidth , nSrcImageHeight , &ScreenGraghics ) ;
 this->GDIPlus_GetBitmap(nDestImageLeft,nDestImageTop,nDestImageWidth,nDestImageHeight);
 //Image是Bitmap的父类
 Graphics* DestBitmapGraphics = Graphics::FromImage( &SrcBitmap1 ) ;

 hScreenBmpDC = ScreenGraghics.GetHDC();
 HDC hDestBmpDC = DestBitmapGraphics->GetHDC();
 BitBlt ( hDestBmpDC , nDestImageLeft , nDestImageTop , nDestImageWidth ,
       nDestImageHeight , hScreenBmpDC , nSrcImageLeft , nSrcImageTop , SRCCOPY ) ;

 //释放Bitmap的DC
 DestBitmapGraphics->ReleaseHDC( hDestBmpDC ) ;
 //释放屏幕的DC
 ScreenGraghics.ReleaseHDC(hScreenBmpDC);

 //获取整个屏幕的dc(设备场景)
 dc.CreateDC(_TEXT("DISPLAY"),NULL,NULL,NULL); 
 Graphics ScreenGraghics2(dc.m_hDC) ;
 //定义被保存的图片的Left、Top、Width和Height
 nDestImageLeft = 0;
 nDestImageTop = 0;
 nDestImageWidth = 1280;
 nDestImageHeight = 1024; 
 //创建以屏幕大小为标准的位图
 Bitmap SrcBitmap2(nSrcImageWidth , nSrcImageHeight , &ScreenGraghics2 ) ;
 //Image是Bitmap的父类
 Graphics* DestBitmapGraphics2 = Graphics::FromImage( &SrcBitmap2 ) ;

 hScreenBmpDC = ScreenGraghics2.GetHDC();
 HDC hDestBmpDC2 = DestBitmapGraphics2->GetHDC();
 BitBlt ( hDestBmpDC2 , nDestImageLeft , nDestImageTop ,nDestImageWidth ,

          nDestImageHeight ,hScreenBmpDC , nSrcImageLeft , nSrcImageTop , SRCCOPY ) ;

 //释放Bitmap的DC
 DestBitmapGraphics2->ReleaseHDC( hDestBmpDC2 ) ;
 //释放屏幕的DC
 ScreenGraghics2.ReleaseHDC(hScreenBmpDC);

 int nbmpCombineWidth = GetSystemMetrics(SM_CXSCREEN); 

 int nbmpCombineHeight= GetSystemMetrics(SM_CYSCREEN);

 //用于Bitmap合并的画布
 Bitmap bmpCombine(2*nbmpCombineWidth,nbmpCombineHeight);

 Graphics * pG = NULL;

 pG = Graphics::FromImage(&bmpCombine);

 if (pG != NULL)
 {
  pG->DrawImage(&SrcBitmap1,0,0,1280,1024);
  pG->DrawImage(&SrcBitmap2,500,0,1280,1024);

  CLSID pngClsid;
  //获取BMP文件的编码方式
  int nResult = GetEncoderClsid(_TEXT("image/bmp"),&pngClsid);

  bmpCombine.Save(_TEXT("c:\\Capture2.bmp"),&pngClsid,NULL);
 }

#endif

 

#ifdef STORE_IN_CLIPBOARD

 //屏幕已拷到兼容位图bitmap上,下面将它保存到剪贴板
 if((::OpenClipboard(NULL) != 0) && (::EmptyClipboard() != 0))
 

  ::SetClipboardData(CF_BITMAP,bitmap.GetSafeHandle()); 
  ::CloseClipboard();

 }

#endif

 return TRUE;

}


//@brief 创建一个画布,在上面任意画图形,并且返回该画布的缩略图
//@date   1-13-2009  
//@return  返回缩略图指针

Bitmap * C**Dlg::GetThumbnailPageImage()
{

 int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
 int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);

 //定义宽为nScreenWidth,高为nScreenHeight的位图
 Bitmap pPageImage(nScreenWidth, nScreenHeight, PixelFormat16bppRGB565);
 //Imagegraphics作为pPageImage的画布,接受绘画的信息
 Graphics Imagegraphics(&pPageImage);

 SolidBrush bkbrush(Color(255,255,255,0));
 //填充画布背景Imagegraphics
 Imagegraphics.FillRectangle(&bkbrush, 0, 0, nScreenWidth, nScreenHeight);

 Pen pen(Color(255,255,0,0), 4.0f);
 //在画布上画个矩形
 Imagegraphics.DrawRectangle(&pen, 200, 200, 500, 500);

 ////在画布上画图形(这是GDI+合并图片的方法之一)
 //Imagegraphics.DrawImage()

 //缩略图的宽
 int nThumbnailWidth = 90;
 //缩略图的高
 int nThumbnailHeight = 90;
 Bitmap *pThumbnailImage = NULL;
 //获取Bitmap(pPageImage)的缩略图
 pThumbnailImage = (Bitmap *)pPageImage.GetThumbnailImage(nThumbnailWidth,    nThumbnailHeight,NULL, NULL);

 //返回缩略图指针
 return pThumbnailImage;
}