4

I want to draw large number of shapes (lines, ellipses and ...) and then save them as bitmap or png. I made the drawings and the question is: how can I convert a DrawingImage to BitmapImage in C#? the code is something like this:

DrawingGroup drawingGroup = new DrawingGroup();
using(DrawingContext context = drawingGroup.Open())
{
    //make some drawing 
}
DrawingImage drawingImage = new DrawingImage(drawingGroup)

// your suggestion? DrawingImage - > BitmapImage
Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116

2 Answers2

14

You may put the ImageDrawing into an Image control and render that into a RenderTargetBitmap, which is a BitmapSource and can therefore be serialized by a BitmapEncoder (PngBitmapEncoder in this example).

public void SaveDrawingToFile(Drawing drawing, string fileName, double scale)
{
    var drawingImage = new Image { Source = new DrawingImage(drawing) };
    var width = drawing.Bounds.Width * scale;
    var height = drawing.Bounds.Height * scale;
    drawingImage.Arrange(new Rect(0, 0, width, height));

    var bitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
    bitmap.Render(drawingImage);

    var encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmap));

    using (var stream = new FileStream(fileName, FileMode.Create))
    {
        encoder.Save(stream);
    }
}

Note that you don't actually need a BitmapImage for encoding, because BitmapSource (or any derived class like RenderTargetBitmap) will be accepted as argument to BitmapFrame.Create.


A slightly different solution would involve a DrawingVisual instead of a DrawingImage:

public void SaveDrawingToFile(Drawing drawing, string fileName, double scale)
{
    var drawingVisual = new DrawingVisual();

    using (var drawingContext = drawingVisual.RenderOpen())
    {
        drawingContext.PushTransform(new ScaleTransform(scale, scale));
        drawingContext.PushTransform(new TranslateTransform(-drawing.Bounds.X, -drawing.Bounds.Y));
        drawingContext.DrawDrawing(drawing);
    }

    var width = drawing.Bounds.Width * scale;
    var height = drawing.Bounds.Height * scale;
    var bitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
    bitmap.Render(drawingVisual);

    var encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmap));

    using (var stream = new FileStream(fileName, FileMode.Create))
    {
        encoder.Save(stream);
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • yes, the aspect ratio of the original drawing. also I need a way to control the scale. (Imagine I have lots of geometries with probably large coordinate values) – Hossein Narimani Rad Jan 21 '13 at 06:29
  • I've edited the answer. Now it uses the DrawingImage's Width and Height to create the RenderTargetBitmap. To control the the scale, just multiply `width` and `height` by an appropriate scaling factor. – Clemens Jan 21 '13 at 06:31
  • It seems the second one is better. in the first one we have to create a control and it's a little strange. but do they differ in performance? – Hossein Narimani Rad Jan 21 '13 at 07:02
  • In the second approach you create a DrawingVisual instead of a very simple control (which also is a Visual). I guess there will be no observable performance difference. – Clemens Jan 21 '13 at 07:10
2

I found it pretty easy this way:

public static BitmapSource ToBitmapSource(DrawingImage source)
{
    DrawingVisual drawingVisual = new DrawingVisual();
    DrawingContext drawingContext = drawingVisual.RenderOpen();
    drawingContext.DrawImage(source, new Rect(new Point(0, 0), new Size(source.Width, source.Height)));
    drawingContext.Close();

    RenderTargetBitmap bmp = new RenderTargetBitmap((int)source.Width, (int)source.Height, 96, 96, PixelFormats.Pbgra32);
    bmp.Render(drawingVisual);
    return bmp;
}

You may use it to get System.Drawing.Bitmap

using (MemoryStream ms = new MemoryStream())
{
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(ToBitmapSource(drawingImage)));
    encoder.Save(ms);

    using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms))
    {
        bmpOut = new System.Drawing.Bitmap(bmp);
    }
}