2019-8-30-C#-从零开始写-SharpDx-应用-笔刷
| C# 從零開始寫 SharpDx 應(yīng)用 筆刷 | lindexi | 2019-8-30 8:50:0 +0800 | 2019-6-23 20:15:4 +0800 | C# |
本文告訴大家如何在 SharpDx 里面使用筆刷,包括純色筆刷、漸變筆刷和圖片筆刷
本文屬于 SharpDx 系列 博客,建議從頭開始讀
初始化
本文將會(huì)在 C# 從零開始寫 SharpDx 應(yīng)用 初始化dx修改顏色的代碼基礎(chǔ)進(jìn)行修改
用到的初始化代碼也不多,請(qǐng)看下面代碼,這些代碼都可以在上一篇博客找到
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 方法里面創(chuàng)建 D2D 的資源,本文這里直接寫上代碼,如果想要了解代碼含義請(qǐng)看 C# 從零開始寫 SharpDx 應(yīng)用 繪制基礎(chǔ)圖形
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;純色筆刷
最基礎(chǔ)使用的是純色筆刷,在 SharpDx 里面?zhèn)魅氲念伾?RawColor4 顏色,顏色的值范圍是 0-1 我寫了一個(gè)方法將 Color 轉(zhuǎn)換
RawColor4 ColorToRaw4(Color color){const float n = 255f;return new RawColor4(color.R / n, color.G / n, color.B / n, color.A / n);}創(chuàng)建純色筆刷需要傳入兩個(gè)值,其中一個(gè)是 RenderTarget 另一個(gè)是顏色
在上面初始化代碼創(chuàng)建了 _d2dRenderTarget 和 _d2dContext 這兩個(gè)都是 RenderTarget 都可以傳入,但是需要知道的傳入的值和使用的對(duì)象需要是相同的
例如傳入的是 _d2dRenderTarget 那么下面嘗試用純色筆刷畫一個(gè)矩形
_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 應(yīng)用 初始化dx修改顏色 創(chuàng)建的 Draw 方法
在開始繪制的時(shí)候調(diào)用 BeginDraw 方法,在繪制完成調(diào)用 EndDraw 方法,然后調(diào)用交換鏈將緩存交換
這里創(chuàng)建 SolidColorBrush 使用的是 _d2dRenderTarget 字段,如果使用 _d2dContext 那么請(qǐng)將上面代碼替換
需要注意在 SharpDx 創(chuàng)建的資源都需要手動(dòng)釋放,創(chuàng)建的純色筆刷需要手動(dòng)釋放
漸變筆刷
在 SharpDx 使用 LinearGradientBrush 做漸變筆刷,漸變筆刷需要用 LinearGradientBrushProperties 和 GradientStopCollection 兩個(gè)值進(jìn)行初始化
在 LinearGradientBrushProperties 可以指定起點(diǎn)和終點(diǎn),通過起點(diǎn)和終點(diǎn)連線做漸變,這里的起點(diǎn)和終點(diǎn)使用的是畫布坐標(biāo)系而不是繪制的圖形的坐標(biāo)系
例如我繪制的矩形在 (10,10) 作為左上角,但是指定的筆刷是在 (0,0) 那么將會(huì)在矩形之外就開始算筆刷
var linearGradientBrushProperties = new LinearGradientBrushProperties(){StartPoint = new RawVector2(10f, 10f),EndPoint = new RawVector2(100f, 100f)};在 GradientStopCollection 可以指定筆刷的漸變點(diǎn)集合,使用的是 GradientStop 數(shù)組表示
在 GradientStop 數(shù)組,在每個(gè)對(duì)象里面需要指定顏色和漸變點(diǎn)的距離,范圍是從 0 到 1 越靠近 0 的就是越靠近 LinearGradientBrushProperties 起點(diǎn)的顏色
例如我創(chuàng)建了 3 個(gè)顏色
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,};使用上面創(chuàng)建的對(duì)象繪制在矩形漸變
_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);運(yùn)行代碼可以看到下圖
在 _d2dRenderTarget.Clear 傳入 null 將會(huì)使用透明的默認(rèn)黑色清空畫布
在上面代碼的 GradientStopCollection 就是畫出一條漸變線,在數(shù)學(xué)的線是沒有寬度的,但是讓大家能看到每個(gè)顏色我就畫了一條矩形
這就是對(duì)應(yīng)的三個(gè)點(diǎn),有了一條線,那么將這條線應(yīng)用到線段上就做出了漸變筆刷
畫出的漸變線需要配合漸變的起點(diǎn)和終點(diǎn)才能畫出漸變效果,在使用的坐標(biāo)是畫布的坐標(biāo),可以讓起點(diǎn)的坐標(biāo)比終點(diǎn)的大
在 LinearGradientBrush 也有起點(diǎn)和終點(diǎn)的屬性,在這里的設(shè)置會(huì)覆蓋 LinearGradientBrushProperties 的設(shè)置
var brush = new LinearGradientBrush(_d2dRenderTarget,linearGradientBrushProperties,gradientStopCollection){StartPoint = new RawVector2(50, 50),EndPoint = new RawVector2(100, 100)};如上面的代碼就會(huì)讓 LinearGradientBrushProperties 設(shè)置的從 10,10 開始修改為從 50 開始畫漸變
圓形漸變
上面使用的是最簡(jiǎn)單的線性漸變筆刷,下面來告訴大家使用圓形漸變的效果
在 SharpDx 使用 RadialGradientBrush 做圓形漸變效果
在 RadialGradientBrush 的創(chuàng)建需要傳入 RadialGradientBrushProperties 和 GradientStopCollection 對(duì)象
在線性漸變筆刷已經(jīng)告訴過大家 GradientStopCollection 是做什么用的,在 GradientStopCollection 可以畫出一條漸變線,這條線沒有指定起點(diǎn)和終點(diǎn),但是指定了顏色在對(duì)應(yīng)的線的比例
在圓形漸變筆刷中 RadialGradientBrushProperties 將會(huì)傳入圓心的坐標(biāo),圓的 x 方向和 y 的大小,也就是可以畫出橢圓,另外還支持設(shè)置實(shí)際的漸變線的起點(diǎn)
var radialGradientBrushProperties = new RadialGradientBrushProperties(){Center = new RawVector2(50, 50),GradientOriginOffset = new RawVector2(0, 0),RadiusX = 50f,RadiusY = 50f};這里的 Center 就是圓形漸變的圓的圓心的坐標(biāo),坐標(biāo)使用的是畫布坐標(biāo),而 RadiusX 和 RadiusY 分別是長度
在上面代碼比較復(fù)雜的是 GradientOriginOffset 這個(gè)變量,在 GradientOriginOffset 指定漸變線的起點(diǎn)距離圓心的距離,也就是這里的坐標(biāo)使用的相對(duì)圓心的坐標(biāo)
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);}運(yùn)行代碼可以看到下圖
在上面圖片的各個(gè)坐標(biāo)如下
下圖是設(shè)置 GradientOriginOffset = new RawVector2(-10,-10) 的效果,通過這個(gè)屬性可以做出燈光的效果
圖片筆刷
在 SharpDx 創(chuàng)建圖片需要比較多的代碼,下面我創(chuàng)建一個(gè)函數(shù)用來加載圖片
在 SharpDx 使用 WIC 解析圖片,先創(chuàng)建圖片工廠
ImagingFactory imagingFactory = new ImagingFactory();然后將傳入的文件作為 NativeFileStream 加載
NativeFileStream fileStream = new NativeFileStream(filePath,NativeFileMode.Open, NativeFileAccess.Read);這里的 filePath 就是絕對(duì)路徑的圖片
創(chuàng)建圖片解碼器
BitmapDecoder bitmapDecoder = new BitmapDecoder(imagingFactory, fileStream, DecodeOptions.CacheOnDemand);在圖片解碼器可以拿到圖片的 Frame 一般的圖片只有一個(gè),一般 gif 圖片可能有多個(gè)圖層序號(hào)和數(shù)組相同
BitmapFrameDecode frame = bitmapDecoder.GetFrame(0);創(chuàng)建轉(zhuǎn)換器
FormatConverter converter = new FormatConverter(imagingFactory);使用 Bitmap.FromWicBitmap 創(chuàng)建圖片
converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);// Create the new Bitmap directly from the FormatConverter.var bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_d2dRenderTarget, converter);因?yàn)槭褂玫膶?duì)象需要手動(dòng)釋放,所以就需要在代碼添加很多 using 代碼,函數(shù)的代碼請(qǐng)看下面
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 方法可以協(xié)助清理某個(gè)對(duì)象,這個(gè)方法的用法和 using 差不多
不過在 C# 8.0 提供了 using var 的寫法,可以在方法結(jié)束的時(shí)候釋放對(duì)象,通過這個(gè)方法會(huì)比上面使用 using 的代碼好
在創(chuàng)建圖片筆刷需要拿到 圖片 然后創(chuàng)建 BitmapBrushProperties 說明如何使用圖片
在 BitmapBrushProperties 有 ExtendModeX 和 ExtendModeY 兩個(gè)屬性,說明在圖片的大小比填充的范圍小的時(shí)候,如何進(jìn)行填充,如進(jìn)行水平方向的復(fù)制還是鏡像
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);}在填充圖片筆刷的時(shí)候,圖片是從畫布的 0,0 開始填充,也就是如果圖片太小了,那么在填充的范圍是看不到填充的,例如我的圖片的寬度只有 5 那么在上面的矩形的左上角坐標(biāo)是 10 就會(huì)看不到圖片
這時(shí)就需要用到 BrushProperties 這個(gè)屬性,其實(shí)在上面的筆刷也是可以添加這個(gè)屬性,在這個(gè)屬性提供了筆刷的透明度和變換的方法
使用變換方法可以移動(dòng)或旋轉(zhuǎn)圖片筆刷,特別是在剛好圖片的大小就是填充的大小的時(shí)候,將圖片移動(dòng)到填充的坐標(biāo)就是使用變換的方法
運(yùn)行上面代碼可以看到下圖
另一個(gè)圖片筆刷是 ImageBrush 用法和上面代碼差不多
這里的 ImageBrush 不是 WPF 的 ImageBrush 而是 SharpDX.Direct2D1.ImageBrush 類
因?yàn)?ImageBrush 使用的是 _d2dContext 所以需要修改 LoadBitmapFromContentFile 里面的方法
var bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_d2dContext, converter);在 SharpDx 使用的資源和創(chuàng)建的資源需要相同
創(chuàng)建 ImageBrush 需要 ImageBrushProperties 屬性 Image 屬性,這里的 Image 可以將 SharpDX.Direct2D1.Bitmap 傳入
在 SharpDx 的 Image 是基類,可以使用 Bitmap1 ImageSourceFromWic CommandList 等
在 ImageBrushProperties 也提供了在畫刷小于填充范圍時(shí),對(duì)畫刷內(nèi)容的圖片做復(fù)制還是做鏡像的方法
但是這個(gè)類需要 SourceRectangle 說明畫刷里的圖片的范圍,也就是支持對(duì)傳入的圖片只顯示里面的部分,對(duì)圖片裁剪的方法
默認(rèn)傳入的就是圖片的大小
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),};上面代碼創(chuàng)建了 ImageBrushProperties 設(shè)置了當(dāng)填充的范圍大于圖片的大小的時(shí)候,使用鏡像的方法。另外設(shè)置圖片的填充大小為原圖大小,也就是坐標(biāo)點(diǎn)是 0 點(diǎn)大小就是圖片大小
創(chuàng)建圖片筆刷然后添加矩形請(qǐng)看下面代碼
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();運(yùn)行代碼可以看到填充圖片
圖片在使用之后需要釋放
在實(shí)際的代碼很少會(huì)在 Draw 方法不斷創(chuàng)建資源同時(shí)進(jìn)行釋放,一般都是在創(chuàng)建資源方法進(jìn)行創(chuàng)建
另外 SharpDx 提供的是很底層的封裝,通過底層的封裝是可以自己寫出一套 UI 界面的,不過逐步 SharpDx 將會(huì)過時(shí),在 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
總結(jié)
以上是生活随笔為你收集整理的2019-8-30-C#-从零开始写-SharpDx-应用-笔刷的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FreeMarker模板语言开发(整理版
- 下一篇: 利用photoshop创建一个3D绚丽的