Search code examples
wpfbitmapmemorystream

How to convert System.Windows.Media.DrawingImage into Stream?


I'am trying convert DrawingImage into MemoryStream. My code looks like this:

public MemoryStream ImageStream(DrawingImage drawingImage)
    {
        MemoryStream stream = new MemoryStream();
        ImageSource imageSource = drawingImage;
        if (imageSource != null)
        {
            BitmapSource bitmap = imageSource as BitmapSource;
            if (bitmap != null)
            {
                BitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmap));
                encoder.Save(stream);
            }     
        }

        return stream;
    }

But problem is after casting ImageSource into BitmapSource bitmap is always null. Any sugestion how to fix that?


Solution

  • The reason your bitmap variable is always null is because DrawingImage does not extend BitmapImage or vice-versa, so the cast is guaranteed to fail. A DrawingImage does not contain any pixel data of any kind. It references a Drawing that is used whenever the image needs to be rasterized.

    How did you find yourself in a situation where you want to rasterize a DrawingImage and serialize it into a stream? I get the feeling you are going about something in an unusual way if you have need of a function like this.

    Nevertheless, you could implement this function by drawing the DrawingImage to a DrawingVisual, rendering it to a RenderTargetBitmap, and then passing the render target to the encoder to serialize the raster data to a stream.

    public MemoryStream ImageStream(DrawingImage drawingImage)
    {
        DrawingVisual visual = new DrawingVisual();
        using (DrawingContext dc = visual.RenderOpen())
        {
            dc.DrawDrawing(drawingImage.Drawing);
            dc.Close();
        }
        RenderTargetBitmap target = new RenderTargetBitmap((int)visual.Drawing.Bounds.Right, (int)visual.Drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
        target.Render(visual);
    
        MemoryStream stream = new MemoryStream();
        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(target));
        encoder.Save(stream);
    
        return stream;
    }
    

    If you want something a little more generic, I would split this into two methods and change some of the types.

    public BitmapSource Rasterize(Drawing drawing)
    {
        DrawingVisual visual = new DrawingVisual();
        using (DrawingContext dc = visual.RenderOpen())
        {
            dc.DrawDrawing(drawing);
            dc.Close();
        }
        RenderTargetBitmap target = new RenderTargetBitmap((int)drawing.Bounds.Right, (int)drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
        target.Render(visual);
    
        return target;
    }
    
    public void SavePng(BitmapSource source, Stream target)
    {
        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(source));
        encoder.Save(target);
    }
    

    Then you could use it with any kind of stream. For example, to save the drawing to a file:

    using (FileStream file = File.Create("somepath.png"))
    {
        SavePng(Rasterize(drawingImage.Drawing), file);
    }