locked
Displaying thousands of bitmaps (gif/jpg/png) RRS feed

  • Question

  • I'm tasked with writing a panel that can display up to 200 or so gif/jpg/png thumbnails on screen at once, but should be able to scroll smoothly over an entire library of potentially many thousands of images.  To attempt to limit memory usage and get decent performance, I'm using visual layer programming (DrawingContext.DrawImage(ImageSource)), I've written a virtualizing panel, and I have an image cache that uses DispatcherOperations to cache images around the current visual range in the background.  However, I'm still not getting the performance I'd like:

    1. In experimenting with small jpgs (~8K on disk), it seems that each BitmapImage I cache ups memory usage by about 120K.  That's a 15X multiplier, and really limits how much caching I can do.
    2. It's very easy to scroll past the cached range of images.  I can keep the UI responsive by presenting image placeholders in that case and filling them in using more DispatcherOperations, but it seems to take a noticable amount of time to load a block of 10-100 images for display.  So the placeholders end up on screen for a while.

    Does anyone have any suggestions for faster / less memory-hungry options?  Should I look into System.Drawing.Bitmap?  Maybe cache those instead of WPF's BitmapImage, and convert on display?  Any other low-level APIs I should check out?  Any suggestions are appreciated.

    On a somewhat unrelated note, is there a way to get an image to display a low-res preview as it decodes the data?  I've seen some apps do this, and it'd at least be better than a generic placeholder.

    Thanks in advance.
    Friday, July 14, 2006 6:04 PM

Answers

  • In BitmapImag, any property set outside of the Begin/End Init section won’t get set. This is a limitation of this API in this version. Also, in your example, using BitmapCacheOption.None will likely decrease performance as this will cause the image to be decoded each time it is rendered. Using the code below will cause the JPEGs to be natively decoded at a smaller size than they originally are. Also, it will cause the image bits to be cached at load time rather than later on.



    BitmapImage source = new BitmapImage();
    source.BeginInit();
    source.CacheOption = BitmapCacheOption.OnLoad;
    source.DecodePixelWidth = 100;
    source.DecodePixelHeight = 75;
    source.UriSource = uri;
    source.EndInit();
    Friday, July 14, 2006 8:22 PM

All replies

  • When using BitmapImage, Have you tried specifying the DecodePixelWidth and/or the DecodePixelHeight?



    BitmapImage img = new BitmapImage();

    img.BeginInit();

    img.DecodePixelWidth = 250;

    img.UriSource = uri;

    img.EndInit();



    Using System.Drawing.Bitmap is not really a good option since you’ll need to create a BitmapSource to render. The System.Drawing.Bitmap class uses GDI+, unlike the System.Windows.Media.Imaging.BitmapSource classes which use WIC. So, you lose much functionality, for example the ability to load images with third party CODECs. Also, the JPEG CODEC within WIC is approximately 1.5 to 2x faster than that in GDI+.



    If you can share snippets of your code that you’re using to load images, that would be very helpful.
    Friday, July 14, 2006 6:29 PM
  • Thanks for the reply. I was not setting the pixel width/height, but adding it didn't seem to make any difference. Here is the load code; it doesn't do anything interesting (except possibly turn off caching, but that seems imperative if I don't want WPF to cache all X thousand images):

    BitmapImage source = new BitmapImage();
    source.CacheOption = BitmapCacheOption.None;
    source.BeginInit();
    source.DecodePixelWidth = 100;
    source.DecodePixelHeight = 75;
    source.UriSource = uri;
    source.EndInit();
    Friday, July 14, 2006 7:00 PM
  • I should note that the given Uri is a "file://" Uri.  I haven't yet experimented much with caching the raw byte[] data and only creating the BitmapImage on display with a StreamSource instead of caching the BitmapImage itself.  It would certainly cut down on memory usage, but I worry about decoding speed.  In any case, it's an avenue I plan on investigating.  In the meantime, I was hoping some expert might suggest additional things to try.
    Friday, July 14, 2006 7:12 PM
  • In BitmapImag, any property set outside of the Begin/End Init section won’t get set. This is a limitation of this API in this version. Also, in your example, using BitmapCacheOption.None will likely decrease performance as this will cause the image to be decoded each time it is rendered. Using the code below will cause the JPEGs to be natively decoded at a smaller size than they originally are. Also, it will cause the image bits to be cached at load time rather than later on.



    BitmapImage source = new BitmapImage();
    source.BeginInit();
    source.CacheOption = BitmapCacheOption.OnLoad;
    source.DecodePixelWidth = 100;
    source.DecodePixelHeight = 75;
    source.UriSource = uri;
    source.EndInit();
    Friday, July 14, 2006 8:22 PM
  • I thought I had tried setting the OnLoad CacheOption within BeginInit, but apparently not.   Too many permutations to keep track of.  It did make a nice difference.  I'm still looking for more dramatic speedups, and so I'll keep experimenting.  But thanks for the help thus far!
    Friday, July 14, 2006 10:42 PM