BitmapImage解码速度性能WPF [英] BitmapImage decoding speed performance wpf

查看:109
本文介绍了BitmapImage解码速度性能WPF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有5个图像,它们的像素高度和像素宽度都相同(对于此问题,像素为2481 * 3508)。但是,一个是gif,一个jpeg,一个png和一个bmp。现在,我将其渲染为BitmapSource,其中(1)DecodePixelHeight的原始像素高度的三分之二和DecodePixelHeight的(2)原始像素高度的。



第一种情况:

  bitmapImage.BeginInit(); 
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.DecodePixelHeight = 2/3 * originalHeight;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();

BMP和Jpeg同样慢。 Png和Gif所需的时间少于一半。为什么?



第二种情况:

  bitmapImage.BeginInit(); 
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();

Png是之前所需时间的一半。 Jpeg和BMP是之前所需时间的五分之一。 Gif与以前相同。



根据文档我会假设Png和Jpeg的性能将比其他格式更独立于实际解码大小。

解决方案

我实现了一种行为,该行为在性能上是最佳的。



尝试一下,让我知道您的想法。



用法非常简单,分配依赖项属性,将行为附加到图像上并使用它来完成。



注意:像素可能是IList,但是由于C#数组实现了IList,因此您也可以分配一个数组。



注2:不要分配图像源,因为这将覆盖行为的分配,只需绑定到行为的 Pixels 依赖项属性即可。

 公共类VideoBehavior:Behavior< Image> 
{

公共静态只读DependencyProperty PixelsProperty = DependencyProperty.Register(
Pixels,typeof(IList< byte>),typeof(VideoBehavior),新的PropertyMetadata(default(IList< ; byte>),OnPixelsChanged));

私有静态无效OnPixelsChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var pixels =(IList< byte>)e.NewValue;


b.RenderPixels(pixels);
}


public IList< byte>像素
{
get {return(IList< byte>)GetValue(PixelsProperty); }
set {SetValue(PixelsProperty,value); }
}

公共静态只读DependencyProperty PixelFormatProperty = DependencyProperty.Register(
PixelFormat,typeof(PixelFormat),typeof(VideoBehavior),新的PropertyMetadata(PixelFormats.Default,OnPixelFormatChanged ));


私有静态无效OnPixelFormatChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var pixelFormat =(PixelFormat)e.NewValue;

if(pixelFormat == PixelFormats.Default)
返回;

b._pixelFormat = pixelFormat;

b.InitializeBufferIfAttached();
}

public PixelFormat PixelFormat
{
get {return(PixelFormat)GetValue(PixelFormatProperty); }
set {SetValue(PixelFormatProperty,value); }
}

公共静态只读DependencyProperty PixelWidthProperty = DependencyProperty.Register(
PixelWidth,typeof(int),typeof(VideoBehavior),新的PropertyMetadata(default(int), OnPixelWidthChanged));

私有静态无效OnPixelWidthChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var value =(int)e.NewValue;

if(value< = 0)
return;

b._pixelWidth =值;

b.InitializeBufferIfAttached();
}

public int PixelWidth
{
get {return(int)GetValue(PixelWidthProperty); }
set {SetValue(PixelWidthProperty,value); }
}


公共静态只读DependencyProperty PixelHeightProperty = DependencyProperty.Register(
PixelHeight,typeof(int),typeof(VideoBehavior),新的PropertyMetadata(默认(int),OnPixelHeightChanged));

私有静态无效OnPixelHeightChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var value =(int)e.NewValue;

如果(值<== 0)
返回;


b._pixelHeight =值;

b.InitializeBufferIfAttached();
}

public int PixelHeight
{
get {return(int)GetValue(PixelHeightProperty); }
set {SetValue(PixelHeightProperty,value); }
}

公共静态只读DependencyProperty DpiXProperty = DependencyProperty.Register(
DpiX,typeof(int),typeof(VideoBehavior),新的PropertyMetadata(96,OnDpiXChanged)) ;

私有静态无效OnDpiXChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var value =(int)e.NewValue;

如果(值<== 0)
返回;


b._dpiX =值;

b.InitializeBufferIfAttached();
}

public int DpiX
{
get {return(int)GetValue(DpiXProperty); }
set {SetValue(DpiXProperty,value); }
}

公共静态只读DependencyProperty DpiYProperty = DependencyProperty.Register(
DpiY,typeof(int),typeof(VideoBehavior),新的PropertyMetadata(96,OnDpiYChanged)) ;

私有静态无效OnDpiYChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var b =(VideoBehavior)d;
var value =(int)e.NewValue;

如果(值<== 0)
返回;


b._dpiY =值;

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目标,IntPtr源,uint长度);

私有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;

私人uint _byteArraySize;
private WriteableBitmap _bitMap;附有

私人布尔;

受保护的覆盖无效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;
}

私有异步void RenderPixels(IList< byte>像素)
{
if(_backBuffer == IntPtr.Zero)
return;

if(pixels == null)
{
return;
}

等待Task.Factory.StartNew(()=>
{
var h = new GCHandle();
varlocated = false ;

try
{
h = GCHandle.Alloc(pixels,GCHandleType.Pinned);
located = true;
var ptr = h.AddrOfPinnedObject( );
CopyMemory(_backBuffer,ptr,_byteArraySize);
}
最后
{
if(allocated)
h.Free();
}
});

_bitMap.Lock();

_bitMap.AddDirtyRect(_rect);
_bitMap.Unlock();
}

private void RecomputeByteArraySize()
{
_byteArraySize =(uint)(_ pixelWidth * _pixelHeight * _bytesPerPixel);
}
}


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?

解决方案

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

这篇关于BitmapImage解码速度性能WPF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆