31、Windows API GDI(3)

简介: 一、绘制图形     GDI所能绘制形状有很多种,可区分为标准形状和非标准形状。标准形状使用几个简单、确定的属性就可以确定的形状,比如矩形(左上角点位置和长度)、椭圆(使用外切矩形)、圆角矩形(一个矩形加圆角半径)、弓形、扇形。

一、绘制图形

    GDI所能绘制形状有很多种,可区分为标准形状和非标准形状。标准形状使用几个简单、确定的属性就可以确定的形状,比如矩形(左上角点位置和长度)、椭圆(使用外切矩形)、圆角矩形(一个矩形加圆角半径)、弓形、扇形。

    也有不标准的形状,所有不标准的形状都可以使用多边形(Polygon)来定义。

定义多边形也需要使用一个点数组。将点数组中的点依其在数组中的顺序连接起来,就是一个多边形。

wps_clip_image-2430

在绘制时,图形内部使用DC的当前画刷对象进行填充,图形的边使用DC的当前画笔对象进行勾勒。

1、相关API

GetClientRect

SetRect

CopyRect

可以将一个RECT在屏幕上显示出来,显出的方式是有内部填充、边沿勾勒,也可以对一个RECT进行反转操作。反转操作会将现有RECT现有的填充颜色取反。FillRectFrameRectInvertRect函数分别实现这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中,这样从显示DCBitBlt到内存DC中的位块颜色数据就被替换,SelectObject的返回值即是从屏幕DC中得到的位图对象(HBITMAP类型)。

之后就可以使用这个HBITMAP句柄来进行位图的操作,比如显示在屏幕的特定区域上。

3、将位图显示在屏幕上

    将位图文件显示在界面上的原理与截屏类似,也是将位图文件选择入DC,区别是显示位图文件时所选择入的DC是用于显示的窗口DC。如果在显示时需要拉伸,则需要使用StretchBlt函数在DC间转换。

    显示位图的流程:

(1)创建内存DC,将BITMAP对象选择入内存DC;

(2)获取用于显示位图的窗口client区域的DC;

(3)从位图对象中获取位图属性,包括大小、长宽等;

(4)计算在client获取中显示的位置:

(5)根据需要调用StretchBltBitBlt函数,在内存DC和窗口Client区域DC间转移;

(6)释放相关资源。

StretchBlt函数的功能是在两个DC的指定区域中复制位图,将源DC中指定区域的位图复制到目的DC的指定区域上,并根据区域的大小和长宽比例关系拉伸或压缩位图。

BitBlt函数的功能是将源DC上的颜色信息转移到目的DC的指定区域上,转移时仅限于DC的最大范围,超出范围的部分不转移,不进行拉伸和压缩。

示例

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 截屏
 
   
**************************************/
/* 头文件 */
#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,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
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)
return 1 ;

// 截取屏幕,可根据需要设置不同的参数,这里只演示截取特定窗口。
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 );
break ;
default :
break ;
}
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填充
else
{
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;
}
else
{
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);
DeleteDC(hmemDC);
return hBitmap;
}

参考

[1] 精通Windows API 函数、接口、编程实例

[2] http://msdn.microsoft.com/en-us/library/dd183553%28VS.85%29.aspx

目录
相关文章
|
6月前
|
监控 编译器 API
[笔记]Windows核心编程《二十二》注入DLL和拦截API(一)
[笔记]Windows核心编程《二十二》注入DLL和拦截API
143 0
|
3月前
|
API Python Windows
python3应用windows api对后台程序窗口及桌面截图并保存的方法
python3应用windows api对后台程序窗口及桌面截图并保存的方法
88 1
|
7月前
|
编解码 应用服务中间件 开发工具
Windows平台RTMP|RTSP播放器为什么要兼容GDI绘制
先说结论,Windows平台播放渲染这块,一般来说99%以上的机器都是支持D3D的,实现GDI模式绘制,除了为了好的兼容性外,在远程连接的场景下,D3D创建不成功,需要使用GDI模式。
|
6月前
|
存储 缓存 API
[总结]Windows Crypto API 自动更新根证书问题原因及解决方案
[总结]Windows Crypto API 自动更新根证书问题原因及解决方案
|
6月前
|
API Windows
[笔记]Windows核心编程《番外篇》常用的NT API及使用示例
[笔记]Windows核心编程《番外篇》常用的NT API及使用示例
|
6月前
|
安全 API Windows
[笔记]Windows核心编程《二十二》注入DLL和拦截API(三)
[笔记]Windows核心编程《二十二》注入DLL和拦截API(三)
129 0
|
6月前
|
消息中间件 编解码 安全
[笔记]Windows核心编程《二十二》注入DLL和拦截API(二)
[笔记]Windows核心编程《二十二》注入DLL和拦截API(二)
103 0
|
6月前
|
API C++ Windows
Windows API Hooking 学习
Windows API Hooking 学习
|
10月前
|
缓存 安全 Unix
C/C++使用Windows的API实现共享内存以及同步
C/C++使用Windows的API实现共享内存以及同步
745 0
|
10月前
|
安全 Java 编译器
如何用 Go 调用 Windows API | 青训营笔记
如何用 Go 调用 Windows API | 青训营笔记
803 0