詳解WPF如何使用WriteableBitmap提升Image性能
WriteableBitmap 背景
WriteableBitmap 繼承至 System.Windows.Media.Imaging.BitmapSource
“巨硬” 官方介紹: WriteableBitmap 類
WriteableBitmap使用 類可按幀更新和呈現(xiàn)位圖。 這對(duì)于生成算法內(nèi)容(如分形圖像)和數(shù)據(jù)可視化(如音樂可視化工具)非常有用。
類 WriteableBitmap 使用兩個(gè)緩沖區(qū)。 后臺(tái)緩沖區(qū) 在系統(tǒng)內(nèi)存中分配,并累積當(dāng)前未顯示的內(nèi)容。 前端緩沖區(qū) 在系統(tǒng)內(nèi)存中分配,并包含當(dāng)前顯示的內(nèi)容。 呈現(xiàn)系統(tǒng)將前緩沖區(qū)復(fù)制到視頻內(nèi)存中以供顯示。
兩個(gè)線程使用這些緩沖區(qū)。 用戶界面 (UI) 線程生成 UI,但不會(huì)將其呈現(xiàn)在屏幕上。 UI 線程響應(yīng)用戶輸入、計(jì)時(shí)器和其他事件。 一個(gè)應(yīng)用程序可以有多個(gè) UI 線程。 呈現(xiàn)線程編寫和呈現(xiàn)來自 UI 線程的更改。 每個(gè)應(yīng)用程序只有一個(gè)呈現(xiàn)線程。
UI 線程將內(nèi)容寫入后臺(tái)緩沖區(qū)。 呈現(xiàn)線程從前緩沖區(qū)讀取內(nèi)容并將其復(fù)制到視頻內(nèi)存。 使用更改的矩形區(qū)域跟蹤對(duì)后臺(tái)緩沖區(qū)所做的更改。
調(diào)用其中 WritePixels 一個(gè)重載以自動(dòng)更新和顯示后臺(tái)緩沖區(qū)中的內(nèi)容。
為了更好地控制更新,并且要對(duì)后臺(tái)緩沖區(qū)進(jìn)行多線程訪問,請(qǐng)使用以下工作流:
1.Lock 調(diào)用 方法以保留更新的后臺(tái)緩沖區(qū)。
2.通過訪問 屬性獲取指向后臺(tái)緩沖區(qū)的 BackBuffer 指針。
3.將更改寫入后臺(tái)緩沖區(qū)。 鎖定時(shí) WriteableBitmap ,其他線程可能會(huì)將更改寫入后臺(tái)緩沖區(qū)。
4.AddDirtyRect 調(diào)用 方法以指示已更改的區(qū)域。
5.Unlock 調(diào)用 方法以釋放后臺(tái)緩沖區(qū)并允許在屏幕上演示。
6.將更新發(fā)送到呈現(xiàn)線程時(shí),呈現(xiàn)線程會(huì)將更改后的矩形從后緩沖區(qū)復(fù)制到前緩沖區(qū)。 呈現(xiàn)系統(tǒng)控制此交換以避免死鎖和重繪項(xiàng)目。
WriteableBitmap 渲染原理
在調(diào)用 WriteableBitmap 的 AddDirtyRect 方法的時(shí)候,實(shí)際上是調(diào)用 MILSwDoubleBufferedBitmap.AddDirtyRect,這是 WPF 專門為 WriteableBitmap 而提供的非托管代碼的雙緩沖位圖的實(shí)現(xiàn)。
在 WriteableBitmap 內(nèi)部數(shù)組修改完畢之后,需要調(diào)用 Unlock 來解鎖內(nèi)部緩沖區(qū)的訪問,這時(shí)會(huì)提交所有的修改。
WriteableBitmap 使用技巧
1.WriteableBitmap 的性能瓶頸源于對(duì)臟區(qū)的重新渲染。
臟區(qū)為 0 或者不在可視化樹渲染,則不消耗性能。
只要有臟區(qū),渲染過程就會(huì)開始成為性能瓶頸。
- CPU 占用基礎(chǔ)值就很高了。
- 臟區(qū)越大,CPU 占用越高,但增幅不大。
2.內(nèi)存拷貝不是 WriteableBitmap 的性能瓶頸。
建議使用 Windows API 或者 .NET API 來拷貝內(nèi)存數(shù)據(jù)。
特殊的應(yīng)用場(chǎng)景,可以適當(dāng)調(diào)整下自己寫代碼的策略:
- 如果你希望有較大臟區(qū)的情況下降低 CPU 占用,可以考慮降低 WriteableBitmap 臟區(qū)的刷新率。
- 如果你希望 WriteableBitmap 有較低的渲染延遲,則考慮減小臟區(qū)。
案例
測(cè)試 Demo 使用 OpenCvSharp 將視頻幀讀取出來,將視頻幀圖像數(shù)據(jù)通過 WriteableBitmap 渲染到界面的 Image 控件。
核心源碼
核心代碼,利用雙緩存區(qū)更新位圖圖像信息
private void ShowImage()
{
Bitmap.Lock();
bitmap = frame.ToBitmap();
bitmapData = bitmap.LockBits(new Rectangle(new System.Drawing.Point(0, 0), bitmap.Size),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Bitmap.WritePixels(rect, bitmapData.Scan0, bitmapData.Height * bitmapData.Stride, bitmapData.Stride, 0, 0);
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
Bitmap.Unlock();
}
完整的 ViewModel 代碼
public class MainWindowViewModel : Prism.Mvvm.BindableBase
{
#region 屬性、變量、命令
private WriteableBitmap _bitmap;
/// <summary>
/// UI綁定的資源對(duì)象
/// </summary>
public WriteableBitmap Bitmap
{
get => _bitmap;
set => SetProperty(ref _bitmap, value);
}
/// <summary>
/// OpenCvSharp 視頻捕獲對(duì)象
/// </summary>
private static VideoCapture videoCapture;
/// <summary>
/// 視頻幀
/// </summary>
private static Mat frame = new Mat();
private static BitmapData bitmapData = new BitmapData();
private static Bitmap bitmap;
Int32Rect rect;
static int width = 0, height = 0;
/// <summary>
/// 打開文件
/// </summary>
public DelegateCommand OpenFileCommand { get; set; }
public DelegateCommand MNCommand { get; set; }
#endregion
public MainWindowViewModel()
{
videoCapture = new VideoCapture();
OpenFileCommand = new DelegateCommand(OpenFile);
MNCommand = new DelegateCommand(MN);
}
#region 私有方法
private void OpenFile()
{
OpenFileDialog open = new OpenFileDialog()
{
Multiselect = false,
Title = "請(qǐng)選擇文件",
Filter = "視頻文件(*.mp4, *.wmv, *.mkv, *.flv)|*.mp4;*.wmv;*.mkv;*.flv|所有文件(*.*)|*.*"
};
if (open.ShowDialog() is true)
{
ShowMove(open.FileName);
}
}
/// <summary>
/// 獲取視頻
/// </summary>
/// <param name="fileName">文件路徑</param>
private void ShowMove(string fileName)
{
videoCapture.Open(fileName, VideoCaptureAPIs.ANY);
if (videoCapture.IsOpened())
{
var timer = (int)Math.Round(1000 / videoCapture.Fps) - 8;
width = videoCapture.FrameWidth;
height = videoCapture.FrameHeight;
Bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
rect = new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight);
while (true)
{
videoCapture.Read(frame);
if (!frame.Empty())
{
ShowImage();
Cv2.WaitKey(timer);
}
}
}
}
private void ShowImage()
{
Bitmap.Lock();
bitmap = frame.ToBitmap();
bitmapData = bitmap.LockBits(new Rectangle(new System.Drawing.Point(0, 0), bitmap.Size),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Bitmap.WritePixels(rect, bitmapData.Scan0, bitmapData.Height * bitmapData.Stride, bitmapData.Stride, 0, 0);
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
Bitmap.Unlock();
}
}
測(cè)試結(jié)果
測(cè)試結(jié)果,經(jīng)供參考,更精準(zhǔn)的性能測(cè)試請(qǐng)使用專業(yè)工具。
- VS Debug模式下的性能監(jiān)測(cè),以及Windows任務(wù)管理器中的資源占用,可以看出各項(xiàng)資源的使用是比較穩(wěn)定的。
- 發(fā)布之后獨(dú)立運(yùn)行資源的占用應(yīng)該會(huì)有5%的降低。

以上就是詳解WPF如何使用WriteableBitmap提升Image性能的詳細(xì)內(nèi)容,更多關(guān)于WPF WriteableBitmap的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
unity實(shí)現(xiàn)延遲回調(diào)工具
這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)延遲回調(diào)工具,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
C#實(shí)現(xiàn)網(wǎng)絡(luò)小程序的步驟詳解
經(jīng)常要檢測(cè)某些IP地址范圍段的計(jì)算機(jī)是否在線。有很多的方法,比如進(jìn)入到網(wǎng)關(guān)的交換機(jī)上去查詢、使用現(xiàn)成的工具或者編寫一個(gè)簡(jiǎn)單的DOS腳本等等,這些都比較容易實(shí)現(xiàn)。本文將用C#來實(shí)現(xiàn),感興趣的可以了解一下2022-12-12
C#向PPT文檔插入圖片以及導(dǎo)出圖片的實(shí)例
PowerPoint演示文稿是我們?nèi)粘9ぷ髦谐S玫霓k公軟件之一,本篇文章介紹了C#向PPT文檔插入圖片以及導(dǎo)出圖片的實(shí)例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-12-12
C# WinForm實(shí)現(xiàn)自動(dòng)更新程序的方法詳解
這一篇就著重寫一下客戶端的代碼,客戶端主要實(shí)現(xiàn)的有:?jiǎn)?dòng)后檢測(cè)本地的xml文件,然后發(fā)送到服務(wù)器獲取需要更新的文件以及版本列表,感興趣的小伙伴可以了解一下2022-10-10
Unity實(shí)現(xiàn)仿3D輪轉(zhuǎn)圖效果
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)仿3D輪轉(zhuǎn)圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
講解C#設(shè)計(jì)模式編程中享元模式的運(yùn)用
這篇文章主要介紹了C#設(shè)計(jì)模式編程中享元模式的運(yùn)用,享元模式主張限制對(duì)象的數(shù)量來優(yōu)化內(nèi)存使用,需要的朋友可以參考下2016-02-02
C#實(shí)現(xiàn)標(biāo)題閃爍效果的示例代碼
在Windows系統(tǒng)中,當(dāng)程序在后臺(tái)運(yùn)行時(shí),如果某個(gè)窗體的提示信息需要用戶瀏覽,該窗體就會(huì)不停地閃爍,這樣就會(huì)吸引用戶的注意,下面我們就來看看如何使用C#實(shí)現(xiàn)這一效果吧2024-04-04

