目前博客園中成系列的Direct2D的教程有
1、萬一的 Direct2D 系列,用的是Delphi 2009
2、zdd的 Direct2D 系列,用的是VS中的C++
3、本文所在的 Direct2D教程 系列,用的是VS2010的Visual Basic語言(可以很方便的轉為C#),基於Windows API Code Pack 1.1。
還有官方的說明文檔 Direct2D ,用的是C++。
本系列的前幾篇文章:
Direct2D教程II——繪制基本圖形和線型(StrokeStyle)的設置詳解
Direct2D中的繪圖函數
在Direct2D中,RenderTarget對象的繪圖函數是DrawBitmap和DrawBitmapAtOrigin。這兩個函數的原型定義如下:
Public Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap)
Public Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As Single, interpolationMode As Direct2D1. BitmapInterpolationMode)
Public Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As Single, interpolationMode As Direct2D1. BitmapInterpolationMode, destinationRectangle As Direct2D1. RectF)
Public Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As Single, interpolationMode As Direct2D1. BitmapInterpolationMode, destinationRectangle As Direct2D1. RectF, sourceRectangle As Direct2D1. RectF)
Public Enum BitmapInterpolationMode
Linear = 1
NearestNeighbor = 0
End Enum
Public Sub DrawBitmapAtOrigin(bitmap As Direct2D1. D2DBitmap, opacity As Single, interpolationMode As Direct2D1. BitmapInterpolationMode, sourceRectangle As Direct2D1. RectF)
從函數的原型定義可以看出,DrawBitmap函數有4個重載函數,我們以最后一個函數說明各個參數的意義。
參數bitmap:要繪制的位圖對象。參數類型是D2DBitmap類。
參數opacity:不透明度。范圍是0-1,0表示全透明,1表示不透明
參數interpolationMode:縮放時的插值算法。參數類型是BitmapInterpolationMode枚舉,分為Linear和NearestNeighbor
參數destinationRectangle:在RenderTarget上繪制圖像的范圍。參數類型是RectF類型。如果不指定的話,默認繪圖在原點(左上角),大小是源位圖的大小。
參數sourceRectangle:源位圖需要繪制的范圍。參數類型是RectF類型。不指定的話,指整個源位圖。這個參數可以繪制源位圖的一部分。
函數DrawBitmapAtOrigin指的是在原點位置繪制位圖,參數意義和上面的一樣,這里不再解釋了(不過由於不牽涉到縮放操作,似乎參數interpolationMode沒什么意義)
從上面的原型定義可以看出,在Direct2D中,有關位圖的類是D2DBitmap類。有關位圖的操作都和這個有關(繪制位圖和位圖筆刷(BitmapBrush))。但是,D2DBitmap類沒有提供位圖和文件轉換的函數(從文件讀取位圖和把位圖保存到文件中)。
在Direct2D中,有關位圖文件的解碼和編碼(讀取位圖文件和保存位圖文件)是WIC組件(和Direct2D命名空間平級的WindowsImagingComponent命名空間)
有關WIC(WindowsImagingComponent)組件的介紹如下:
Windows Imaging Component (WIC) 是 Windows Vista 中用於進行數字成像的擴展平台,它可以作為 .NET Framework 3.0 的部件或能夠重新發布的獨立組件,也用於 Windows XP 和 Windows Server 2003 中。WIC 在應用程序和 CODEC 之間提供一個抽象層,使應用程序不必專門認知特定的圖像格式。無論哪種圖像格式,只要機器上裝有用於該圖像格式的支持 WIC 的 CODEC,任何使用 Windows Imaging Component 的應用程序就能夠通過一組一致的接口訪問、顯示、處理、保存和打印圖像。
從上可以看出,WIC(WindowsImagingComponent)組件提供了Direct2D和文件系統的交流平台(Direct2D不必關心位圖文件的編碼和解碼,WIC能自動的把各種格式的位圖文件轉換為Direct2D認可的位圖格式)
利用WIC在Direct2D中繪制位圖文件
由前面的內容可知,Direct2D中只能繪制D2DBitmap對象。
因此,利用WIC在Direct2D中繪制位圖文件的核心內容就是把位圖文件轉換為D2DBitmap對象。它的操作過程如下:
1、創建WIC的ImagingFactory類。這個和Direct2D中的D2DFactory類類似。總管類,負責WIC的相關操作。很多的WIC中的類都得依靠它的相關函數才能創建
2、利用ImagingFactory類的CreateDecoderFromFileName函數,根據位圖文件創建BitmapDecoder對象(實際上調用系統解碼器解析位圖文件)。
BitmapDecoder對象有1個屬性和1個函數
FrameCount屬性:只讀屬性,說明該位圖對象包含的幀數。一般gif文件能包含多個幀,其余格式的一般只有1個幀
GetFrame函數:返回指定幀對象。參數index是整形,說明是第幾幀(從0開始)。返回的是BitmapFrameDecode對象
3、利用BitmapDecoder對象的GetFrame函數,返回指定幀的BitmapFrameDecode對象。(參數index一般是0,返回第1幀)
BitmapFrameDecode對象只有1個函數。ToBitmapSource函數。將該對象轉換為BitmapSource對象。
一般情況下,到此就可以了。但是,位圖格式有很多,你可能不是很確定你的位圖格式是否兼容Direct2D的D2DBitmap對象。因此,比較好的做法是繼續下面的步驟,將位圖格式轉換為兼容Direct2D的D2DBitmap對象
4、利用ImagingFactory類的CreateFormatConverter函數創建FormatConverter對象。該對象負責進行格式轉換。
5、調用FormatConverter對象的Initialize方法,進行格式轉換。該對象還有一個ToBitmapSource函數。將轉換好的位圖對象轉換為BitmapSource對象。
6、最后,利用RenderTarget對象的CreateBitmapFromWicBitmap函數將之前的BitmapSource對象轉換為Direct2D的D2DBitmap對象
將Direct2D中的繪圖內容保存到位圖文件
前面提到如何在Direct2D中繪制位圖文件。那么,反過來,將Direct2D中的繪圖內容保存到位圖文件也應該是可以的。
具體的操作過程如下:
1、利用RenderTarget對象的CreateBitmap函數創建D2DBitmap對象
2、利用D2DBitmap對象的CopyFromRenderTarget方法將RenderTarget上的內容復制到D2DBitmap對象中
3、利用WIC中的ImagingFactory類的CreateImagingBitmap函數創建ImagingBitmap對象
4、利用D2DFactory對象的CreateWicBitmapRenderTarget的函數創建基於ImagingBitmap對象的RenderTarget對象。我們命名為wicRenderTarget,以示區別。
5、調用wicRenderTarget對象的BeginDraw方法,准備繪圖
6、調用wicRenderTarget對象的DrawBitmap方法,將步驟2中的D2DBitmap對象繪制到wicRenderTarget,實際上是繪制到對應的ImagingBitmap對象。
7、調用wicRenderTarget對象的EndDraw方法,結束繪圖(也可以在步驟6中,利用繪圖函數添加自己的水印)
8、根據要保存文件的后綴名,獲得相對應的Guid對象
9、調用ImagingBitmap對象的SaveToFile方法,將ImagingBitmap中的內容保存到文件中。一共有三個參數:參數imagingFactory,類型是ImagingFactory;參數containerFormat,類型是Guid對象,指明文件的編碼方式;參數fileName,要保存的文件名。
需要注意的是,上面的過程中,實際上有兩個RenderTarget對象,一個是Direct2D中的RenderTarget和臨時創建的wicRenderTarget對象。再利用wicRenderTarget對象的DrawBitmap方法把RenderTarget對象創建的D2DBitmap對象繪制到wicRenderTarget上。這里牽涉到資源共享的問題,在兩個(或多個)RenderTarget對象之間共享D2DBitmap對象,則這兩個RenderTarget對象創建時的RenderTargetProperties屬性中的RenderTargetType的值必須相同(要么是Software,要么是Hardware)。但由於WIC只能是基於Software。因此,如果要用上面的步驟保存到位圖文件,則必須把RenderTarget對象的RenderTargetType的值設為Software。但也因此降低了Direct2D的效率。
RenderTargetType的值有Software(用軟件方式呈現繪圖)、Hardware(用硬件方式呈現繪圖)、Default(由系統判斷采用軟件還是硬件方式呈現繪圖)
下面是讀取位圖文件和保存位圖文件的示例代碼:
Imports Microsoft.WindowsAPICodePack.DirectX
Imports WIC = Microsoft.WindowsAPICodePack.DirectX.WindowsImagingComponent
Public Class clsDirect2DSample10
Protected _d2DFactory As Direct2D1. D2DFactory
Protected _renderTarget As Direct2D1. RenderTarget
Protected _renderProps As Direct2D1. RenderTargetProperties
Protected _imagingFactory As WIC. ImagingFactory
Public Sub New()
_d2DFactory = Direct2D1. D2DFactory.CreateFactory()
_imagingFactory = WIC. ImagingFactory.Create
End Sub
Public Sub CreateDeviceResource(Target As Control)
If _renderTarget Is Nothing Then
_renderProps = New Direct2D1. RenderTargetProperties( _
Direct2D1. RenderTargetType.Software, _
New Direct2D1. PixelFormat( _
Graphics. Format.B8G8R8A8UNorm, _
Direct2D1. AlphaMode.Ignore), _
0, 0, Direct2D1. RenderTargetUsages.None, Direct3D. FeatureLevel.Default)
_renderTarget = _d2DFactory.CreateHwndRenderTarget( _
_renderProps, _
New Direct2D1. HwndRenderTargetProperties( _
Target.Handle, _
New Direct2D1. SizeU(Target.Width, Target.Height), _
Direct2D1. PresentOptions.None) _
)
End If
End Sub
Public Function LoadBitmapFromFile(fileName As String, Optional frameIndex As Integer = 0) As Direct2D1. D2DBitmap
Dim decoder As WIC. BitmapDecoder = _imagingFactory.CreateDecoderFromFileName( _
fileName, _
WIC. DesiredAccess.Read, _
WIC. DecodeMetadataCacheOption.OnLoad)
If frameIndex > decoder.FrameCount - 1 OrElse frameIndex < 0 Then frameIndex = 0
Dim source As WIC. BitmapFrameDecode = decoder.GetFrame(frameIndex)
Dim converter As WIC. FormatConverter = _imagingFactory.CreateFormatConverter()
converter.Initialize(source.ToBitmapSource(), WIC. PixelFormats.Pbgra32Bpp, WIC. BitmapDitherType.None, WIC. BitmapPaletteType.MedianCut)
Return _renderTarget.CreateBitmapFromWicBitmap(converter.ToBitmapSource())
End Function
Public Sub Render()
If Not _renderTarget Is Nothing Then
With _renderTarget
.BeginDraw()
Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
.DrawBitmap(B, 1, Direct2D1. BitmapInterpolationMode.Linear, New Direct2D1. RectF(150, 150, 280, 280), New Direct2D1. RectF(0, 0, 260, 260))
.DrawBitmapAtOrigin(B, 1, Direct2D1. BitmapInterpolationMode.Linear, New Direct2D1. RectF(10, 10, 130, 130))
.EndDraw()
End With
SaveToFile( "217.png")
End If
End Sub
Public Sub SaveToFile(FileName As String)
Dim size As Direct2D1. SizeU = _renderTarget.PixelSize
Dim d2dBitmap As Direct2D1. D2DBitmap = _renderTarget.CreateBitmap(size, _
New Direct2D1. BitmapProperties( _
New Direct2D1. PixelFormat( _
Graphics. Format.B8G8R8A8UNorm, _
Direct2D1. AlphaMode.Ignore), _
_renderTarget.Dpi.X, _renderTarget.Dpi.Y))
d2dBitmap.CopyFromRenderTarget(_renderTarget)
Dim wicBitmap As WIC. ImagingBitmap = _imagingFactory.CreateImagingBitmap( _
size.Width, size.Height, _
WIC. PixelFormats.Bgr32Bpp, _
WIC. BitmapCreateCacheOption.CacheOnLoad)
Dim wicRenderTarget As Direct2D1. RenderTarget = _d2DFactory.CreateWicBitmapRenderTarget(wicBitmap, _renderProps)
wicRenderTarget.BeginDraw()
wicRenderTarget.DrawBitmap(d2dBitmap)
wicRenderTarget.EndDraw()
Dim fileType As Guid
Select Case FileName.Substring(FileName.LastIndexOf( ".") + 1).ToUpper
Case "PNG"
fileType = WIC. ContainerFormats.Png
Case "JPEG", "JPG"
fileType = WIC. ContainerFormats.Jpeg
Case "GIF"
fileType = WIC. ContainerFormats.Gif
Case "TIFF", "TIF"
fileType = WIC. ContainerFormats.Tiff
Case "WMP"
fileType = WIC. ContainerFormats.Wmp
Case Else
fileType = WIC. ContainerFormats.Bmp
End Select
wicBitmap.SaveToFile(_imagingFactory, fileType, FileName)
End Sub
End Class
上面的例子中,添加了一個LoadBitmapFromFile函數,負責把位圖文件轉換為D2DBitmap對象。在LoadBitmapFromFile函數中,一些枚舉參數的設置都是采用默認值,最好不要改成其他值。還添加了一個SaveToFile方法,負責把RenderTarget對象上的內容保存到指定文件.
在Render方法中,先用LoadBitmapFromFile函數加載216.png,然后通過DrawBitmap和DrawBitmapAtOrigin方法繪制圖片。並調用SaveToFile方法把RenderTarget上的內容保存到217.png文件中。
先看看216.png圖片
來看看示例代碼的效果圖,來了解DrawBitmap和DrawBitmapAtOrigin方法的區別
位圖筆刷(BitmapBrush)
在前文介紹了純色筆刷(SolidColorBrush)、線性漸變筆刷(LinearGradientBrush)、徑向漸變筆刷(RadialGradientBrush)
這里再介紹最后一種筆刷——位圖筆刷(BitmapBrush)
先看看位圖筆刷(BitmapBrush)對應的RenderTarget對象的CreateBitmapBrush函數的原型定義
Public Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap) As Direct2D1. BitmapBrush
Public Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, brushProperties As Direct2D1. BrushProperties) As Direct2D1. BitmapBrush
Public Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, bitmapBrushProperties As Direct2D1. BitmapBrushProperties) As Direct2D1. BitmapBrush
Public Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, bitmapBrushProperties As Direct2D1. BitmapBrushProperties, brushProperties As Direct2D1. BrushProperties) As Direct2D1. BitmapBrush
Direct2D1. BitmapBrushProperties(extendModeX As Direct2D1. ExtendMode, extendModeY As Direct2D1. ExtendMode, interpolationMode As Direct2D1. BitmapInterpolationMode)
從上面的函數的原型定義來看,類型BitmapBrushProperties有三個參數,extendModeX指明位圖筆刷(BitmapBrush)在水平擴展區域的擴展模式;extendModeY指明位圖筆刷(BitmapBrush)在垂直擴展區域的擴展方式,擴展方式還是有3中,分別是Clamp(延伸:按照筆刷邊界點的顏色延伸)、Wrap(換行:按筆刷的方向重新設置顏色)、Mirror(鏡像:按筆刷的反方向重新設置顏色)。枚舉BitmapInterpolationMode指明位圖的插值算法。並且這三個參數在位圖筆刷(BitmapBrush)對象中也有對應的屬性,分別是ExtendModeX、ExtendModeY、InterpolationMode
我們先看看一個位圖筆刷(BitmapBrush)的示例代碼
Public Class clsDirect2DSample11
Inherits clsDirect2DSample
Protected _imagingFactory As WIC. ImagingFactory
Public Sub New()
MyBase.New()
_imagingFactory = WIC. ImagingFactory.Create
End Sub
Public Function LoadBitmapFromFile(fileName As String, Optional frameIndex As Integer = 0) As Direct2D1. D2DBitmap
Dim decoder As WIC. BitmapDecoder = _imagingFactory.CreateDecoderFromFileName(fileName, WIC. DesiredAccess.Read, WIC. DecodeMetadataCacheOption.OnLoad)
If frameIndex > decoder.FrameCount - 1 OrElse frameIndex < 0 Then frameIndex = 0
Dim source As WIC. BitmapFrameDecode = decoder.GetFrame(frameIndex)
Dim converter As WIC. FormatConverter = _imagingFactory.CreateFormatConverter()
converter.Initialize(source.ToBitmapSource(), WIC. PixelFormats.Pbgra32Bpp, WIC. BitmapDitherType.None, WIC. BitmapPaletteType.MedianCut)
Return _renderTarget.CreateBitmapFromWicBitmap(converter.ToBitmapSource())
End Function
Public Shadows Sub Render()
If Not _renderTarget Is Nothing Then
With _renderTarget
.BeginDraw()
.Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))
Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)
Dim R As New Direct2D1. RectF(130, 130, 390, 390)
Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))
.DrawRectangle(R, SB, 3)
.FillRectangle(R, BB)
.EndDraw()
End With
End If
End Sub
End Class
上面的代碼中,首先定義了一個位圖筆刷(BitmapBrush),然后用該位圖筆刷填充一個矩形。下面看看效果
效果有點出乎意料,設置因為,216.png這個位圖大小為260*260px,在設置位圖筆刷(BitmapBrush)時,默認筆刷的起始位置是畫布的原點(左上角)。而由於矩形的范圍是(130,130,390,390),因此位圖筆刷只有一部分畫在矩形的范圍里(位圖的右下角的部分),而由於默認的擴展模式是Clamp(延伸:按照筆刷邊界點的顏色延伸),故在向右和向下延伸了邊界的顏色。
如何更改位圖筆刷的起始位置?通過位圖筆刷(BitmapBrush)的Transform屬性來更改起始位置。如下面的示例代碼所示(有關Transform的詳細內容留待后文再詳解)
Public Class clsDirect2DSample12
Inherits clsDirect2DSample11
Public Shadows Sub Render()
If Not _renderTarget Is Nothing Then
With _renderTarget
.BeginDraw()
.Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))
Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)
BB.Transform = Direct2D1. Matrix3x2F.Translation(130, 130)
Dim R As New Direct2D1. RectF(130, 130, 390, 390)
Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))
.DrawRectangle(R, SB, 3)
.FillRectangle(R, BB)
.EndDraw()
End With
End If
End Sub
End Class
下面是示例代碼的效果圖,看看效果,了解如何更改位圖筆刷(BitmapBrush)的起始位置
下面再看看另一個示例的代碼,把前面的內容總結復習一下
Public Class clsDirect2DSample13
Inherits clsDirect2DSample11
Public Shadows Sub Render()
If Not _renderTarget Is Nothing Then
With _renderTarget
.BeginDraw()
.Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))
Dim PG As Direct2D1. PathGeometry
Dim sink As Direct2D1. GeometrySink
PG = _d2DFactory.CreatePathGeometry
sink = PG.Open
sink.BeginFigure( New Direct2D1. Point2F(30, 30), Direct2D1. FigureBegin.Filled)
sink.AddLine( New Direct2D1. Point2F(240, 30))
sink.AddArc( New Direct2D1. ArcSegment( _
New Direct2D1. Point2F(240, 240), _
New Direct2D1. SizeF(60, 100), _
45, _
Direct2D1. SweepDirection.Clockwise,
Direct2D1. ArcSize.Large))
sink.AddBezier( New Direct2D1. BezierSegment( _
New Direct2D1. Point2F(170, 120), _
New Direct2D1. Point2F(100, 360), _
New Direct2D1. Point2F(30, 240)))
sink.EndFigure(Direct2D1. FigureEnd.Closed)
sink.Close()
Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)
Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))
BB.ExtendModeX = Direct2D1. ExtendMode.Mirror
BB.ExtendModeY = Direct2D1. ExtendMode.Mirror
BB.Transform = Direct2D1. Matrix3x2F.Scale(0.4, 0.4)
.DrawGeometry(PG, SB, 3)
.FillGeometry(PG, BB)
.EndDraw()
End With
End If
End Sub
End Class
上面的示例代碼中,把位圖筆刷(BitmapBrush)的水平擴展模式和垂直擴展模式都改成Mirror(鏡像),並且把位圖筆刷的大小改為原來的0.4倍。下面看看效果
最后,介紹一個非常好用的工具:ILSpy。文章中的很多原型定義都是直接從該工具中復制而來,省了不少的功夫。