convert array of bytes to bitmapimage - c#

I'm going to convert array of bytes to System.Windows.Media.Imaging.BitmapImage and show the BitmapImage in an image control.
When I'm using the first code, noting happens! no error and no image is displayed. But when I'm using the second one it works fine! can anyone say what is going on?
first code is here:
public BitmapImage ToImage(byte[] array)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(array))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = ms;
image.EndInit();
return image;
}
}
second code is here:
public BitmapImage ToImage(byte[] array)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = new System.IO.MemoryStream(array);
image.EndInit();
return image;
}

In the first code example the stream is closed (by leaving the using block) before the image is actually loaded. You must also set BitmapCacheOptions.OnLoad to achieve that the image is loaded immediately, otherwise the stream needs to be kept open, as in your second example.
public BitmapImage ToImage(byte[] array)
{
using (var ms = new System.IO.MemoryStream(array))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
return image;
}
}
From the Remarks section in BitmapImage.StreamSource:
Set the CacheOption property to BitmapCacheOption.OnLoad if you wish
to close the stream after the BitmapImage is created.
Besides that, you can also use built-in type conversion to convert from type byte[] to type ImageSource (or the derived BitmapSource):
var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(array);
ImageSourceConverter is called implicitly when you bind a property of type ImageSource (e.g. the Image control's Source property) to a source property of type string, Uri or byte[].

In the first case, you defined your MemoryStream in a using block, which causes the object to be disposed when you go out of the block. So you return a BitmapImage with a disposes (and non-existing) stream.
MemoryStreams keep no unmanaged resources, so you can leave the memory and let the GC handle the freeing process (but that's not a good practice).

Related

How do I fix this error - "System.ObjectDisposedException: 'Cannot access a closed Stream.'"

The error is that the memory stream is not accessible when it is closed. It occurs when I try to set the previous image of the image control back to a Gif. When the previous image was a gif, I convert it to a bitmap with a memory stream. When the previous image was a normal png or jpg, it can display successfully.
I have tried to research how to fix the error. A solution I've tried is to convert the images to byte[] but then the gif isn't animating like they have been converted to a static image.
To display the gif I use a NuGet package called WPFAnimatedGif and to make the Wpf look better I use the NuGet package Materialdesigntheme
This is the event code that fires upon drag leave
if (previousIcon != null)
{
ToggleButton buttonControl = (ToggleButton)sender;
Image imageControl = (Image)((Grid)buttonControl.Content).Children[1];
if (previousIcon.ContainsKey(buttonControl))
ImageBehavior.SetAnimatedSource(imageControl, previousIcon[buttonControl]);
}
How I convert it
BitmapImage bmImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmImage.BeginInit();
bmImage.CacheOption = BitmapCacheOption.OnLoad;
bmImage.StreamSource = stream;
bmImage.EndInit();
bmImage.Freeze();
}
For full code visit these Pastebins:
https://pastebin.com/PQkFAhQj
https://pastebin.com/wCEBeN1X
https://pastebin.com/wAERvDW3
Edit
Updated conversion function based upon responses
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
BitmapImage bmImage = new BitmapImage();
MemoryStream stream = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
//stream.Position = 0;
bmImage.BeginInit();
bmImage.CacheOption = BitmapCacheOption.OnLoad;
bmImage.StreamSource = stream;
bmImage.EndInit();
bmImage.Freeze();
return bmImage;
}

Deleting a file with File.Delete throws System.IO.IOException [duplicate]

I'm trying create a user perfil edit window, in this window has a Image control
When I selected a image file, it will show in this Image control and copy this file at my image folder, first time is all right, but second time, it show a error
"The process cannot access the file 'C:\1.jpg' because it is being used by another process."
I think it is because my Image control is using this file, so, I don't know what can I do
private void Select_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
if (od.ShowDialog() == true)
{
string imageLocal = #"C:/1.jpg";
File.Copy(od.FileName, imageLocal, true);
image1.Source = new BitmapImage(new Uri(imageLocal));
}
}
If you want to load and display an image, and keep the file amenable to operations in the file system (like reloading it or moving it to another directory), the Uri constructor will not work because (as you point out), the BitmapImage class hangs on to the file handle.
Instead, use a method like this...
private static BitmapImage ByStream(FileInfo info)
{ //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
try
{
if (info.Exists)
{
// do this so that the image file can be moved in the file system
BitmapImage result = new BitmapImage();
// Create new BitmapImage
Stream stream = new MemoryStream(); // Create new MemoryStream
Bitmap bitmap = new Bitmap(info.FullName);
// Create new Bitmap (System.Drawing.Bitmap) from the existing image file
(albumArtSource set to its path name)
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
// Save the loaded Bitmap into the MemoryStream - Png format was the only one I
tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)
bitmap.Dispose(); // Dispose bitmap so it releases the source image file
result.BeginInit(); // Begin the BitmapImage's initialisation
result.StreamSource = stream;
// Set the BitmapImage's StreamSource to the MemoryStream containing the image
result.EndInit(); // End the BitmapImage's initialisation
return result; // Finally, set the WPF Image component's source to the
BitmapImage
}
return null;
}
catch
{
return null;
}
}
This method takes a FileInfo and returns a BitmapImage which you can display and simultaneously move it to another directory or display it again.
A much simpler method, copied from another answer below, is this:
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
The method shown below loads a BitmapImage from file and immediately closes the file after loading. Note that it is necessary to set the BitmapCacheOption.OnLoad flag when the source stream is closed right after EndInit.
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // just in case you want to load the image in another thread
return bitmapImage;
}
}
This code will work for any image format that is supported by WPF. When passing the image file content as stream to the StreamSource property, WPF will automatically create the appropriate decoder.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);

Image file copy, is being used by another process

I'm trying create a user perfil edit window, in this window has a Image control
When I selected a image file, it will show in this Image control and copy this file at my image folder, first time is all right, but second time, it show a error
"The process cannot access the file 'C:\1.jpg' because it is being used by another process."
I think it is because my Image control is using this file, so, I don't know what can I do
private void Select_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
if (od.ShowDialog() == true)
{
string imageLocal = #"C:/1.jpg";
File.Copy(od.FileName, imageLocal, true);
image1.Source = new BitmapImage(new Uri(imageLocal));
}
}
If you want to load and display an image, and keep the file amenable to operations in the file system (like reloading it or moving it to another directory), the Uri constructor will not work because (as you point out), the BitmapImage class hangs on to the file handle.
Instead, use a method like this...
private static BitmapImage ByStream(FileInfo info)
{ //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
try
{
if (info.Exists)
{
// do this so that the image file can be moved in the file system
BitmapImage result = new BitmapImage();
// Create new BitmapImage
Stream stream = new MemoryStream(); // Create new MemoryStream
Bitmap bitmap = new Bitmap(info.FullName);
// Create new Bitmap (System.Drawing.Bitmap) from the existing image file
(albumArtSource set to its path name)
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
// Save the loaded Bitmap into the MemoryStream - Png format was the only one I
tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)
bitmap.Dispose(); // Dispose bitmap so it releases the source image file
result.BeginInit(); // Begin the BitmapImage's initialisation
result.StreamSource = stream;
// Set the BitmapImage's StreamSource to the MemoryStream containing the image
result.EndInit(); // End the BitmapImage's initialisation
return result; // Finally, set the WPF Image component's source to the
BitmapImage
}
return null;
}
catch
{
return null;
}
}
This method takes a FileInfo and returns a BitmapImage which you can display and simultaneously move it to another directory or display it again.
A much simpler method, copied from another answer below, is this:
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
The method shown below loads a BitmapImage from file and immediately closes the file after loading. Note that it is necessary to set the BitmapCacheOption.OnLoad flag when the source stream is closed right after EndInit.
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // just in case you want to load the image in another thread
return bitmapImage;
}
}
This code will work for any image format that is supported by WPF. When passing the image file content as stream to the StreamSource property, WPF will automatically create the appropriate decoder.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);

WPF image.source change during runtime, from byte[]. But image is blank(white)

I am trying to change an image for image1 during runtime. However the image turns blank(blank), what am I doing wrong?
ImageAsBytes is a Byte[] containing an image.
ScrollViewer1 is the where image1 is located.
using (MemoryStream ms = new MemoryStream(ImagesAsBytes, 0, ImagesAsBytes.Length))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = ms;
image.EndInit();
}
image1.Source = image;
scrollViewer1.UpdateLayout();
I think your Image can't be displayed because the MemoryStream you are using is being disposed. Remove the surrounding using block and see if that helps. (you would need to dispose of the stream manually if you don't need it anymore then)

WPF Images not rendering from in-memory sources

I have a data class in my application which maintains a collection of byte arrays representing JPEG images. It's defined as:
private ArrayList FrameList = new ArrayList();
I'm having some troubles with my Image object rendering a blank page (and taking its sweet time to do it as well). When I insert a blank image with a 2K in-memory byte array (byte x[] = { lots of hex values };):
FrameList.Insert(currFrame, x);
and then import a JPEG file over it later on with:
byte[] bytes = File.ReadAllBytes(fspec);
FrameList[currFrame] = bytes;
the array is read correctly into memory and stored in the ArrayList (confirmed with the debugger).
However,I then have a function to get the image:
public BitmapImage getCurrPicture()
{
MemoryStream strm;
BitmapImage bmp = new BitmapImage();
strm = new MemoryStream((byte[])FrameList[currFrame-1]);
bmp.CacheOption = BitmapCacheOption.None;
bmp.BeginInit();
bmp.StreamSource = strm;
bmp.EndInit();
strm.Close();
return bmp;
}
which is called:
imgPicB.Source = data.getCurrPicture();
and it doesn't always render.
imgPicB is defined in my XAML as:
<Image x:Name="imgPicB"
Width="400"
Height="300"
Stretch="Fill"
VerticalAlignment="Top" />
Funny thing is, if I use the exact same JPEG setting the source with setting the source to the file URI directly, it renders fine.
Is there some problem with using in-memory JPEG images in WPF? Is there some extra smarts performed when loading from a file (say auto-detection of the image type)?
Try this:
public BitmapSource GetCurrPicture()
{
var bitmapImage = new BitmapImage();
using (Stream stream = new MemoryStream((byte[])FrameList[currFrame-1]))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
This works by having WPF decode the image immediately with OnLoad, and then release the reference to the stream when it is done. This means the stream can be garbage collected when it leaves the function. Otherwise, BitmapImage could hold onto the stream until it is rendered.
In the code posted in the question, the render would fail because:
the decode is delayed; and
the stream has been closed at the point where the decode tries to happen.
Link
DwayneNeed 20 Jun 2008 5:11 PM Comments
Caching BitmapImage will store the decoded bitmap in system memory. You can control when this happens by setting the CacheOption property. BitmapImage also maintains a cache of previous BitmapImage instances (via weak references) so that loading the same Uri multiple times will share the same instance. To avoid this cache, you can include the BitmapCreateOptions.IgnoreImageCache flag in the CreateOptions property.

Categories

Resources