PngBitmapEncoder creating an "empty" file - c#

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

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

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

[C#][WPF] How to save multiple canvas as an image?

I am just looking around and tried to making a working version of saving multiple canvas as one image. For example would be nice to save them nearby each other, but I can not figure out how to do it.
public static void CreateSaveBitmap(Canvas canvas1, Canvas canvas2, string filename)
{
RenderTargetBitmap renderBitmap1 = new RenderTargetBitmap((int)canvas1.ActualWidth, (int)canvas1.ActualWidth, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas1.Measure(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualWidth));
canvas1.Arrange(new Rect(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualWidth)));
renderBitmap1.Render(canvas1);
RenderTargetBitmap renderBitmap2 = new RenderTargetBitmap((int)canvas2.ActualWidth, (int)canvas2.ActualWidth, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas2.Measure(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualWidth));
canvas2.Arrange(new Rect(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualWidth)));
renderBitmap2.Render(canvas2);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap1));
encoder.Frames.Add(BitmapFrame.Create(renderBitmap2));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
}
}
This is how I tried, but its only saving the first image. Can someone help me to solve this please?
UPDATE
I corrected some mistakes what told by #TheLethalCoder, and I also changed the output filetype to Tiff, but it doesn't helped. This is the latest version now:
public static void CreateSaveBitmap(Canvas canvas1, Canvas canvas2, string filename)
{
RenderTargetBitmap renderBitmap1 = new RenderTargetBitmap((int)canvas1.ActualWidth, (int)canvas1.ActualHeight, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas1.Measure(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualHeight));
canvas1.Arrange(new Rect(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualHeight)));
renderBitmap1.Render(canvas1);
RenderTargetBitmap renderBitmap2 = new RenderTargetBitmap((int)canvas2.ActualWidth, (int)canvas2.ActualHeight, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas2.Measure(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualHeight));
canvas2.Arrange(new Rect(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualHeight)));
renderBitmap2.Render(canvas2);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap1));
encoder.Frames.Add(BitmapFrame.Create(renderBitmap2));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
}
}
What you want is not multiple-frame image but to combine the images of your canvases by juxtaposing them. So actually you need just 1 Frame here but a big combined one. There are some ways to combine them but there is 1 simple way you can do with DrawingVisual. We can draw a Drawing on that visual and use RenderTargetBitmap to capture it. We use DrawingGroup to combine the ImageDrawings (from the 2 RenderTargetBitmaps). In summary, we have to do the following steps: from 2 RenderTargetBitmaps, you put them in 2 ImageDrawings and combine those into a DrawingGroup. Then you draw that combined Drawing on a DrawingVisual. Finally use RenderTargetBitmap to render that visual normally.
Here is the code you can try out:
public static void CreateSaveBitmap(Canvas canvas1, Canvas canvas2, string filename)
{
RenderTargetBitmap renderBitmap1 = new RenderTargetBitmap((int)canvas1.ActualWidth, (int)canvas1.ActualHeight, 96d, 96d, PixelFormats.Pbgra32);
canvas1.Measure(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualHeight));
canvas1.Arrange(new Rect(new Size((int)canvas1.ActualWidth, (int)canvas1.ActualHeight)));
renderBitmap1.Render(canvas1);
RenderTargetBitmap renderBitmap2 = new RenderTargetBitmap((int)canvas2.ActualWidth, (int)canvas2.ActualHeight, 96d, 96d, PixelFormats.Pbgra32);
canvas2.Measure(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualHeight));
canvas2.Arrange(new Rect(new Size((int)canvas2.ActualWidth, (int)canvas2.ActualHeight)));
renderBitmap2.Render(canvas2);
//Combine the images here
var dg = new DrawingGroup();
var id1 = new ImageDrawing(renderBitmap1,
new Rect(0,0,renderBitmap1.Width, renderBitmap1.Height));
var id2 = new ImageDrawing(renderBitmap2,
new Rect(renderBitmap1.Width, 0
renderBitmap2.Width,
renderBitmap2.Height));
dg.Children.Add(id1);
dg.Children.Add(id2);
var combinedImg = new RenderTargetBitmap((int)(renderBitmap1.Width + renderBitmap2.Width + 0.5),
(int)(Math.Max(renderBitmap1.Height, renderBitmap2.Height) + 0.5), 96, 96, PixelFormats.Pbgra32);
var dv = new DrawingVisual();
using(var dc = dv.RenderOpen()){
dc.DrawDrawing(dg);
}
combinedImg.Render(dv);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(combinedImg));
using (FileStream file = File.Create(filename)) {
encoder.Save(file);
}
}

Adding footer/EXIF (binary data) to JPG image in C#

Hello all I have an image and I want to add binary data as a footer to that image.
RGBImage rgbImage = (RGBImage) RGBImage.LoadImage(#"test.tiff");
byte[] bytes = File.ReadAllBytes(#"C:\TEMP\gili.bin");
int padding =(int) Math.Ceiling((double)bytes.Length/(rgbImage.Width*3));
byte[] newMakerNoteImage = new byte[rgbImage[0].Data.Length + (rgbImage.Width * 3 * padding)];
Buffer.BlockCopy(rgbImage[0].Data, 0, newMakerNoteImage, 0, rgbImage[0].Data.Length);
Buffer.BlockCopy(bytes, 0, newMakerNoteImage, rgbImage[0].Data.Length, bytes.Length);
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
rgbImage.Width,
rgbImage.Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
newMakerNoteImage,
rgbImage.Width * 3);
FileStream stream = new FileStream(#"C:\TEMP\new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
the image outputs fine however the binary data is not added to the end of the image.
Can you tell me if I'm doing it correctly?
I think i might be looking at it all wrong and I need to use EXIF in order to add this makernote data into the image. the data shoudln't be visible to the user of the image.
here is my tested solution for EXIF/MakerNote creation
As far as I have read 37500 is the makernote hexdecimal tag inside EXIF.
http://nicholasarmstrong.com/2010/02/exif-quick-reference/
public void CreateMakerNoteJpgImage(byte[] makerNoteArray, string path)
{
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
Width,
Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
_channels[0].Data,
Width * 3);
FileStream stream = new FileStream(path, FileMode.Create);
BitmapMetadata metadata = new BitmapMetadata("jpg");
//adding makernote data into EXIF of the jpeg image
metadata.SetQuery("/app1/ifd/exif:{uint=37500}", makerNoteArray);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
BitmapFrame frame = BitmapFrame.Create(image, null, metadata, null);
encoder.Frames.Add(frame);
encoder.Save(stream);
}

Convert DrawingImage to BitmapImage

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

Categories