Saving images (.bmp, .png, etc) in WPF/Silverlight

I’ve recently added a new feature to Live Geometry that allows users to save the current drawing as a bitmap or a .png file. Just push the save button and pick the desired image format in the Save dialog:

image

Fortunately, both WPF and Silverlight support saving full visual contents of any visual into a file on disk. However the approach is somewhat different.

Saving images in WPF

WPF can save any Visual to an image and it supports several formats out of the box via a concept of Encoders. Here’s a sample for .bmp and .png:

 void SaveToBmp(FrameworkElement visual, string fileName)
{
    var encoder = new BmpBitmapEncoder();
    SaveUsingEncoder(visual, fileName, encoder);
}

void SaveToPng(FrameworkElement visual, string fileName)
{
    var encoder = new PngBitmapEncoder();
    SaveUsingEncoder(visual, fileName, encoder);
}

void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
    RenderTargetBitmap bitmap = new RenderTargetBitmap(
        (int)visual.ActualWidth,
        (int)visual.ActualHeight,
        96,
        96,
        PixelFormats.Pbgra32);
    bitmap.Render(visual);
    BitmapFrame frame = BitmapFrame.Create(bitmap);
    encoder.Frames.Add(frame);

    using (var stream = File.Create(fileName))
    {
        encoder.Save(stream);
    }
}

These types are all in System.Windows.Media.Imaging.

Saving images in Silverlight 3

In Silverlight, the encoders don’t come as part of the Silverlight runtime – but fortunately there is a project on CodePlex called ImageTools (https://imagetools.codeplex.com) that provides necessary support. You will need to download the following binaries and add them as references to your Silverlight project:

  • ICSharpCode.SharpZipLib.Silverlight
  • ImageTools
  • ImageTools.IO
  • ImageTools.IO.Png (only if you want .png support)
  • ImageTools.IO.Bmp (only if you want .bmp support)
  • ImageTools.Utils

After that, you can call the ToImage() extension method on any Canvas:

 void SaveAsPng(Canvas canvas, SaveFileDialog dialog)
{
    SaveToImage(canvas, dialog, new PngEncoder());
}

void SaveAsBmp(Canvas canvas, SaveFileDialog dialog)
{
    SaveToImage(canvas, dialog, new BmpEncoder());
}

void SaveToImage(Canvas canvas, SaveFileDialog dialog, IImageEncoder encoder)
{
    using (var stream = dialog.OpenFile())
    {
        var image = canvas.ToImage();
        encoder.Encode(image, stream);
    }
}

Since you can’t write to disk directly in Silverlight, you can pass a SaveFileDialog and use its stream, or you can obtain a stream elsewhere and pass that. The ToImage() extension method does the dirty work that we had to do ourselves in WPF.

Big thanks to https://imagetools.codeplex.com for their awesome library and encoders!