Convert DrawingImage to BitmapImage - c#

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

C# WPF - Creating Png-Image File of Usercontrol results in black image [duplicate]

I want to create a Snapshot of the Canvas Area in my Application. I'm using Visual brush to get the Snapshot and saving the same using PngEncoder. But the resulting PNG is just a empty black image. I'm not sure the issue is with the BitmapSource created or the PNGEncoder issue. Here is the code I'm using to obtain the same.
public void ConvertToBitmapSource(UIElement element)
{
var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
var brush = new VisualBrush(element);
var visual = new DrawingVisual();
var drawingContext = visual.RenderOpen();
drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
new Point(element.RenderSize.Width, element.RenderSize.Height)));
drawingContext.Close();
target.Render(visual);
PngBitmapEncoder encoder = new PngBitmapEncoder();
BitmapFrame outputFrame = BitmapFrame.Create(target);
encoder.Frames.Add(outputFrame);
using (FileStream file = File.OpenWrite("TestImage.png"))
{
encoder.Save(file);
}
}
Not sure why exactly your code isn't working. This works:
public void WriteToPng(UIElement element, string filename)
{
var rect = new Rect(element.RenderSize);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(element), null, rect);
}
var bitmap = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
bitmap.Render(visual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var file = File.OpenWrite(filename))
{
encoder.Save(file);
}
}
Thank you both for the question and the answer.
For the benefit of the others looking for the same answer
I found that Clemens way leaves a black band in the image with the image shifted either down or right. As if it was not rendering the element at the correct position in the bitmap.
So I had to use the VisualBrush as Amar suggested.
Here is the code that worked for me:
RenderTargetBitmap RenderVisual(UIElement elt)
{
PresentationSource source = PresentationSource.FromVisual(elt);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)elt.RenderSize.Width,
(int)elt.RenderSize.Height, 96, 96, PixelFormats.Default);
VisualBrush sourceBrush = new VisualBrush(elt);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0),
new Point(elt.RenderSize.Width, elt.RenderSize.Height)));
}
rtb.Render(drawingVisual);
return rtb;
}

Convert D3DImage to byte array

Is there any way to do that ?
I can cast the object to ImageSource and assign to Image but I have to be able to store it in byte[]. All the methods I found use casting to BitMap and that won't work here.
Here is a solution I found. The key was the use of DrawingVisual to create temporary image internally.
public static byte[] ImageToBytes(ImageSource imageSource)
{
var bitmapSource = imageSource as BitmapSource;
if (bitmapSource == null)
{
var width = (int)imageSource.Width;
var height = (int)imageSource.Height;
var dv = new DrawingVisual();
using (var dc = dv.RenderOpen())
{
dc.DrawImage(imageSource, new Rect(0, 0, width, height));
}
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(dv);
bitmapSource = BitmapFrame.Create(rtb);
}
byte[] data;
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}

PngBitmapEncoder creating an "empty" file

I have a DVC Chart displaying data. I have created a button to export the graph.
Here is the code:
private void btnExptGraph_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)mcChart.ActualWidth, (int)mcChart.ActualHeight,96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(mcChart);
Console.WriteLine(renderBitmap.ToString());
//JpegBitmapEncoder encode = new JpegBitmapEncoder();
PngBitmapEncoder encode = new PngBitmapEncoder();
encode.Frames.Add(BitmapFrame.Create(renderBitmap));
string filename = "test.bmp";
FileStream fout = new FileStream(filename, FileMode.Create);
encode.Save(fout);
MessageBox.Show("File Saved Successfully");
fout.Close();
}
So my issue is that this executes properly with the exception that my file is basically empty. I only have a file of 1KB in size and no graph.
I've looked at the MSDN documentation and other stack overflow examples. All of them follow this form and people claim that it works. I've run with a debugger and the renderBitmap object is getting the correct height and weight values in all necessary properties. Any ideas?
My friend took a look at it and this was the solution that we came up with and does work:
Size size = new Size(mcChart.ActualWidth, mcChart.ActualHeight);
if (size.IsEmpty)
return;
size.Height *= 2;
size.Width *= 2;
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(mcChart), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
string filename = "test.png";
FileStream fout = new FileStream(filename, FileMode.Create);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(result));
encoder.Save(fout);
fout.Close();
MessageBox.Show("Done");

How can close the image file using FileStream

I need to make image from canvas and from internet I find this code who works perfect. Is make the image and is good. But now I need to add some text in this new image and i think this is the problem. Sometimes is working, sometimes is throw me this exception. How can I close the image and change the image again. Or how can I change the image without is have exception.
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
}
This is what Exception I have:
The process cannot access the file 'image.png' because it is being used by another process.
I try with file.Close(), I try without file.Close() filename is path to image file who create and then used and try delete
Here and my code This code is running every 1 second.
private void CreateSaveBitmap(Canvas canvas, string filename, string sensor)
{
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height,
96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
file.Close();
}
string head = SetHeadForPrintImage(sensor);
Bitmap bmp = new Bitmap(filename);
Graphics g = Graphics.FromImage(bmp);
g.DrawString(head, new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Regular), System.Drawing.Brushes.Black, new PointF(200, 0));
bmp.SetResolution(300, 300);
File.Delete(filename); // throw me exception
File.Delete(#".\Print\" + sensor + ".jpg");
bmp.Save(#".\Print\" + sensor + ".jpg");
}
private void CreateSaveBitmap(Canvas canvas, string filename, string sensor)
{
if(File.Exists(#".\Print\" + sensor + ".jpg"))
{
File.Delete(#".\Print\" + sensor + ".jpg");
}
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height,
96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
file.Close();
}
string head = SetHeadForPrintImage(sensor);
Bitmap bmp = new Bitmap(filename);
Graphics g = Graphics.FromImage(bmp);
g.DrawString(head, new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Regular), System.Drawing.Brushes.Black, new PointF(200, 0));
bmp.SetResolution(300, 300);
bmp.Save(#".\Print\" + sensor + ".jpg");
bmp.Dispose();
File.Delete(filename);
}
Bitmap objects often hold a reference to the file that they are read from , and the file may not be deleted while the bitmap is open (this differs depending on which file format the image is; it may be safe deleting the origninal file of a png but not jpg etc).
You should always call Dispose() on objects that are IDisposable.
The best thing would be if you could put the bmp object in a using statement

How do I convert DrawingVisual to a Bitmap?

Using Visual C# 2010, I'm trying to write an .avi file from frames received from a Windows Kinect. The frames can be saved easily enough as .png files with the use of a BitmapEncoder and PngBitmapEncoder (saving to a stream) but I can't add these images at my discretion to a VideoStream provided here:
http://www.codeproject.com/Articles/7388/A-Simple-C-Wrapper-for-the-AviFile-Library
because I need to be able to convert either a RenderTargetBitmap or a DrawingVisual to a System.Drawing.Bitmap.
I've found example codes that do similar things but they all seem to want to instantiate the Image class which Visual Studio tells me is abstract and can't be instantiated.
I'm going round in circles and not getting anywhere.
I just want to do something like this:
...
renderBitmap.Render(dv);
Bitmap bmp=new Bitmap(dv);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bmp);
...
But Bitmap has no useful constructors to get me from dv (DrawingVisual) to bmp. :(
Those 3 lines come from this snippet:
var renderBitmap=new RenderTargetBitmap(colorWidth,colorHeight,96.0,96.0,PixelFormats.Pbgra32);
DrawingVisual dv=new DrawingVisual();
using(DrawingContext dc=dv.RenderOpen())
{
VisualBrush backdropBrush=new VisualBrush(Backdrop);
dc.DrawRectangle(backdropBrush,null,new Rect(0,0,colorWidth,colorHeight));
VisualBrush colorBrush=new VisualBrush(MaskedColor);
dc.DrawRectangle(colorBrush,null,new Rect(0,0,colorWidth,colorHeight));
VisualBrush watermarkBrush=new VisualBrush(Watermark);
dc.DrawRectangle(watermarkBrush,null,new Rect(colorWidth-96,colorHeight-80,64,48));
}
renderBitmap.Render(dv);
Bitmap bmp=new Bitmap(dv);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bmp);
The result of using RenderTargetBitMap is a WPF BitMapSource it doesn't convert the Visual itself to a BitmapSource, it contains the result of the conversion as a BitmapSource. In order to convert a BitmapSource to a System.Drawing.Bitmap try using a modified version of the code from this MSDN Forum Post.
renderBitmap.Render(dv);
BitmapSource bmp = renderBitmap;
using(MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmp));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bitmap);
}
Created a Method to return your Bitmap
renderBitmap.Render(dv);
BitmapSource bmp =renderBitmap;
VideoStream aviStream = aviManager.AddVideoStream(true, 60, ConvertToBitmap(bmp));
private System.Drawing.Bitmap ConvertToBitmap(BitmapSource target)
{
System.Drawing.Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(target));
enc.Save(outStream);
bitmap = new System.Drawing.Bitmap(outStream);
}
return bitmap;
}
private BitmapSource ToBitmapSource(Visual visual, Brush transparentBackground)
{
var bounds = VisualTreeHelper.GetDescendantBounds(visual);
var scale = VisualTreeHelper.GetDpi(visual);
var bitmapSource = new RenderTargetBitmap(
(int)(bounds.Width * scale.DpiScaleX),
(int)(bounds.Height * scale.DpiScaleY),
scale.PixelsPerInchX,
scale.PixelsPerInchY,
PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(transparentBackground, null, new Rect(bounds.Size));
drawingContext.DrawRectangle(new VisualBrush(visual), null, new Rect(bounds.Size));
}
bitmapSource.Render(drawingVisual);
return bitmapSource;
}
private System.Drawing.Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
System.Drawing.Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new System.Drawing.Bitmap(outStream);
}
return bitmap;
}

Categories