问题
I have 5 images all the same pixel height and pixel width (2481 * 3508 for that matter). But, one is gif, one jpeg, one png and one bmp. Now I render them into a BitmapSource with (1) two thirds of the original pixel height for DecodePixelHeight and (2) original pixel height for DecodePixelHeight.
First scenario:
bitmapImage.BeginInit();
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.DecodePixelHeight = 2/3 * originalHeight;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();
BMP and Jpeg are equally slow. Png and Gif need less than half the time. Why?
Second scenario:
bitmapImage.BeginInit();
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();
Png half of the time needed before. Jpeg and BMP one 5th of the time needed before. Gif the same time as before.
According to documentation I would have assumed that Png and Jpeg performance would somehow be more independent of actual decode size than the other formats. What could be the reason, that it is not?
回答1:
I have implemented a behavior which is optimal, performance wise.. I use it for streaming live feed from Home security cameras, works like a charm with fairly large images..
Try this out and let me know what you think. Usage is pretty straightforward, Assign the dependency properties, attach the behavior to an image and be done with it. cheers.
Note: Pixels may be IList, but you can assign an array as well since the C# array implements IList.
Note 2: Do not assign the Image Source since this will override the behavior's assignment, just bind to 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);
}
}
来源:https://stackoverflow.com/questions/13054909/bitmapimage-decoding-speed-performance-wpf