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);
}
}
Related
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?
I want to save my label Control, but I have no idea to save it.
I tried draw text in Graphics class, but I can't draw "even allocation".
Is there any good way?
public Label makeLabel(string text, double width, double height, FontFamily fontFamily, FontStyle fontStyle, FontStretch fontStretch)
{
var label = new Label();
label.Width = width;
label.Height = height;
label.FontFamily = fontFamily;
label.FontStyle = fontStyle;
label.FontStretch = fontStretch;
label.Content = text;
return label;
}
public void SavePicture(Label label)
{
var path = "label.png";
// I have no idea to save;
}
public void SavePicture(Label label)
{
var path = "label.png";
var width = label.Width;
var height = label.Height;
var viewBox = new Viewbox();
var renderTargetBitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
var pngBitmapEncoder = new PngBitmapEncoder();
viewBox.Child = label;
viewBox.Measure(new Size(width, height));
viewBox.Arrange(new Rect(0, 0, width, height));
viewBox.UpdateLayout();
renderTargetBitmap.Render(viewBox);
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (var fileStream = File.Create(path))
{
pngBitmapEncoder.Save(fileStream);
}
}
This works without having label displayed on screen.
void SavePicture(FrameworkElement el)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)el.ActualWidth, (int)el.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(el);
using (FileStream stream = File.Create(#"label.png"))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream);
}
}
But you can call this method only after the Label is displayed.
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);
}
}
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
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();