按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
hMaskBmp指定了一幅黑白位图,如果位图中对应位置的像素为黑(即为0),那么使用背景ROP码,如果对应位置的像素为白(即为1),那么使用前景ROP码。注意:遮掩位图一定要是黑白两色的,如果使用其他颜色深度的位图,那么函数调用将会失败。
读者一定有个问题:参数中只有一个dwROP参数,如何指定两个ROP码?这正是这个函数比较费解的地方,实际上dwROP参数是两个ROP码的组合,组合的算法是:
((背景ROP码 shl 8)& 0ff000000h)or 前景ROP码
灵活使用这个函数可以带来很多的方便,比如在_CreateBackGround子程序中,以“;”为注释之间的8句代码完全可以改成下面的4句:
invoke CreatePatternBrush;@hBmpBack
invoke SelectObject;hDcBack;eax
invoke DeleteObject;eax
invoke MaskBlt;hDcBack;0;0;CLOCK_SIZE;CLOCK_SIZE;
@hDcCircle;0;0;@hBmpMask;0;0;
((SRCCOPY shl 8) and 0ff000000h) or PATCOPY
相比之下少了一个PatBlt和一个BitBlt调用。因为程序的要求是遮掩图片中黑色的部分使用边框图片,而白色部分使用背景,所以将背景ROP设置为SRCCOPY就可以将需要的边框部分复制过去,而前景ROP使用PATCOPY就免除了使用PatBlt填充背景的步骤。当然,使用前需要把Mask1。bmp和Mask2。bmp重新保存成黑白色,因为现在这两个位图文件是256色格式的。
如果hMaskBmp不指定(参数写为NULL),那么MasmBlt函数就相当于BitBlt函数,只不过ROP是使用dwROP中指定的前景ROP码。
4。 PlgBlt函数
PlgBlt实现平行四边形旋转传送操作(Parallelogram Block Transfer),它复制一幅位图,同时将其转换成一个平行四边形,所以利用它可对位图进行旋转处理。PlgBlt函数的使用语法是:
invoke PlgBlt,hdcDest,lpPoint,
hdcSrc,xSrc,ySrc,dwWidth,dwHeight,
hBmpMask,xMask,yMask
这个函数和ROP码无关,但它同样指定了一个单色的遮掩位图,遮掩位图中为1的像素对应的位置会被传送,为0的像素不被传送。(xSrc,ySrc)指定了源DC中需要传送的矩形的左上角,dwWidth和dwHeight指定宽度和高度,这个矩形将被旋转后传送到目的DC中去,旋转后的平行四边形位置由lpPoint指定的POINT结构阵列指出。
lpPoint是一个指针,指向含有3个POINT结构的内存中(这种使用POINT结构数组的方法在PolyLine中已经使用过),其中第一个点对应于一个平行四边形的左上角位置,第二个点代表右上角位置,第三个点代表右下角的位置,不需要第四个点是因为它可以从上面3个点的坐标推导出来。
5。 StretchBlt函数
StretchBlt函数实现像素的缩放功能,它的语法是:
invoke StretchBlt;hDcDest;xDest;yDest;dwWidthDest;dwHeightDest;
hDcSrc;xSrc;ySrc;dwWidthSrc;dwHeightSrc;dwROP
这个函数将源hDcSrc中以(xSrc,ySrc)为左上角,dwWidthSrc和dwHeightSrc为宽度和高度的矩形以dwROP指定的方式传送到目标hDcDest中去,目标位置是(xDest,yDest),新的矩形区域大小为dwWidthDest和dwHeight,如果源DC中的矩形大小和目标DC中的不一样,函数会将像素数据自动缩放。
但是StretchBlt对像素的缩放办法仅仅是删除多余的像素(从大到小)或者重复像素(从小到大),并不像一些图形处理软件一样进行插值计算,所以缩放的效果并不好,笔者建议如果能够不用这个函数就不要去用它,除非对图形的质量并没有要求。
6。 TransparentBlt函数
还有一个函数可以方便地实现不规则区域的像素拷贝操作,那就是TransparentBlt函数,它的用法如下:
invoke TransparentBlt;hDcDest;xDest;yDest;dwWidthDest;dwHeightDest;
hDcSrc;xSrc;ySrc;dwWidthSrc;dwHeightSrc;crTransparent
可以看出,它和StretchBlt函数的参数很像,也有dwWidthSrc和dwHeightSrc参数,难道这个函数也可以缩放吗?答案是肯定的,它也可以进行像素缩放。TransparentBlt函数的最后一个参数指定了一个透明色,源DC指定的矩形区域中和这个颜色相同的像素不会被拷贝,所以,BmpClock程序中的下列两个语句:
invoke BitBlt;hDcBack;0;0;CLOCK_SIZE;CLOCK_SIZE;@hDcMask;0;0;SRCAND
invoke BitBlt;hDcBack;0;0;CLOCK_SIZE;CLOCK_SIZE;
@hDcCircle;0;0;SRCPAINT
完全可以用下面这一句来代替:
invoke TransparentBlt;hDcBack;0;0;CLOCK_SIZE;CLOCK_SIZE;
@hDcCircle;0;0;CLOCK_SIZE;CLOCK_SIZE;0
这样甚至连遮掩图片Back1。bmp和Back2。bmp都可以省掉了。
当然,这个函数的传送方式使用拷贝方式,如果需要用到ROP码,那么只有使用其他函数了。由此可见,各种块传送函数都有它们的优缺点,在实际应用中,最好的办法就是根据实际情况灵活使用。
TransparentBlt函数并不包含在Gdi32。dll中,而是在Msimg32。dll中,所以使用时注意在源程序头部加上下面的包含语句:
include Msimg32。inc
includelib Msimg32。lib
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
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 bineRgn,hDestRgn,hSrcRgn1,hSrcRgn2,dwbine
该函数将hSrcRgn1和hSrcRgn2两个区域合并起来放入hDestRgn指定的区域句柄中,但hDestRgn并不是新生成的,它必须是一个已经存在的区域句柄,当函数执行后,hDestRgn中原来的区域会被破坏并替换成新合并成的区域,但可以对hDestRgn和hSrcRgn1使用同一个句柄,这样就相当于把hSrcRgn2合并到hSrcRgn1中去。
dwbine指定了合并的方式,它可以是以下的取值:
● RGN_AND 新区域是两个区域的共同部分。
● RGN_COPY 新区域是hSrcRgn1中的区域。
● RGN_DIFF 新区域是hSrcRgn1区域除去hSrcRgn2中的部分。
● RGN_OR 新区域是两个区域的叠加。
● RGN_XOR 新区域是两个区域的叠加除去共同部分。
bineRgn函数的返回值代表新区域的形状大致是什么样子的,它可能是以下值:
● NULLREGION 新区域是空区域。
● SIMPLEREGION 新区域是个简单形状(可以用上面的单个函数创建)。
● PLEXREGION 新区域是个复杂的形状。
● 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函数以后,原有的路径定义就会被破坏掉。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第8章 通用对话框
8。1 通用对话框简介
Windows操作系统为一些常用功能提供了一些通用对话框(mon Dialog Box),比如在不同的应用程序中进行打开文件、选择字体、选择颜色等操作时,不同程序显示的对话框的模样都是一样的。另外,把同样的应用程序放到不同版本的Windows下执行就会发现,这些对话框会随着操作系统版本的不同而不同,如图8。1所示,选择同样的“打开”文件菜单项时,在Windows XP下显示的对话框是左边的样子,而在Windows 98下显示的是右边的样子,但程序中并没有为不同版本的操作系统设计不同的对话框。造成这些现象的原因就是这些对话框是操作系统提供的,实现对话框的代码包括在dlg32。dll库文件中,由于不同版本的dlg32。dll在设计上可能有所不同,所以不同版本Windows下的对话框会有所不同。
图8。1 不同操作系统下的“打开”文件对话框
8。1 通用对话框简介
通用对话框函数由dlg32。dll提供,所以在使用之前需要在源程序中包含相应的include和includelib语句:
include dlg32。inc
includelib dlg32。lib
Windows提供多种通用对话框,每种通用对话框都使用一个专用的函数来创建和显示,另外,提供一个数据结构供初始化对话框使用,并在同一个数据结构中返回用户在对话框中输入的数据。使用这些对话框是很简单的,只需要填写结构中的初始化数据,然后调用函数并把结构地址传入就可以了。
dlg32。dll中提供的通用对话框如表8。1所示,表中还包括创建这些对话框使用的函数以及数据结构名称。
表8。1 通用对话框列表
通用对话框
使 用 函 数
使用数据结构
选择颜色
ChooseColor
CHOOSECOLOR
查找字符串
FindText
FINDREPLACE
替换字符串
ReplaceText
FINDREPLACE
选择字体
ChooseFont
CHOOSEFONT
打开文件
GetOpenFileName
OPENFILENAME
保存文件
GetSaveFileName
OPENFILENAME
页面设置
PageSetupDlg
PAGESETUPDLG
在这些对话框中,查找字符串和替换字符串的对话框是非模态的,方便用户在查找到一个字符串以后,不必关闭对话框就可以继续查找另一个字符串,其他的对话框都是模态对话框。通用对话框使用的例子可以参考所附光盘的Chapter08mDlg目录中的代码,包括汇编源代码mDlg。asm和资源脚本文件mDlg。rc。mDlg。asm文件的内容如下:
。386
。model flat; stdcall
option casemap :none
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》