如何清除WriteableBitmap

时间:2014-05-15 09:03:32

标签: c# wpf writeablebitmap

我有一个WriteableBitmap,我想反复清理和重绘。

我已经看到过直接写入Pixels数组的引用,但是在WriteableBitmap中无法访问Pixels数组。我也尝试过每次都重新创建WriteableBitmap的建议,但是我的应用程序几乎立即耗尽内存。

有没有一种简单的方法来清除WriteableBitmap?

4 个答案:

答案 0 :(得分:1)

要使用writeablebitmap.clear(),您需要WriteableBitmapEx库。 http://writeablebitmapex.codeplex.com

答案 1 :(得分:1)

我们阻止将空白字节数组复制到可写位图的后缓冲区来执行此操作。对于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.InvokeAsync(() =>
        {
            _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();

        }

    }

答案 2 :(得分:0)

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();
    });
}

答案 3 :(得分:0)

使用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