0

I am saving the rendered image using render target bitmap, and it is saved properly in the given size, but when I set background to the grid in which image is placed, I am getting different output. Can any one explain this behavior?

    <Grid x:Name="grid1" Grid.Row="0" Background="Red">

        <Image x:Name="image1" Source="Images/butterfly.jpg"  >              
        </Image>

    </Grid>

Code behind

        RenderTargetBitmap result = GetImage(this.grid1);
        Stream imageStream = new MemoryStream();
        SaveAsPng(result, imageStream);

    public static RenderTargetBitmap GetImage(Grid view)
    {
        Size size = new Size(1122, 750);
        if (size.IsEmpty)
            return null;

        RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

        DrawingVisual drawingvisual = new DrawingVisual();
        using (DrawingContext context = drawingvisual.RenderOpen())
        {
            context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
            context.Close();
        }

        result.Render(drawingvisual);
        return result;
    }

    public static void SaveAsPng(RenderTargetBitmap src, Stream outputStream)
    {
        var saveFileDialog = new SaveFileDialog()
        {
            Filter = "Image Files (*.bmp, *.png, *.jpg)|*.bmp;*.png;*.jpg"
        };
        if (saveFileDialog.ShowDialog() == true)
        {              
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(src));
            using (FileStream stream = new FileStream(saveFileDialog.FileName, FileMode.Create))
                encoder.Save(stream);
        }
    }

Without Background With Background

  • I don't know for sure why it's doing that, but it works correctly if I change the parameter type of of GetImage to Visual (`GetImage(Visual view)`), and pass `this.image1` to it instead of `this.grid1`. I *suspect* the VisualBrush ignores the parts with no background. – 15ee8f99-57ff-4f92-890c-b56153 Aug 20 '19 at 18:20
  • It appears that the VisualBrush only uses the parts of `grid1` that have visuble content. When those parts are wider than the width you're giving it, in accordance with the VisualBrush's default Stretch value (`Fill`), it condenses the image it's got laterally to fit in the space you told it to use. You could set Stretch = Stretch.Uniform on the VisualBrush, but then it would add a white border above and below to maintain the required aspect ratio without squeezing anything. – 15ee8f99-57ff-4f92-890c-b56153 Aug 20 '19 at 18:35

1 Answers1

3

In order to retain the original element dimensions in the DrawingVisual, you should set the VisualBrush's Stretch to None. If necessary, you can also get precise control of the placement of the visual by setting the VisualBrush's Viewport, Viewbox, AlignmentX and AlignmentY properties.

Also consider passing the result size as an argument to your GetImage method, and use the most general type for the view argument:

public static BitmapSource GetImage(Visual view, Size size)
{
    var bitmap = new RenderTargetBitmap(
        (int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    var visualBrush = new VisualBrush
    {
        Visual = view,
        Stretch = Stretch.None
    };

    var drawingvisual = new DrawingVisual();

    using (var context = drawingvisual.RenderOpen())
    {
        context.DrawRectangle(visualBrush, null, new Rect(size));
    }

    bitmap.Render(drawingvisual);

    return bitmap;
}

Also make the SaveAsPng method more flexible by changing the argument type. The outputStream argument isn't used at all, so remove it.

public static void SaveAsPng(BitmapSource src)

Then call both methods like this:

var result = GetImage(grid1, new Size(1122, 750));
SaveAsPng(result);
Clemens
  • 123,504
  • 12
  • 155
  • 268