5.6 矩形、区域和裁剪
来源:互联网 发布:3ds max模型优化 编辑:程序博客网 时间:2024/05/16 12:01
摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P161
Windows 还有其他几个使用 RECT(矩形)结构和区域的绘图函数。一个区域指的是屏幕上的一块空间,它由矩形、多边形和椭圆组合而成。
5.6.1 处理矩形
下面三个绘图函数需呀一个指向矩形结构的指针:
在这三个函数中,参数 rect 是一个类型为 RECT 的结构,它有 4 个字段:left、top、right 和 bottom。在这个结构中,坐标是逻辑坐标。FillRect 函数使用指定的画刷填充矩形(达到但不包括右下坐标)。这个函数不需要事先把画刷选入设备环境。
FrameRect 使用画刷绘制一个矩形框,但是它并不填充矩形。使用画刷来绘制边框似乎有点奇怪,因为到目前为止,你所看到的绘制边框的函数(例如 Rectangle)都是由当前画笔绘制的。FrameRect 函数允许你绘制一个不一定是纯色的矩形框。矩形的边框是 1 个逻辑单位宽。如果逻辑单位大于设备单位,边框的宽度将是 2 个或者更多的像素。
InvertRect 函数翻转矩形内所有的像素,将 1 变为 0,0 变为 1。即这个函数将白色区域变为黑色区域,黑色区域变为白色区域,绿色区域变为洋红色区域。
Windows 还包含 9 个可用于轻松便捷地操纵 RECT 结构的函数。例如,通常使用下面的代码将 RECT 结构的 4 个字段设置为特定值:
然而,通过 SetRect 函数的调用,只用一行代码即可实现相同的结果:如果想做下列事情之一,可以方便的使用其他 8 个函数。
- 将矩形沿 x 轴和 y 轴移动几个单位:
- 增大或减小矩形的尺寸:
- 把矩形结构的各字段设置为0:
- 将一个矩形结构复制到另一个矩形结构:
- 获取两个矩形的交集:
- 获取两个矩形的并集:
- 判断矩形是否为空:
- 判断点是否在矩形内部:
大多数情况下,还有一些简单的代码可以实现与这些函数相同的功能。例如,复制结构时,可以通过逐个字段的结构复制操作,来代替调用 CopyRect 函数,如下面的语句:
5.6.2 随机矩形
在任何一个图形系统中,总存在这样一个有趣的程序,即简单地使用随机的尺寸和颜色不停地绘制一系列的图像,例如,随机大小和颜色的矩形。在 Windows 中可以创建这样的一个程序,但是这并不像想象的那样容易。我希望你能够意识到,不能在处理 WM_PAINT 消息中简单地使用 while(TRUE) 循环。当然,这样做会奏效,但是这样做的结果是,程序将停止对其他消息的处理,而且程序不能退出或者最小化。一种可接受的方式是设置一个向你的窗口函数发送 WM_TIMER 消息的 Windows 计时器。(我将在第 8 章介绍计时器。)对于每个 WM_TIMER 消息,可以调用 GetDC 函数获取设备环境,然后绘制一个随机矩形,接着调用 ReleaseDC 函数释放设备环境。但是那样做又会使程序失去一些趣味性,因为程序不能很快地绘制随机矩形。必须等待每个 WM_TIMER 消息,那样会依赖于系统时钟的精度。
在 Windows 中有很多的“空闲时间”,在这期间所有的消息队列都是空的,Windows 就在等待键盘或者鼠标的输入。那么能否在空闲期间从某种程度上获取控制并绘制随机矩形,而一旦有消息加载到程序的消息队列,就释放控制呢?这正是 PeekMessage 函数的“用武之地”。下面是 PeekMessage 函数调用的一个例子:
函数的前 4 个参数(一个是指向 MSG 结构的指针,一个是窗口句柄,另外两个值表示信息范围)与 GetMessage 函数相同。设置第二、三、四个参数为 NULL 或者 0,表示我们想使用 PeekMessage 函数返回程序中所有窗口的所有消息。如果要删除消息队列中的消息,可以把 PeekMessage 函数的最后一个参数设置为 PM_REMOVE。如果不想删除,就设置为 PM_NOREMOVE。这就是 PeekMessage 名字的意思,它是“偷看”而不是“获得”。它允许一个程序检查程序队列中的下一个消息,而不是真实地获得并删除它看到的消息。
GetMessage 函数并不把控制权交还给程序,除非它从程序的消息队列中获得了消息。但是 PeekMessage 函数却总是立即返回,不管消息是否出现。当一个消息在程序的消息队列中时,PeekMessage 函数的返回值是TRUE(非 0),而消息则像正常情况一样处理。当队列中没有消息时,PeekMessage 函数返回FALSE(0)。
这允许我们替换正常的消息循环,正常的消息循环如下所示:
替换后的消息循环如下:注意:在这里,必须明确检查 WM_QUIT 消息,在一个正常的消息循环中,不需要这样做,因为当获取一个 WM_QUIT 消息时,GetMessage 函数的返回值是 FLASE(0)。但是 PeekMessage 函数的返回值是表示队列中是否有消息,因此检查 WM_QUIT 是必要的。
如果 PeekMessage 函数返回 TRUE,那么消息会正常执行。如果返回 FLASE,那么程序可以在返回给 Windows 控制之前做些事情(如显示另一个随机矩形)。
(尽管 Windows 文档中指出不能使用 PeekMessage 函数从消息队列中删除 WM_PAINT 消息,但是这并没有什么问题。毕竟,GetMessage 函数其实也不能从队列中删除 WM_PAINT 消息。使客户区的无效区域变成有效是从队列中删除 WM_PAINT 消息的唯一办法,可以使用 ValidateRect、ValidateRgn 或者成对的 BeginPaint 和 EndPaint 函数来完成。如果使用 PeekMessage 函数从消息队列获取 WM_PAINT 消息后,按照正常的方式对它进行处理,就不会又任何问题。但使用下面的代码来清除消息队列中的所有消息是不允许的:
这条语句表示从你的消息队列中删除 WM_PAINT 消息之外的所有消息。如果 WM_PAINT 在队列中,你将永远陷于 while 循环无法终止。)PeekMessage 函数在早期版本的 Windows 中比在 Windows 98 中重要得多。这是因为 16 位版本的 Windows 使用非抢占式多任务系统。Windows 自带的 Terminal 程序使用 PeekMessage 函数循环检查从通信端口接收到的数据。打印机管理程序也使用这项技术来打印,其他的 Windows 打印程序通常也使用一个 PeekMessage 函数的循环。在抢占式多任务的 Windows 98 中,应用程序可以建立多个线程。
5.6.3 建立和绘制区域
一个区域是对显示器一块空间的描述,这个空间可以是矩形、多边形和椭圆的组合。可以使用区域进行绘图或者裁剪。将区域选入设备环境,就可以使用这个区域来裁剪(也就是说,将绘制动作限制在客户区的一个特定部分)。同画笔和画刷一样,区域也就是 GDI 对象,应当通过调用 DeleteObject 函数来删除所有建立的区域。
当建立一个区域时,Windows 返回一个类型为 HRGN 的区域句柄。最简单的区域类型是一个矩形区域。可以用下面的两种办法建立一个矩形区域:
或者也可以使用下面的函数建立椭圆区域:或者创建圆角矩形区域可以通过 CreateRoundRectRgn 函数实现。创建一个多边形区域的函数和 Polygon 函数类似:
参数 point 是一个类型为 POINT 结构的数组, iCount 是点的个数,iPolyFillMode 或者是 ALTERNATE,或者是 WINDING。你也可以调用 CreatePolygonRgn 函数创建多个多边形区域。那么你会问,区域有什么特别之处吗?下面的函数显示了区域的作用:
这个函数将两个源区域(hSrcRgn1 和 hSrcRgn2)结合起来,并产生目标区域句柄(hDestRgn) 来表示那个组合区域。这三个区域句柄都必须有效,但是函数调用后 hDestRgn 先前描述的区域都被销毁了。(当使用这个函数时,可能要让 hDestRgn 在初始时表示一个很小的矩形区域。) 参数 iCombine 描述 hSrcRgn1 区域和 hSrcRgn2 区域结合的方式:
iRgnType 值是从 CombineRgn 返回的下列值之一:NULLREGION,指的是一个空的区域;SIMPLEREGION,指的是一个简单的矩形、椭圆或者多边形;COMPLEXREGION,指的是矩形、椭圆或多边形的组合;ERROR,指的是有错误发生。
一旦有了一个区域的句柄,就可以使用下面 4 个绘图函数:
FillRgn、FrameRgn 和 InvertRgn 函数类似于 FillRect、FrameRect 和 InvertRect 函数。FrameRgn 的参数 xFrame 和 yFrame 是表示在区域周围的、要绘制的边框的逻辑宽度和高度。PaintRgn 函数使用当前被选入设备环境的画刷来填充区域。所有的这些函数都假定使用的是逻辑坐标。用完一个区域后,可以用于删除其他 GDI 对象相同的函数来删除它:
5.6.4 矩形与区域的裁剪
区域在裁剪中也扮演着重要角色。InvalidRect 函数使显示的矩形区域无效,并产生一个 WM_PAINT 消息。例如,可以使用 InvalidateRect 函数来擦除客户区的内容,并产生一个 WM_PAINT 消息:可以通过调用 GetUpdateRect 函数获取无效矩形的坐标,并且使用 ValidateRect 使客户区的矩形有效。当接收到一个 WM_PAINT 消息时,PAINTSTRUCT 结构中的无效矩形的坐标是可以利用的。这个结构是通过 BeginPaint 函数填充的。这个无效矩形也定义了一个“裁剪区域”。不能在裁剪区域之外绘图。Windows 有两个类似 InvalidateRect 和 ValidateRect 的函数,用于处理区域而不是矩形;
和当接收一条由无效区域产生的 WM_PAINT 消息时,裁剪区域在形状不一定是矩形。可以通过将一个区域选入到设备环境来创建你自己的裁剪区域,将区域选入设备环境可以使用
或裁剪区域被假定使用的是设备坐标。 GDI 为裁剪区域做了一个副本,因此当把区域对象选入到设备环境后,可以删除它。Windows 还包括几个操纵这个裁剪区域的函数,例如 ExcludeClipRect 函数用来从裁剪区域中去除一个矩形;IntersectClipRect 函数用来建立一个新的裁剪区域,这个新的裁剪区域是先前的裁剪区域和某个矩形的交集;OffsetClipRgn 函数用来把一个裁剪区域移动到客户区的另外一部分。
5.6.5 CLOVER 程序
CLOVER 程序由四个椭圆形成一个区域,然后把这个区域选入设备环境,接着从窗口区中心发散绘制一系列直线。这些直线仅出现剪裁区域内。
如果使用传统的方法绘制这个图形,必须依据椭圆的圆周角公式计算出每条线段的端点。但是通过使用一个复杂的裁剪区域,就可以直接绘制直线,而让 Windows 去确定这些端点。
因为区域总是使用设备坐标,所以 CLOVER 程序不得不在每次收到 WM_SIZE 消息时重新创建区域。几年前,运行 Windows 的机器要花费几秒钟来重绘这个图形。今天,快速的机器几乎在瞬间就能完成绘制。
CLOVER 先创建 4 个椭圆区域,它们被存储在 hRgnTemp 数组的前 4 个元素中。接着,程序创建三个 “空”区域:
在客户区左边和右边的两个区域先合并:同样地,在客户区顶部的两个椭圆区域也合并了:最后两个合并后的区域再合并成 hRgnClip:RGN_XOR 标识符表示要从结果区域中排除重叠的区域。最后,6 个临时的区域被删除:相对结果而言,WM_PAINT 消息处理很简单。视口原点设置在客户区的中心(这样使画直线更容易),在处理 WM_SIZE 消息时创建的区域被选入设备环境作为裁剪区域:
现在,剩下要做的就是画直线了,一共画 360 条,每一度画一条。每条线的长度是变量 fRadius,它表示的是从中心到客户区角落的距离:
在处理 WM_DESTROY 消息期间,裁剪区域被删除:- 5.6 矩形、区域和裁剪
- 5.6 矩形、区域和裁剪
- 矩形、区域和裁剪
- 矩形框和区域
- 矩形和区域
- 矩形和区域
- 在Matlab中利用OpenCV裁剪出旋转矩形区域
- 理解OpenGL 裁剪区域和视口
- 详解路径层和裁剪区域应用
- 详解路径层和裁剪区域应用
- 裁剪区域和视口区域(1)
- 裁剪区域和视口区域(2)
- 《Windows程序设计》读书笔记------------->>矩形、区域和剪裁<<
- 图形基础---矩形、区域和剪裁
- 矩形区域的交和并
- [Win32]画刷、矩形、不规则区域和剪裁
- OpenCV鼠标绘制矩形和截取图像的矩形区域
- opencv鼠标绘制矩形和截取图像的矩形区域
- centos7+nginx+keepalived互为主从
- 通过 String.intern() 方法来优化字符串
- Java 给定一个指定日期 加上天数 输出加上天数的日期
- 如何隐藏QTabWidget中的一个tab
- 学习
- 5.6 矩形、区域和裁剪
- Flume-ng spoolDir目录监控踩过的坑
- Apache Shiro学习(五)Shiro 配置说明
- atoi字符串转换函数应用实例
- 唤起QQ应用
- Acm弱校奋斗史
- java 实例
- 6.1.3 队列和同步
- 新闻客户端07 - 新闻详情页