C# WPF Capture UI element to picture with white background - c#

I need to capture UI element in this case LiveCharts chart and save it to a PNG/JPEG
I got this code
private void Button_Click(object sender, RoutedEventArgs e) {
var filePath = "qwerty.png";
var res = CaptureScreen(charts, charts.ActualWidth, charts.ActualHeight);
using (var fileStream = new FileStream(filePath, FileMode.Create)) {
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(res));
encoder.Save(fileStream);
}
}
private BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY) {
if (target == null) {
return null;
}
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
(int)(bounds.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen()) {
VisualBrush vb = new VisualBrush(target);
ctx.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}
I can get a png no problem. Though it is the picture created is 30k x 9k pixels. The picture has a transparent background. what would be the best practice to capture a UI element and export as a picture with a white backgroun?

Related

How to save bmp (image) to file

I want to Video File thumbnail. Get Video path and convert to Image. Then convert to bmp, and save the bmp as an image file. If this is possible, please show me a way.
private void add_Video_Image(string sFullname_Path_of_Video)
{
//*create mediaplayer in memory and jump to position
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += new EventHandler(mediaplayer_OpenMedia);
mediaPlayer.ScrubbingEnabled = true;
mediaPlayer.Open(new Uri(sFullname_Path_of_Video));
mediaPlayer.Position = TimeSpan.FromSeconds(0);
}
private void mediaplayer_OpenMedia(object sender, EventArgs e)
{
MediaPlayer mediaPlayer = sender as MediaPlayer;
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawVideo(mediaPlayer, new Rect(0, 0, 160, 100));
drawingContext.Close();
double dpiX = 1 / 200;
double dpiY = 1 / 200;
RenderTargetBitmap bmp = new RenderTargetBitmap(160, 100, dpiX, dpiY, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
Image newImage = new Image();
newImage.Source = bmp;
newImage.Stretch = Stretch.Uniform;
newImage.Height = 100;
//save bmp to image
}
Please check this link. It holds the answer to your question: Easiest way of saving wpf Image control to a file
You can do the save prior to setting the 'RenderTargetBitmap' to the 'Image Control'.

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);
}
}

Convert DrawingImage to System.Drawing.Image [duplicate]

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
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);
}
}
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);
}
}

Saving image from inkCanvas as png or jpeg file

Here is my wpf code
<InkCanvas x:Name="inkCanvas" Margin="9,325,210,193" Background="Azure"></InkCanvas>
And also there is a button
When pressing the button, i want to save image drawn to a file. here is my code
private void button1_Click(object sender, RoutedEventArgs e)
{
int margin = (int)inkCanvas.Margin.Left;
int width = (int)inkCanvas.ActualWidth - margin;
int height = (int)inkCanvas.ActualHeight - margin;
RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
rtb.Render(inkCanvas);
using (FileStream fs = new FileStream("path", FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fs);
}
}
But image dusplayed is all black (whatching from an explorer) or complitely white, if opened in paint.
What do i do to get an image exactely as drawn? ty.
The problem is that you are trying to save Vector Graphics as a Bitmap and thats not possible, so first what you need to do is draw the Vectors and then you can save the Drawing
this class will draw Ink on to a existing bitmap
public class InkImage
{
public static BitmapFrame MergeInk(StrokeCollection ink, BitmapSource background)
{
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(background, new Rect(0, 0, background.Width, background.Height));
foreach (var item in ink)
{
item.Draw(drawingContext);
}
drawingContext.Close();
var bitmap = new RenderTargetBitmap((int)background.Width, (int)background.Height, background.DpiX, background.DpiY, PixelFormats.Pbgra32);
bitmap.Render(drawingVisual);
return BitmapFrame.Create(bitmap);
}
}
}
you can then save the bitmap using the JPEG or PNG encoder

RenderTargetBitmap problem

I am trying to add the image of a usercontrol to viewbox.
I am creating the usercontrol dynamically. I am using the code below.
private static RenderTargetBitmap CaptureScreen(Visual target, double dpiX, double dpiY)
{
if (target == null)
{
return null;
}
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
//RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
// (int)(bounds.Height * dpiY / 96.0),
// dpiX,
// dpiY,
// PixelFormats.Pbgra32);
RenderTargetBitmap rtb = new RenderTargetBitmap(596,596,dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(target);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}
I am creating the user control dynamically and passing this to capture screen method.
UserControls.UserControl1 uc1 = new UserControls.UserControl1();
visualList.Add(uc1);
for(int i = 0;i<=6;i++)
{
Image img = new Image();
img.Source = CaptureScreen(visualList[i], 96, 96);
img.Margin = new Thickness { Top = 2 };
usingWorkaround.Children.Add(img);
}
the VisualTreeHelper.GetDescendantBounds(target) is returning empty bounds. Thats why the image of the screen can not be created. Is there any other method to capture screen of dynamically created user control?
you can call Measure and Arrange As follows
private void ForceUpdate(FrameworkElement element, double width, double height)
{
Size size = new Size(width, height);
element.Measure(size);
element.Arrange(new Rect(0, 0, width, height));
element.UpdateLayout();
}
At the time when you try to create the image the controls don't yet exist in the visual tree and the size has not been calculated.
You will need to call Measure and Arrange on your visual first.

Categories

Resources