今天是DirectDraw的最后一章了,前几天因为ISAS考试,所以文章发布迟了这么久,向大家道个歉。
这次的内容很偏僻,我也是在网上查了很久才有头绪,经过千辛万苦的总结才出来,这也是更新迟的原因,下面废话不多说,马上开始。
===============华丽的分割线===============
DirectX与VB.NET编程(十七)*图片结构与DirectDraw截图(下)学习内容:
·在DirectDraw中实现截图
===============华丽的分割线===============
这次的例子,我们使用的原来《全屏动画》的例子上的扩展,文章链接:http://hi.baidu.com/jadechoice/blog/item/910b7617eb1f5c054b90a7fe.html
DirectDraw实现截图的原理是实现将要截图的表面进行LOCK,此时会返回一个GraphicsStream,这个流是存在于DirectX的名称空间的,因此在你未引入DirectX名称空间前你是无法使用到它的。获得这个流后,我们再通过这个流的句柄来创建一个Bitmap对象,最后使用Bitmap对象的Save方法储存图片。由于上一章还没有预料到会如此简单,于是说了那么多相关于BMP的内容,不过了解下也无妨。
首先我们创建一个内存表面,它的目的就是将当前图像保存到内存中,虽然我们可以直接处理主表面,但是速度会慢很多,使用这种方法再保存到硬盘的时候可以大大提高保存速度,代码如下:
'声明内存表面
Dim vMemorySurfaceCaps As New SurfaceCaps
vMemorySurfaceCaps.SystemMemory = True
vMemorySurfaceCaps.OffScreenPlain = True
Dim vMemorySurfaceDesc As New SurfaceDescription(vMemorySurfaceCaps)
vMemorySurfaceDesc.Width = vPrimarySurface.SurfaceDescription.Width
vMemorySurfaceDesc.Height = vPrimarySurface.SurfaceDescription.Height
Dim vMemorySurface As New Surface(vMemorySurfaceDesc, vDev)
然后需要做的是将主表面的内容绘制到内存表面中:
'将主屏内容绘制到内存表面中
vMemorySurface.DrawFast(0, 0, vPrimarySurface, DrawFastFlags.Wait)
下面便是将表面锁定并取出其中的数据了,在我们LOCK表面时,会返回一个LockData类型的对象,我们需要的GraphicsStream便储存在其中,代码如下:
Dim vLockedData As LockedData = vMemorySurface.Lock(LockFlags.Wait)
Dim GS As New GraphicsStream(vLockedData.Data.InternalData, vLockedData.Width * vLockedData.Height * vLockedData.RGBBitCount, True, True)
需要注意的是GraphicsStream的长度需要自己指定,它的长度等于表面宽*表面高*色深。
GraphicsStream已经取到了,我们需要使用它来构造Bitmap的对象了,代码如下:
Dim Img As New Bitmap(vLockedData.Width, vLockedData.Height, vLockedData.Width * 4, Imaging.PixelFormat.Format32bppRgb, GS.InternalData)
这个构造函数的参数中,第一个、第二个分别是Bitmap的宽和高,第三个参数根据描述是像素数据流中的内存大小,根据我的实验,它等于Bitmap宽的4倍,第四个是环境色深,它应该等于你桌面的色深,最后一个就是流的句柄,通过流对象的InternalData方法来获得。
最后就是保存Bitmap内的图像信息,通过Save方法即可,保存路径可随意,格式选为JPEG:
Img.Save("C:\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
最后截图结束时,再将表面解锁就可以了,虽然这个表面即将被垃圾回收,但我们还是养成这个习惯比较好,代码如下:
vMemorySurface.Unlock()
我们设置,在程序运行中,按下F1键代表截图,而且为了防止这段代码出现错误,我们也应将其加入到Try块中,因此,在Form1.KeyDown事件方法中追加如下代码:
If e.KeyCode = Keys.F1 Then
If e.KeyCode = Keys.F1 Then
Try
'声明内存表面
Dim vMemorySurfaceCaps As New SurfaceCaps
vMemorySurfaceCaps.SystemMemory = True
vMemorySurfaceCaps.OffScreenPlain = True
Dim vMemorySurfaceDesc As New SurfaceDescription(vMemorySurfaceCaps)
vMemorySurfaceDesc.Width = vPrimarySurface.SurfaceDescription.Width
vMemorySurfaceDesc.Height = vPrimarySurface.SurfaceDescription.Height
Dim vMemorySurface As New Surface(vMemorySurfaceDesc, vDev)
'将主屏内容绘制到内存表面中
vMemorySurface.DrawFast(0, 0, vPrimarySurface, DrawFastFlags.Wait)
'将表面锁定到流中
Dim vLockedData As LockedData = vMemorySurface.Lock(LockFlags.Wait)
Dim GS As New GraphicsStream(vLockedData.Data.InternalData, vLockedData.Width * vLockedData.Height * vLockedData.RGBBitCount, True, True)
Dim Img As New Bitmap(vLockedData.Width, vLockedData.Height, vLockedData.Width * 4, Imaging.PixelFormat.Format32bppRgb, GS.InternalData)
Img.Save("C:\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
vMemorySurface.Unlock()
Catch ex As Exception
MsgBox("程序出现异常,被迫退出。")
ExitProg(False)
Exit Sub
End Try
End If
End If
===============华丽的分割线===============
下面是程序全部的代码:
Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectDraw
Imports System.Threading
Public Class Form1
Inherits System.Windows.Forms.Form
Dim vDev As Device
Dim vPrimarySurface As Surface
Dim vBackSurface As Surface
Dim vOffSurface As Surface
Public Const BALL_COUNT As Integer = 50
Dim X(BALL_COUNT) As Integer
Dim Y(BALL_COUNT) As Integer
Dim SpeedX(BALL_COUNT) As Integer
Dim SpeedY(BALL_COUNT) As Integer
Dim Running As Boolean
Dim DrawThread As Thread
#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 窗体设计器修改此过程。
'不要使用代码编辑器修改它。
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
Me.ClientSize = New System.Drawing.Size(216, 128)
Me.Name = "Form1"
Me.Text = "动画"
End Sub
#End Region
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'初始化设备
vDev = New Device(CreateFlags.Default)
vDev.SetCooperativeLevel(Me, CooperativeLevelFlags.FullscreenExclusive)
vDev.SetDisplayMode(640, 480, 2 ^ 24, 60, True)
'初始化主表面
Dim vPrimarySurfaceCaps As New SurfaceCaps
vPrimarySurfaceCaps.PrimarySurface = True
vPrimarySurfaceCaps.VideoMemory = True
vPrimarySurfaceCaps.Flip = True
vPrimarySurfaceCaps.Complex = True
Dim vPrimarySurfaceDesc As New SurfaceDescription(vPrimarySurfaceCaps)
vPrimarySurfaceDesc.BackBufferCount = 1
vPrimarySurface = New Surface(vPrimarySurfaceDesc, vDev)
'初始化后备表面
Dim vBackSurfaceCaps As New SurfaceCaps
vBackSurfaceCaps.BackBuffer = True
vBackSurfaceCaps.VideoMemory = True
vBackSurface = vPrimarySurface.GetAttachedSurface(vBackSurfaceCaps)
'初始化离屏表面
Dim vOffSurfaceCaps As New SurfaceCaps
vOffSurfaceCaps.OffScreenPlain = True
vOffSurfaceCaps.VideoMemory = True
Dim vOffSurfaceDesc As New SurfaceDescription(vOffSurfaceCaps)DirectX与VB.NET编程(十七)*图片结构与DirectDraw截图(下)
vOffSurface = New Surface("C:\ball.bmp", vOffSurfaceDesc, vDev)
'设置关键色以去除背景
Dim vColorKey As New ColorKey
vColorKey.ColorSpaceHighValue = RGB(255, 0, 255)
vColorKey.ColorSpaceLowValue = RGB(255, 0, 255)
vOffSurface.SetColorKey(ColorKeyFlags.SourceDraw, vColorKey)
'随机初始化各小球位置
Randomize()
For i As Integer = 0 To BALL_COUNT
X(i) = Int(Rnd() * 620)
Y(i) = Int(Rnd() * 460)
SpeedX(i) = -5 + Int(Rnd() * 10)
SpeedY(i) = -5 + Int(Rnd() * 10)
Next
'初始化设置线程
Running = True
DrawThread = New Thread(AddressOf Draw)
DrawThread.Start()
End Sub
Private Sub Draw()
While Running
Try
'将各小球重复地从离屏表面中绘制到后备表面上
For i As Integer = 0 To BALL_COUNT
vBackSurface.DrawFast(X(i), Y(i), vOffSurface, New Rectangle(0, 0, 20, 20), DrawFastFlags.SourceColorKey)
Next
'让主表面实现翻转,从而将后备表面中的图像绘制到主表面上
vPrimarySurface.Flip(Nothing, FlipFlags.NoVSync)
'清楚后备表面中的图片
vBackSurface.ColorFill(Color.Black)
Catch ex As Exception
MsgBox("程序出现异常,被迫退出。")
ExitProg(False)
Exit Sub
End Try
'小球处理位置
For i As Integer = 0 To BALL_COUNT
X(i) += SpeedX(i)
Y(i) += SpeedY(i)
If X(i) <= 0 Then
X(i) = 0
SpeedX(i) = -SpeedX(i)
End If
If X(i) >= 620 Then
X(i) = 620
SpeedX(i) = -SpeedX(i)
End If
If Y(i) <= 0 Then
Y(i) = 0
SpeedY(i) = -SpeedY(i)
End If
If Y(i) >= 460 Then
Y(i) = 460
SpeedY(i) = -SpeedY(i)
End If
Next
'线程延迟休息
Thread.Sleep(50)
End While
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
'按ESC退出程序
If e.KeyCode = Keys.Escape Then
ExitProg(True)
End If
If e.KeyCode = Keys.F1 Then
If e.KeyCode = Keys.F1 Then
Try
'声明内存表面
Dim vMemorySurfaceCaps As New SurfaceCaps
vMemorySurfaceCaps.SystemMemory = True
vMemorySurfaceCaps.OffScreenPlain = True
Dim vMemorySurfaceDesc As New SurfaceDescription(vMemorySurfaceCaps)
vMemorySurfaceDesc.Width = vPrimarySurface.SurfaceDescription.Width
vMemorySurfaceDesc.Height = vPrimarySurface.SurfaceDescription.Height
Dim vMemorySurface As New Surface(vMemorySurfaceDesc, vDev)
'将主屏内容绘制到内存表面中
vMemorySurface.DrawFast(0, 0, vPrimarySurface, DrawFastFlags.Wait)
'将表面锁定到流中
Dim vLockedData As LockedData = vMemorySurface.Lock(LockFlags.Wait)
Dim GS As New GraphicsStream(vLockedData.Data.InternalData, vLockedData.Width * vLockedData.Height * vLockedData.RGBBitCount, True, True)
Dim Img As New Bitmap(vLockedData.Width, vLockedData.Height, vLockedData.Width * 4, Imaging.PixelFormat.Format32bppRgb, GS.InternalData)
Img.Save("C:\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
vMemorySurface.Unlock()
Catch ex As Exception
MsgBox("程序出现异常,被迫退出。")
ExitProg(False)
Exit Sub
End Try
End If
End If
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
ExitProg(True)
End Sub
Private Sub ExitProg(ByVal Wait As Boolean)
'结束程序
Running = False
'等待线程结束
If Wait Then
While DrawThread.ThreadState <> ThreadState.Stopped
End While
End If
'释放内存
vOffSurface.Dispose()
vOffSurface = Nothing
vBackSurface.Dispose()
vBackSurface = Nothing
vPrimarySurface.Dispose()
vPrimarySurface = Nothing
vDev.Dispose()
vDev = Nothing
X = Nothing
Y = Nothing
SpeedX = Nothing
SpeedY = Nothing
Running = Nothing
DrawThread = Nothing
'退出程序
End
End Sub
End Class
===============华丽的分割线===============
其实根据BMP图片的构造原理,你一样也可以通过GraphicsStream的内容直接写入到文件,只是需要对颜色进行复杂的处理。截图的效果如下:
DirectX与VB.NET编程(十七)*图片结构与DirectDraw截图(下)
===============华丽的分割线===============
DirectDraw的内容已经全部结束了,我将在以后更新关于DirectPlay的内容~谢谢各位的支持。顺便说明下,我将安装Visual Studio 2005,因此以后的所有文章将会基于Visual Studio 2005。