|
楼主 |
发表于 2006-4-19 08:39
|
显示全部楼层
实现铅笔工具功能
这个程序让我们在实现一个铅笔工具的功能。
gTrZ7eRE.zip
(19.59 KB, 下载次数: 429)
我们来分析一下程序:一些与绘图函数不大相关的就不分析了,比如其中的类模块以及一些函数之类的,使用类模块主要是为了减少代码
程序中用到了以下几个API函数:
Public Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Public Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Public Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Public Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Public Declare Function SetPixelV Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Long
Public Declare Function CreatePen Lib "gdi32.dll" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long
Public Declare Function LineTo Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
Public Declare Function MoveToEx Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByRef lpPoint As POINTAPI) As Long
API函数说明:
----------------------------------------------------------
GetDC
VB声明
Declare Function GetDC Lib "user32" Alias "GetDC" (ByVal hwnd As Long) As Long
说明
获取指定窗口的设备场景
返回值
Long,指定窗口的设备场景句柄,出错则为0
参数表
参数类型及说明
hwndLong,将获取其设备场景的窗口的句柄。若为0,则要获取整个屏幕的DC
注解
若窗口所属类具有CS_OWNDC, CS_CLASSDC 或 CS_PARENTDC样式,则获取的设备场景属窗口或类专有。vb的窗体和图片框控件也是这种情况,它用该函数取得的结果和控件的hdc属性相同(在autoredraw为FALSE时)。您无须考虑取回的窗体或图片框控件设备场景的默认状态,特别是绘图对象。另外,默认状态随着窗体和控件autoredraw属性的设置而不同。在设备场景释放前您必须回复其状态为初始值。对于没有CS_OWNDC, CS_CLASSDC 或 CS_PARENTDC样式的窗口的设备场景,可从通用windows缓存中获取,其状态为默认值。缓存中可用设备场景数量是有限的,因此只要可能就释放设备场景
用本函数获取的设备场景一定要用ReleaseDC函数释放,不能用DeleteDC
----------------------------------------------------------
几乎所有的绘图函数都需要一个设备场景作为参数,我们可以用GetDC函数来获得它,这个函数中只有一个参数,那就是窗口句柄.如果我们知道了窗口句柄就可以很容易的获得(程序中使用ListView控件来作为画布的一个主要原因就是该控件提供了hWnd属性,另一个原因就是该控件的Mouse事件中使用的坐标就是设备场景中所用的逻辑坐标,这样我们就省去了换算了.当然可能多占点内存,但这不是我们在这里要讨论的问题了)
----------------------------------------------------------
ReleaseDC
VB声明
Declare Function ReleaseDC Lib "user32" Alias "ReleaseDC" (ByVal hwnd As Long, ByVal hdc As Long) As Long
说明
释放由调用GetDC或GetWindowDC函数获取的指定设备场景。它对类或私有设备场景无效(但这样的调用不会造成损害)
返回值
Long,执行成功为1,否则为0
参数表
参数类型及说明
hwndLong,要释放的设备场景相关的窗口句柄
hdcLong,要释放的设备场景句柄
注解
对那些用CreateDC一类的DC创建函数生成的设备场景,不要用本函数
-----------------------------------------------------------
上面注解中已说明了,使用GetDC获取的设备场景我们需要使用ReleaseDC函数来释放它.在相关的教程中建议在每一个过程中都重新用GetDC获取DC并在过程结束时释放它。我是半路出家,对此不甚了解,你自已看着办吧)
-----------------------------------------------------------
SelectObject
VB声明
Declare Function SelectObject Lib "gdi32" Alias "SelectObject" (ByVal hdc As Long, ByVal hObject As Long) As Long
说明
每个设备场景都可能有选入其中的图形对象。其中包括位图、刷子、字体、画笔以及区域等等。一次选入设备场景的只能有一个对象。选定的对象会在设备场景的绘图操作中使用。例如,当前选定的画笔决定了在设备场景中描绘的线段颜色及样式
返回值
Long,与以前选入设备场景的相同hObject类型的一个对象的句柄,零表示出错。如选定的对象是一个区域(Region),结果就是下列常数之一:SIMPLEREGION, COMPLEXREGION 或 NULLREGION 对区域进行描述, GDI_ERROR 表示出错
参数表
参数类型及说明
hdcLong,一个设备场景的句柄
hObjectLong,一个画笔、位图、刷子、字体或区域的句柄
注解
返回值通常用于获得选入DC的对象的原始值。绘图操作完成后,原始的对象通常选回设备场景。在清除一个设备场景前,务必注意恢复原始的对象
----------------------------------------------------------
这个函数让我们选择当前设备场景中的对象,我们可以先产生一个对象再把对象选进设备场景中去。说明中说到:一次选入设备场景的只能有一个对象。选定的对象会在设备场景的绘图操作中使用。例如,当前选定的画笔决定了在设备场景中描绘的线段颜色及样式。这只是针对每个对象,但我们可以选择多个对象,比如我们选择了画笔,我们同时也可以选择刷子
----------------------------------------------------------
DeleteObject
VB声明
Declare Function DeleteObject Lib "gdi32" Alias "DeleteObject" (ByVal hObject As Long) As Long
说明
用这个函数删除GDI对象,比如画笔、刷子、字体、位图、区域以及调色板等等。对象使用的所有系统资源都会被释放
返回值
Long,非零表示成功,零表示失败
参数表
参数类型及说明
hObjectLong,一个GDI对象的句柄
注解
不要删除一个已选入设备场景的画笔、刷子或位图。如删除以位图为基础的阴影(图案)刷子,位图不会由这个函数删除——只有刷子被删掉
----------------------------------------------------------
与GetDC和ReleaseDC一样,此函数删除一个已选进设备场景的对象,相关教程中建议在对象不在需要的时候删除它。在清除对象之后,设备场景恢复默认对象。
----------------------------------------------------------
SetPixelV
VB声明
Declare Function SetPixelV Lib "gdi32" Alias "SetPixelV" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Long
说明
在指定的设备场景中设置一个像素的RGB值
返回值
Long,非零表示成功,零表示失败。会设置GetLastError
参数表
参数类型及说明
hdcLong,一个设备场景的句柄
x,yLong,要设置的点,用逻辑坐标表示
crColorLong,指定像素的新RGB颜色值
注解
这个函数比SetPixel快一些,但不会返回设置的实际颜色。可用GetDeviceCaps判断设备是否支持这个函数
----------------------------------------------------------
这个函数很简单,没什么可说的,把场景中相应的点设置为我们要设置的颜色。-
---------------------------------------------------------
CreatePen
VB声明
Declare Function CreatePen Lib "gdi32" Alias "CreatePen" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long
说明
用指定的样式、宽度和颜色创建一个画笔
返回值
Long,如函数执行成功,就返回指向新画笔的一个句柄;否则返回零
参数表
参数类型及说明
nPenStyleLong,指定画笔样式,可以是下述常数之一
PS_SOLID画笔画出的是实线
PS_DASH画笔画出的是虚线(nWidth必须是1)
PS_DOT画笔画出的是点线(nWidth必须是1)
PS_DASHDOT画笔画出的是点划线(nWidth必须是1)
PS_DASHDOTDOT画笔画出的是点-点-划线(nWidth必须是1)
PS_NULL画笔不能画图
PS_INSIDEFRAME画笔在由椭圆、矩形、圆角矩形、饼图以及弦等生成的封闭对象框中画图。如指定的准确RGB颜色不存在,就进行抖动处理
nWidthLong,以逻辑单位表示的画笔的宽度
crColorLong,画笔的RGB颜色
注解
一旦不再需要画笔,记得用DeleteObject函数将其删除
----------------------------------------------------------
在代码中我们使用CreatePen来生成一个新的画笔,然后用SelectObject把它选进设备场景中
----------------------------------------------------------
OK,下面我们来看看主要的过程
Private Sub Canvas_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
pStart.x = x
pStart.y = y
Pen Button, x, y, True
End Sub
画布MouseDown事件中,我们使用一个POINTAPI结构类型的变量来保存它,并把鼠标按键信息,当前位置传递给Pen过程,最后一个参数在Pen过程中表示为是MouseDown事件,在Pen过程中我们根据是MouseDown事件还是MouseMove事件来执行不同代码,实际上我们也可以把Pen过程中的代码分别放入各事件当中的。这就看个人喜好了
Private Sub Canvas_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
If Button <> 0 Then Pen Button, x, y, False’如果有按键信息执行代码
End Sub
下面就是主要功能代码了
Sub Pen(Button As Integer, xx As Long, yy As Long, MDown As Boolean) '铅笔
Dim pt As POINTAPI
Dim nColor As Long, dc As Long
dc = GetDC(hCanvas)’获得设备场景句柄,hCanvas是画布窗口ListView控件句柄
If MDown Then’判断是否是MouseDown事件发生
If Button = 1 Then’判断是左键还是右键
nColor = FrColor’左键时,使用前景色
ElseIf Button = 2 Then
nColor = BkColor’右键时,使用背景色
End If
SetPixelV dc, xx, yy, nColor,把当前位置设置成我们要设置的颜色
Else’MouseMove事件时执行以下代码
If Button = 1 Then’左键
hPen0 = CreatePen(0, 1, FrColor)’左键使用前景色生成新画笔,这里第一个参数为0,我们要生成一个实线画笔
ElseIf Button = 2 Then’右键
hPen0 = CreatePen(0, 1, BkColor)’右键使用前景色生成新画笔,常数PS_SOLID=0,线条大小为1象素
End If
pStop.x = xx’把当们位置保存进变量中
pStop.y = yy’
SelectObject dc, hPen0’将画笔选进设备场景中,之后我们就可以用我们设置的画笔作画了:)
MoveToEx dc, pStart.x, pStart.y, pt’
LineTo dc, pStop.x, pStop.y
DeleteObject hPen0’删除画笔
pStart.x = pStop.x’将当前位置作为开始位置
pStart.y = pStop.y
End If
DeleteObject hPen0’删除画笔,这里是多余的,并不会发生错误
ReleaseDC hCanvas, dc’释放设备场景
End Sub
好了,这个小程序分析完毕,您可有收获吗?如果您有什么疑问,请跟贴
咦,等等,还有LineTo及MoveToex函数还没解析呢?这两个函数又是做什么和如何使用的呢?嗯,别急,在楼下我们将对它进行解释。这里先简单的说一下:
LineTo函数在场景中画一条直线,哦,既然是画直线,那为什么在铅笔工具中要用直线呢,这是因为当我们在画布中移动的速度过快时,如果用SetPixelV那么有可能会出现断节现象。明白了吧^_^
OK,您也累了吧?歇一下,喝杯茶先……
[此贴子已经被作者于2006-4-21 13:07:33编辑过] |
|