Why is a PNG file from a Grid completely black? - c#

I'm trying to save a PNG image of a WPF Grid to file. The Grid is shown fine on the MainWindow, but the created file is completely black (though its dimensions are fine.)
Here is the code:
public MainWindow()
{
InitializeComponent();
int length = 30;
//Create Grid:
Grid myGrid = new Grid
{
Width = length,
Height = length,
Background = Brushes.Aqua,
};
//Save to file:
RenderTargetBitmap bmp = new RenderTargetBitmap(length, length, 96, 96, PixelFormats.Pbgra32);
bmp.Render(myGrid);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
FileStream fileStream = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\test.png", FileMode.Create);
encoder.Save(fileStream);
//Show on Window: (shows fine.)
gridOnMainWindow.Children.Add(myGrid);
}

When using a tool like RenderTargetBitmap, you must involve yourself in the Measure/Arrange cycle of the WPF layout process. See MSDN > WPF > Advanced > Layout > Measuring and Arranging Children.
Changing your example to:
int length = 30;
//Create Grid:
Grid myGrid = new Grid
{
Width = length,
Height = length,
Background = Brushes.Aqua,
};
myGrid.Measure(new Size(length, length));
myGrid.Arrange(new Rect(0, 0, length, length));
//Save to file:
RenderTargetBitmap bmp = new RenderTargetBitmap(length, length, 96, 96, PixelFormats.Pbgra32);
bmp.Render(myGrid);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (var fileStream = File.OpenWrite("test.png"))
{
encoder.Save(fileStream);
}
Results in the expected image:

Related

Low quality picture produced by snapshot. WPF

The program below creates a snapshot with the content of the main window of the application itself. However the quality of the produced picture is not equivalent to the print screen program of windows 10, which produces the desired result.
Here is a snapshot of the program running, taken with the print screen program of windows 10, zoomed in:
https://ibb.co/wz4pb4d
And here is the snapshot that the program below is producing, zoomed in:
https://ibb.co/DLsNb8X
Is there something we can try to improve the quality of the snapshot that this program produse?
I tried Bitmap Encoder but is the same result , just without transparency, (Don't need to have transparency) also tried some other Pixel Formats but I get errors, only Pbgra32 seems to work as the program is.
if (e.Key == Key.P)
{
//Set scrollviewer's Content property as UI element to capture full content
UIElement element = mainwindow.Content as UIElement;
Uri path = new Uri(#"C:\Users\4gry\Desktop\screenshot.png");
CaptureScreen(element, path);
}
}
public void CaptureScreen(UIElement source, Uri destination)
{
try
{
Double Height, renderHeight, Width, renderWidth;
Height = renderHeight = source.RenderSize.Height;
Width = renderWidth = source.RenderSize.Width;
//Specification for target bitmap like width/height pixel etc.
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
//creates Visual Brush of UIElement
VisualBrush visualBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
//draws image of element
drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(0, 0), new Point(Width, Height)));
}
//renders image
renderTarget.Render(drawingVisual);
//PNG encoder for creating PNG file
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new FileStream(destination.LocalPath, FileMode.Create, FileAccess.Write))
{
encoder.Save(stream);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
}
Not sure if this is exactly what you are trying to achieve, but you can avoid anti-aliasing effects by setting the RenderOptions.EdgeMode property of the source element to EdgeMode.Aliased.
Note also that you can also write your CaptureScreen method somewhat simpler:
public void CaptureScreen(UIElement source, string destination)
{
RenderOptions.SetEdgeMode(source, EdgeMode.Aliased); // here
var drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(
new VisualBrush(source),
null,
new Rect(source.RenderSize));
}
var bitmap = new RenderTargetBitmap(
(int)Math.Round(source.RenderSize.Width),
(int)Math.Round(source.RenderSize.Height),
96, 96, PixelFormats.Default);
bitmap.Render(drawingVisual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new FileStream(destination, FileMode.Create))
{
encoder.Save(stream);
}
}
In order to create an image with a higher resolution, use a DPI parameter instead of the default 96 like this:
public void CaptureScreen(UIElement source, double dpi, string destination)
{
RenderOptions.SetEdgeMode(source, EdgeMode.Aliased);
var drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(
new VisualBrush(source),
null,
new Rect(source.RenderSize));
}
var bitmap = new RenderTargetBitmap(
(int)Math.Round(source.RenderSize.Width * dpi / 96),
(int)Math.Round(source.RenderSize.Height * dpi / 96),
dpi, dpi, PixelFormats.Default);
bitmap.Render(drawingVisual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new FileStream(destination, FileMode.Create))
{
encoder.Save(stream);
}
}

Re positioning canvas control

I have Grid with three columns and layout is like
radio button | Canvas Control | radio button
For some feature of my application I need to take a screenshot of current view of canvas feature and save into a file.
I am using following method to save canvas to bmp,where I measure and arrange canvas.My problem is after image is saved ,canvas position in original grid is shifted to left,how can I re position my canvas back in grid as it was earlier.
private void SaveCanvasContentToImage(string path, Canvas canvasControl)
{
if (path == null && canvasControl != null) return;
// Save current canvas transform
Transform transform = canvasControl.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
canvasControl.LayoutTransform = null;
// Get the size of canvas
Size size = new Size(canvasControl.Width, canvasControl.Height);
// Measure and arrange the canvas
canvasControl.Measure(size);
canvasControl.Arrange(new Rect(size));
// Create a render bitmap and push the canvas to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(canvasControl);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(path, FileMode.CreateNew,FileAccess.ReadWrite))
{
// Use bitmap encoder for our data
BitmapEncoder encoder = new BmpBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
outStream.Close();
outStream.Dispose();
}
// Restore previously saved layout
canvasControl.LayoutTransform = transform;
}
You may avoid the layout problem by using an intermediate DrawingVisual:
var renderTargetBitmap = new RenderTargetBitmap(
(int)canvasControl.ActualWidth, (int)canvasControl.ActualHeight,
96, 96, PixelFormats.Default);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(
new VisualBrush(canvasControl),
null,
new Rect(0, 0, canvasControl.ActualWidth, canvasControl.ActualHeight));
}
renderTargetBitmap.Render(visual);

Take a screenshot of a WPF window with the predefined image size without losing quality

When I take a screenshot of a current WPF window, the image resolution is that of my monitor (if the app is maximized), which is ok. However, if I was to print that image to a much bigger format, the image would look blurry. I found the way to capture the current window, and save it as a png file, but it's not doing the trick. The image is saved with the resolution I set, but the actual wpf window takes only a small portion of the saved image. Example is taken from:
http://blogs.msdn.com/b/saveenr/archive/2008/09/18/wpf-xaml-saving-a-window-or-canvas-as-a-png-bitmap.aspx
var screen = System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
var rtb = new RenderTargetBitmap(4000, 4000, 96, 96, PixelFormats.Pbgra32);
rtb.Render(screen);
var enc = new System.Windows.Media.Imaging.PngBitmapEncoder();
enc.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(rtb));
using (var stm = System.IO.File.Create("ScreenShot.png"))
{
enc.Save(stm);
using (Image img = Image.FromStream(stm))
{
Rectangle dest = new Rectangle(0, 0, 6000, 4000);
using (Graphics imgG = Graphics.FromImage(img))
{
imgG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
imgG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
imgG.DrawImage(img, dest);
}
img.Save("NewScreenShot.png");
}
}
So basically, I'd like to capture the screenshot with the resolution of 4000 x 4000, if that's possible, without losing quality.
The above code produces an image of 4000 x 4000, however the screenshot only takes a small portion of it, its original resolution.
To scale your image, you can use a DrawingVisual and a ScaleTransform :
var w = 4000;
var h = 4000;
var screen = System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
var visual = new DrawingVisual();
using (var context = visual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(screen),
null,
new Rect(new Point(), new Size(screen.Width, screen.Height)));
}
visual.Transform = new ScaleTransform(w / screen.ActualWidth, h / screen.ActualHeight);
var rtb = new RenderTargetBitmap(w, h, 96, 96, PixelFormats.Pbgra32);
rtb.Render(visual);
var enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(rtb));
using (var stm = File.Create("ScreenShot.png"))
{
enc.Save(stm);
}

re-sizing image in c# doesn't work

I am trying to resize image to width=100 height=100
public ActionResult RegisterUser(userAuthModel user, HttpPostedFileBase userimage)
{
if (userimage.ContentLength > 0 && userimage != null)
{
string fname = userimage.FileName;
var path = Server.MapPath("~/Images/User_DP/" + userimage.FileName);
var image_100 = new System.Drawing.Bitmap(userimage, new Size(100, 100));
userimage.SaveAs(path);
}
.
.
.
.
.
}
Here I am using bitmap approach to resize imgae to some specific width and height.
But this shows me error in line-
var image_100 = new System.Drawing.Bitmap(userimage, new Size(100, 100));
of invalid arguments. How Do Ii resize image with using bitmap?
The System.Drawing.Bitmap class asks for two parameters in the constructor your are using:
System.Drawing.Image
System.Drawing.Size
In your code, you are passing the size correctly, but not the image. HttpPostedFileBase does not extend the Image class.
You will need to change that part of your code. If you are using the HttpPostedFileBase as a stream, then keep in mind that System.Drawing.Bitmap has no constructor asking for a Stream and a Size.
References:
System.Drawing.Image
System.Web.HttpPostedFileBase
you have to use mentioned method it may help you
private static BitmapFrame CreateResizedImage(ImageSource source, int width, int height, int margin)
{
var rect = new Rect(margin, margin, width - margin * 2, height - margin * 2);
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
group.Children.Add(new ImageDrawing(source, rect));
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawDrawing(group);
var resizedImage = new RenderTargetBitmap(
width, height, // Resized dimensions
96, 96, // Default DPI values
PixelFormats.Default); // Default pixel format
resizedImage.Render(drawingVisual);
return BitmapFrame.Create(resizedImage);
}

Rendering a UserControl in code rather than XAML

I want to use RenderTargetBitmap to render a UserControl to a bitmap without having to write the XAML for it. When I do this I get a blank image, am I missing a crucial step?
ValTool.Controls.VideoFisheyeOverlayControl vfoc = new Controls.VideoFisheyeOverlayControl();
vfoc.Width = (int)this.VideoContainer.ActualWidth;
vfoc.Height = (int)this.VideoContainer.ActualHeight;
vfoc.FieldsOfView=this.FieldsOfView;
vfoc.CountLines = this.CountLines;
vfoc.UpdateLayout();
vfoc.InvalidateVisual();
RenderTargetBitmap visual = new RenderTargetBitmap((int)this.VideoContainer.ActualWidth, (int)this.VideoContainer.ActualHeight, 96, 96, PixelFormats.Pbgra32);
visual.Render(vfoc);
var finalImage = BitmapFrame.Create(visual);
// Encoding the RenderBitmapTarget as a PNG file.
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(finalImage));
using (Stream stm = File.Create(#"new.png"))
{
png.Save(stm);
}
Instead of UpdateLayout you have to call Measure and Arrange to get the layout done:
var width = VideoContainer.ActualWidth;
var height = VideoContainer.ActualHeight;
vfoc.Measure(new Size(width, height));
vfoc.Arrange(new Rect(0, 0, width, height));
vfoc.InvalidateVisual();

Categories

Resources