比較與整理GDI與GDIPlus抓取、合併和保存位圖的方法

/**
@defgroup COMPARE_GDI_GDIPlus
@brief    比較與整理GDI與GDIPlus在抓取、合併和保存位圖之間的不同
@author   華仔103
@date     2009-01-14
@{
\*/

在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;
}

發佈了14 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章