windows中,矩形结构RECT通过左上角和右下角两个点控制。
RECT rect;
rect.left= xLeft ;
rect.top= xTop ;
rect.right= xRight ;
rect.bottom= xBottom ;
矩形支持操作很多,比如OffsetRect (&rect, x, y) ;(将矩形移动);IntersectRect (&DestRect, &SrcRect1, &SrcRect2) ;获得两个矩形的交集;bInRect = PtInRect (&rect, point) ;判断点是否在矩形里等。这里不一一细讲了。
有一点需要注意,通常我们可以使用GetClientRect来或得整个客户区,将它存在一个RECT结构中,非常方便。
什么又是区域呢?区域可以使矩形、椭圆,多边形,或者是多个它们的组合,如何或得区域呢,以矩形为例,通过CreateRectRgnIndirect就能或得一个矩形区域了。先看一个简单程序:
#include <windows.h>
#include <math.h>
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, //当前实例句柄
HINSTANCE hPrevInstance, //先前实例句柄
LPSTR lpCmdLine, //命令行
int iCmdShow) //显示状态
{
static TCHAR szAppName[] = TEXT("画图");
//窗口句柄
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))
{
return -1;
}
//创建窗口
hwnd = CreateWindow(szAppName, //窗口类的名称,必须是已经注册的
TEXT("我的画图"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口风格
CW_USEDEFAULT, //X坐标
CW_USEDEFAULT, //Y坐标
CW_USEDEFAULT, //宽度
CW_USEDEFAULT, //高度
NULL, //父窗口句柄
NULL, //菜单窗口句柄
hInstance, //高级版本的windos忽略
NULL);
//显示窗口
//ShowWindow(hwnd,SW_SHOWNA);
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;
//区域句柄
HRGN hRgn;
//刷子句柄
HBRUSH hBrush;
//矩形区域
static RECT rect;
//客户区的矩形区域
RECT clientRect;
static int x,y;
switch (message)
{
case WM_CREATE:
x = 100;
y = 100;
//矩形区域的默认位置
rect.left = 0;
rect.top = 0;
rect.right = x + 100;
rect.bottom = y + 100;
return 0;
case WM_LBUTTONDOWN:
hdc = GetDC(hwnd);
//把整个客户区刷白
GetClientRect(hwnd,&clientRect);
hBrush = CreateSolidBrush(RGB(255,255,255));
FillRect(hdc,&clientRect,hBrush);
//设置指定颜色的刷子
hBrush = CreateSolidBrush(RGB(255,0,0));
//使用指定的刷子填充矩形
FillRect(hdc,&rect,hBrush);
//创建一个矩形区域
//hRgn = CreateRectRgnIndirect(&rect);
//使用指定的刷子填充区域
//FillRgn(hdc,hRgn,hBrush);
//将矩形移动指定的偏移
OffsetRect(&rect,x,y);
ReleaseDC(hwnd,hdc);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
这个程序实现了通过单击鼠标左键是得矩形实现偏移。程序比较简单,唯一值得注意的是,有两种方法实现显示红色的矩形:第一是用红刷子对矩形填充,第二是用矩形创建一个区域,用刷子填充整个区域。
下面这个程序着重讨论区域的拼接问题:
#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, TEXT ("Draw a Clover"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
//全局区域句柄
static HRGN hRgnClip ;
static int cxClient, cyClient ;
double fAngle, fRadius ;
HCURSOR hCursor ;
HDC hdc ;
//局部区域句柄数组
HRGN hRgnTemp[6] ;
int i ;
PAINTSTRUCT ps ;
switch (iMsg)
{
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
//装载等待光标
hCursor= SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
//显示光标
ShowCursor (TRUE) ;
if (hRgnClip)
DeleteObject (hRgnClip) ;
//创建椭圆区域的参数为外接矩形的左上角x坐标,左上角y坐标;右下角x坐标,右下角y坐标
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) ;
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 (i = 0 ; i < 6 ; i++)
DeleteObject (hRgnTemp[i]) ;
SetCursor (hCursor) ;
ShowCursor (FALSE) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
//将视口原点设置到客户区中心
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
//选择我们已经合并好的区域
SelectClipRgn (hdc, hRgnClip) ;
//通过两条直角边计算斜边
fRadius = _hypot (cxClient / 2.0, cyClient / 2.0) ;
//1度1度的变化
for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)
{
//起点移动到原点
MoveToEx (hdc, 0, 0, NULL) ;
//画线
LineTo (hdc, (int) ( fRadius * cos (fAngle) + 0.5), (int) (-fRadius * sin (fAngle) + 0.5)) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
//删除该区域
DeleteObject (hRgnClip) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
程序中将4个椭圆拼接起来,形成了新的区域。fRadius是中心到客户区顶点的距离。
将视口的原点设置到了客户区的中心,然后对于每一度,计算一条线与客户区边沿的交点的x,y坐标(加0.5是为了四舍五入),但是由于我们之前定义了区域,所以不论线实际画了多长,屏幕上只会显示出现在区域中的部分。
但是这个程序有一个bug,在win7下,如果将客户区最大化(或者放的非常大)时,画的线会出现一些明显的裂缝,我通过如下代码测试:
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
//获取整个区域的轮廓
SelectClipRgn (hdc, hRgnClip) ;
hBrush = CreateSolidBrush(RGB(0,0,0));
FillRgn(hdc,hRgnClip,hBrush);
/*
//将视口原点设置到客户区中心
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
//选择我们已经合并好的区域
SelectClipRgn (hdc, hRgnClip) ;
//通过两条直角边计算斜边
fRadius = _hypot (cxClient / 2.0, cyClient / 2.0) ;
//1度1度的变化
for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)
{
//起点移动到原点
MoveToEx (hdc, 0, 0, NULL) ;
//画线
LineTo (hdc, (int) ( fRadius * cos (fAngle) + 0.5), (int) (-fRadius * sin (fAngle) + 0.5)) ;
}
*/
EndPaint (hwnd, &ps) ;
return 0 ;
发现最大化窗口时,区域依然四个椭圆拼接而成的的,但是画出的线却出现了问题。
不论是VS2010还是VC++6.0这个问题依然存在。有人说xp下不会出现这种情况,具体为什么我也不知道。