You may want to create an image from a Stream or from a file on the system. You would want to use the static methods
Image.FromStream or
Image.FromFile to do it. However, you soon realize that this would not work if you immediately dispose the stream. Also, trying to delete the file would result in a locked file error.
Could it be that some reference of the stream or file is kept in the resulting Image object? The answer is yes.Microsoft explains it like this:
GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object. And they have a fix to this, here:
Bitmap and Image constructor dependenciesAs you can see in the link above, their solution is different from indexed versus non indexed images. You can determine if an image is indexed or not by looking at the PixelFormat property. Also, you need to do some pretty weird stuff in the indexed case and there is no code on the Microsoft page. So
here is a helper class I've cropped up to get images from Stream, File or byte array without locking any of the original resources:
/// <summary>
/// Helper for Image objects
/// </summary>
public static class ImageHelper
{
/// <summary>
/// Get an image from a byte array
/// </summary>
/// <param name="iconBytes"></param>
/// <returns></returns>
public static Image GetImageFromBytes(byte[] iconBytes)
{
if (iconBytes == null || iconBytes.Length == 0)
return null;
using (MemoryStream ms = new MemoryStream(iconBytes))
{
try
{
return GetImageFromStream(ms);
}
catch
{
return null;
}
}
}
/// <summary>
/// Get an image from a file without locking the file
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static Image GetImageFromFile(string fileName)
{
try
{
using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return GetImageFromStream(stream);
}
}
catch
{
return null;
}
}
/// <summary>
/// Determines if an image is indexed (with a color palette)
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public static bool IsImageIndexed(Image image)
{
switch (image.PixelFormat)
{
case PixelFormat.Format1bppIndexed:
case PixelFormat.Format4bppIndexed:
case PixelFormat.Format8bppIndexed:
case PixelFormat.Indexed:
return true;
}
return false;
}
/// <summary>
/// Get an image from stream without locking the stream
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public static Image GetImageFromStream(Stream stream)
{
try
{
using (var image = Image.FromStream(stream))
{
var srcBitmap = image as Bitmap;
var destBitmap = new Bitmap(image.Width, image.Height,image.PixelFormat);
if (IsImageIndexed(image)&&srcBitmap!=null)
{
Rectangle srcArea = new Rectangle(0, 0, image.Width, image.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, image.PixelFormat);
Rectangle destArea = new Rectangle(0, 0, image.Width, image.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, image.PixelFormat);
IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride*image.Height];
Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
Marshal.Copy(buffer, 0, destPtr, buffer.Length);
srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);
destBitmap.Palette = srcBitmap.Palette;
} else
{
using (var graphics = Graphics.FromImage(destBitmap))
{
graphics.DrawImage(image, 0, 0);
}
}
return destBitmap;
}
}
catch
{
return null;
}
}
}