Convert ImageBrush to byte[] - c#

The Goal:
I want to convert an ImageBrush to a byte[].
Why?:
Because I want to print out the image, but I can't create any UI elements besides something like a MessageBox. SO I found an online tool that takes in a byte array and produces an image.
How do I do this in C# (WPF)?
What I've tried so far:
I have already converted the ImageBrush to a BitmapSource as so:
BitmapSource src = (BitmapSource)imageBrush.ImageSource;
and have converted the BitmapSource to a Bitmap Image as so:
private BitmapImage BitmapSourceToBitmapImage(BitmapSource bmpSrc)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memoryStream = new MemoryStream();
BitmapImage bImg = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bmpSrc));
encoder.Save(memoryStream);
memoryStream.Position = 0;
bImg.BeginInit();
bImg.StreamSource = memoryStream;
bImg.EndInit();
memoryStream.Close();
return bImg;
}
But for the life of me I cannot get this BitmapImage to a byte array! I have tried this:
private byte[] BitmapImagetoByteArray(BitmapImage bitmapImage) {
byte[] data;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
using(MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
The Problem/Error:
But when the encoder tries to save the memory stream, it says a frame has been disposed... see error message below:
Void SaveFrame(System.Windows.Media.SafeMILHandle, System.Windows.Media.SafeMILHandle, System.Windows.Media.Imaging.BitmapFrame)
Cannot access a disposed object.
System.ObjectDisposedException: Cannot access a disposed object.
at System.Windows.Media.Imaging.BitmapEncoder.SaveFrame(SafeMILHandle frameEncodeHandle, SafeMILHandle encoderOptions, BitmapFrame frame)
at System.Windows.Media.Imaging.BitmapEncoder.Save(Stream stream)
Any help? How can I display the ImageBrush without creating UI elements!

The error was not to set BitmapCacheOption.OnLoad, which is necessary when the source stream is to be closed right after EndInit:
bImg.BeginInit();
bImg.CacheOption = BitmapCacheOption.OnLoad;
bImg.StreamSource = memoryStream;
bImg.EndInit();
memoryStream.Close();
However, creating the intermediate BitmapImage wasn't necessary at all. The code should simply look like this:
private byte[] BitmapSourceToByteArray(BitmapSource bmpSrc)
{
var encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 100;
encoder.Frames.Add(BitmapFrame.Create(bmpSrc));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}

Related

How can i clear the memory after converting BitmapImage to Byte

I have two functions: one to convert from image to byte and other to convert from byte to bitmapImage.
So, when I open the window with that images, I convert from byte to bitmapImage and it works great, but when I close and open it again it just keeps on memory and if I continue to do that time and time again it just throws an exception Out Of Memory exception
Image to byte->
private byte[] ConvertImageToBinary(Image img)
{
using (MemoryStream ss = new MemoryStream())
{
img.Save(ss, System.Drawing.Imaging.ImageFormat.Jpeg);
var s = ss.ToArray();
var jpegQuality = 50;
Image image;
using (var inputStream = new MemoryStream(s))
{
image = Image.FromStream(inputStream);
var jpegEncoder = System.Drawing.Imaging.ImageCodecInfo.GetImageDecoders()
.First(c => c.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid);
var encoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, jpegQuality);
Byte[] outputBytes;
using (var outputStream = new MemoryStream())
{
image.Save(outputStream, jpegEncoder, encoderParameters);
return outputBytes = outputStream.ToArray();
}
}
}
}
Byte to bitmap ->
public BitmapImage ConvertBinaryToImage(byte[] array)
{
var image = new BitmapImage();
using (var ms = new MemoryStream(array))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
image.Freeze();
}
return image;
}
When I open the WindowDragAndDrop it loads all the images
But when I close it it still uses the same amount of memory
Image is indeed disposable (https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image?view=netframework-4.8), so you also need:
using (var image = Image.FromStream(inputStream)){
}
Around everywhere you use Image objects.

WPF: MemoryStream Occupying large amount of memory

I am using MemoryStram for converting Bitmap to BitmapImage and when I checked the CPU usage it is consuming more memory. I wanted to reduce the memory consumption of MemoryStream object. I used it in Using statement also and the result is same as earlier mentioned.
I am pating my code snippet please can anyone help me out to find the solution or any other alternative that can be used.
Code :
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
System.Windows.Media.Imaging.BitmapImage bImg = new System.Windows.Media.Imaging.BitmapImage();
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(ms.ToArray());
bImg.EndInit();
return bImg;
}
}
Or
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
return bi;
}
There is no need for a second MemoryStream.
Just rewind the one that the Bitmap was encoded to, before decoding the BitmapImage, and set BitmapCacheOption.OnLoad to make sure that the stream can be closed after EndInit():
public static BitmapImage ConvertBitmapImage(this System.Drawing.Bitmap bitmap)
{
var bImg = new BitmapImage();
using (var ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0; // here, alternatively use ms.Seek(0, SeekOrigin.Begin);
bImg.BeginInit();
bImg.CacheOption = BitmapCacheOption.OnLoad; // and here
bImg.StreamSource = ms;
bImg.EndInit();
}
return bImg;
}
Note that there are also other ways of converting between Bitmap and BitmapImage, e.g. this: fast converting Bitmap to BitmapSource wpf

How to get BitmapImage from byte array in several different ImageFormats

I have saved images in our database by using the following method to convert them to byte arrays in different ImageFormats:
public byte[] foo()
{
Image img = Image.FromFile(path);
var tmpStream = new MemoryStream();
ImageFormat format = img.RawFormat;
img.Save(tmpStream, format);
tmpStream.Seek(0, SeekOrigin.Begin);
var imgBytes = new byte[MAX_IMG_SIZE];
tmpStream.Read(imgBytes, 0, MAX_IMG_SIZE);
return imgBytes;
}
Now I need to read them out and convert them back into the BitmapImage type so I can display them to the user. I was thinking about using the Image.FromStream(Stream) method but that doesn't seem to take into account the different ImageFormats... Anyone know what to do? Thanks in advance.
You shouldn't use classes from the WinForms System.Drawing namespace in a WPF application (like you do with Image.FromFile).
WPF provides its own set of classes to load and save bitmaps from Streams and URIs, and has built-in support for automatically detecting the format of a bitmap frame buffer.
Just create a BitmapImage or a BitmapFrame directly from a Stream:
public static BitmapSource BitmaSourceFromByteArray(byte[] buffer)
{
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(buffer))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze(); // optionally make it cross-thread accessible
return bitmap;
}
or
public static BitmapSource BitmaSourceFromByteArray(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
Either method returns a BitmapSource, which is the base class of BitmapImage and BitmapFrame, and should be sufficient to deal with bitmaps in the rest of your application. E.g. the Source property of an Image control uses another base class, ImageSource, as property type.
Note also that when you load a BitmapSource from a Stream that is to be closed after loading, you have to set BitmapCacheOption.OnLoad. Otherwise the Stream must be kept open until the bitmap is eventually shown.
For encoding a BitmapSource you should be using a method like this:
public static byte[] BitmapSourceToByteArray(BitmapSource bitmap)
{
var encoder = new PngBitmapEncoder(); // or any other BitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}

Convert resource to byte[]

I am having trouble converting an image resource into byte[].
For example, I have the following resource:
pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg
in my program. How do I convert this into a byte[].
I've tried using a BitMapImage, but it's ImageSource ends up being null after initialised.
This seems to work:
var info = Application.GetResourceStream(uri);
var memoryStream = new MemoryStream();
info.Stream.CopyTo(memoryStream);
return memoryStream.ToArray();
A general solution to convert a BitmapSource into a byte[] would look like this:
public byte[] GetImageBuffer(BitmapSource bitmap, BitmapEncoder encoder)
{
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}
You would use it like shown below, with any of the BitmapEncoders that are available in WPF.
var uri = new Uri("pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg");
var bitmap = new BitmapImage(uri);
var buffer = GetImageBuffer(bitmap, new JpegBitmapEncoder());

How to Convert system.windows.controls.image to byte[]

I need to convert System.Windows.Controls.Image to byte[].
How to do it ?
Assuming that you answer my query above that you are actually trying to convert an image into a byte array rather than an image control into a byte array here is the code to do so;
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
In general, to convert an object to a byte array, you may want to use binary serialization. This will generate a memory stream from which you can extract a byte array.
You may start with this Question and Answer
System.Windows.Controls.Image img = new System.Windows.Controls.Image();
var uri = new Uri("pack://application:,,,/Images/myImage.jpg");
img.Source = new BitmapImage(uri);
byte[] arr;
using (MemoryStream ms = new MemoryStream())
{
var bmp = img.Source as BitmapImage;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.Save(ms);
arr = ms.ToArray();
}

Categories