• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            幽幽
             
            posts - 51,  comments - 28,  trackbacks - 0
            一、 簡介

            屏幕抓圖程序在處理圖形中應用廣泛。作為Windows XP及以后版本操作系統的圖形處理內核,GDI+在二維幾何圖形處理、圖像顯示與轉換和字符排版等方面簡直是傳統GDI程序員的一種解脫。但是,至少在目前情況下,GDI+尚不能完全代替GDI。與GDI相比,它至少還存在以下不足:

            不支持從內存到屏幕的位傳輸操作;

            不支持光柵“位運算”操作;

            如果程序性能、速度要求比較嚴格,在圖片輸出方面的表現較差時,GDI往往能取代實現高性能的輸出。

            本文通過對流行的屏幕抓圖程序工作原理的剖析,力圖向讀者闡明GDI+與GDI各自在圖形處理方面的優缺點,并給出相應的VC++ .NET代碼實現。

                二、 GDI在抓圖中的關鍵作用

                要實現屏幕抓圖,關鍵有兩點:一是獲取圖片所在窗口的窗口句柄,即在何處捕獲圖片;二是保存抓取的圖片,實現這一點正是GDI+的強項。

                對于問題一,可以利用SetCapture函數,它能夠追蹤鼠標指針的移動(包括在屏幕抓圖程序窗口之外的窗口)。在移動鼠標的過程中,它還可以根據鼠標的指針所在位置來判斷當前窗口的窗口句柄。我們還可以使用函數WindowFromPoint,這個函數能夠找出鼠標指針當前位置所對應的窗口句柄。

                使用過知名的抓圖軟件SnagIT的讀者都知道,在選擇抓圖窗口時,鼠標指針所在位置的窗口都會出現加粗的紅色邊框,以提醒目前所選擇的窗口,這個功能實現起來有些復雜。下面介紹在GDI中如何使這個紅色邊框出現。

                【注意】正是由于這個紅色邊框的實現,讀者才能發現GDI+在這方面的弱點。

            在GDI中,一個最基本的概念就是設備環境(DC),每一個窗口都具有自己的DC。如果能夠找到窗口的DC,那么,用戶就能夠在該窗口的任何位置繪圖。然而,在屏幕抓圖程序中,由于用戶所選擇的窗口不固定,所以,要想得到鼠標指針所處窗口的DC并不容易。這一問題的答案在于GetDC函數。下面是GetDC的函數聲明:

            HDC GetDC(HWND hWnd);

                這里,hWnd是DC對應的窗口句柄。注意,當hWnd為空時,該函數返回的是整個屏幕的設備環境句柄。這就意味著,開發人員可以在屏幕上的任何位置進行任意的繪圖操作。

                在鼠標指針所處的窗口繪圖時,繪圖的目的只是為了提醒用戶目前所選擇的窗口,所以,在繪圖時,必須保證不會破壞窗口原有的畫面。這時可將窗口的繪圖模式設為RS_NOTXORPEN,將畫筆顏色與屏幕顏色進行異或運算之后,再對屏幕顏色取反即可。RS_NOTXORPEN運算方式的特點在于:對同一像素進行兩次RS_NOTXORPEN運算后,像素值并不會發生變化。這樣,在同一個地方進行兩次繪圖后,窗口的畫面并不會發生任何變化。

                【注意】這些功能在GDI+中很難實現。

                三、 編碼實現

            由上可知,屏幕抓圖至少分為3個步驟:

                (1) 啟用鼠標指針捕獲。

                (2) 在鼠標指針所在處的窗口進行繪圖,提示抓圖的目標。

                (3) 選定目標窗口時,將目標窗口的畫面保存為自定義的位圖并終止鼠標指針捕獲。

                以下是具體的編程步驟:

                (1)在Visual C++ .NET中按照GDI+程序的框架新建一個基于對話框的項目ScreenCapture,然后準備好一個外形為相機的光標文件(*.cur),將之引入資源管理器(IDC_CAMERA)。接著在CScreenCaptureDlg類中加入以下兩個全局變量:

            HWND hwndCapture;

            Crect rectCapture;

               (2)通過類向導加入對WM_MOUSEMOVE及WM_LBUTTONUP事件的響應函數,分別如下所示。

            void CScreenCaptureDlg::OnMouseMove(UINT nFlags, CPoint point)

            {

            //如果用戶按隹鼠標左鍵不放,則開始抓取圖片

            if(nFlags==MK_LBUTTON){

            //隱藏程序窗口,以免影響在抓取時的“視野”

            ShowWindow(SW_HIDE);

            //載入“照相機”鼠標指針,開始追蹤鼠標指針的移動

            HCURSOR cur=LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CAMERA));

            SetCursor(cur);

            SetCapture();

            //獲得鼠標指針所在窗口的句柄

            this->ClientToScreen(&point);

            hwndCapture=(HWND)::WindowFromPoint(point);

            //取得屏幕的設備環境句柄,以便在屏幕的任何位置繪圖

            HDC hDC=::GetDC(NULL);

            //建立一個紅色的畫筆

            HPEN hPen=CreatePen(PS_INSIDEFRAME,6,RGB(255,0,0));

            //將繪圖模式設為R2_NOTXORPEN,在繪圖時可以不破壞原有的背景

            int nMode=SetROP2(hDC,R2_NOTXORPEN);

            HPEN hpenOld=(HPEN)SelectObject(hDC,hPen);

            //得到鼠標指針所在窗口的區域

            ::GetWindowRect(hwndCapture,&rectCapture);

            //在鼠標指針所在處的窗口四周畫一紅色的矩形,做為選定時的提示

            POINT pt[5];

            pt[0]=CPoint(rectCapture.left,rectCapture.top);

            pt[1]=CPoint(rectCapture.right,rectCapture.top);

            pt[2]=CPoint(rectCapture.right,rectCapture.bottom);

            pt[3]=CPoint(rectCapture.left,rectCapture.bottom);

            pt[4]=CPoint(rectCapture.left,rectCapture.top);

            ::Polyline(hDC,pt,5);

            //延時后再重繪紅色矩形,這樣不會破壞原有的內容

            Sleep(100);

            ::Polyline(hDC,pt,5);

            ::SelectObject(hDC,hpenOld);

            ::ReleaseDC(NULL,hDC);

            }

            CDialog::OnMouseMove(nFlags, point);

            }

            void CScreenCaptureDlg::OnLButtonUp(UINT nFlags, CPoint point)

            {

            // 得到鼠標指針所在窗口的區域寬、高

            int nWidth=rectCapture.Width();

            int nHeight=rectCapture.Height();

            HDC hdcScreen,hMemDC;

            HBITMAP hBitmap,hOldBitmap;

            //建立一個屏幕設備環境句柄

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

            hMemDC=CreateCompatibleDC(hdcScreen);

            //建立一個與屏幕設備環境句柄兼容、與鼠標指針所在窗口的區域等大的位圖

            hBitmap=CreateCompatibleBitmap(hdcScreen,nWidth,nHeight);

            //把新位圖選到內存設備描述表中

            hOldBitmap=(HBITMAP)SelectObject(hMemDC,hBitmap);

            //把屏幕設備描述表拷貝到內存設備描述表中

            BitBlt(hMemDC,0,0,nWidth,nHeight,hdcScreen,rectCapture.left,rectCapture.top,SRCCOPY);

            DeleteDC(hdcScreen);

            DeleteDC(hMemDC);

            //返回位圖句柄

            //打開剪貼板,并將位圖拷到剪貼板上

            OpenClipboard();

            EmptyClipboard();

            SetClipboardData(CF_BITMAP,hBitmap);

            //關閉剪貼板

            CloseClipboard();

            MessageBox("屏幕內容已經拷到剪貼板!");

            ReleaseCapture();

            //恢復窗口顯示模式

            ShowWindow(SW_NORMAL);

            CDialog::OnLButtonUp(nFlags, point);

            }

                至此,一個具有專業效果的屏幕抓圖程序的核心已經搞定。

                四、 用GDI+實現畫面的保存

                經過上面兩步,如果用戶在對話框中按住鼠標左鍵不放,程序便開始“抓圖”。當選擇好抓圖的目標后,松開鼠標左鍵,抓圖的目標窗口的畫面就自動保存到剪貼板中了。但是,把畫面保存到文件中更為重要。如果用GDI的方式來操作,需要對各種類位圖的結構有詳盡的了解,極其麻煩。但如果用GDI+來實現之則極為容易。下面介紹如何將已經抓到的圖片保存到一個BMP文件中。

            由上面知,抓圖程序已經得到了所捕獲的窗口的位圖句柄,接下來要將位圖句柄保存為相應的位圖文件。這一切歸功于GDI+的Bitmap類,詳見下列代碼。

            void CScreenCaptureDlg::OnLButtonUp(UINT nFlags, CPoint point)

            {

            //……省略

            if(GetSaveFileName(&ofn))

            {

            CLSID pngClsid;

            Bitmap bmp(hBitmap,NULL);

            //獲取BMP文件的編碼方式

            GetEncoderClsid(L"image/bmp",&pngClsid);//幫助函數

            CString tmp(ofn.lpstrFile);

            CStringW filename((LPCSTR)tmp);

            //保存所截取的屏幕圖片

            bmp.Save(filename,&pngClsid);

            }

            ReleaseCapture();

            MessageBox("屏幕內容已經保存到文件中!");

            //恢復窗口顯示模式

            ShowWindow(SW_NORMAL);

            CDialog::OnLButtonUp(nFlags, point);

            }

                五、 小結

                本文通過一個專業的屏幕抓圖程序的核心實現,對比分析了GDI與GDI+各自的優缺點。但我們相信,GDI+作為新一代圖形引擎,隨著版本的不斷升級,其遲早要淘汰掉GDI。本人拙見,不足處還望讀者指正。

            另外,本文源碼在Windows 2000/VC++.NET 2003環境中調試通過。調試過程中注意:

            確保工程對GDI+庫的正確引用:在頭文件stdafx.h中要加入相應引用;在應用程序類的InitInstance成員函數前后及其析構函數中加適當的操作;工程編譯時要加入對gdiplus.lib的引用(“項目”|“添加現有項”,我的機器上是在C:\Program Files\Microsoft Visual Studio.NET\vc7\platformSDK\lib下找到庫文件)。

             

            posted on 2008-08-13 23:02 幽幽 閱讀(2020) 評論(0)  編輯 收藏 引用

            <2008年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            常用鏈接

            留言簿(6)

            隨筆分類(35)

            隨筆檔案(51)

            文章分類(3)

            文章檔案(3)

            相冊

            我的鏈接

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            国产精品成人99久久久久 | 久久精品99久久香蕉国产色戒 | 人人狠狠综合久久88成人| avtt天堂网久久精品| 国产精品美女久久久免费| 色综合久久综合网观看| 青青草原综合久久大伊人| 久久国产乱子伦免费精品| 久久国产精品99久久久久久老狼| 香蕉久久久久久狠狠色| 99精品伊人久久久大香线蕉| 亚洲AV无码久久| 日韩美女18网站久久精品| 国产成人精品久久一区二区三区| 蜜桃麻豆WWW久久囤产精品| 国产精品久久国产精麻豆99网站| 久久精品国产精品亚洲人人| 久久精品国产69国产精品亚洲| 久久久久国产一区二区| 99久久人妻无码精品系列| 日本亚洲色大成网站WWW久久| 国产精品久久久久久搜索| 亚洲午夜久久久| 综合久久给合久久狠狠狠97色| 国产精品久久精品| 欧美va久久久噜噜噜久久| 久久九九兔免费精品6| 久久人做人爽一区二区三区 | 奇米综合四色77777久久| 国产亚州精品女人久久久久久| 久久综合狠狠综合久久激情 | 久久亚洲精品无码AV红樱桃| 亚洲国产小视频精品久久久三级| 嫩草影院久久99| 久久久久免费精品国产| 久久亚洲国产成人精品性色| 久久夜色精品国产噜噜亚洲a| 久久婷婷五月综合色99啪ak| 亚洲欧美国产精品专区久久| 国产精品欧美久久久久无广告| 久久最近最新中文字幕大全|