I implemented a behavior that is optimal in terms of performance. I use it to stream live tape from home security cameras, it works like a charm with fairly large images.
Try this and let me know what you think. Usage is quite simple, assign dependency properties, attach behavior to the image and do with it. amuses.
Note. Pixels can be an IList, but you can also assign an array, since a C # array implements IList.
Note 2: Do not assign an image source, as this will override the purpose of the behavior, just bind the Pixels dependency property of the behavior.
public class VideoBehavior : Behavior<Image> { public static readonly DependencyProperty PixelsProperty = DependencyProperty.Register( "Pixels", typeof (IList<byte>), typeof (VideoBehavior), new PropertyMetadata(default(IList<byte>),OnPixelsChanged)); private static void OnPixelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior) d; var pixels = (IList<byte>) e.NewValue; b.RenderPixels(pixels); } public IList<byte> Pixels { get { return (IList<byte>) GetValue(PixelsProperty); } set { SetValue(PixelsProperty, value); } } public static readonly DependencyProperty PixelFormatProperty = DependencyProperty.Register( "PixelFormat", typeof (PixelFormat), typeof (VideoBehavior), new PropertyMetadata(PixelFormats.Default,OnPixelFormatChanged)); private static void OnPixelFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior) d; var pixelFormat = (PixelFormat) e.NewValue; if(pixelFormat==PixelFormats.Default) return; b._pixelFormat = pixelFormat; b.InitializeBufferIfAttached(); } public PixelFormat PixelFormat { get { return (PixelFormat) GetValue(PixelFormatProperty); } set { SetValue(PixelFormatProperty, value); } } public static readonly DependencyProperty PixelWidthProperty = DependencyProperty.Register( "PixelWidth", typeof (int), typeof (VideoBehavior), new PropertyMetadata(default(int),OnPixelWidthChanged)); private static void OnPixelWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior)d; var value = (int)e.NewValue; if(value<=0) return; b._pixelWidth = value; b.InitializeBufferIfAttached(); } public int PixelWidth { get { return (int) GetValue(PixelWidthProperty); } set { SetValue(PixelWidthProperty, value); } } public static readonly DependencyProperty PixelHeightProperty = DependencyProperty.Register( "PixelHeight", typeof (int), typeof (VideoBehavior), new PropertyMetadata(default(int),OnPixelHeightChanged)); private static void OnPixelHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior)d; var value = (int)e.NewValue; if (value <= 0) return; b._pixelHeight = value; b.InitializeBufferIfAttached(); } public int PixelHeight { get { return (int) GetValue(PixelHeightProperty); } set { SetValue(PixelHeightProperty, value); } } public static readonly DependencyProperty DpiXProperty = DependencyProperty.Register( "DpiX", typeof (int), typeof (VideoBehavior), new PropertyMetadata(96,OnDpiXChanged)); private static void OnDpiXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior)d; var value = (int)e.NewValue; if (value <= 0) return; b._dpiX = value; b.InitializeBufferIfAttached(); } public int DpiX { get { return (int) GetValue(DpiXProperty); } set { SetValue(DpiXProperty, value); } } public static readonly DependencyProperty DpiYProperty = DependencyProperty.Register( "DpiY", typeof (int), typeof (VideoBehavior), new PropertyMetadata(96,OnDpiYChanged)); private static void OnDpiYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var b = (VideoBehavior)d; var value = (int)e.NewValue; if (value <= 0) return; b._dpiY = value; b.InitializeBufferIfAttached(); } public int DpiY { get { return (int) GetValue(DpiYProperty); } set { SetValue(DpiYProperty, value); } } [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")] public static extern void CopyMemory(IntPtr destination, IntPtr source, uint length); private IntPtr _backBuffer = IntPtr.Zero; private int _bytesPerPixel; private const int BitsPerByte = 8; private int _pixelWidth; private int _pixelHeight; private int _dpiX; private int _dpiY; private PixelFormat _pixelFormat; private Int32Rect _rect; private uint _byteArraySize; private WriteableBitmap _bitMap; private bool _attached; protected override void OnAttached() { _attached = true; InitializeBufferIfAttached(); } private void InitializeBufferIfAttached() { if(_attached==false) return; ReevaluateBitsPerPixel(); RecomputeByteArraySize(); ReinitializeImageSource(); } private void ReevaluateBitsPerPixel() { if(_pixelFormat==PixelFormats.Default) return; _bytesPerPixel = _pixelFormat.BitsPerPixel/BitsPerByte; } private void ReinitializeImageSource() { if(_pixelHeight<=0|| _pixelHeight<=0) return; _bitMap = new WriteableBitmap(_pixelWidth, _pixelHeight, _dpiX, _dpiY, _pixelFormat, null); _backBuffer = _bitMap.BackBuffer; _rect = new Int32Rect(0, 0, _pixelWidth, _pixelHeight); AssociatedObject.Source = _bitMap; } private async void RenderPixels(IList<byte> pixels) { if (_backBuffer == IntPtr.Zero) return; if (pixels == null) { return; } await Task.Factory.StartNew(() => { var h = new GCHandle(); var allocated = false; try { h = GCHandle.Alloc(pixels, GCHandleType.Pinned); allocated = true; var ptr = h.AddrOfPinnedObject(); CopyMemory(_backBuffer, ptr, _byteArraySize); } finally { if (allocated) h.Free(); } }); _bitMap.Lock(); _bitMap.AddDirtyRect(_rect); _bitMap.Unlock(); } private void RecomputeByteArraySize() { _byteArraySize = (uint)(_pixelWidth * _pixelHeight * _bytesPerPixel); } }