WIN32汇编语言教程:第07章 图形操作 · 7.5 区域和路径

如果读者用过PhotoShop绘图软件,就一定记得它有“选择区域”以及“路径”的概念,区域用来选定一个范围,以便对指定的范围进行某种操作;而路径相当于用虚拟的线条去进行“圈地运动”,虽然路径圈出来的看上去也是一个区域,但路径记录的是“圈地”用的线条而不是圈出来的地。

7.5.1 使用区域

1. 创建区域

首先来看如何建立一个区域,GDI中可以用下列区域建立函数来创建区域:

(1) invoke CreateRectRgn,dwLeft,dwTop,dwRight,dwBottom
(2) invoke CreateEllipticRgn,dwLeft,dwTop,dwRight,dwBottom
(3) invoke CreateEllipticRgnIndirect,lpRect
(4) invoke CreatePolygonRgn,lpPoint,iCount,iPolyFillMode
(5) invoke CreateRoundRectRgn,dwLeft,dwTop,dwRight,dwBottom,\
           dwWidthEllipse,dwHeightEllipse

这些函数分别创建矩形(1)、椭圆(2)和(3)、多边形(4)和圆角矩形(5)的区域,参数中坐标值的含义和绘画填充区域的函数Rectangle,Ellipse,Polygon与RoundRect的参数定义是相同的。

CreateRectRgn函数的参数指定了左上和右下两个对角的坐标。CreateEllipticRgn的参数指定了一个矩形,产生的椭圆区域和这个矩形是相切的。

CreateEllipticRgnIndirect函数同样建立椭圆区域,但指定与椭圆相切的矩形是由一个RECT结构定义的。

CreatePolygonRgn建立一个多边形区域。lpPoint指向一系列的POINT结构,iCount指定了点的数量,iPolyFillMode参数就是SetPolyFillMode函数使用的参数:ALTERNATE或WINDING,结果的不同之处相当于表7.4中Polygon函数的两种结果的区别。

如果创建区域成功的话,这些函数返回一个区域句柄hRgn,区域和画笔、画刷等一样,都是GDI的对象,如果不再使用一个区域,需要用DeleteObject将它删除。

2. 合并区域

仅使用上面的函数可能用途不是很大,但是将不同形状的区域按照各种方式合并起来,用处就大了,可以因此定义出很复杂的区域。要合并区域可以使用函数:

invoke CombineRgn,hDestRgn,hSrcRgn1,hSrcRgn2,dwCombine

该函数将hSrcRgn1和hSrcRgn2两个区域合并起来放入hDestRgn指定的区域句柄中,但hDestRgn并不是新生成的,它必须是一个已经存在的区域句柄,当函数执行后,hDestRgn中原来的区域会被破坏并替换成新合并成的区域,但可以对hDestRgn和hSrcRgn1使用同一个句柄,这样就相当于把hSrcRgn2合并到hSrcRgn1中去。

dwCombine指定了合并的方式,它可以是以下的取值:

● RGN_AND                 新区域是两个区域的共同部分。

● RGN_COPY               新区域是hSrcRgn1中的区域。

● RGN_DIFF                新区域是hSrcRgn1区域除去hSrcRgn2中的部分。

● RGN_OR                   新区域是两个区域的叠加。

● RGN_XOR                 新区域是两个区域的叠加除去共同部分。

CombineRgn函数的返回值代表新区域的形状大致是什么样子的,它可能是以下值:

● NULLREGION           新区域是空区域。

● SIMPLEREGION        新区域是个简单形状(可以用上面的单个函数创建)。

● COMPLEXREGION    新区域是个复杂的形状。

● ERROR                      函数执行出错,没有区域被创建。

3. 区域的用途

区域主要可以用在两个地方:建立特殊形状的窗口和对绘画区域进行裁剪。

使用SetWindowRgn函数可以使窗口的形状由区域指定,如BmpClock时钟程序是圆形的,当把时钟移动到其他窗口上面的时候,它的四角并不覆盖住下层窗口,这就是因为程序中有下面的代码:

   invoke CreateEllipticRgn,0,0,CLOCK_SIZE+1,CLOCK_SIZE+1
   push   eax
   invoke SetWindowRgn,hWinMain,eax,TRUE
   pop eax
   invoke DeleteObject,eax

在这几句代码中,CreateEllipticRgn函数建立了一个圆形的区域并在eax中返回区域句柄,SetWindowRgn函数根据这个区域设置时钟窗口的形状。如果建立了一个很复杂的区域,那么窗口的形状同样会很复杂。SetWindowRgn的最后一个参数为TRUE,表示设置窗口形状后Windows要发送一个WM_PAINT消息将窗口重画。

由于Windows对使用的区域保存一个拷贝,所以程序在调用SetWindowRgn函数后就可以使用DeleteObject把区域删除掉,并不需要在退出时才去删除。

另外,区域可以用来对绘画区域进行裁剪,任意使用下面的两条语句之一:

   invoke SelectObject,hDC,hRgn
   invoke SelectClipRgn,hDC,hRgn

那么以后在hDC上使用绘图函数的话,只有hRgn指定的区域中的点才会被绘画,对裁剪区域外的绘图将被忽略。同样,Windows会对选入DC的区域建立一个拷贝,如果以后不需要这个区域了,那么在函数执行后,可以马上用DeleteObject函数把它删除掉。

7.5.2 使用路径

1. 创建路径

路径并不是GDI对象,它并没有一个句柄,Windows对每个DC在内部保存一个路径,每次新开始建立一个路径,原有的路径就会被破坏掉。如果要建立一个路径,可以使用BeginPath函数:

invoke BeginPath,hDC

调用了这个函数以后,对hDC使用画线函数所画的线条都会被当做路径记录,使用画线函数画出来的线条可能是不连续的,比如调用多次的LineTo函数,最后一点和开始一点不同,这时需要使用CloseFigure函数将路径封闭起来:

invoke CloseFigure,hDC

CloseFigure函数从最后一点到第一点画一条直线把路径封闭起来。Windows允许创建多个子路径,封闭前面一条路径以后,可以继续“圈”出和前面路径不相连的另一条路径。

最后,使用EndPath函数结束创建路径:

invoke EndPath,hDC

图7.11示范了一次创建路径的过程,在第1步调用BeginPath和第11步调用EndPath之间,用LineTo函数和Rectangle画出了包含两个三角形和一个矩形的路径,其中两个三角形用CloseFigure去封闭。


图7.11 路径创建过程

2. 使用路径

创建了路径以后,可以进行下面的操作。

首先,可以对路径进行画线操作,或者对路径围起来的区域进行填充操作:

   invoke StrokePath,hDC            ;(1)
   invoke FillPath,hDC              ;(2)
   invoke StrokeAndFillPath,hDC     ;(3)

第(1)个函数沿着路径用当前画笔绘画线条,第(2)个函数使用当前画刷填充路径围起来的区域,第(3)个函数既使用当前画笔绘画边线也使用当前画刷填充中间区域。当执行了任何一个函数的时候,路径都会被破坏掉。实际上,这些函数完成的功能就相当于7.2.3

节中的画线和填充函数,那么为什么要这样大动干戈呢?惟一的好处就是用这种方法可以操作很复杂的形状,因为定义路径时可以使用任何画线函数,包括画弧与画贝塞尔曲线函数等,而用普通的填充函数是无法填充出一个由贝塞尔曲线围成的区域的。

路径的另一个用途是定义一个复杂形状的区域,可以使用下面的函数将路径转化成区域:

   invoke PathToRegion,hDC
   mov    hRgn,eax

理由是同样的,因为用区域创建函数创建出来的只能是椭圆、矩形、多边形等形状的组合,用先创建路径再转化成区域的方法可以定义形状复杂得多的区域。同样,执行了PathToRegion函数以后,原有的路径定义就会被破坏掉。

上页:第07章 图形操作 · 7.4 块传送操作(2) 下页:第08章 通用对话框 · 8.1 通用对话框简介

第07章 图形操作

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号