Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make displaying images on WPF more “snappy”?

I’m writing a WPF image viewer, displaying a grid of images. I’m baffled because of the sluggish performance: Displaying even an 11 x 11 grid makes a VM unresponsive, slow and sluggish for long durations of times. Even on the powerful host the performance in non-snappy.

The program is based on the design in SO WPF: arranging collection items in a grid: An ItemsControl is bound to Items, an ObservableCollection. Each Item contains has a file-system absolute URI. The ItemsControl‘s DataTemplate contains an Image element whose Source is bound to the URI.

It seems the problem can’t be the disk (SSD), the memory (8GB VM, 24GB host), or CPU (i750). Moreover, most of the work is done by WPF, so it’s not even as if I could locate a problem in my code: My code simply loads URIs (i.e. the paths to the images, not the images) to the collection and quickly returns. Then there is a wait, and WPF displays the images.

The only problem I could think about is image processing – down scaling by WPF. But even on the host, which has a "good-enough" 5850 ATI Radeon HD card, the performance is not snappy.

So, my question is: How can I make displaying images on WPF more “snappy”?

Edit: The images are 1920x1080 22 bit HD JPEGs captured from HD m2ts video. I tried pre-scaling them (using FFmpeg) to 'ega' 640x350. There was a performance improvement but FFmpeg's scaled-down images looks much worse than WPF's.

Edit: Thanks to David Osborne the code now runs as x64. Still sluggish.

Edit What really improved the situation is what Matěj Zábský called scalling the images: reducing the resolution. For the benefit of future readers:

            fullPath = new Uri(path, UriKind.Absolute);


            BitmapImage smallerBitmapImage = new BitmapImage();
            smallerBitmapImage.BeginInit();
            smallerBitmapImage.DecodePixelWidth = (int) (theWidthOfTheGrid / theNumberOfColumns);
            smallerBitmapImage.UriSource = fullPath;
            smallerBitmapImage.EndInit();

            FormatConvertedBitmap formatConvertedBitmap = new FormatConvertedBitmap();
            formatConvertedBitmap.BeginInit();
            formatConvertedBitmap.Source = smallerBitmapImage;
            formatConvertedBitmap.DestinationFormat = PixelFormats.Gray16;
            formatConvertedBitmap.EndInit();
            formatConvertedBitmap.Freeze();

            this.ImageSource = formatConvertedBitmap;
like image 341
Avi Avatar asked Jan 22 '12 09:01

Avi


2 Answers

I have been in similar position (I had to display thumbnail versions of large images in real-time).

If you are using data binding to display the images, you can try two things:

  • Make the binding OneWay (this helped me the most).
  • Try making the binding async.

The only other thing is to prescale the images - preferably separate thread. Possibly you could even prescale images which could be potentially used in the future while the processor is idle - for example if you are developing a map-like application, you could prescale images in areas to which the user is most likely to move.

like image 135
Matěj Zábský Avatar answered Sep 29 '22 11:09

Matěj Zábský


You can try Virtualization for control, It help considerably when loading more images in listview. VirtualizingStackPanel

<ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Vertical" VirtualizingStackPanel.IsVirtualizing="True"  VirtualizingStackPanel.VirtualizationMode="Recycling"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>

If want to use Viruatalization with wrap-panel, there is some from Codeplex, Virtualalizing Wrappanel

I know it is an old thread, but if someone is still interested.

like image 43
Hardik Avatar answered Sep 29 '22 12:09

Hardik