nb1forxp
nb1forxp

Reputation: 385

How to save a non-fuzzy image of a control in WPF?

I am using a DrawingContext to draw images. I then render the result to a RenderTargetBitmap. I also render a Canvas to the same RenderTargetBitmap. Even though the pixel boundaries are crisp on screen, they become blurred and fuzzy when saved.

In the screenshot below, you can see the issue (with BitmapScalingMode = NearestNeighbor). enter image description here

Here it is with BitmapScalingMode = HighQuality. It's smoother but not crisp and clean. enter image description here

Here is the relevant section of my code. You can see that I tried setting the RenderOptions at multiple places but it seems to have no effect.

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

        RenderOptions.SetBitmapScalingMode(drawingVisual, BitmapScalingMode.NearestNeighbor);   // This forces the scaling to be on even-pixel boundaries
        RenderOptions.SetBitmapScalingMode(drawCanvas, BitmapScalingMode.NearestNeighbor);  // This forces the scaling to be on even-pixel boundaries
        RenderOptions.SetBitmapScalingMode(result, BitmapScalingMode.NearestNeighbor);  // This forces the scaling to be on even-pixel boundaries

        using (DrawingContext context = drawingVisual.RenderOpen()) {
            context.DrawRectangle(Brushes.Black, null, new Rect(new Point(), new Size(size.Width, size.Height)));

            if (layers.Count >= 1 && layers[0].LayerImage != null && layers[0].LayerImage.Source != null && gridImage.Children[1].Visibility == System.Windows.Visibility.Visible)
                context.DrawImage(layers[0].LayerImage.Source, new Rect(size)); // Draw first image.

            context.Close();
        }

        result.Render(drawingVisual);

        drawCanvas.Measure(drawCanvas.RenderSize);
        drawCanvas.Arrange(new Rect(drawCanvas.RenderSize));

        for (int i = 0; i < drawCanvas.Children.Count; i++) {
            RenderOptions.SetBitmapScalingMode(drawCanvas.Children[i], BitmapScalingMode.NearestNeighbor);  // This forces the scaling to be on even-pixel boundaries
        }

        result.Render(drawCanvas);

        BitmapEncoder encoder = new PngBitmapEncoder();
        if (result!= null) {
            encoder.Frames.Add(BitmapFrame.Create((BitmapSource)result));
            encoder.Save(fileStream);
        }

Upvotes: 4

Views: 1608

Answers (2)

Andreas
Andreas

Reputation: 4013

 public static BitmapSource CaptureScreen(this UIElement visualElement, int? desiredLongestEdge = null)
    {
        double scale = 1;
        if (desiredLongestEdge.HasValue)
        {
            if (visualElement.RenderSize.Width > visualElement.RenderSize.Height)
            {
                scale = desiredLongestEdge.Value/ visualElement.RenderSize.Width;
            }
            else
            {
                scale = desiredLongestEdge.Value / visualElement.RenderSize.Height ;
            }
        }

        var targetBitmap =
                     new RenderTargetBitmap(
                        (int) Math.Ceiling(scale * (visualElement.RenderSize.Width + 1)),
                         (int) Math.Ceiling(scale * (visualElement.RenderSize.Height + 1)),
                                                       scale * 96,
                                                       scale * 96,
                                                        PixelFormats.Pbgra32);

        visualElement.Measure(visualElement.RenderSize); //Important
        visualElement.Arrange(new Rect(visualElement.RenderSize)); //Important

        targetBitmap.Render(visualElement);

        return targetBitmap;
    }

Upvotes: -1

gmetax
gmetax

Reputation: 3973

I don't know if you have fixed that but that function works very good on my side.

    public BitmapSource SnapShotPNG(UIElement source)
    {
        double actualWidth = source.RenderSize.Width;
        double actualHeight = source.RenderSize.Height;

        RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)actualWidth, (int)actualHeight, 96, 96, PixelFormats.Pbgra32);


        DrawingVisual visual = new DrawingVisual();

        using (DrawingContext context = visual.RenderOpen())
        {
            VisualBrush sourceBrush = new VisualBrush(source);
            context.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
        }
        source.Measure(source.RenderSize); //Important
        source.Arrange(new Rect(source.RenderSize)); //Important

        renderTarget.Render(visual);

        try
        {
            return new CroppedBitmap(renderTarget, new Int32Rect(0, 0, (int)actualWidth, (int)actualHeight));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return null;
        }
    }

Upvotes: 0

Related Questions