5.6.1 处理矩形
(1)绘图函数
绘图函数 |
说明 |
FillRect(hdc,&rect,hBrush) |
不用先将画刷选入设备环境 |
FrameRect(hdc,&rect,hBrush) |
绘制边框,不填充(注意是用画刷,而不是画笔) |
InvertRect(hdc,&rect) |
像素反转,由1变0,0变1。 |
(2)操纵RECT结构的函数
操作矩形 |
函数 |
1、设置RECT字段 |
SetRect(&rect,xLeft,yTop,xRight,yBottom); |
2、沿x和y移动几个单位 |
OffsetRect(&rect,x,y); |
3、增大或减小矩形 |
InflateRect(&rect,x,y); |
4、矩形各字段设为0 |
SetRectEmpty(&rect); |
5、复制矩形 |
CopyRect(&DestRect,&SrcRect); |
6、获取矩形的交集 |
IntersectRect(&DestRect,&SrcRect1,&SrcRect2); |
7、判断矩形是否为空 |
bEmpty = IsRectEmpty(&rect); |
8、点是否在矩形内部 |
bInRect = PtInRect(&rect,point); |
5.6.2 随机矩形
(1)GetMessage与PeekMesssage的区别
GetMessage |
PeekMessage |
|
作用 |
获取一条消息,并从消息队列里删掉除该消息(除WM_PAINT外)。 |
检查消息队列的消息,是否删除,取决于最后一个参数是PM_REMOVE或PM_NOREMOVE。 |
控制权 |
获得消息,才返回。 |
立即返回,不管是否有消息 |
返回值 |
获得非WM_QUIT消息时,返回非零 获得WM_QUIT时,返回0 |
TRUE表示有消息,FALSE表示没有消息。 |
(2)删除WM_PAINT消息:唯一的方法是使无效区域变成有效。可以用ValidateRect等。
//以下是错误,如果WM_PAINT在队列中,循环将无法退出。
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE));
(3)消息循环另一法表示法
GetMessage方法 |
PeekMessage方法 |
while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; |
While(TRUE) { //有消息时正常处理 if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { //必须检查WM_QUIT消息 if(msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else //消息队列为空时 { //空闲时,做其他事情 } } return msg.wParam; |
【RandRect程序】
/*----------------------------------------- RANDRECT.C -- (c) Charles Petzold, 1998 -----------------------------------------*/ #include <windows.h> static int cxClient, cyClient; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void DrawRectangle(HWND hwnd) { HBRUSH hBrush; HDC hdc; RECT rect; //随机矩形rand()返回0-0x7fff之间的整数 SetRect(&rect, rand() % cxClient, rand() % cyClient, rand() % cxClient, rand() % cyClient); //随机颜色 hBrush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)); //填充 hdc = GetDC(hwnd); FillRect(hdc, &rect, hBrush); ReleaseDC(hwnd, hdc); DeleteObject(hBrush); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("RandRect"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, TEXT("RandRect"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //用PeekMessage的消息循环 while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { DrawRectangle(hwnd); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
5.6.3 矩形与区域的剪裁
(1)区域可进行绘图或剪裁,是一个GDI对象,得选入设备和DeleteObject。
(2)创建区域
①hRgn =CreateRectRgn(xLeft,yTop,xRight,yBottom); //矩形区域
②hRgn =CreateEllispseRgn(xLeft,yTop,xRight,yBottom);//椭圆区域
③hRgn =CreatePolygonRgn(&apt,iCount,iPolyFillMode); //多边形区域
(3)区域的合并iRgnType=CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine)
参数 iCombine值 |
RGN_AND |
两个源区域公共部分 |
RGN_OR |
两个源区域的全部 |
|
RGN_XOR |
两源区域全部,但去除公共部分 |
|
RGN_DIFF |
源1不在源2的部分 |
|
RGN_COPY |
源1全部(忽略源2) |
|
返回值 iRgnType |
NULLREGION |
空区域 |
SIMPLEREGION |
简单矩形、椭圆或多边形 |
|
COMPLEXREGION |
复杂区域 |
|
ERROR |
有错误发生 |
说明:①将两个源区域组合起来,并产生目标句柄,hDestRgn先前的区域将被销毁。
②使用函数前,可让hDestRgn在初始时表示一个很小的矩形区域。
(4)区域绘图函数
FillRgn(hdc,hRgn,hBrush) |
//与FillRect类似 |
FrameRgn(hdc,hRgn,hBrush,xFrame,yFrame) |
//xFrame,yFrame表示区域周围的边框的逻辑宽度和高度 |
InvertRgn(hdc,hRgn); |
//与InvertRect类似 |
PaintRgn(hdc,hRgn); |
//用当前设备环境的画刷来填充区域 |
(5)删除区域:DeleteObject(hRgn); //要删除,因为是GDI对象。
5.6.4 矩形与区域的剪裁
(1)无效矩形和无效区域的产生与获取
无效区产生 |
获取 |
//无效矩形 InvalidateRect(hwnd,NULL,TRUE) //无效区域(形状不一定是矩形) InvalidateRgn(hwnd,hRgn,TRUE) |
BeginPaint(hdc,&ps)或 GetUpdateRect |
(2)创建自己的剪裁区域
①SelectObject(hdc,hRgn);
②SelectClipRgn(hdc,hRgn);
(3)删除剪裁区域:GDI为剪裁区域做了一个副本,因此区域选入设备环境后,可以删除。
5.6.5 CLOVER程序
/*------------------------------------------------------------ CLOVER.C -- Clover Drawing Program Using Regions (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include <math.h> #define TWO_PI (2.0 * 3.14159) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Clover"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("三叶虫图形"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static int cxClient, cyClient; static HRGN hRgnClip; double fAngle, fRadius; HRGN hRgnTemp[6]; switch (message) { case WM_CREATE: return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); if (hRgnClip) DeleteObject(hRgnClip); //创建椭圆区域 hRgnTemp[0] = CreateEllipticRgn(0, cyClient / 3, cxClient / 2, 2 * cyClient / 3); //左椭圆 hRgnTemp[1] = CreateEllipticRgn(cxClient / 2, cyClient / 3, cxClient, 2 * cyClient / 3); //右椭圆 hRgnTemp[2] = CreateEllipticRgn(cxClient / 3, 0, 2 * cxClient / 3, cyClient / 2); //上椭圆 hRgnTemp[3] = CreateEllipticRgn(cxClient / 3, cyClient / 2, 2 * cxClient / 3, cyClient); //下椭圆 //初始化各合并区域 hRgnTemp[4] = CreateRectRgn(0, 0, 1, 1); //1个像素大小的区域 hRgnTemp[5] = CreateRectRgn(0, 0, 1, 1); hRgnClip = CreateRectRgn(0, 0, 1, 1); //合并 CombineRgn(hRgnTemp[4], hRgnTemp[0], hRgnTemp[1], RGN_OR); //合并全部 CombineRgn(hRgnTemp[5], hRgnTemp[2], hRgnTemp[3], RGN_OR); //合并合部 CombineRgn(hRgnClip, hRgnTemp[4], hRgnTemp[5], RGN_XOR); //去除公共部分 for (int i = 0; i < 6; i++) { DeleteObject(hRgnTemp[i]); } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //将逻辑点(0,0)映射到客户区中央 SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL); //设置剪裁区域 SelectClipRgn(hdc, hRgnClip); //画射线 int xPos, yPos; fRadius = _hypot(cxClient / 2.0, cyClient / 2.0);//射线长度 for (fAngle = 0.0; fAngle < TWO_PI; fAngle += TWO_PI / 360) { MoveToEx(hdc, 0, 0, NULL); xPos = (int)(fRadius * cos(fAngle) + 0.5); //加0.5是为了四舍五入取整,如3.5+0.5,取整4。 yPos = (int)(fRadius * sin(fAngle) + 0.5); LineTo(hdc, xPos, yPos); } EndPaint(hwnd, &ps); return 0; case WM_DESTROY: if (hRgnClip) DeleteObject(hRgnClip); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }