2019-8-30-C#-从零开始写-SharpDx-应用-笔刷
| C# 從零開始寫 SharpDx 應用 筆刷 | lindexi | 2019-8-30 8:50:0 +0800 | 2019-6-23 20:15:4 +0800 | C# |
本文告訴大家如何在 SharpDx 里面使用筆刷,包括純色筆刷、漸變筆刷和圖片筆刷
本文屬于 SharpDx 系列 博客,建議從頭開始讀
初始化
本文將會在 C# 從零開始寫 SharpDx 應用 初始化dx修改顏色的代碼基礎進行修改
用到的初始化代碼也不多,請看下面代碼,這些代碼都可以在上一篇博客找到
private void InitializeDeviceResources(){var backBufferDesc =new ModeDescription(Width, Height, new Rational(60, 1), Format.R8G8B8A8_UNorm);var swapChainDesc = new SwapChainDescription{ModeDescription = backBufferDesc,SampleDescription = new SampleDescription(1, 0),SwapEffect = SwapEffect.Discard,Usage = Usage.RenderTargetOutput,BufferCount = 1,OutputHandle = _renderForm.Handle,IsWindowed = true};Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc,out _d3DDevice, out _swapChain);_d3DDeviceContext = _d3DDevice.ImmediateContext;using (var backBuffer = _swapChain.GetBackBuffer<Texture2D>(0)){_renderTargetView = new RenderTargetView(_d3DDevice, backBuffer);_viewport = new Viewport(0, 0, Width, Height);_d3DDeviceContext.Rasterizer.SetViewport(_viewport);}CreateD2DRender();}在 CreateD2DRender 方法里面創建 D2D 的資源,本文這里直接寫上代碼,如果想要了解代碼含義請看 C# 從零開始寫 SharpDx 應用 繪制基礎圖形
private void CreateD2DRender(){var d2dFactory = new SharpDX.Direct2D1.Factory();Texture2D backBuffer = D3D11.Resource.FromSwapChain<Texture2D>(_swapChain, 0);Surface surface = backBuffer.QueryInterface<Surface>();var d2dRenderTarget = new RenderTarget(d2dFactory, surface,new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)));var defaultDevice = _d3DDevice.QueryInterface<SharpDX.Direct3D11.Device1>();var dxgiDevice2 = defaultDevice.QueryInterface<SharpDX.DXGI.Device2>();var d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice2);_d2dFactory = d2dFactory;_d2dRenderTarget = d2dRenderTarget;_d2dContext = new DeviceContext(surface);_d2dDevice = d2dDevice;}private Factory _d2dFactory;private RenderTarget _d2dRenderTarget;private DeviceContext _d2dContext;private SharpDX.Direct2D1.Device _d2dDevice;純色筆刷
最基礎使用的是純色筆刷,在 SharpDx 里面傳入的顏色是 RawColor4 顏色,顏色的值范圍是 0-1 我寫了一個方法將 Color 轉換
RawColor4 ColorToRaw4(Color color){const float n = 255f;return new RawColor4(color.R / n, color.G / n, color.B / n, color.A / n);}創建純色筆刷需要傳入兩個值,其中一個是 RenderTarget 另一個是顏色
在上面初始化代碼創建了 _d2dRenderTarget 和 _d2dContext 這兩個都是 RenderTarget 都可以傳入,但是需要知道的傳入的值和使用的對象需要是相同的
例如傳入的是 _d2dRenderTarget 那么下面嘗試用純色筆刷畫一個矩形
_d2dRenderTarget.BeginDraw();_d2dRenderTarget.Clear(ColorToRaw4(Color.White));var brush = new SolidColorBrush(_d2dRenderTarget, ColorToRaw4(Color.Bisque));using (brush){var rect = new RawRectangleF(left: 10, top: 10, right: 500, bottom: 500);_d2dRenderTarget.FillRectangle(rect, brush);}_d2dRenderTarget.EndDraw();_swapChain.Present(1, PresentFlags.None);上面代碼寫在 C# 從零開始寫 SharpDx 應用 初始化dx修改顏色 創建的 Draw 方法
在開始繪制的時候調用 BeginDraw 方法,在繪制完成調用 EndDraw 方法,然后調用交換鏈將緩存交換
這里創建 SolidColorBrush 使用的是 _d2dRenderTarget 字段,如果使用 _d2dContext 那么請將上面代碼替換
需要注意在 SharpDx 創建的資源都需要手動釋放,創建的純色筆刷需要手動釋放
漸變筆刷
在 SharpDx 使用 LinearGradientBrush 做漸變筆刷,漸變筆刷需要用 LinearGradientBrushProperties 和 GradientStopCollection 兩個值進行初始化
在 LinearGradientBrushProperties 可以指定起點和終點,通過起點和終點連線做漸變,這里的起點和終點使用的是畫布坐標系而不是繪制的圖形的坐標系
例如我繪制的矩形在 (10,10) 作為左上角,但是指定的筆刷是在 (0,0) 那么將會在矩形之外就開始算筆刷
var linearGradientBrushProperties = new LinearGradientBrushProperties(){StartPoint = new RawVector2(10f, 10f),EndPoint = new RawVector2(100f, 100f)};在 GradientStopCollection 可以指定筆刷的漸變點集合,使用的是 GradientStop 數組表示
在 GradientStop 數組,在每個對象里面需要指定顏色和漸變點的距離,范圍是從 0 到 1 越靠近 0 的就是越靠近 LinearGradientBrushProperties 起點的顏色
例如我創建了 3 個顏色
var gradientStop0 = new GradientStop(){Color = ColorToRaw4(Color.Yellow),Position = 0f};var gradientStop1 = new GradientStop(){Color = ColorToRaw4(Color.ForestGreen),Position = 0.5f};var gradientStop2 = new GradientStop(){Color = ColorToRaw4(Color.White),Position = 1f};var gradientStops = new GradientStop[]{gradientStop0,gradientStop1,gradientStop2,};使用上面創建的對象繪制在矩形漸變
_d2dRenderTarget.BeginDraw();_d2dRenderTarget.Clear(null);var gradientStopCollection = new GradientStopCollection(_d2dRenderTarget, gradientStops);var brush = new LinearGradientBrush(_d2dRenderTarget,linearGradientBrushProperties,gradientStopCollection);using (gradientStopCollection)using (brush){var rect = new RawRectangleF(left: 10, top: 10, right: 100, bottom: 100);_d2dRenderTarget.FillRectangle(rect, brush);}_d2dRenderTarget.EndDraw();_swapChain.Present(1, PresentFlags.None);運行代碼可以看到下圖
在 _d2dRenderTarget.Clear 傳入 null 將會使用透明的默認黑色清空畫布
在上面代碼的 GradientStopCollection 就是畫出一條漸變線,在數學的線是沒有寬度的,但是讓大家能看到每個顏色我就畫了一條矩形
這就是對應的三個點,有了一條線,那么將這條線應用到線段上就做出了漸變筆刷
畫出的漸變線需要配合漸變的起點和終點才能畫出漸變效果,在使用的坐標是畫布的坐標,可以讓起點的坐標比終點的大
在 LinearGradientBrush 也有起點和終點的屬性,在這里的設置會覆蓋 LinearGradientBrushProperties 的設置
var brush = new LinearGradientBrush(_d2dRenderTarget,linearGradientBrushProperties,gradientStopCollection){StartPoint = new RawVector2(50, 50),EndPoint = new RawVector2(100, 100)};如上面的代碼就會讓 LinearGradientBrushProperties 設置的從 10,10 開始修改為從 50 開始畫漸變
圓形漸變
上面使用的是最簡單的線性漸變筆刷,下面來告訴大家使用圓形漸變的效果
在 SharpDx 使用 RadialGradientBrush 做圓形漸變效果
在 RadialGradientBrush 的創建需要傳入 RadialGradientBrushProperties 和 GradientStopCollection 對象
在線性漸變筆刷已經告訴過大家 GradientStopCollection 是做什么用的,在 GradientStopCollection 可以畫出一條漸變線,這條線沒有指定起點和終點,但是指定了顏色在對應的線的比例
在圓形漸變筆刷中 RadialGradientBrushProperties 將會傳入圓心的坐標,圓的 x 方向和 y 的大小,也就是可以畫出橢圓,另外還支持設置實際的漸變線的起點
var radialGradientBrushProperties = new RadialGradientBrushProperties(){Center = new RawVector2(50, 50),GradientOriginOffset = new RawVector2(0, 0),RadiusX = 50f,RadiusY = 50f};這里的 Center 就是圓形漸變的圓的圓心的坐標,坐標使用的是畫布坐標,而 RadiusX 和 RadiusY 分別是長度
在上面代碼比較復雜的是 GradientOriginOffset 這個變量,在 GradientOriginOffset 指定漸變線的起點距離圓心的距離,也就是這里的坐標使用的相對圓心的坐標
var gradientStop0 = new GradientStop(){Color = ColorToRaw4(Color.Yellow),Position = 0f};var gradientStop1 = new GradientStop(){Color = ColorToRaw4(Color.ForestGreen),Position = 0.5f};var gradientStop2 = new GradientStop(){Color = ColorToRaw4(Color.White),Position = 1f};var gradientStops = new GradientStop[]{gradientStop0,gradientStop1,gradientStop2,};var gradientStopCollection = new GradientStopCollection(_d2dRenderTarget, gradientStops);var radialGradientBrushProperties = new RadialGradientBrushProperties(){Center = new RawVector2(50, 50),GradientOriginOffset = new RawVector2(0, 0),RadiusX = 50f,RadiusY = 50f};var brush = new RadialGradientBrush(_d2dRenderTarget, ref radialGradientBrushProperties, gradientStopCollection);using (gradientStopCollection)using (brush){var rect = new RawRectangleF(left: 10, top: 10, right: 100, bottom: 100);_d2dRenderTarget.FillRectangle(rect, brush);}運行代碼可以看到下圖
在上面圖片的各個坐標如下
下圖是設置 GradientOriginOffset = new RawVector2(-10,-10) 的效果,通過這個屬性可以做出燈光的效果
圖片筆刷
在 SharpDx 創建圖片需要比較多的代碼,下面我創建一個函數用來加載圖片
在 SharpDx 使用 WIC 解析圖片,先創建圖片工廠
ImagingFactory imagingFactory = new ImagingFactory();然后將傳入的文件作為 NativeFileStream 加載
NativeFileStream fileStream = new NativeFileStream(filePath,NativeFileMode.Open, NativeFileAccess.Read);這里的 filePath 就是絕對路徑的圖片
創建圖片解碼器
BitmapDecoder bitmapDecoder = new BitmapDecoder(imagingFactory, fileStream, DecodeOptions.CacheOnDemand);在圖片解碼器可以拿到圖片的 Frame 一般的圖片只有一個,一般 gif 圖片可能有多個圖層序號和數組相同
BitmapFrameDecode frame = bitmapDecoder.GetFrame(0);創建轉換器
FormatConverter converter = new FormatConverter(imagingFactory);使用 Bitmap.FromWicBitmap 創建圖片
converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);// Create the new Bitmap directly from the FormatConverter.var bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_d2dRenderTarget, converter);因為使用的對象需要手動釋放,所以就需要在代碼添加很多 using 代碼,函數的代碼請看下面
private Bitmap LoadBitmapFromContentFile(string filePath){ImagingFactory imagingFactory = new ImagingFactory();NativeFileStream fileStream = new NativeFileStream(filePath,NativeFileMode.Open, NativeFileAccess.Read);BitmapDecoder bitmapDecoder = new BitmapDecoder(imagingFactory, fileStream, DecodeOptions.CacheOnDemand);using (imagingFactory)using (fileStream)using (bitmapDecoder){BitmapFrameDecode frame = bitmapDecoder.GetFrame(0);using (frame){FormatConverter converter = new FormatConverter(imagingFactory);using (converter){converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);var bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_d2dRenderTarget, converter);return bitmap;}}}}通過上面方法就可以在 SharpDx 加載圖片文件
在 SharpDx 也有 Utilities.Dispose 方法可以協助清理某個對象,這個方法的用法和 using 差不多
不過在 C# 8.0 提供了 using var 的寫法,可以在方法結束的時候釋放對象,通過這個方法會比上面使用 using 的代碼好
在創建圖片筆刷需要拿到 圖片 然后創建 BitmapBrushProperties 說明如何使用圖片
在 BitmapBrushProperties 有 ExtendModeX 和 ExtendModeY 兩個屬性,說明在圖片的大小比填充的范圍小的時候,如何進行填充,如進行水平方向的復制還是鏡像
Bitmap bitmap = LoadBitmapFromContentFile(@"D:\lindexi\1.png");var bitmapBrushProperties = new BitmapBrushProperties(){ExtendModeX = ExtendMode.Wrap,ExtendModeY = ExtendMode.Wrap,};var brushProperties = new BrushProperties(){Opacity = 1f,Transform = new RawMatrix3x2(){M11 = 1f,M22 = 1f}};var brush = new BitmapBrush(_d2dRenderTarget, bitmap, bitmapBrushProperties, brushProperties);using (bitmap)using (brush){var rect = new RawRectangleF(left: 10, top: 10, right: 500, bottom: 500);_d2dRenderTarget.FillRectangle(rect, brush);}在填充圖片筆刷的時候,圖片是從畫布的 0,0 開始填充,也就是如果圖片太小了,那么在填充的范圍是看不到填充的,例如我的圖片的寬度只有 5 那么在上面的矩形的左上角坐標是 10 就會看不到圖片
這時就需要用到 BrushProperties 這個屬性,其實在上面的筆刷也是可以添加這個屬性,在這個屬性提供了筆刷的透明度和變換的方法
使用變換方法可以移動或旋轉圖片筆刷,特別是在剛好圖片的大小就是填充的大小的時候,將圖片移動到填充的坐標就是使用變換的方法
運行上面代碼可以看到下圖
另一個圖片筆刷是 ImageBrush 用法和上面代碼差不多
這里的 ImageBrush 不是 WPF 的 ImageBrush 而是 SharpDX.Direct2D1.ImageBrush 類
因為 ImageBrush 使用的是 _d2dContext 所以需要修改 LoadBitmapFromContentFile 里面的方法
var bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_d2dContext, converter);在 SharpDx 使用的資源和創建的資源需要相同
創建 ImageBrush 需要 ImageBrushProperties 屬性 Image 屬性,這里的 Image 可以將 SharpDX.Direct2D1.Bitmap 傳入
在 SharpDx 的 Image 是基類,可以使用 Bitmap1 ImageSourceFromWic CommandList 等
在 ImageBrushProperties 也提供了在畫刷小于填充范圍時,對畫刷內容的圖片做復制還是做鏡像的方法
但是這個類需要 SourceRectangle 說明畫刷里的圖片的范圍,也就是支持對傳入的圖片只顯示里面的部分,對圖片裁剪的方法
默認傳入的就是圖片的大小
Bitmap bitmap = LoadBitmapFromContentFile(@"D:\lindexi\1.png");var imageBrushProperties = new ImageBrushProperties(){ExtendModeX = ExtendMode.Wrap,ExtendModeY = ExtendMode.Wrap,SourceRectangle = new RectangleF(0, 0, bitmap.Size.Width, bitmap.Size.Height),};上面代碼創建了 ImageBrushProperties 設置了當填充的范圍大于圖片的大小的時候,使用鏡像的方法。另外設置圖片的填充大小為原圖大小,也就是坐標點是 0 點大小就是圖片大小
創建圖片筆刷然后添加矩形請看下面代碼
var brush = new ImageBrush(_d2dContext, bitmap, imageBrushProperties);_d2dContext.BeginDraw();_d2dContext.Clear(ColorToRaw4(Color.White));using (bitmap)using (brush){var rect = new RawRectangleF(left: 10, top: 10, right: 500, bottom: 500);_d2dContext.FillRectangle(rect, brush);}_d2dContext.EndDraw();運行代碼可以看到填充圖片
圖片在使用之后需要釋放
在實際的代碼很少會在 Draw 方法不斷創建資源同時進行釋放,一般都是在創建資源方法進行創建
另外 SharpDx 提供的是很底層的封裝,通過底層的封裝是可以自己寫出一套 UI 界面的,不過逐步 SharpDx 將會過時,在 Windows 下的底層渲染是 Win2d 才比較好用
本文在加載圖片參考了下面的博客
SharpDX之Direct2D教程II——加載位圖文件和保存位圖文件 - 萬倉一黍 - 博客園
Loading and drawing bitmaps with Direct2D using SharpDX – int main
Applying Direct2D built-in effects to bitmaps with SharpDX – int main
Brushes Overview - Windows applications
總結
以上是生活随笔為你收集整理的2019-8-30-C#-从零开始写-SharpDx-应用-笔刷的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FreeMarker模板语言开发(整理版
- 下一篇: 利用photoshop创建一个3D绚丽的