×

DirectX与VB.NET编程(十二)图片的绘制

Kalet Kalet 发表于2009-03-20 12:00:14 浏览314 评论0

抢沙发发表评论

这次的内容是图片的绘制和关键色的使用,由于绘制静态图片无法使用剪切,所以剪切的内容将被移到动画章节中。


===============华丽的分割线===============


学习内容:DirectX与VB.NET编程(十二)图片的绘制
·利用离屏表面加载图片
·了解关键色的概念和作用
·使用快速绘制函数(DrawFast)
·使用绘制函数(Draw)


===============华丽的分割线===============


这次的范例运行后按下“绘制”按钮时会在屏幕左上角绘制普通图片和被调整大小的图片。
打开VB.NET,新建一个项目,添加DirectDraw引用,在窗口中放置一个按钮(Button),控件设计请参考下图:



这次的范例使用的图片如下图,你可以右键单机它来下载它。




===============华丽的分割线===============


引入名称空间:
Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectDraw

声明设备和主表面
Dim vDev As New Device
Dim vSurface As Surface

初始化设备和主表面
'初始化设备
vDev.SetCooperativeLevel(Me, CooperativeLevelFlags.Normal)


'初始化主表面
Dim vSurfaceCaps As New SurfaceCaps
vSurfaceCaps.VideoMemory = True
vSurfaceCaps.PrimarySurface = True


Dim vSurfaceDes As New SurfaceDescription(vSurfaceCaps)


vSurface = New Surface(vSurfaceDes, vDev)


===============华丽的分割线===============


在DirectDraw中绘制图片,必须现将图片从硬盘加载,才能进行绘制,DirectDraw中,提供了一种叫“离屏表面(Off-Screen Surface)”的概念来储存从硬盘加载的图片,主表面可以从离屏表面中的图片引用过来并进行绘制,这就是DirectDraw绘制图片的原理。
要声明一个离屏表面很简单,离屏表面的类依然是Surface,只是在初始化时将OffScreenPlain属性设置为True,而不像主表面那样将PrimarySurface设置为True,不同种类的表面的唯一区别就是这些属性了。


===============华丽的分割线===============


下面声明离屏表面的变量,我们用vSecondarySurface来代表它:
Dim vSecondarySurface As Surface
下面是初始化离屏表面:
'初始化离屏表面
Dim vSecondarySurfaceCaps As New SurfaceCaps
vSecondarySurfaceCaps.VideoMemory = True
vSecondarySurfaceCaps.OffScreenPlain = True


Dim vSecondarySurfaceDes As New SurfaceDescription(vSecondarySurfaceCaps)


vSecondarySurface = New Surface("C:\Fore.bmp", vSecondarySurfaceDes, vDev)
你可能已经发现了,离屏表面的构造函数多了一个字符串参数,这个参数就是图片的路径,你也可以在此更改为自己需要的路径。
在这里需要注意的是,以这种方式初始化离屏表面只能加载BMP格式的图片,如果是JPG、TGA等其它类型则会发生错误。
要解决这个问题也比较简单,通过快速信息,我们可以看到Surface还有一个如下的构造函数:



我们便可以利用这个构造函数,这句程序改为如下:
vSecondarySurface = New Surface(New Bitmap("C:\Fore.bmp"), vSecondarySurfaceDes, vDev)
但这种构造函数有一个缺点,我将在下面进行陈述。


===============华丽的分割线===============


下面是关键色(ColorKey)了,所谓关键色,就是被抠去的颜色,只要与关键色重合的颜色就不会被绘制出来,像是被抠去一样,比如在下图中:



左边的龙属于图片的原型,以紫红色作为背景,如果此时我设置该图片的关键色为紫红色,那么紫红色将抠掉,绘制出来的效果就如右图,如果不设置关键色,那么图片中就会是一个拖着大块背景色的图片了。
下面是设置关键色的代码:
'关键色
Dim vColorKey As New ColorKey
vColorKey.ColorSpaceHighValue = RGB(255, 255, 255)
vColorKey.ColorSpaceLowValue = RGB(255, 255, 255)
vSecondarySurface.SetColorKey(ColorKeyFlags.SourceDraw, vColorKey)

从以上代码可以看出,关键色是以一个名为ColorKey的类代表,这个类有ColorSpaceHighValueColorSpaceLowValue两个属性,这两个属性就代表关键色所指代的颜色,因为我需要抠去白色的背景,因此使用了RGB(255,255,255)这个颜色作为关键色。
可能你会有疑问,一个关键色为什么要有两个属性控制?恩,其实我也不知道。。不过根据我的实践,ColorSpaceLowValue这个属性才是真正掌控生杀大权的,ColorSpaceHighValue无论怎样改变都没有任何变化,不过为了保险,我们还是它们都同时设置为需要的关键色。设置好关键色后便通过SetColorKey方法放置到表面中,注意,因为离屏表面是储存图片的,因此关键色应该放置到离屏表面而非主表面。
在选择关键色时尽量不要选择与图片中内容有重复或相近的颜色,不然正常的颜色也会被抠掉。


===============华丽的分割线===============


下面就是绘制图片了,在这里我们需要用到两个函数DrawDrawFast,从字面上理解,Draw为绘制,DrawFast为快速绘制,的确,DrawFast的绘制速度比Draw要快,这是由于DrawFast省略了很多中间步骤,只将最基本的功能实现,而Draw则具有诸多复杂的功能,如缩放、旋转、透明度等,如果只是单单的图片绘制,那么用DrawFast足矣,高级功能就可以使用Draw了。
代码如下:
'快速绘制
vSurface.DrawFast(0, 0, vSecondarySurface, DrawFastFlags.SourceColorKey)
'按比例绘制
vSurface.Draw(New Rectangle(0, 100, 200, 50), vSecondarySurface, New Rectangle(0, 0, vSecondarySurface.SurfaceDescription.Width, vSecondarySurface.SurfaceDescription.Height), DrawFlags.KeySource)

在以上代码中,DrawFast的前两个参数代表绘制的坐标点,第三个用于指定图片所在的离屏表面,第四个参数是一个枚举,代表使用关键色进行绘图。DrawFast还有另外一个重载,多了一个矩形参数,用于表示绘制图片的指定矩形范围内的内容。
Draw的第一个参数代表在指定矩形内绘制图片,如果图片的尺寸与指定尺寸不符则会被拉伸或压缩,第二个参数用于指定图片所在的离屏表面,第三个参数也是表示绘制图片的指定矩形范围内的内容,在这里,我们将矩形的宽和高直接引用为表面的宽和高,即代表将图片全部绘制出来,最后一个也代表使用关键色进行绘图。由于Draw重载函数过多,就不作介绍了。


在这里需要注意,如果你使用Bitmap对象来加载图像,那么在Draw函数中关键色功能将被屏蔽。
虽然Draw函数还可以实现旋转、透明绘制、Z层绘制等效果,但这些功能基本上没有多少硬件支持,也无法实现这种效果。


===============华丽的分割线===============


退出程序时释放内存:
If Not vSecondarySurface Is Nothing Then
        vSecondarySurface.Dispose()
        vSecondarySurface = Nothing
End If
If Not vSurface Is Nothing Then
        vSurface.Dispose()
        vSurface = Nothing
End If
If Not vDev Is Nothing Then
        vDev.Dispose()
        vDev = Nothing
End If


===============华丽的分割线===============


下面是全部代码:
Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectDraw


Public Class Form1
    Inherits System.Windows.Forms.Form


    Dim vDev As New Device
    Dim vSurface As SurfaceDirectX与VB.NET编程(十二)图片的绘制
    Dim vSecondarySurface As Surface


#Region " Windows 窗体设计器生成的代码 "


    Public Sub New()
        MyBase.New()


        '该调用是 Windows 窗体设计器所必需的。
        InitializeComponent()


        '在 InitializeComponent() 调用之后添加任何初始化


    End Sub


    '窗体重写 dispose 以清理组件列表。
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub


    'Windows 窗体设计器所必需的
    Private components As System.ComponentModel.IContainer


    '注意: 以下过程是 Windows 窗体设计器所必需的
    '可以使用 Windows 窗体设计器修改此过程。
    '不要使用代码编辑器修改它。
    Friend WithEvents Button1 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(8, 8)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(104, 24)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "绘制"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
        Me.ClientSize = New System.Drawing.Size(272, 218)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form1"
        Me.Text = "DirectDraw"
        Me.ResumeLayout(False)


    End Sub


#End Region


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        '初始化设备
        vDev.SetCooperativeLevel(Me, CooperativeLevelFlags.Normal)


        '初始化主表面
        Dim vSurfaceCaps As New SurfaceCaps
        vSurfaceCaps.VideoMemory = True
        vSurfaceCaps.PrimarySurface = True


        Dim vSurfaceDes As New SurfaceDescription(vSurfaceCaps)


        vSurface = New Surface(vSurfaceDes, vDev)


        '初始化离屏表面
        Dim vSecondarySurfaceCaps As New SurfaceCaps
        vSecondarySurfaceCaps.VideoMemory = True
        vSecondarySurfaceCaps.OffScreenPlain = True


        Dim vSecondarySurfaceDes As New SurfaceDescription(vSecondarySurfaceCaps)


        vSecondarySurface = New Surface("C:\Fore.bmp", vSecondarySurfaceDes, vDev)


        '关键色
        Dim vColorKey As New ColorKey
        vColorKey.ColorSpaceHighValue = RGB(255, 255, 255)
        vColorKey.ColorSpaceLowValue = RGB(255, 255, 255)
        vSecondarySurface.SetColorKey(ColorKeyFlags.SourceDraw, vColorKey)
    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        '快速绘制
        vSurface.DrawFast(0, 0, vSecondarySurface, DrawFastFlags.SourceColorKey)
        '按比例绘制
        vSurface.Draw(New Rectangle(0, 100, 200, 50), vSecondarySurface, New Rectangle(0, 0, vSecondarySurface.SurfaceDescription.Width, vSecondarySurface.SurfaceDescription.Height), DrawFlags.KeySource)
    End Sub


    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        If Not vSecondarySurface Is Nothing Then
            vSecondarySurface.Dispose()
            vSecondarySurface = Nothing
        End If
        If Not vSurface Is Nothing Then
            vSurface.Dispose()
            vSurface = Nothing
        End If
        If Not vDev Is Nothing Then
            vDev.Dispose()
            vDev = Nothing
        End If
    End Sub
End Class


===============华丽的分割线===============


程序运行效果如下图:




===============华丽的分割线===============

DirectX与VB.NET编程(十二)图片的绘制

下次是动画和剪切的使用。



群贤毕至

访客