Brrrr ... it's cold out here Freezing my WPF off ...
I posted a few days ago about a technique for loading a BitmapImage in the background. Charles suggested I try loading (the image) in a background thread and then Freeze the BitmapImage; finally, handing it off to the UI thread.
If you'd like to know more about freezing and WPF, there's a decent article available on MSDN here.
Success! It works. But I discovered something sinister going on. Downright evil.
The new implementation was slower. Way slower. How could that be? I was still using a background thread. The code was simpler. I did notice that I had changed from using the MemoryStream to using the UriSource property. A little bit of experimentation later, I discovered that the StreamSource property was also slow when I passed it a FileStream object. Confused thoroughly at this point, I switched back to the MemoryStream in a last ditched attempt knowing it wouldn't help -- but it did!!!! It was fast again! Here's what I ended up with:
private void DoLoadImage(object o) { if (!_loading) { return; }byte[] buffer = File.ReadAllBytes(_filename);
// by reading the data into an in-memory buffer,
we prevent the file from being read in the UI thread -- which speeds up
// access dramatically!
MemoryStream mem = new MemoryStream(buffer);if (!_loading)
{
mem.Dispose();
return;
}BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = 80;
bi.DecodePixelHeight = 60;
//bi.UriSource = new Uri(_filename); slow slow slow
bi.StreamSource = mem;
bi.EndInit();
bi.Freeze();_imageSource = bi;
_loading = false;
RaisePropetyChanged("IsLoading");
RaisePropetyChanged("ImageSource");
// if you dispose of the memory stream here, the image will be toast (burnt toast)
// (as the dispatcher won't have run yet).
}
For some reason, the MemoryStream technique blows away the performance of any other option. It's at least 5 - 10 times faster than the other options! If anyone knows why, I'd love to hear it! As you can see though, if you compare to my original post, I've gotten rid of the Dispatcher reference and just directly create the BitmapImage. The key though is that I "Freeze" the BitmapImage so that it can be used outside of the creating thread and back in the UI thread.




