MFC下截屏 & 灰度顯示

PomeloWu原作,轉載請指明出處

用MFC很容易把當前屏幕截取,並顯示在自己程序的UI上。以對話框爲例,在執行繪製的單元(比如OnPaint)中調用下面這個函數就能做到:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

BOOL CSrnShotDlg::GetMyScreen(

                   CDC *pdc                                      // 目標DC

)

{

                   CDC dc;

                   dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC

 

                   CRect clientRect;

                   GetClientRect(clientRect);                                           // 對話框矩形區域

 

                   pdc->BitBlt(0, 0,                                                             // 起始位置

                                     clientRect.Width(),clientRect.Height(),    // 寬高

                                     &dc,                                                                 // CDC對象

                                     0, 0,                                                                    // 源位置

                                     SRCCOPY                                                       // 複製方法

                                     );

                   dc.DeleteDC();
                   return TRUE;

}

 

接下來改造一下,把屏幕截圖先轉換爲灰度(Gray Scale)圖,再顯示出來。轉換灰度圖的公式是,對一個RGB值,RGB分別是其3色分量,計算:

              Gray = R * 0.299 + G *0.587 + B * 0.114

然後將Gray分別替換掉原來的3色分量。到這個地方,很自然想到用SetPixel/GetPixel來實現。因爲要對DC進行操作,當然就不能直接在上面GetMyScreen裏邊的dc直接操作了,爲此對GetMyScreen進行一下改造,並且,爲了程序的可讀性,增加一個ConvertToGray函數負責轉換(與上面代碼不同的地方用紅色區分)

 

void ConvertToGray (CDC * pdc)

{

                   for (int xx = 0; xx < clientRect.right ; xx ++)

                                     for (int yy = 0; yy < clientRect.bottom ; yy ++)

                                     {

                                                        COLORREF crTemp = pdc->GetPixel(xx,yy);

                                                        BYTE pixelR = GetRValue(crTemp);

                                                        BYTE pixelG = GetGValue(crTemp);

                                                        BYTE pixelB = GetBValue(crTemp);

                                                        BYTE gray = (BYTE) (pixelR * 0.299 + pixelG * 0.587 +pixelB * 0.114);

                                                        pdc->SetPixelV(xx,yy,RGB(gray, gray, gray));

                                     }

}

 

BOOL CSrnShotDlg::GetMyScreen(

                   CDC *pdc                                      // 目標DC

)

{

                   CDC dc;

                   dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC

 

                   CRect clientRect;

                   GetClientRect(clientRect);                                           // 對話框矩形區域

 

                   CDC          *pMemDC = NULL;                                    // 兼容DC

 

                   pMemDC = new CDC;

                   if (!pMemDC)

                                     return FALSE;

                   pMemDC->CreateCompatibleDC(&dc);

                   ShowWindow(SW_HIDE);

                   pMemDC->BitBlt(0, 0,

                                     clientRect.Width(), clientRect.Height(),

                                     &dc, 0, 0, SRCCOPY);

 

                   ConvertToGray(pMemDC);

 

                   pdc->BitBlt(0, 0,                                                             // 起始位置

                                     clientRect.Width(),clientRect.Height(),    // 寬高

                                     pMemDC,                                                        // CDC對象

                                     0, 0,                                                                    // 源位置

                                     SRCCOPY                                                       // 複製方法

                                     );

                   pMemDC->DeleteDC();

                   delete pMemDC;

                   dc.DeleteDC();

                   return TRUE;

}

 

效果出來了,但是並不完美。實際上我用SetPixelV代替了SetPixel,但顯示的速度還是很慢,CPU使用率也很高。如何提高效率呢?直接改DC上附着的位圖數據似乎是個好辦法。下面就轉而對CBitmap類對象進行操作。

因爲是直接截屏,所以需要先用CDC::GetDeviceCapsBITSPIXEL參數獲得屏幕色深,因爲不同色深的位圖的儲存方式不同。簡要說明一下:16位色位圖,每個象素佔2字節;24位色,每個象素佔3字節;32位色,每個象素佔4字節儲存空間。我們可以用CBitmap::GetBitmapBits函數來獲得位圖數據,這其實是一個BYTE數組。這個數組的結構,最簡單的是24位色的情況。前面說過了每個象素佔3個字節,按數組下標從低到高分別是BGR3色分量,而32位色的情況跟24位色類似,4個字節只不過多了一個alpha值。下面就是處理24位色深的ConvertToGray24

 

#define BITS24        (int)(1024 * 768 * 3)

void ConvertToGray24(CBitmap *pBmp)

{

                   LPBYTE lpbits = NULL;

                   lpbits = new BYTE[BITS24];

                   if (!lpbits)

                                     return;

 

                   ZeroMemory(lpbits, BITS24);

                   pBmp->GetBitmapBits(BITS24, lpbits);

                   for (int index = 0, j = 0, k = 0; index < BITS24; index ++)

                   {

                                     lpbits[index] = (BYTE)(0.114 * lpbits[index]);

                                     j = index + 1; k = index + 2;

                                     lpbits[j] = (BYTE)(0.587 * lpbits[j]);

                                     lpbits[k] = (BYTE)(0.299 * lpbits[k]);

                                     lpbits[index] += lpbits[j] + lpbits[k];

                                     lpbits[j] = lpbits[index];

                                     lpbits[k] = lpbits[index];

                                     index = k;

                   }

 

                   pBmp->SetBitmapBits(BITS24, lpbits);

                   delete [] lpbits;

}

 

GetDeviceCaps(BITSPIXEL)返回16的時候,又有兩種情況:16位色和15位色。16位色的情況下,位圖數組使用2字節保存數據,其中從高位往低位分別是BGR3色分量按位565佔用。需要用位操作來獲得每個分量的色值:

 

#define GetRValueX(rgb)      ((BYTE)(rgb) & 0x1f)

#define GetGValueX(rgb)      ((BYTE)(((rgb) & 0x07E0) >> 5))

#define GetBValueX(rgb)      ((BYTE)(((rgb) & 0xF800) >> 11))

#define RGBX(r,g,b) /

          ((WORD)(((BYTE)(r)|((WORD)((BYTE)(g))<<5))|(((WORD)(BYTE)(b))<<11)))

 

要注意的是因爲綠色分量佔用了6bit,其儲存精度是其它兩個分量的2倍,所以在進行後繼的計算的時候公式的因數會有所改變。(另外,使用15位色的適配器比較少,其儲存規則也是佔用2字節,但是最高位無意義,其餘15位按555分配,這裏不詳細討論了。)

#define BITS16        (int)(1024 * 768 * 2)

void ConvertToGray16(CBitmap *pBmp)

{

                   LPBYTE lpbits = NULL;

                   WORD *wBits;

                   lpbits = new BYTE[BITS16];

                   if (!lpbits)

                                     return;

 

                   ZeroMemory(lpDibits, BITS16);

                   pBmp->GetBitmapBits(BITS16, lpbits);

                   for (int index = 0, j = 0, k = 0; index < BITS16; index ++)

                   {

                                     wBits = (WORD *)(lpbits + index);

                                     BYTE pixelR = GetRValueX(*wBits) * 2;

                                     BYTE pixelG = GetGValueX(*wBits) ;     // 注意係數

                                     BYTE pixelB = GetBValueX(*wBits) * 2;

                                     BYTE gray =(BYTE) (pixelR * 0.299 + pixelG * 0.587 +pixelB * 0.114);

                                     *wBits = RGBX(gray / (BYTE)2, gray, gray / (BYTE)2);

                                     index ++;

                   }

                   pBmp->SetBitmapBits(BITS16, lpbits);

                   delete [] lpbits;

}

 

 

最後,第三次改造GetMyScreen

 

BOOL CSrnShotDlg::GetMyScreen(

                   CDC *pdc                                      // 目標DC

)

{

                   CDC dc;

                   dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC

 

                   CRect clientRect;

                   GetClientRect(clientRect);                                           // 對話框矩形區域

 

                   CDC          *pMemDC = NULL;                                    // 兼容DC

                   CBitmap *pBmp = NULL;                                           // 兼容位圖

 

                   pMemDC = new CDC;

                   if (!pMemDC)

                                     return FALSE;

                   pMemDC->CreateCompatibleDC(&dc);

                  

                   pBmp = new CBitmap;

                   if (!pBmp)

                   {

                                     pMemDC->DeleteDC();

                                     delete pMemDC;

                                     return FALSE;

                   }

                   pBmp->CreateCompatibleBitmap(&dc, clientRect.Width(),clientRect.Height());

                   pMemDC->SelectObject(pBmp);

                   ShowWindow(SW_HIDE);

                   pMemDC->BitBlt(0, 0,

                                     clientRect.Width(), clientRect.Height(),

                                     &dc, 0, 0, SRCCOPY);

 

                   switch(pMemDC->GetDeviceCaps(BITSPIXEL))

                   {

                   case: 16

                                     ConvertToGray16(pBmp);

                                     break;

                   case: 24

                                     ConvertToGray24(pBmp);

                                     break;

                   case: 32

                                     ConvertToGray32(pBmp);                          //未給出

                                     break;

                   default:

                                     pBmp->DeleteObject();

                                     pMemDC->DeleteDC();

                                     delete pBmp;

                                     delete pMemDC;

                                     dc.DeleteDC();

                                     return FALSE;

                   }

                   pdc->BitBlt(0, 0,                                                             // 起始位置

                                     clientRect.Width(),clientRect.Height(),    // 寬高

                                     pMemDC,                                                       // CDC對象

                                     0, 0,                                                                    // 源位置

                                     SRCCOPY                                                       // 複製方法

                                     );

 

                   pBmp->DeleteObject();

                   pMemDC->DeleteDC();

                   delete pBmp;

                   delete pMemDC;

                   dc.DeleteDC();

                   return TRUE;

}

 

附,感謝puhuofeie的幫助

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