抓屏的各種方法(http://www.codeproject.com/KB/dialog/screencap.aspx)

文章翻譯自 P.GopalaKrishna Various methods for capturing the screen 一文,原版地址見下面。本文章版權歸原作者所有。
???
如果轉載該譯文 , 請保證文章的完整性,并注明來自 www.farproc.com
袁曉輝 ??
2005/6/12

原版地址: http://www.codeproject.com/dialog/screencap.asp#Windows%20Media%20API%20for%20Capturing%20the%20Screen%20:

?

本文附帶源碼 1 下載 39K

本文附帶源碼 2 下載 135.5K

本文附帶源碼 3 下載 59.8K

?

目錄:

?

l????????? 導言

l????????? GID 函數抓屏

l????????? DirectX 方式抓屏

l????????? Windows Media API 抓屏

?

導言

?

有時候我們需要編程抓取整個屏幕上的內容,下面我將介紹抓屏是如何實現的。典型地,我們可以用 GID DirectX 來完成,另外一個選擇是 Windows Media API ,在這篇文章我會逐一加以分析。在每一種方法里,一旦我們把屏幕的內容保存到了程序定義的內存塊或 bitmap 文件里,我們就可以進一步利用它們來生成動畫和電影,這個過程你可以參考“ HBitmap 創建電影 ”一文中,以獲得更多的幫助。

?

GDI 函數抓屏

如果我們不太在意抓屏的效率,并且我們想要的只是一個屏幕快照的話,可以考慮使用 GDI 方式。這種抓屏機制是以“桌面也是一個窗口,桌面也有一個窗口句柄( HWND )”這個簡單的常識為基礎的,如果我們得到了桌面的設備上下文( DC ),就可以利用 blit (復制)它的內容到我們創建的 DC 中。我們可以用 GetDeskWindow ()得到桌面的窗口句柄,從句柄得到 DC 也是很容易的。具體的實現步驟為:

?

1.????????? 通過 GetDesktopWindow ()函數得到桌面的窗口句柄

2.????????? GetDC ()取得桌面窗口的 DC

3.????????? 創建和屏幕 DC 兼容的位圖和 DC CreateCompatibleBitmap ()和 CreateCompatibleDC ()),并把這個位圖選進該 DC SelectObject ())

4.????????? 當你準備好抓屏時,就復制桌面窗口 DC 的內容到兼容 DC ,你就完成的抓屏過程,兼容位圖中就是抓屏時刻的屏幕內容

5.????????? 完成后別忘了釋放你創建的對象,內存是寶貴的(對別的程序來說)

?

示例代碼:

void CaptureScreen()

{

??? int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);

??? int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

??? HWND hDesktopWnd = GetDesktopWindow();

??? HDC hDesktopDC = GetDC(hDesktopWnd);

??? HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);

??? HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC,

??????? nScreenWidth, nScreenHeight);

??? SelectObject(hCaptureDC,hCaptureBitmap);

??? BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,hDesktopDC,0,0,SRCCOPY);

??? SaveCapturedBitmap(hCaptureBitmap); //Place holder - Put your code

??????????????????????????????? //here to save the captured image to disk

??? ReleaseDC(hDesktopWnd,hDesktopDC);

??? DeleteDC(hCaptureDC);

??? DeleteObject(hCaptureBitmap);

}

?

上面代碼段中, GetSystemMetrics ()返回屏幕的寬度( SM_CXSCREEN )和高度( SM_CYSCREEN )。關于如何保存抓到的位圖到文件和如何置到剪貼板,請參看附帶的源代碼,很簡單的。示例代碼每隔一段時間就通過上述技術抓屏,并把圖像序列保存到動畫。

?

DirectX 方式

DreictX 進行抓屏也是很簡單的, DirectX 提供了很優雅的實現。

每個 DirectX 程序都包含一個被我們稱作緩沖的內存區域,其中保存了和該程序有關的顯存內容,這在程序中被稱作后臺緩沖( Back Buffer ),有些程序有不止一個的后臺緩沖。還有一個緩沖,在默認情況下每個程序都可以訪問-前臺緩沖。前臺緩沖保存了和桌面相關的顯存內容,實質上就是屏幕圖像。

我們的程序通過訪問前臺緩沖就可以捕捉到當前屏幕的內容。由 DirectX 的底層優化機制做保證,我們的抓屏效率是很高的,至少比 GDI 方式高。

DirectX 程序中訪問前臺緩沖是很簡單的, IDirect3DDevice8 接口提供了 GetFrontBuffer() 方法,它接收一個 IDirect3DSurface8 對象指針做參數,并復制前臺緩沖的內容到該 Surface IDirect3DSurfce8 對象可以用 IDirect3DDevice8::CreateImageSurface() 得到。一旦屏幕內容被保存到了這個 surface ,我們就可以用 D3DXSaveSurfaceToFile() 方法直接把內容保存到磁盤 bmp 文件。示例代碼如下:

extern IDirect3DDevice8* g_pd3dDevice;

Void CaptureScreen()

{

??? IDirect3DSurface8 * pSurface;

??? g_pd3dDeviceàCreateImageSurface(ScreenWidth,ScreenHeight,

??????? D3DFMT_A8R8G8B8,&pSurface);

??? g_pd3dDevice->GetFrontBuffer(pSurface);

??? D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,

??????? NULL,NULL);

??? pSurface->Release();

}

上面, g_pd3dDevice 是一個初始化好的 IDirect3DDevice 對象,這個例子直接把捕捉到的圖像保存到文件。然而,有時候我們想訪問直接這個圖像中的各個位,我們可以使用 IDirect3DSurface8::LockRect() ,它給我們一個執行 surface 內存的指針,也就是捕捉到的圖像的數據。我們復制這些數據到程序定義的內存中就可以操作它了。看下面的代碼:

extern void* pBits;

extern IDirect3DDevice8* g_pd3dDevice;

IDirect3DSurface8 * pSurface;

g_pd3dDeviceàCreateImageSurface(ScreenWidth,ScreenHeight,

??????????????????????????????? D3DFMT_A8R8G8B8,&pSurface);

g_pd3dDevice->GetFrontBuffer(pSurface);

D3DLOCKED_RECT lockedRect;

pSurfaceàLockRect(&lockedRect,NULL,

????????????????? D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|

????????????????? D3DLOCK_READONLY)));

for( int i=0 ; i < ScreenHeight ; i++)

{

??? memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 ,

??????? (BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,

??????? ScreenWidth * BITSPERPIXEL / 8);

}

g_pSurface->UnlockRect();

pSurface->Release();

?

上面的 pBits 是一個 void* ,請保證為先為它分配組足夠的內存空間。 BITSPERPIXEL 一般用 32 位色即可,它也取決于你的顯示器當前配置。一個需要注意的是, surface 的寬度和被捕捉的屏幕寬度不一樣。由于內存對齊的原因 ( WORD 對齊的內存通常在訪問時效率較高 ) surface 在每行結尾處可能會有多余的 bits 以使它對齊到 word 邊界上。 lockedRect.Pitch 給我們提供了兩個連續行的開端之間的字節數。也就是說我們在讀取一行時要向后移動指針 Pitch 字節而不是 Width 字節。你可以用下面的代碼反序復制 surface

for( int i=0 ; i < ScreenHeight ; i++)

{

??? memcpy((BYTE*) pBits +( ScreenHeight - i - 1) *

??????? ScreenWidth * BITSPERPIXEL/8 ,

??????? (BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,

??????? ScreenWidth* BITSPERPIXEL/8);

}

這對于從 top-down 位圖到 bottom-up 位圖很有用。

?

我們還可以使用 IDirect3DSurface9 GetDC() 方法取得 DirectX surface GDI 兼容 DC ,然后復制它的內容到我們的兼容 DC 。如果你用的是 DirectX9 ,試試吧。

最后,需要注意的一點,文檔提到: FrontBuffer 是一個比較慢的操作,設計就是如此,所以在效率很關鍵的程序中應避免使用。已經警告你了!本文附帶的源代碼用這種技術定時捕捉屏幕,并保存為動畫。

Windows Media API 抓屏

Windows Media 9.0 支持用 Windows Media Encoder 9 API 來抓屏。它有一個編碼器叫 Windows Media Video 9 Screen codec ,特別為抓屏優化過。 Windows Media Encoder API 提供了一個 IWMEncoder2 接口可以用來高效地捕捉屏幕圖像。

?

用這種技術進行抓屏也很簡單,首先我們用 CoCreateInstance() 創建一個 IWMEncoder2 對象:

IWMEncoder2* g_pEncoder=NULL;

CoCreateInstance(CLSID_WMEncoder,NULL,CLSCTX_INPROC_SERVER,

??????? IID_IWMEncoder2,(void**)&g_pEncoder);

這個 Encoder 對象包含了捕捉屏幕的所需的全部操作,然而為了正確地工作,編碼器對象的行為取決于被稱作 profile 的設置。一個 profile 只是一個包含了所有控制編碼操作設置的文件,我們可以根據被捕捉的數據的特性在運行時創建包含自定義設置的 profile 。為了在你的抓屏程序中使用 profile ,我們基于 Windows Media Video 9 Screen codec 來創建自定義的 profile 。自定義的 profile 對象從 IWMEncProfile2 開始就被支持了。我們可以用 CoCreateInstance 來創建自定義 profile

IWMEncProfile2* g_pProfile=NULL;

CoCreateInstance(CLSID_WMEncProfile2,NULL,CLSCTX_INPROC_SERVER,

??????? IID_IWMEncProfile2,(void**)&g_pProfile);

我需要在 profile 里指定編碼器的聽眾( audience )。每個 profile 可以包含多個聽眾配置,它們是 IWMEncAudienceObj 接口對象。這里我們為 profile 使用一個聽眾。我們可以通過 IWMEncProfile::AddAudience() 為我們的 profile 創建聽眾,這個函數返回一個 IWMEncAudienceObj 指針,可以用來配置視頻編碼器 ( IWMEncAudienceObj::put_VideoCodec() ) ,視頻幀對象 ( IWMEncAudienceObj::put_VideoHeight() IWMEncAudienceObj::put_VideoWidth() ) 我們用下面的代碼來配置視頻編碼器:

extern IWMEncAudienceObj* pAudience;

#define VIDEOCODEC MAKEFOURCC('M','S','S','2')

??? //MSS2 is the fourcc for the screen codec

?

long lCodecIndex=-1;

g_pProfile->GetCodecIndexFromFourCC(WMENC_VIDEO,VIDEOCODEC,

??? &lCodecIndex); //Get the Index of the Codec

pAudience->put_VideoCodec(0,lCodecIndex);

?

fourcc 是針對每個編碼器的唯一的標識, Windows Media Video 9 Screen codec fourcc MSS2 IWMEncAudienceObj::put_VideoCodec() 接受 profile 索引來組織一個 profile ,索引可以用 IWMEncProfile::GetCodecIndexFromFourCC() 取得。

?

一旦我們配置完畢一個 profile 對象,我們就可以用 IWMEncSourceGroup :: put_Profile() 選擇這個 profile 到我們的編碼器。一個源組( SourceGruop )是一組視頻流來源或音頻流來源,或 html 來源。每個編碼器可以使用許多源組,并從中取得輸入數據。由于我們的程序僅僅使用視頻流中是視頻來源。這個視頻來源需要用 IWMEncVideoSource2::SetInput(BSTR) Screen Device 來配置為輸入來源:

extern IWMEncVideoSource2* pSrcVid;

pSrcVid->SetInput(CComBSTR("ScreenCap://ScreenCapture1");

?

目的輸出可以用 IWMEncFile::put_LocalFileName() 配置為保存到視頻文件( wmv 文件)。 IWMEncFile 對象可以用 IWMEncoder::get_File() 得到:

IWMEncFile* pOutFile=NULL;

g_pEncoder->get_File(&pOutFile);

pOutFile->put_LocalFileName(CComBSTR(szOutputFileName);

?

現在,一旦編碼器對象的一切所需配置都完成后,我們就可以用 IWMEncoder::Start() 開始抓屏。 IWMEncoder::Stop() IWMEncoder::Pause 可以用來停止和暫停捕捉。

這些適用于全屏捕捉,我們也可以通過調整輸入視頻來源流的屬性來選擇一個區域進行捕捉。我們可以用 IWmEnVideoSource2 I PropertyBag 接口來實現:

#define WMSCRNCAP_WINDOWLEFT CComBSTR("Left")

#define WMSCRNCAP_WINDOWTOP CComBSTR("Top")

#define WMSCRNCAP_WINDOWRIGHT CComBSTR("Right")

#define WMSCRNCAP_WINDOWBOTTOM CComBSTR("Bottom")

#define WMSCRNCAP_FLASHRECT CComBSTR("FlashRect")

#define WMSCRNCAP_ENTIRESCREEN CComBSTR("Screen")

#define WMSCRNCAP_WINDOWTITLE CComBSTR("WindowTitle")

extern IWMEncVideoSource2* pSrcVid;

int nLeft, nRight, nTop, nBottom;

pSrcVid->QueryInterface(IID_IPropertyBag,(void**)&pPropertyBag);

CComVariant varValue = false;

pPropertyBag->Write(WMSCRNCAP_ENTIRESCREEN,&varValue);

varValue = nLeft;

pPropertyBag->Write( WMSCRNCAP_WINDOWLEFT, &varValue );

varValue = nRight;

pPropertyBag->Write( WMSCRNCAP_WINDOWRIGHT, &varValue );

varValue = nTop;

pPropertyBag->Write( WMSCRNCAP_WINDOWTOP, &varValue );

varValue = nBottom;

pPropertyBag->Write( WMSCRNCAP_WINDOWBOTTOM, &varValue );

?

本文附帶的源碼實現此中技術的抓屏。除去生成的動畫質量很好外,一個有意思的地方是鼠標指針也被抓到了( GDI DirectX 默認是不抓取鼠標指針的)。

?

注意,為了適用 WindowMedia9.0 API ,你的電腦必須安裝 Windows Media9.0 SDK ,你可以用下面地址下載:

  • http://msdn.microsoft.com/library/default.asp?url=/downloads/list/winmedia.asp

最終用戶必須安裝 Windows Media Encoder 9 系列才能運行你的程序。在發布基于 Windows Media Encoder SDK 的程序時, Windows Media Encoder 軟件也必須附帶上去,要么在你的軟件安裝時自動安裝 Windows Media Encoder 要么讓用戶自己下載安裝。

?

Windows Encoder 9.0 可以從下面地址下載:

http://www.microsoft.com/windows/windowsmedia/ 9series/encoder/default.aspx

?

結論

上面討論的各種方法都是基于一個目標-抓取屏幕的內容。然而適用不同的技術,得到的結果也不一樣。如果我們需要的只是偶爾的抓屏, GDI 方式是個好的選擇,因為它簡單。然而如果你想得到更專業的結果,可以使用 Windows Media 。一個可能沒有意義的要點是,這些技術捕捉到的內容的質量很大程度上決于你的系統設置,比如進制硬件加速會大大提高抓屏的質量和程序的運行效率。 ?

?

?

?

?

?

?

?

?

?

?

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255034.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255034.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255034.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

與 OpenCV 1 同時使用

與 OpenCV 1 同時使用 目的 對于OpenCV的開發團隊來說&#xff0c;持續穩定地提高代碼庫非常重要。我們一直在思考如何在使其易用的同時保持靈活性。新的C接口即為此而來。盡管如此&#xff0c;向下兼容仍然十分重要。我們并不想打斷你基于早期OpenCV庫的開發。因此&am…

第五周 Leetcode 99. Recover Binary Search Tree (HARD)

Leetcode99 給定一個 二叉搜索樹&#xff0c;其中兩個節點被交換&#xff0c;寫一個程序恢復這顆BST. 只想到了時間復雜度O&#xff08;n&#xff09;空間復雜度O&#xff08;h&#xff09; h為樹高的解法&#xff0c;還沒想到空間O(1&#xff09;的解法。 交換的情況只有兩種&…

Fedora15安裝NVIDIA顯卡驅動全過程

Fedora安裝N卡驅動全過程 Fedora安裝NVIDIA顯卡全過程&#xff0c;經過自己親自安裝 折騰了一個上午&#xff0c;搞定了N卡驅動安裝&#xff0c;現將安裝步驟整理如下&#xff1a; 1、首先訪問Nvidia官網下載最新的Linux驅動&#xff1a;http://www.nvidia.cn/Download/index…

板鄧:wordpress自定義登錄頁面實現用戶登錄

首先檢查用戶是否已經登錄&#xff0c;如果已經登錄就返回info目錄下的頁面。 <?phpglobal $current_user;$loginuserid $current_user->ID;if($loginuserid){//如果已經登錄header("Location:".get_bloginfo(url)."/info/"); exit;} 如果用戶未登…

機器學習(machine learning)之AdaBoost算法

轉自&#xff1a;http://blog.csdn.net/haidao2009/article/details/7514787 淺談 Adaboost 算法 機器學習是利用一些方法來使機器實現人的學習行為&#xff0c;以便獲取新的知識或技能&#xff0c;重新組織已有的知識結構使之不斷改善自身的性能。 AdaBoost全名“adaptive B…

交換兩個整形變量的數值

課堂問題一: #include<stdio.h>void swap(int *p,int *q) {int *m;printf("m%d\n",m);printf("%s\n",*m);*m*p;*p*q;*q*m; } int main(){int a,b;scanf("%d,%d",&a,&b);swap(&a,&b);printf("a%d b%d\n",a,b);re…

使用CodeFirst創建并更新數據庫

本文主要介紹如何使用CodeFirst模式來新建并更新數據庫 在使用Entity Framwork的三種方式&#xff08;ModelFist、DBFirst、CodeFirst&#xff09;中&#xff0c;CodeFirst方式書寫的代碼最為干凈。 至于CodeFist方式的詳細優缺點請各位讀者自行搜索&#xff0c;這里不多贅述。…

fedora 15怎么修改運行級別?

inittab改了已經在fedora15中&#xff0c;你vim它就可以看到更改說明&#xff0c;就是說都改到/etc/systemd/system/default.target這里了&#xff0c;就是缺省的設置。如果你要改變缺省值就把對應的runlevel移動過去覆蓋了。 To 3 字符 [root15 system]# rm -rf /etc/systemd…

淺析人臉檢測之Haar分類器方法

由于工作需要&#xff0c;我開始研究人臉檢測部分的算法&#xff0c;這期間斷斷續續地學習Haar分類器的訓練以及檢測過程&#xff0c;在這里根據各種論文、網絡資源的查閱和對代碼的理解做一個簡單的總結。我試圖概括性的給出算法的起源、全貌以及細節的來龍去脈&#xff0c;但…

利用微軟平臺生成報表,線性圖,柱形圖

說來慚愧,以前的工作中一直借助第三方dll進行報表制作,比如線性圖,柱形圖. 因為現在工作的這家公司不允許隨便引入第三方dll,聽同事說起可以建rdl類型文件進行引入到winform窗體中,窗體上使用reportViewer控件進行關聯展示.下面是我今天摸索3個小時的結果分享. 第一步. 首先找到…

Linux ffmpeg的安裝編譯過程

Linux ffmpeg的安裝編譯過程 1、下載ffmpeg。    在網上搜索一下,或者到官方網站下載2、解壓   tar命令解壓3、配置  ./configure --enable-shared --prefix/usr/local/ffmpeg  其中&#xff1a;--enable-shared 是允許其編譯產生動態庫&#xff0c;在以后的編程中…

opencv 模板匹配(cvMatchTemplate)

opencv 模板匹配(cvMatchTemplate) 模板匹配是通過在輸入圖像上滑動模板圖像塊對實際的圖像塊和輸入圖像進行匹配&#xff0c;并且可以利用函數cvMinMaxLoc()找到最佳匹配的位置。例如在工業應用中&#xff0c;可以鎖定圖像中零部件的位置&#xff0c;并根據具體的位置&…

爬蟲系統Lucene分詞

思路&#xff1a;查詢數據庫中信息&#xff0c;查詢出id和name把那么進行分詞存入文件 package com.open1111.index; import java.io.IOException;import java.nio.file.Paths;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet; impor…

[BZOJ1880] [Sdoi2009] Elaxia的路線 (SPFA 拓撲排序)

Description 最近&#xff0c;Elaxia和w**的關系特別好&#xff0c;他們很想整天在一起&#xff0c;但是大學的學習太緊張了&#xff0c;他們 必須合理地安排兩個人在一起的時間。Elaxia和w**每天都要奔波于宿舍和實驗室之間&#xff0c;他們 希望在節約時間的前提下&#xff0…

ffmpeg的編譯大全

ffmpeg的編譯大全 最近互聯網視頻共享的網站很火&#xff0c;公司也想搞類似的網站&#xff0c;初步是用fmsffmpeg形式 fms負責在線錄制&#xff0c;播放&#xff0c;ffmpeg則在后臺處理上傳的資源轉換成一定的格式。 為了讓ffmpeg支持的格式盡量多&#xff0c;所以特把我的編譯…

用OPENCV視覺解數獨

用OPENCV視覺解數獨 2010-06-29 看到增強視覺網站上介紹老外用視覺解SUDOKU(http://www.cvchina.info/2011/05/29/video-sudoku-solver/)&#xff0c;覺得應該不難&#xff0c;于是用OPENCV和訓練好的數字分類器&#xff0c;也試著做一個&#xff0c;純屬娛樂 基本思路如下&…

集成ffmpeg/x264:ERROR: libx264 not found的問題

集成ffmpeg/x264:ERROR: libx264 not found的問題--拔劍集成ffmpeg/x264碰到如下問題&#xff1a; ERROR: libx264 not found察看config.log,詳細信息如下&#xff1a;check_lib x264.h x264_encoder_encode -lx264check_header x264.hcheck_cppBEGIN/tmp/ffconf.isuazGlg.c1 …

[ActionScript 3.0] AS3.0 下雨及漣漪效果

幀代碼&#xff1a; stage.frameRate 80;function init(x1:Number,y1:Number) {var mc:MovieClipnew MovieClip();addChild(mc);mc.x x1;mc.y y1;mc.graphics.lineStyle(0.5,0xbbffff,0.6);mc.graphics.drawEllipse(-1,-0.3,2,0.6);mc.addEventListener(Event.ENTER_FRAME,f…

JS Math.round()方法原理

請先測試代碼&#xff1a; 1 <!doctype html>2 <html lang"en">3 4 <head>5 <meta charset"UTF-8" />6 <title>Math.round方法</title>7 <style type"text/css">8 …

一個通用Makefile的編寫

我們在 LinuxLinux Linux是一套免費使用和自由傳播的操作系統&#xff0c;它主要用于基于Intel系列CPU的計算機上。這個系統是由全世界各地的成千上萬的程序員設計和實現的&#xff0c;其目的是建立不受任何商品化軟件的版權制約的、全世界都能自由使用的Unix兼容產品。 環境下…