方案1.WPF异步加载BitmapImage当你在WPF主线程中时不时需要加载图片时,界面上的Animation会因为IO操作而卡壳,要想保持动画的流畅就必须将IO操作放到后台线程中操作,如下:privatevoidChangeImage(){Imageimage=newImage();newThread(newThreadStart(()={BitmapImagebitmap=newBitmapImage();bitmap.BeginInit();bitmap.UriSource=newUri(images[index++%images.Count]);bitmap.CacheOption=BitmapCacheOption.OnLoad;bitmap.EndInit();bitmap.Freeze();Dispatcher.BeginInvoke((Action)(()={image.Source=bitmap;image.Stretch=Stretch.UniformToFill;TransitionBox.Content=image;}));})).Start();}方案2.WPF中加载高分辨率图片性能优化在最近的项目中,遇到一个关于WPF中同时加载多张图片时,内存占用非常高的问题。问题背景:在一个ListView中同时加载多张图片,注意:我们需要加载的图片分辨率非常高。代码XAML:GridGrid.RowDefinitionsRowDefinitionHeight=Auto/RowDefinitionHeight=*//Grid.RowDefinitionsButtonContent=LoadWidth=100Height=35Margin=0,10Click=Button_Click/ListViewGrid.Row=1x:Name=lvImagesListView.ItemTemplateDataTemplateImageSource={BindingImageSource}MaxWidth=800//DataTemplate/ListView.ItemTemplateListView.TemplateControlTemplateGridScrollViewerVerticalScrollBarVisibility=AutoHorizontalScrollBarVisibility=HiddenItemsPresenter//ScrollViewer/Grid/ControlTemplate/ListView.TemplateListView.ItemsPanelItemsPanelTemplateStackPanelIsItemsHost=TrueVirtualizingPanel.VirtualizationMode=RecyclingVirtualizingPanel.IsVirtualizing=True//ItemsPanelTemplate/ListView.ItemsPanel/ListView/GridC#:publicpartialclassMainWindow:Window{publicMainWindow(){InitializeComponent();}privatevoidButton_Click(objectsender,RoutedEventArgse){lvImages.Items.Clear();//Imagefolderlocation:D:\Picsstring[]files=System.IO.Directory.GetFiles(@D:\Pics);ListImageSourceModelmodels=newListImageSourceModel();foreach(varpathinfiles){BitmapImageimage=newBitmapImage();image.BeginInit();image.UriSource=newSystem.Uri(path);image.EndInit();image.Freeze();models.Add(newImageSourceModel(){ImageSource=image});}lvImages.ItemsSource=models;}}publicclassImageSourceModel{publicImageSourceImageSource{get;set;}}优化方案:(1).初始加载时,只加载部分图片并显示。当ScrollViewer滚动到底部时,再加载一部分。关于这个方案,可以参考WPFMVVM模式下实现ListView下拉显示更多内容;但是这并不能解决最终内存占用过高的情况。(2).给图片设置DecodePixelWidth属性,BitmapImageimage=newBitmapImage();image.BeginInit();image.UriSource=newSystem.Uri(path);image.DecodePixelWidth=800;image.EndInit();image.Freeze();models.Add(newImageSourceModel(){ImageSource=image});方案3.wpf大图片处理速度优化:指针操作,并行操作,几十倍优化(1)标准的int30数组遍历计算release0.28sunsafepublicstaticvoidTestGray1(thisWriteableBitmapbmp){using(varcontext=bmp.GetBitmapContext()){intheight=context.Height;intwidth=context.Width;for(inty=0;yheight;y++){for(intx=0;xwidth;x++){intpos=y*context.Width+x;varc=context.Pixels[pos];varr=(byte)(c16);varg=(byte)(c8);varb=(byte)(c);vargray=((r*38+g*75+b*15)7);varcolor=(25524)|(gray16)|(gray8)|gray;context.Pixels[pos]=color;}}}}(2)标准的int32指针遍历计算release0.04sunsafepublicstaticvoidTestGray2(thisWriteableBitmapbmp){using(varcontext=bmp.GetBitmapContext()){varptr=context.Pixels;intheight=context.Height;intwidth=context.Width;for(inty=0;yheight;y++){for(intx=0;xwidth;x++){varc=*ptr;varr=(byte)(c16);varg=(byte)(c8);varb=(byte)(c);vargray=((r*38+g*75+b*15)7);varcolor=(25524)|(gray16)|(gray8)|gray;*ptr=color;ptr++;}}}}(3)colorstruct指针便利计算0.02s[StructLayout(LayoutKind.Sequential)]publicstructPixelColor{publicbyteBlue;publicbyteGreen;publicbyteRed;publicbyteAlpha;}unsafepublicstaticvoidTestGray3(thisWriteableBitmapbmp){using(varcontext=bmp.GetBitmapContext()){varptr=(PixelColor*)context.Pixels;intheight=context.Height;intwidth=context.Width;for(inty=0;yheight;y++){for(intx=0;xwidth;x++){varc=*ptr;vargray=((c.Red*38+c.Green*75+c.Blue*15)7);(*ptr).Green=(*ptr).Red=(*ptr).Blue=(byte)gray;ptr++;}}}}}(4)作为对比,我又测试了一下GDI+的指针处理图片的速度0.06spublicstaticunsafeBitmapToGray(Bitmapimg){varrect=newSystem.Drawing.Rectangle(0,0,img.Width,img.Height);vardata=img.LockBits(rect,System.Drawing.Imaging.ImageLockMode.ReadOnly,System.Drawing.Imaging.PixelFormat.Format32bppArgb);varptr=(ColorType*)data.Scan0.ToPointer();varbytes=newInt32[img.Width*img.Height];varheight=img.Height;varwidth=img.Width;for(inty=0;yheight;y++){for(intx=0;xwidth;x++){]varcolor=*ptr;vargray=((color.R*38+color.G*75+color.B*15)7);(*ptr).R=(*ptr).G=(*ptr).B=(byte)gray;ptr++;}}img.UnlockBits(data);returnimg;}(5)重头戏来了。我一直对Parallel.For很迷惑,为什么他的消耗时间是普通for的好几倍。今天仔细研究了一下,发现原来是用错了0.01srelease笔记本i5cpu,如果台式机的I7会更加强悍,速度会成半成半降低。主要是利用了微软的任务并行库的循环并行化的方法。注意:默认的并行循环对于函数体很小的情况是很慢的,这种情况必须用Partitioner创建循环体,这在MSDN有介绍,是关键之中的关键unsafepublicstaticvoidTestGray5(thisWriteableBitmapbmp){using(varcontext=bmp.GetBitmapContext()){intheight=context.Height;intwidth=context.Width;Parallel.ForEach(Partitioner.Create(0,height),(h)={varptr=(PixelColor*)context.Pixels;ptr+=h.Item1*width;for(inty=h.Item1;yh.Item2;y++){for(intx=0;xwidth;x++){varc=*ptr;vargray=((c.Red*38+c.Green*75+c.Blue*15)7);(*ptr).Green=(*ptr).Red=(*ptr).Blue=(byte)gray;ptr++;}}});}}感想:1.绝对不要在循环体内使用属性或函数,很有可能会降低数倍计算速度。因为属性本质上是个函数,而在循环体内最好不要再调用函数,如果确实需要用内联代码的方式,c#没有inline,那么copy代码吧,反正为了速度。2.用指针移位操作似乎比直接数组访问要快10倍啊,我感觉要么是cache命中的原因,要么是数组本身存取被属性封装了。相