【问题标题】:How to clear a WriteableBitmap如何清除 WriteableBitmap
【发布时间】:2014-05-15 09:03:32
【问题描述】:

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

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

有没有清除 WriteableBitmap 的简单方法?

【问题讨论】:

    标签: c# wpf writeablebitmap


    【解决方案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.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();
    
            }
    
        }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 哪个选项抛出了访问异常?在生产中,我们通常通过将空白图像固定一次并避免对固定或 GCHandle.Alloc 的循环调用来保持指针处于活动状态。顺便说一句,很好的简化。
        • IIRC 异常将开始出现在应用程序其他地方的一个不可预测的点,大概是在 GC 运行洗牌之后。
        • 我回去查看了生产代码,看起来不同之处在于使用“Invoke”而不是“InvokeAsync”,它在固定范围内。我将更新示例。
        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2014-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-11
          • 2011-12-24
          • 2011-01-16
          • 2010-11-07
          相关资源
          最近更新 更多