How to get BitmapImage from byte array in several different ImageFormats - c#

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

Related

Convert ImageBrush to byte[]

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

Image does not show up in Wpf Image Control

I'm re-writing a app of mine, where I create a barcode image using the Barcode Image Generation Libary by Brad Barnhill (http://www.codeproject.com/Articles/20823/Barcode-Image-Generation-Library).
In this article everything is explaned how to do it in Windows Forms. But now - using Wpf - there are some errors. E.g.: The result of the function Encode returns a System.Drawing.Image but when I want to display this Image in a Wpf Image Control the Source property wants a System.Windows.Media.ImageSource.
So I did some reserach of how to convert a Drawing.Image in a Media.ImageSource. I found some snippets but they don't work as expected.
Currently I use this code:
// Import:
using Media = System.Windows.Media;
using Forms = System.Windows.Forms;
// Setting some porperties of the barcode-object
this.barcode.RotateFlipType = this.bcvm.Rotation.Rotation;
this.barcode.Alignment = this.bcvm.Ausrichtung.Alignment;
this.barcode.LabelPosition = this.bcvm.Position.Position;
// this.bcvm is my BarcodeViewModel for MVVM
var img = this.barcode.Encode(
this.bcvm.Encoding.Encoding,
this.bcvm.EingabeWert,
this.bcvm.ForeColor.ToDrawingColor(),
this.bcvm.BackColor.ToDrawingColor(),
(int)this.bcvm.Breite,
(int)this.bcvm.Hoehe
);
this.imgBarcode.Source = img.DrawingImageToWpfImage();
this.imgBarcode.Width = img.Width;
this.imgBarcode.Height = img.Height;
// My conversion methode. It takes a Drawing.Image and returns a Media.ImageSource
public static Media.ImageSource ToImageSource(this Drawing.Image drawingImage)
{
Media.ImageSource imgSrc = new Media.Imaging.BitmapImage();
using (MemoryStream ms = new MemoryStream())
{
drawingImage.Save(ms, Drawing.Imaging.ImageFormat.Png);
(imgSrc as Media.Imaging.BitmapImage).BeginInit();
(imgSrc as Media.Imaging.BitmapImage).StreamSource = new MemoryStream(ms.ToArray());
(imgSrc as Media.Imaging.BitmapImage).EndInit();
}
return imgSrc;
}
When running this code an converting the image (and assigning it to the Image control) there is nothing displayed
This conversion method should work:
public static ImageSource ToImageSource(this System.Drawing.Image image)
{
var bitmap = new BitmapImage();
using (var stream = new MemoryStream())
{
image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Position = 0;
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
return bitmap;
}
In case the System.Drawing.Image is actually a System.Drawing.Bitmap you may also use some other conversion methods, as shown here: fast converting Bitmap to BitmapSource wpf

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

convert binary to bitmap using memory stream

Hi I wanna convert binary array to bitmap and show image in a picturebox. I wrote the following code but I got exception that says that the parameter is not valid .
public static Bitmap ByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream);
mStream.Dispose();
return bm;
}
It really depends on what is in blob. Is it a valid bitmap format (like PNG, BMP, GIF, etc?). If it is raw byte information about the pixels in the bitmap, you can not do it like that.
It may help to rewind the stream to the beginning using mStream.Seek(0, SeekOrigin.Begin) before the line Bitmap bm = new Bitmap(mStream);.
public static Bitmap ByteToImage(byte[] blob)
{
using (MemoryStream mStream = new MemoryStream())
{
mStream.Write(blob, 0, blob.Length);
mStream.Seek(0, SeekOrigin.Begin);
Bitmap bm = new Bitmap(mStream);
return bm;
}
}
Don't dispose of the MemoryStream. It now belongs to the image object and will be disposed when you dispose the image.
Also consider doing it like this
var ms = new MemoryStream(blob);
var img = Image.FromStream(ms);
.....
img.Dispose(); //once you are done with the image.
System.IO.MemoryStream mStrm = new System.IO.MemoryStream(your byte array);
Image im = Image.FromStream(mStrm);
im.Save("image.bmp");
Try this. If you still get any error or exception; please post your bytes which you are trying to convert to image. There should be problem in your image stream....

Saving a TransformedBitmap Object to disk.

Working in WPF and C#, I have a TransformedBitmap object that I either:
Need to save to disk as a bitmap type of file (ideally, I'll allow users to choose whether it's saved as a BMP, JPG, TIF, etc, though, I'm not to that stage yet...)
Need to convert to a BitmapImage object as I know how to get a byte[] from a BitmapImage object.
Unfortunately, at this point I'm really struggling to get either one of those two things done.
Can anyone offer any help or point out any methods I might be missing?
All of your encoders are using BitmapFrame class for creating frames that will be added to Frames collection property of encoder. BitmapFrame.Create method has variety of overloads and one of them accepts parameter of BitmapSource type. So as we know that TransformedBitmap inherits from BitmapSource we can pass it as a parameter to BitmapFrame.Create method. Here are the methods which works as you have described:
public bool WriteTransformedBitmapToFile<T>(BitmapSource bitmapSource, string fileName) where T : BitmapEncoder, new()
{
if (string.IsNullOrEmpty(fileName) || bitmapSource == null)
return false;
//creating frame and putting it to Frames collection of selected encoder
var frame = BitmapFrame.Create(bitmapSource);
var encoder = new T();
encoder.Frames.Add(frame);
try
{
using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}
catch (Exception e)
{
return false;
}
return true;
}
private BitmapImage GetBitmapImage<T>(BitmapSource bitmapSource) where T : BitmapEncoder, new()
{
var frame = BitmapFrame.Create(bitmapSource);
var encoder = new T();
encoder.Frames.Add(frame);
var bitmapImage = new BitmapImage();
bool isCreated;
try
{
using (var ms = new MemoryStream())
{
encoder.Save(ms);
ms.Position = 0;
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
isCreated = true;
}
}
catch
{
isCreated = false;
}
return isCreated ? bitmapImage : null;
}
They accept any BitmapSource as the first parameter and any BitmapEncoder as a generic type parameter.
Hope this helps.

Categories