天天看点

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 函数、接口、编程实例

继续阅读