【发布时间】:2014-05-15 09:03:32
【问题描述】:
我有一个 WriteableBitmap 我想反复清除和重绘。
我看到了直接写入 Pixels 数组的引用,但在 WriteableBitmap 中无法访问 Pixels 数组。我也尝试过每次重新创建 WriteableBitmap 的建议,但我的应用程序几乎立即耗尽了内存。
有没有清除 WriteableBitmap 的简单方法?
【问题讨论】:
标签: c# wpf writeablebitmap
我有一个 WriteableBitmap 我想反复清除和重绘。
我看到了直接写入 Pixels 数组的引用,但在 WriteableBitmap 中无法访问 Pixels 数组。我也尝试过每次重新创建 WriteableBitmap 的建议,但我的应用程序几乎立即耗尽了内存。
有没有清除 WriteableBitmap 的简单方法?
【问题讨论】:
标签: c# wpf writeablebitmap
我们阻止将一个空白字节数组复制到可写位图的后台缓冲区来执行此操作。我们还可以为 1k x 1k 位图以每秒 30 帧以上的速度复制原始帧。
两个快速或安全的示例(并且仍然非常快)。 我更喜欢不安全的版本。
#region External
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
#endregion
private const int PixelHeight = 1024;
private const int PixelWidth = 1024;
private const int DpiHeight = 96;
private const int DpiWidth = 96;
private const int RgbBytesPerPixel = 3;
WriteableBitmap _myBitmap = new WriteableBitmap(PixelHeight, PixelWidth, DpiHeight, DpiWidth, PixelFormats.Rgb24, null);
private readonly byte[] _blankImage = new byte[PixelHeight * PixelWidth * RgbBytesPerPixel];
private unsafe void FastClear()
{
fixed (byte* b = _blankImage)
{
CopyMemory(_myBitmap.BackBuffer, (IntPtr)b, (uint)_blankImage.Length);
Application.Current.Dispatcher.Invoke(() =>
{
_myBitmap.Lock();
_myBitmap.AddDirtyRect(new Int32Rect(0, 0, _myBitmap.PixelWidth, _myBitmap.PixelHeight));
_myBitmap.Unlock();
});
}
}
private void SafeClear()
{
GCHandle pinnedArray = new GCHandle();
IntPtr pointer = IntPtr.Zero;
try
{
//n.b. If pinnedArray is used often wrap it in a class with IDisopsable and keep it around
pinnedArray = GCHandle.Alloc(_blankImage, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
CopyMemory(_myBitmap.BackBuffer, pointer, (uint)_blankImage.Length);
Application.Current.Dispatcher.InvokeAsync(() =>
{
_myBitmap.Lock();
_myBitmap.AddDirtyRect(new Int32Rect(0, 0, _myBitmap.PixelWidth, _myBitmap.PixelHeight));
_myBitmap.Unlock();
});
}
finally
{
pointer = IntPtr.Zero;
pinnedArray.Free();
}
}
【讨论】:
要使用 writeablebitmap.clear(),您需要 WriteableBitmapEx 库。 http://writeablebitmapex.codeplex.com
【讨论】:
CCondron 给出的答案最终会开始抛出 AccessViolationException。值得庆幸的是,我们可以以更简单、更快、更稳定的方式解决这个问题:
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern void RtlZeroMemory(IntPtr dst, int length);
protected void ClearWriteableBitmap(WriteableBitmap bmp)
{
RtlZeroMemory(bmp.BackBuffer, bmp.PixelWidth * bmp.PixelHeight * (bmp.Format.BitsPerPixel / 8));
bmp.Dispatcher.Invoke(() =>
{
bmp.Lock();
bmp.AddDirtyRect(new Int32Rect(0, 0, bmp.PixelWidth, bmp.PixelHeight));
bmp.Unlock();
});
}
【讨论】:
使用 net472,您可以非常快地清除 WriteableBitmap(在我的全屏测试中平均为 1 毫秒),而无需导入您自己的 Kernel32.dll(无论如何都在后台调用):
WriteableBitmap bitmap = ...;
Int32Rect rect = new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
int bytesPerPixel = bitmap.Format.BitsPerPixel / 8; // typically 4 (BGR32)
byte[] empty = new byte[rect.Width * rect.Height * bytesPerPixel]; // cache this one
int emptyStride = rect.Width * bytesPerPixel;
bitmap.WritePixels(rect, empty, emptyStride, 0);
您可以轻松地调整此代码以进行重复调用(将字节数组存储在某处以便快速检索)。棘手的是“步幅”,它是一行像素的字节大小。对于Bgr32 表示(WriteableBitmap 最喜欢的表示),这应该是4*bitmap.PixelWidth。
【讨论】: