API的填充函数

在“笔画组字的编程问题”中,我已说到,为了寻找笔画轮廓线内的填充子程序,摸索了五六年时间。后来网友告诉我用API函数。我选用了简单的API函数FloodFill,速度也快,以为问题解决了。但当修改好我的无字库程序,也修改了250个笔画轮廓线数据,而且个别地写每个笔画都没有问题后,在实际组字时却常常出问题。有些问题很难理解其原因,无论如何没法克服。问题描述如下:

1、                在字形不很大时,填充不完全。这倒好理解。因为填充是从“种子点”开始向外填,碰到边界就停止的。字形缩小后,笔画窄的地方,轮廓线就可能碰在一起,使原来的连通区一分为二了。为此,我修改轮廓线,尽量避免笔画中部的收缩,把“种子点”都移到最宽部分的正中。

2、                组字时,个别笔画的填充会溢出,使后半个字成为黑方块。因为这些笔画在别的汉字内、或在别的部位都是不会溢出的,其发生原因真不好理解。后查明,汉字的最后一横易发生溢出,且大多是组字结构中要把它向上移位时。有时改变汉字的这种结构,是可以消除溢出。是不是我的程序在计算笔画数据时,把轮廓线上移了,没有让“种子点”同步上移,使它落到轮廓线之外?如果确是这样,应该成为反白的笔画呀。

3、                有时填充只填四周,中间却留有空白。空白很大,好象仅使轮廓线加粗了些,成了空心笔画。在调试单个笔画时,虽然笔画很大,但当“种子点”选得不当时,中心也会出现一些空白。不知道该函数用的是怎样的算法,会出现这种情形。

4、                更怪的是,填充竟是阴影线,一条黑,一条白,黑白相间。无论那一种算法,似乎也不应该出现这样的效果。

改用函数ExtFloodFill,结果亦如此。还有其它函数,因为不知道函数的参数是如何取的,没法试。现在我把“api函数大全在线速查中所有填充函数及其说明都列于下,4绘图函数,2个设备场景函数,共5个。

 

绘图函数

FloodFill

 

VB声明

Declare Function FloodFill Lib "gdi32" Alias "FloodFill" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Long

说明

用当前选定的刷子在指定的设备场景中填充一个区域。区域是由颜色crColor定义的

返回值

Long,非零表示成功,零表示失败。会设置GetLastError

参数表

参数

类型及说明

hdc

Long,设备场景的句柄

x,y

Long,开始填充的那个点,用逻辑坐标表示

crColor

Long,欲使用的边界颜色。由这个颜色包围的表面会被填充

注解

x,y绝对不能有颜色crColor,而且必须在剪切区域内。这个函数只对光栅设备有效,请参考ExtFloodFill的注解

 

ExtFloodFill

 

VB声明

Declare Function ExtFloodFill Lib "gdi32" Alias "ExtFloodFill" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long, ByVal wFillType As Long) As Long

说明

在指定的设备场景里,用当前选择的刷子填充一个区域

返回值

Long,非零表示成功,零表示失败。会设置GetLastError

参数表

参数

类型及说明

hdc

Long,设备场景的句柄

x,y

Long,开始填充的一个点,采用逻辑坐标表示

crColor

Long,要使用的边界颜色

wFillType

Long,欲执行的填充类型,由下述任何一个常数决定

FLOODFILLBORDER

等同于FloodFill函数的功能

FLOODFILLSURFACE

从指定的点向外填充,只到找到了crColor颜色(在边框采用了多种颜色时使用)

注解

如指定了FLOODFILLBORDER,那么x,y点绝对不能为crColor颜色。如指定了FLOODFILLSURFACE,那么x,y点必须是crColor颜色。这个函数只能在光栅设备中使用。可用GetDeviceCaps函数判断设备是否支持这个函数

提示

一旦指定了FLOODFILLBORDER,务必保证初始点的颜色没有crColor。如果使用的是FLOODFILLSURFACE,务必保证初始点有颜色crColor(这是函数执行失败最常见的两个原因)。注意保证初始点位于剪切区内

 

FillPath

 

VB声明

Declare Function FillPath Lib "gdi32" Alias "FillPath" (ByVal hdc As Long) As Long

说明

关闭路径中任何打开的图形,并用当前刷子填充

返回值

Long,非零表示成功,零表示失败。会将GetLastError设置为下述值之一:ERROR_CAN_NOT_COMPLETEERROR_INVALID_PARAMETERERROR_NOT_ENOUGH_MEMORY

参数表

hdc

Long,欲在其中操作的设备场景

注解

函数执行完毕后,选定的路径会自行清除

 

FillRect

 

VB声明

Declare Function FillRect Lib "user32" Alias "FillRect" (ByVal hdc As Long, lpRect As RECT, ByVal hBrush As Long) As Long

说明

用指定的刷子填充一个矩形

返回值

Long,非零表示成功,零表示失败。会设置GetLastError

参数表

参数

类型及说明

hdc

Long,设备场景的句柄

lpRect

RECT,对填充区域进行描述的一个矩形,采用逻辑坐标

hBrush

Long,欲使用的刷子的句柄

注解

矩形的右边和底边不会描绘

 

设备场景函数

FillRgn

 

VB声明

Declare Function FillRgn Lib "gdi32" Alias "FillRgn" (ByVal hdc As Long, ByVal hRgn As Long, ByVal hBrush As Long) As Long

说明

用指定刷子填充指定区域

返回值

Long,执行成功为非零值,失败则为0

参数表

参数

类型及说明

hdc

Long,设备场景句柄

hRgn

Long,以数据设备坐标填充的区域句柄

hBrush

Long,要用的刷子的句柄

 

PaintRgn

 

VB声明

Declare Function PaintRgn Lib "gdi32" Alias "PaintRgn" (ByVal hdc As Long, ByVal hRgn As Long) As Long

说明

用当前刷子背景色填充指定区域

返回值

Long,执行成功为非零值,失败为0

参数表

参数

类型及说明

hdc

Long,设备场景句柄

hRgn

Long,将填充的区域句柄

 

所有函数第一个参数是设备场景句柄hdc。网友tjestar告诉我,PictureBox等控件都有这个属性,我直接拿过来用,才实现了函数FloodFill的调用。最关键的是指定填充的区域的句柄hRgn。(指定正方形的lpRect,大概与指定多边形相似,我用不到。第3个函数FillPath,看似没有这样的参数,但不懂说明中的“路径”指的是什么,也没法用)。只得再次上网求助,感谢网友tjestar指给我如下的一个使用函数FillRgn的实例,才使我用上它,解决了问题。

                      

 

使用函数FillRgn的实例:

 

'此部分为窗体内代码

Dim hRgn As Long '多边形句柄

Dim hBrush As Long '画刷句柄

 

Private Sub Command1_Click()

 

'API函数使用的最小单位是像素,窗口的最小单位默认为缇,要注意转换。

'左上角与右下角坐标是根据选入的设备来决定的(左上角永远默认为0,0)(右下角为窗体宽度(像素),窗体高度(像素))

'一定要记住使用完毕后用DeleteObject函数将申请到的句柄释放点

 

Dim P(3) As POINTAPI '多边形顶点变量数组

 

P(0).x = 0

P(0).y = 0

P(1).x = 10

P(1).y = 10

P(2).x = 0

P(2).y = 10

P(3).x = 0

P(3).y = 0

 

hRgn = CreatePolygonRgn(P(0), 3, WINDING) '根据多边形顶点数据创建多边形

SelectObject Me.hdc, hRgn '将多边形选入窗口图形设备

 

hBrush = CreateSolidBrush(RGB(255, 255, 255)) '建立画刷(实色)

 

FillRgn Me.hdc, hRgn, hBrush '用建立的画刷填充多边形

 

DeleteObject hRgn '释放掉多边形句柄占用的内存

DeleteObject hBrush '释放掉画刷句柄占用的内存

End Sub

 

'下面部分为模块内代码

Public Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As POINTAPI, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long

Public Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long

Public Declare Function FillRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long, ByVal hBrush As Long) As Long

Public Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long

Public Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long

 

Public Type POINTAPI

x As Long

y As Long

End Type

Public Const WINDING = 2

 

 

原来参数hRgn 是用另一个API函数 CreatePolygonRgn建立的。使用这个函数也很简单,3个参数依次是:放多边形各顶点数据的数组的第一点,顶点的总数,填充方式。另一个参数画刷句柄hBrush,则用函数CreateSolidBrush来建立。所以在VB程序的模块内,除FillRgn的声明外,还要增加上述两个建立函数及相应的删除函数(共4个)的声明,因为每次调用FillRgn后,都要把这两个句柄消除。

使用填充函数FillRgn以后,一切问题都没有了。用这个函数填充不要“种子点”,且自动将末点连到首点形成闭合,永远不会发生溢出。另外,折线无论发生怎样的交叉都没有关系。它的算法大概是这样的:沿着闭合折线走一遍,毎到一点,用异或的方式,从该点向右(或左)画一条射线。由于射线在闭合区外总是画了偶数次,结果会保持原状。

 

                     张时钊   2005.5.2