2

I'm trying to load a very large image (about 16000x7000 pixel) into a Material, and I've tried to load it asynchronously. My first try was to create a task that loaded the BitmapImage and then use it into the material:

var bmp = await System.Threading.Tasks.Task.Run(() =>
{
    BitmapImage img = new BitmapImage(new Uri(path));
    ImageBrush brush = new ImageBrush(img);
    var material = new System.Windows.Media.Media3D.DiffuseMaterial(brush);
    material.Freeze();
    return material;
});
BackMaterial = bmp;

But what I've found is that the image it is not loaded and expanded into memory until the material is shown (it's the same if I use the ImageBrush directly).

I'm trying to avoid that because it freezes my UI, but I haven't found the right way to force the bitmap loadi and decode. If I add a WriteableBitmap the load of the picture is performed inside the Task, but then I'm doubling the amount of memory being used:

var bmp = await System.Threading.Tasks.Task.Run(() =>
{
    BitmapImage img = new BitmapImage(new Uri(path));
    WriteableBitmap wbmp = new WriteableBitmap(img);
    ImageBrush brush = new ImageBrush(wbmp);
    var material = new System.Windows.Media.Media3D.DiffuseMaterial(brush);
    material.Freeze();
    return material;
});
BackMaterial = bmp;

Is there any way to force the load without doubling it into memory. I also tried to load it with a decoder, but I'm also loading into memory twice:

var decoder = BitmapDecoder.Create(new Uri(path), BitmapCreateOptions.PreservePixelFormat,
     BitmapCacheOption.None);
var frame = decoder.Frames[0];
int stride = frame.PixelWidth * frame.Format.BitsPerPixel / 8;
byte[] lines = new byte[frame.PixelHeight * stride];
frame.CopyPixels(lines, stride, 0);
var img = BitmapImage.Create(
    frame.PixelWidth, frame.PixelHeight, frame.DpiX, frame.DpiY, frame.Format,
    frame.Palette, lines, stride);
frame = null;
lines = null;

Thanks!

svick
  • 236,525
  • 50
  • 385
  • 514
jmservera
  • 6,454
  • 2
  • 32
  • 45
  • Try only creating img in the the task. – paparazzo Oct 10 '12 at 17:59
  • I've already tried, BitmapImage is created immediately and then, after the task completion, the file is loaded and decoded. I want the load and decode to happen into the Task to allow me to show a Loading animation while decoding the large picture. – jmservera Oct 10 '12 at 18:51
  • 1
    I am not a WPF guru, but maybe [this thread](http://stackoverflow.com/questions/36748/asynchronously-loading-a-bitmapimage-in-c-sharp-using-wpf) will give you some ideas? – Victor Zakharov Oct 10 '12 at 20:08
  • Thanks, I tried but didn't work in my case. – jmservera Oct 10 '12 at 21:22

1 Answers1

5

I'm not sure about this asynchronous scenario, but you would usually set BitmapCacheOption.OnLoad to force immediate caching to memory:

var bmp = await System.Threading.Tasks.Task.Run(() => 
{ 
    BitmapImage img = new BitmapImage(); 
    img.BeginInit(); 
    img.CacheOption = BitmapCacheOption.OnLoad; 
    img.UriSource = new Uri(path); 
    img.EndInit(); 
    ImageBrush brush = new ImageBrush(img); 
    ...
}
Clemens
  • 123,504
  • 12
  • 155
  • 268