天天看點

31、Windows API GDI(3)

一、繪制圖形

    GDI所能繪制形狀有很多種,可區分為标準形狀和非标準形狀。标準形狀使用幾個簡單、确定的屬性就可以确定的形狀,比如矩形(左上角點位置和長度)、橢圓(使用外切矩形)、圓角矩形(一個矩形加圓角半徑)、弓形、扇形。

    也有不标準的形狀,所有不标準的形狀都可以使用多邊形(Polygon)來定義。

定義多邊形也需要使用一個點數組。将點數組中的點依其在數組中的順序連接配接起來,就是一個多邊形。

31、Windows API GDI(3)

在繪制時,圖形内部使用DC的目前畫刷對象進行填充,圖形的邊使用DC的目前畫筆對象進行勾勒。

1、相關API

GetClientRect

SetRect

CopyRect

可以将一個RECT在螢幕上顯示出來,顯出的方式是有内部填充、邊沿勾勒,也可以對一個RECT進行反轉操作。反轉操作會将現有RECT現有的填充顔色取反。FillRect、FrameRect、InvertRect函數分别實作這3個功能。

坐标原點是左上角。

ScreenToClient函數和ClientToScreen函數是兩個操作點坐标的API函數,其功能是将點從相對于螢幕的位置計算相對于視窗客戶區的位置,以及從視窗客戶區的位置計算相對于螢幕的位置。

二、位圖操作

1、相關API函數及資料結構

◇ CreateDC。

◇ CreateCompatibleDC。

◇ GetDeviceCapS。

◇ SelectObject。

◇ BitBlt。

◇ stretchBlt。

◇ GetDIBits。

◇ DeleteDC。

◇ ReleaseDC。

◇ GetWindowRect。

主要涉及以下結構和資料類型。

◇ HDC。

◇ HBITMAP。

◇ BITMAPINFO。

◇ BITMAPFILEHEADER。

◇ RECT。

2、截取螢幕的流程

截取螢幕輸出一般需要經過以下步驟。

◇使用字元串“DISPLAY”為參教,調用CreateDC,得到類型為“顯示”的DC。

◇調用CreateCompatibleDC建立一個記憶體DC。

◇得到需要截取的螢幕區域,比如調用GetDeviceCaps擷取螢幕的大小,GetWindowRect擷取視窗區域等。

◇調用CreateCompatibleBitmap建立BITMAP對象。

◇調用SelectObject,将建立的BITMAP選擇入記憶體DC,并将傳回值使用另外一個BITMAP對象句柄來儲存。SelectObj ect的傳回值表示了DC中被替換的圖形對象。

◇調用BitBlt函數将源DC(顯示類型的DC)指定位塊(bit-block)的顔色資料轉移到目的DC(記憶體)中,.

◇将之前儲存的舊的BITMAP對象再調用SelectObject選擇回記憶體DC中,這樣從顯示DC中BitBlt到記憶體DC中的位塊顔色資料就被替換,SelectObject的傳回值即是從螢幕DC中得到的位圖對象(HBITMAP類型)。

之後就可以使用這個HBITMAP句柄來進行位圖的操作,比如顯示在螢幕的特定區域上。

3、将位圖顯示在螢幕上

    将位圖檔案顯示在界面上的原理與截屏類似,也是将位圖檔案選擇入DC,差別是顯示位圖檔案時所選擇入的DC是用于顯示的視窗DC。如果在顯示時需要拉伸,則需要使用StretchBlt函數在DC間轉換。

    顯示位圖的流程:

(1)建立記憶體DC,将BITMAP對象選擇入記憶體DC;

(2)擷取用于顯示位圖的視窗client區域的DC;

(3)從位圖對象中擷取位圖屬性,包括大小、長寬等;

(4)計算在client擷取中顯示的位置:

(5)根據需要調用StretchBlt或BitBlt函數,在記憶體DC和視窗Client區域DC間轉移;

(6)釋放相關資源。

StretchBlt函數的功能是在兩個DC的指定區域中複制位圖,将源DC中指定區域的位圖複制到目的DC的指定區域上,并根據區域的大小和長寬比例關系拉伸或壓縮位圖。

BitBlt函數的功能是将源DC上的顔色資訊轉移到目的DC的指定區域上,轉移時僅限于DC的最大範圍,超出範圍的部分不轉移,不進行拉伸和壓縮。

示例

31、Windows API GDI(3)
31、Windows API GDI(3)

截屏

**************************************/

/* 頭檔案 */

#include <windows.h>

/* 常量定義 */

#define PALVERSION 0x300

#define CAP_SHOW_MODE_STRTCH 1

#define CAP_SHOW_MODE_NOSTRTCH 0

/* 全局變量 */

HBITMAP ghBitmap = NULL;

RECT rectShow;

// 修改這裡截取不同的視窗,如果為NULL,則截取螢幕

LPSTR szCaptureWindowName = "Windows 任務管理器";

/* 函數申明 */

LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

HBITMAP ScreenCapture(LPSTR filename ,WORD BitCount,LPRECT lpRect);

VOID DoPaint(HWND hWnd, DWORD dwMode);

/*************************************

* DWORD WINAPI WinMain(HINSTANCE hinstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

* 功能 截屏,儲存為檔案,并顯示在視窗上

*

INT WINAPI WinMain(HINSTANCE hinstance,

{

WNDCLASSEX wcx;

HWND hwnd;

MSG msg;

WORD wport = 80;

BOOL fGotMessage;

HWND hwndCap = NULL;

// 截取全螢幕還是視窗

if(szCaptureWindowName != NULL)

hwndCap = FindWindow(NULL,"Windows 任務管理器");

// 擷取視窗的RECT,可自行修改,擷取螢幕中的任意區域

if(!GetWindowRect(hwndCap,&rectShow))

MessageBox(NULL,"Can not find window to capture", "erroe",MB_OK);

return 0;

}

// 注冊視窗類,并建立視窗,用于顯示截取的位圖

wcx.cbSize = sizeof(wcx);

wcx.style = CS_HREDRAW | CS_VREDRAW;

wcx.lpfnWndProc = MainWndProc;

wcx.cbClsExtra = 0;

wcx.cbWndExtra = 0;

wcx.hInstance = hinstance;

wcx.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));

wcx.hCursor = LoadCursor(NULL, IDC_ARROW);

wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

wcx.lpszMenuName = NULL;

wcx.lpszClassName = "MainWClass";

wcx.hIconSm = NULL;

if( !RegisterClassEx(&wcx))

return 1;

// 建立視窗

hwnd = CreateWindow(

"MainWClass",

"CAP",

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |

WS_MAXIMIZE | WS_POPUPWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

(HWND) NULL, (HMENU) NULL, hinstance, (LPVOID) NULL);

if (!hwnd)

// 截取螢幕,可根據需要設定不同的參數,這裡隻示範截取特定視窗。

ghBitmap = ScreenCapture("taskmgr.bmp" ,32, &rectShow);

// 顯示

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

while ((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 && fGotMessage != -1)

TranslateMessage(&msg);

DispatchMessage(&msg);

return msg.wParam;

UNREFERENCED_PARAMETER(lpCmdLine);

LRESULT CALLBACK MainWndProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam)

switch (uMsg)

case WM_PAINT:

// 顯示截取的螢幕

DoPaint(hwnd,CAP_SHOW_MODE_STRTCH);

break;

case WM_DESTROY:

// 建立的BITMAP對象需要删除,以釋放資源

DeleteObject(ghBitmap);

ExitProcess(0);

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

* VOID DoPaint(HWND hWnd, DWORD dwMode)

* 功能 将位圖(全局變量ghBitmap)顯示在界面上

* 參數 HWND hWnd,用于顯示位圖的視窗

* DWORD dwMode,模式,是否拉申

* 無傳回值

VOID DoPaint(HWND hWnd, DWORD dwMode)

PAINTSTRUCT ps;

RECT rect;

HDC hdcMem;

BITMAP bm;

// BeginPaint

HDC hDC = BeginPaint(hWnd, &ps);

// 擷取視窗的Client區域,用于顯示位圖

GetClientRect(hWnd, &rect);

// 設定拉申模式

SetStretchBltMode(hDC, HALFTONE);

// 将BITMAP對象選擇入記憶體DC

hdcMem = CreateCompatibleDC(hDC);

SelectObject(hdcMem, ghBitmap);

if (ghBitmap)

// 擷取DIB屬性

if (GetObject(ghBitmap, sizeof(BITMAP), &bm))

// 判斷參數示:是否根據顯示視窗大小拉申位圖

// 采用不同的方面将記憶體DC StretchBl t至視窗Client區域DC

if(dwMode == CAP_SHOW_MODE_STRTCH)

StretchBlt(hDC, 0, 0, rect.right, rect.bottom,

hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

else

// 不拉伸,計算顯示的位置,将其顯示在Client的中央

INT ixStart = (rect.right - rect.left - bm.bmWidth)/2;

INT iyStart = (rect.bottom - rect.top - bm.bmHeight)/2;

ixStart = ixStart < 0 ? 0 : ixStart;

iyStart = iyStart < 0 ? 0 : iyStart;

BitBlt(hDC, 0, 0, rect.right, rect.bottom,

hdcMem,-ixStart,-iyStart, SRCCOPY);

DeleteDC(hdcMem);

// 如果沒有位圖,則使用Brush填充

PatBlt(hDC, 0, 0, rect.right, rect.bottom, BLACKNESS);

// EndPaint

EndPaint(hWnd, &ps);

* BITMAP ScreenCapture(LPSTR filename ,WORD BitCount,LPRECT lpRect);

* 功能 截取指定區域的螢幕,并儲存為檔案

* 參數 LPSTR filename 儲存位圖檔案的檔案路徑,如果為NULL,則不儲存

* WORD BitCount Bit深度,用于表示一個像素點所使用的資料長度

* LPRECT lpRect 所需截取的螢幕區域,如果為NULL,則擷取全螢幕

* 傳回 HBITMAP 所截取的位圖對象

HBITMAP ScreenCapture(LPSTR filename ,WORD BitCount,LPRECT lpRect)

HBITMAP hBitmap;

// 顯示器螢幕DC

HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);

HDC hmemDC = CreateCompatibleDC(hScreenDC);

// 顯示器螢幕的寬和高

int ScreenWidth = GetDeviceCaps(hScreenDC, HORZRES);

int ScreenHeight = GetDeviceCaps(hScreenDC, VERTRES);

// 舊的BITMAP,用于與所需截取的位置交換

HBITMAP hOldBM;

// 儲存位圖資料

PVOID lpvpxldata;

// 截屏擷取的長寬及起點

INT ixStart;

INT iyStart;

INT iX;

INT iY;

// 位圖資料大小

DWORD dwBitmapArraySize;

// 幾個大小

DWORD nBitsOffset;

DWORD lImageSize ;

DWORD lFileSize ;

// 位圖資訊頭

BITMAPINFO bmInfo;

// 位圖檔案頭

BITMAPFILEHEADER bmFileHeader;

// 寫檔案用

HANDLE hbmfile;

DWORD dwWritten;

// 如果LPRECT 為NULL 截取整個螢幕

if(lpRect == NULL)

ixStart = iyStart = 0;

iX = ScreenWidth;

iY =ScreenHeight;

ixStart = lpRect->left;

iyStart = lpRect->top;

iX = lpRect->right - lpRect->left;

iY = lpRect->bottom - lpRect->top;

// 建立BTIMAP

hBitmap = CreateCompatibleBitmap(hScreenDC, iX, iY);

// 将BITMAP選擇入記憶體DC。

hOldBM = (HBITMAP)SelectObject(hmemDC, hBitmap);

// BitBlt螢幕DC到記憶體DC,根據所需截取的擷取設定參數

BitBlt(hmemDC, 0, 0, iX, iY, hScreenDC, ixStart, iyStart, SRCCOPY);

// 将舊的BITMAP對象選擇回記憶體DC,傳回值為被替換的對象,既所截取的位圖

hBitmap = (HBITMAP)SelectObject(hmemDC, hOldBM);

if(filename == NULL)

DeleteDC( hScreenDC);

DeleteDC(hmemDC);

return hBitmap;

// 為位圖資料申請記憶體空間

dwBitmapArraySize = ((((iX*32) + 31) & ~31)>> 3)* iY;

lpvpxldata = HeapAlloc(GetProcessHeap(),HEAP_NO_SERIALIZE,dwBitmapArraySize);

ZeroMemory(lpvpxldata,dwBitmapArraySize);

// 添充 BITMAPINFO 結構

ZeroMemory(&bmInfo,sizeof(BITMAPINFO));

bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

bmInfo.bmiHeader.biWidth = iX;

bmInfo.bmiHeader.biHeight = iY;

bmInfo.bmiHeader.biPlanes = 1;

bmInfo.bmiHeader.biBitCount = BitCount;

bmInfo.bmiHeader.biCompression = BI_RGB;

// 添充 BITMAPFILEHEADER 結構

ZeroMemory(&bmFileHeader,sizeof(BITMAPFILEHEADER));

nBitsOffset = sizeof(BITMAPFILEHEADER) + bmInfo.bmiHeader.biSize;

lImageSize =

((((bmInfo.bmiHeader.biWidth * bmInfo.bmiHeader.biBitCount) + 31) & ~31)>> 3)

* bmInfo.bmiHeader.biHeight;

lFileSize = nBitsOffset + lImageSize;

bmFileHeader.bfType = 'B'+('M'<<8);

bmFileHeader.bfSize = lFileSize;

bmFileHeader.bfOffBits = nBitsOffset;

// 擷取DIB用于寫入到檔案

GetDIBits(hmemDC, hBitmap, 0, bmInfo.bmiHeader.biHeight,

lpvpxldata, &bmInfo, DIB_RGB_COLORS);

// 寫檔案

hbmfile = CreateFile(filename,

GENERIC_WRITE,

FILE_SHARE_WRITE,

NULL,

CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL,

NULL);

if(hbmfile == INVALID_HANDLE_VALUE)

MessageBox(NULL,"create file error","error",MB_OK);

WriteFile(hbmfile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWritten,NULL);

WriteFile(hbmfile,&bmInfo,sizeof(BITMAPINFO),&dwWritten,NULL);

WriteFile(hbmfile,lpvpxldata,lImageSize,&dwWritten,NULL);

CloseHandle(hbmfile);

// 釋放記憶體,清除不同的DC。

// 這裡沒有删除BITMAP對象,需在顯示完成後删除

HeapFree(GetProcessHeap(),HEAP_NO_SERIALIZE,lpvpxldata);

ReleaseDC(0, hScreenDC);

參考

[1] 精通Windows API 函數、接口、程式設計執行個體

繼續閱讀