WPF 使用 Direct2D1 畫圖 繪製基本圖形

来源:https://www.cnblogs.com/lindexi/archive/2018/04/20/8888094.html
-Advertisement-
Play Games

本文來告訴大家如何在 Direct2D1 繪製基本圖形,包括線段、矩形、橢圓 ...


本文來告訴大家如何在 Direct2D1 繪製基本圖形,包括線段、矩形、橢圓

本文是一個系列

本文的組織參考Direct2D,對大神表示感謝。

在開始前先告訴大家為何需要使用 Direct2D ,雖然 WPF 也是基於 DX 進行渲染,但是 WPF 做了很多相容處理,所以沒有比直接使用 Direct2D 的性能高。經過測試,在使用下麵的所有代碼,占用 CPU 幾乎都是 0% ,因為沒有佈局、透明和事件處理,所以速度是很快。

在 Direct2D 使用的 點是 Point2F ,傳入的是兩個 float ,和 Point 差不多。

Point2F 也是一個結構體,所以和 Point 類型差不多

線段

線段需要使用 DrawLine ,方法的簽名

    public void DrawLine(Point2F firstPoint 起始點 , Point2F secondPoint 終點, Brush brush 筆刷, float strokeWidth 線段寬度)

 public unsafe void DrawLine(Point2F firstPoint, Point2F secondPoint, Brush brush, float strokeWidth, StrokeStyle strokeStyle 線段樣式)

所以使用下麵的方法就可以在 (10,10) (100,10) 畫出一條寬度為 2 的紅線

            _renderTarget.DrawLine(new D2D.Point2F(10, 10), new D2D.Point2F(100, 10),
                _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 2);

上面的代碼運行在WPF 使用 Direct2D1 畫圖入門文章的 OnRendering 方法,為了讓大家也可以試試下麵的代碼,建議大家先去看這篇博客。

關於筆刷會在後面說

StrokeStyle

可以看到上麵線段的最後一個參數是 StrokeStyle 那麼這個參數是如何創建?在 Direct2D 有很多類都不能直接直接創建需要使用 D2DFactory 或 RenderTarget 才能創建。StrokeStyle 就需要使用 D2DFactory 進行創建。

創建 StrokeStyle 需要參數 StrokeStyleProperties,這個類的構造有兩個重載,一個是不需要參數,另一個是需要很多參數。代碼請看下麵。

public StrokeStyleProperties(CapStyle startCap, CapStyle endCap, CapStyle dashCap, LineJoin lineJoin, float miterLimit, DashStyle dashStyle, float dashOffset)

從代碼的命名大概大家也可以知道 StrokeStyleProperties 參數的意思,下麵先創建一個沒有構造函數的來創建 StrokeStyle ,請看下麵代碼

            var strokeStyleProperties = new D2D.StrokeStyleProperties();

            var strokeStyle = d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10,10),new D2D.Point2F(100,10), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)),2, strokeStyle);

            _renderTarget.EndDraw();

需要註意,創建 strokeStyle 的工廠需要和創建 RenderTarget 一樣,如果使用不一樣的工廠就會出現下麵異常。

Microsoft.WindowsAPICodePack.DirectX.Direct2D1.Direct2DException:“EndDraw has failed with error: 一起使用的對象必須創建自相同的工廠實例。 (異常來自 HRESULT:0x88990012) Tags=(0,0).”

所以需要修改WPF 使用 Direct2D1 畫圖入門文章的代碼,把 D2DFactory 寫為欄位

   public MainWindow()
        {
            InitializeComponent();

            CompositionTarget.Rendering += OnRendering;

            Loaded += (s, e) =>
            {
                var d2DFactory = D2D.D2DFactory.CreateFactory(D2D.D2DFactoryType.Multithreaded);

                var windowHandle = new WindowInteropHelper(this).Handle;
                var renderTarget = d2DFactory.CreateHwndRenderTarget(new D2D.RenderTargetProperties(),
                    new D2D.HwndRenderTargetProperties(windowHandle,
                        new D2D.SizeU((uint) ActualWidth, (uint) ActualHeight),
                        D2D.PresentOptions.RetainContents));

                _redBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1));

                _greenBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(0, 1, 0, 1));

                _blueBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(0, 0, 1, 1));

                _renderTarget = renderTarget;

                _d2DFactory = d2DFactory;
            };
        }

StrokeStyleProperties

關於 StrokeStyleProperties 需要說一下,就是各個參數。

從名字可以看到 StartCap 和 EndCap 就是線段的兩端的圖形,可以選的參數

  • Flat
  • Square
  • Round
  • Triangle

具體表示是什麼,我會使用下麵的例子

Flat

平的

            var strokeStyleProperties = new D2D.StrokeStyleProperties();

            strokeStyleProperties.StartCap = D2D.CapStyle.Flat;
            strokeStyleProperties.EndCap = D2D.CapStyle.Flat;

            var strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10,10),new D2D.Point2F(100,10), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)),2, strokeStyle);

            _renderTarget.EndDraw();

Round

圓的

       float h = 10;

            strokeStyleProperties.StartCap = D2D.CapStyle.Round;
            strokeStyleProperties.EndCap = D2D.CapStyle.Round;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

Square

方形

  strokeStyleProperties.StartCap = D2D.CapStyle.Square;
            strokeStyleProperties.EndCap = D2D.CapStyle.Square;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

Triangle

三角形


            strokeStyleProperties.StartCap = D2D.CapStyle.Triangle;
            strokeStyleProperties.EndCap = D2D.CapStyle.Triangle;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

DashStyle

如果需要畫虛線就可以使用 DashStyle ,虛線顯示就是使用 CapStyle


            strokeStyleProperties.DashStyle = D2D.DashStyle.DashDot;
            strokeStyleProperties.DashCap = D2D.CapStyle.Square;
            strokeStyleProperties.DashOffset = 2;

            h += 20;

            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h),
                _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

大家自己試一試就知道

裡面還有屬性 LineJoin 這個不是線段可以做的,是折線才可以使用,表示兩個線段如何鏈接

矩形

畫矩形使用 DrawRectangle ,參數需要傳入 RectF 需要傳入上下左右的浮點數。

            _renderTarget.DrawRectangle(new D2D.RectF(10, 10, 100, 100), brush, 10);

矩形有兩個重載

    public void DrawRectangle(RectF rect, Brush brush, float strokeWidth)
    public unsafe void DrawRectangle(RectF rect, Brush brush, float strokeWidth, StrokeStyle strokeStyle)

矩形的 StrokeStyle 和線段一樣。

橢圓

實際上畫圓和橢圓是一樣的,畫圓的函數有兩個重載

    public void DrawEllipse(Ellipse ellipse, Brush brush, float strokeWidth)
 public unsafe void DrawEllipse(Ellipse ellipse, Brush brush, float strokeWidth, StrokeStyle strokeStyle)

需要先創建 Ellipse 和筆刷。

創建 Ellipse 需要給圓心和兩個軸,下麵創建一個圓心在 (100,100) ,兩個軸都是50的橢圓。實際上就是半徑是50的圓形。

            var ellipse = new D2D.Ellipse(new D2D.Point2F(100, 100), 50, 50);

這就是繪製基本的圖形。

那麼如何填充圖形?實際上所有 Draw 都有對應的 Fill 函數,除了線段。所以填充就是調用對應的 Fill 函數。

嘗試運行程式,看看這時的 CPU ,實際上是幾乎不會動,因為所有的計算都在 GPU 計算。不過程式里的代碼包括創建圖形,實際上是在 CPU 創建,但是因為速度很快,幾乎不需要計算,所以需要的時間很短。

文字

最後就是告訴大家如何繪製文字。

繪製文字需要使用 DirectWrite ,需要先創建 DWriteFactory 然後才可以繪製文本。

繪製文本有多個方式,因為需要的很多參數都不能直接創建需要使用 DWriteFactory 創建,所以這裡需要先使用下麵代碼

            var dWriteFactory = DWriteFactory.CreateFactory();

創建文字有多個方法

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush)

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, MeasuringMode measuringMode)

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, DrawTextOptions options)

public unsafe void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, DrawTextOptions options, MeasuringMode measuringMode)


 public unsafe void DrawTextLayout(Point2F origin, TextLayout textLayout, Brush defaultForegroundBrush)

 public unsafe void DrawTextLayout(Point2F origin, TextLayout textLayout, Brush defaultForegroundBrush, DrawTextOptions options)

因為有很多個參數,需要大家自己去試試

下麵來寫出簡單文字

需要先創建 textFormat 需要告訴使用哪個字形,和字體大小

            var textFormat = dWriteFactory.CreateTextFormat("宋體", 20);

下麵就是畫出文字,文字換行可以使用\n,複雜的換行請使用文字重載方法,這裡我就不說了

            _renderTarget.BeginDraw();

            _renderTarget.DrawText("lindexi 本文所有博客放在 lindexi.oschina.io \n歡迎大家來訪問\n\n這是系列博客,告訴大家如何在 WPF 使用Direct2D1", textFormat, new D2D.RectF(10, 10, 1000, 1000), brush);

            _renderTarget.EndDraw();

需要說的是 Windows API Code Pack 1.1 已經很久沒更新,而且有錯誤,所以建議使用 SharpDX

參見:Using Direct2D with WPF - CodeProject

https://jeremiahmorrill.wordpress.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 在 "上一篇" 中回顧了Java的三大特性:封裝、繼承和多態。本篇則來介紹下集合。 集合介紹 我們在進行Java程式開發的時候,除了最常用的基礎數據類型和String對象外,也經常會用到集合相關類。 集合類存放的都是對象的引用,而非對象本身,出於表達上的便利,我們稱集合中的對象就是指集合中對象 ...
  • 章節:enote筆記語言(3) what&why(why not)&how&when&where&which:紫色,象徵著神秘而又潛蘊著強大的力量,故取紫色。 key&keyword:“2k”和以上的“5w1h”合稱為“5w1h2k分析法”。棕色,大地泥土的顏色,給人一種朴實無華而又穩重可靠的感覺, ...
  • 模塊初始 sys模塊 import sys sys.path #列印環境變數 sys.argv#列印該文件路徑 #註意:該文件名字不能跟導入模塊名字相同 os模塊 import os cmd_res = os.system("dir")#只執行system命令,不保存結果,返回一個值0代表執行成功, ...
  • 上一篇我們已經創建好了一個Xadmin的基礎項目,現在我們將在此基礎上構建一個同樣很基礎的學生信息管理系統。 一、創建模型 模型是表示我們的資料庫表或集合類,並且其中所述類的每個屬性是表或集合的欄位,在 app/models.py 中定義。 1、首先,導入models模塊 接下來創建個學生信息類,其 ...
  • 名字 全局變數使用描述性的名字,局部變數則使用簡潔的名字。根據定義,全局變數可以出現在整個程式中的任何地方,因此他們需要一個足夠長並且詳細描述的名字讓讀者想起它們的意義。給每個全局變數聲明附加一個簡短註釋也非常有幫助: 全局函數、類和結構應該也要有描述性的名字,以表明它們在程式里扮演的角色。對於局部 ...
  • 之前有大概介紹了音頻採樣相關的思路,詳情見《簡潔明瞭的插值音頻重採樣演算法例子 (附完整C代碼)》。 音頻方面的開源項目很多很多。 最知名的莫過於谷歌開源的WebRTC, 其中的音頻模塊就包含有 AGC自動增益補償(Automatic Gain Control)自動調麥克風的收音量,使與會者收到一定的 ...
  • 測試代碼1: 輸出結果(myapplication為包名): Environment.getDataDirectory():/dataEnvironment.getExternalStorageDirectory():/storage/emulated/0 Environment.getRootDi ...
  • 1、多線程有什麼用? 一個可能在很多人看來很扯淡的一個問題:我會用多線程就好了,還管它有什麼用?在我看來,這個回答更扯淡。所謂”知其然知其所以然”,”會用”只是”知其然”,”為什麼用”才是”知其所以然”,只有達到”知其然知其所以然”的程度才可以說是把一個知識點運用自如。OK,下麵說說我對這個問題的看 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...