I have to get a BitmapSource from an Image and for this I use an extension method like this:
public static BitmapSource ToBitmapSource(this Image image)
{
LogEx.FunctionEnter();
if (image == null)
throw new ArgumentNullException("image");
BitmapImage bitmapImage = null;
using (MemoryStream memory = new MemoryStream())
{
image.Save(memory, ImageFormat.Jpeg);
memory.Position = 0;
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
}
LogEx.FunctionLeave("Rendered image as BitmapSource");
return bitmapImage;
}
If I now release the handle of this and dispose the original Image it stays in the memory, even after calling the GC by hand multiple times. I tested to use files instead of streams with this little piece of code:
string filename = $"c:\\temp\\{page}.jpg";
if (File.Exists(filename))
{
File.Delete(filename);
}
_highResPageImages[page].Save(filename, ImageFormat.Jpeg);
Uri uri = new Uri(filename);
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = uri;
source.CacheOption = BitmapCacheOption.OnLoad;
source.EndInit();
Document.PageImage = source;
// Ducument.PageImage = _highResPageImages[page].ToBitmapSource();
And even if there is also an OnLoad, it gets disposed when the handle is released. So it must be something with the MemoryStream. But what? I tried this WrapperStream I found somewhere else and to use the Freeze() Method of BitmapImage, but both to no a avail. The problem is, that I cannot cache the images on the drive of the customer (even if it wouldn't cost a huge amount of time to do so) and I get the Image from another DLL, so I cannot change it beforehand. Has someone else an idea?
Edit: I use WPF and the value of the handle is being used in a Binding for display. Maybe that matters somehow. Or that I use BitmapSource in the Binding and handle and not the original BitmapImage.
Try this in ToBitmapSource:
...
bitmapImage.StreamSource = null;
return bitmapImage
Related
I have a method that opens a FileStream and creates a BitmapImage, by using the StreamSource property.
Somehow, in a single machine, trying to open a big image (6000x4000px) results in the method returning a 1x1px image instead.
First I thought that the image was being loaded from a shared folder on the local network, but It was stored in the downloads folder of the same computer.
I saw that the image was "blocked/locked" by Windows because it was downloaded from an unverified source, so I opened Properties and unlocked it. Trying to load the image again resulted in the same problem.
The image was fully downloaded.
Background information:
The machine with the problem:
Windows 7 SP1.
32 bits.
Net Framework 4.6.2.
4GB of memory, with 2.5GB being used.
My machine (it works as expected):
Windows 10, build 15063.413.
64 bits.
Net Framework 4.7.
8GB of memory, with 6GB being used.
Code:
public static BitmapSource SourceFrom(this string fileSource, int? size = null)
{
using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
if (size.HasValue)
{
//It's not possible to get the size of image while opening, so get from another place.
var refSize = fileSource.ScaledSize(); //Gets the size of the image.
if (refSize.Height > refSize.Width)
bitmapImage.DecodePixelHeight = size.Value;
else
bitmapImage.DecodePixelWidth = size.Value;
}
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); //Just in case you want to load the image in another thread.
return bitmapImage;
}
}
Usage:
var image = "C:\Image.jpg".SourceFrom(); //Without any other parameter.
Question:
Is there any case that my code is not treating properly, at least that explains why I'm getting a 1x1px image instead of the full size one?
Also, why it does not throw an Exception if it can't load the image?
Probable answer:
My code does not handle if the image finished downloading or not, but the image was fully downloaded, so I'm not sure if this is the case. I've found a similar thread about the same problem.
Working code:
using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
using (var memory = new MemoryStream())
{
stream.CopyTo(memory);
memory.Position = 0;
//...
Just replace this part of the code and use the memory variable instead of stream while setting the StreamSource object.
It seems that when the image file is very large, or for some other reason the source stream can't be read immediately, you'll have to copy the source stream to an intermediate MemoryStream, and assign that to the StreamSource property of the BitmapImage:
using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
using (var memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
memoryStream.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
The BitmapImage creates a default image with 1x1px when decoding of the image fails. You need to register the DecodeFailed event to detect this.
Exception decodeEx = null;
using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = fileStream;
bitmapImage.DecodeFailed += (_, e) => decodeEx = e.ErrorException;
bitmapImage.EndInit();
if (decodeEx != null)
throw decodeEx;
bitmapImage.Freeze();
return bitmapImage;
}
In my case it turned out to be a OutOfMemoryException. Indeed decoding only failed when the memory usage was high and the native functionwhich is actually called by BitmapImage (using unmanaged memory) was probably unable to allocate enough memory.
i had the same problem, the CacheOption is not in the right place on your code!! juste add it befor the endInit();
source.CacheOption = BitmapCacheOption.OnLoad;
like this ->
BitmapImage source = new BitmapImage();
source.BeginInit();
source.CacheOption = BitmapCacheOption.OnLoad;
source.StreamSource = fs;
source.EndInit();
I try to compress ImageSource with MagickImage in memory. But it consumes too much memory. With VS performance tool, every call of this method with consume a lot of memory. It should take 0Mb once it exists, right?
internal static System.Windows.Media.ImageSource compressImage(System.Windows.Media.ImageSource ims)
{
using (MemoryStream stream = new MemoryStream())
{
using (MemoryStream outS = new MemoryStream())
{
BitmapSource bs = ims as BitmapSource;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
BitmapFrame bf = BitmapFrame.Create(bs);
//encoder.Frames.Add(BitmapFrame.Create(image1.Source));
encoder.Frames.Add(bf);
encoder.Save(stream);
stream.Flush();
try
{
// Read first frame of gif image
using (MagickImage image = new MagickImage(stream))
{
image.Quality = 75;
image.Resize(new Percentage(0.65));
image.Density = new Density(200, DensityUnit.PixelsPerInch);
image.Write(outS);
}
stream.Close();
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
outS.Position = 0;
bitmap.StreamSource = outS;
//
bitmap.EndInit();
//bitmap.Freeze();
outS.Flush();
outS.Close();
ims = null;
return bitmap;
}
catch (Exception e)
{
return null;
}
}
}
}
}
Since Image is stored in memory pixel by pixel. It will consume much memory depends on its size.
Specifying its size will reduce much memory.
bitmap.DecodePizelWidth = 800;
I cannot decode images back from their encoded form as a (Jpeg) byte array retrieved from my database to be used as image sources for my WPF application.
The code I am using to encode them as a Jpeg byte array is as follows:
public byte[] bytesFromBitmap(BitmapImage bit)
{
byte[] data;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bit));
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
This is taking my Image taken directly from a webpage and assigned to an Image control like so:
var img = new BitmapImage(new Uri(entity.Image.ImageSrc)); //the entity has been saved in my DB, having been parsed from html
pbImage.Source = img;
This works just fine, I encode the BitmapImage and it saves just fine. But when I retrieve it from the DB and try to display it in another window, I cannot get it to work after trying every example I can see online - all either render nothing, or a black box or a visual mess not at all similar to the image I encoded.
Neither of the following have worked for me:
public BitmapSource GetBMImage(byte[] data)
{
using (var ms = new MemoryStream(data))
{
JpegBitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource frame = decoder.Frames[0];
return frame;
}
}
public static BitmapImage ImageFromBytes(byte[] imageData)
{
if (imageData == null)
{
return null;
}
else
{
var image = new BitmapImage();
using (var mem = new MemoryStream())
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
} //this throws a 'No imaging component suitable to complete this operation was found' exception
Among other uses of memory streams and decoders I just can't get this to work - can anyone help?
I have created WPF windows application for display more images using grid. My below code getting OutOfMemory Exception when I run my application.exe.
byte[] buffer = File.ReadAllBytes(path);
File.Delete(path);
if (buffer == null)
return null;
using (MemoryStream mStream = new MemoryStream(buffer))
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = mStream;
bi.EndInit();
bitmap = bi;
bitmap.Freeze();
mStream.Close();
mStream.Dispose();
}
I found some solution from stackoverflow and changed my coding as following below,
BitmapImage image = new BitmapImage();
{
image.BeginInit();
// image.CreateOptions = BitmapCreateOptions.n;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(path);
image.EndInit();
File.Delete(path);
bitmap = image;
image.UriSource = null;
image = null;
}
But this code getting exception as that image used by another process or cant open from locked file.
I am totally confused why my application often caused by OutOfMemory or used by another process exception?
Taken from the comments, you are doing something wrong. You are initializing an obejct ob type BitmapImage and instantly declare it as null. So everything you have declared beforehand has gone down the crapper.
you should leverage the using() statements functionality here. If the code leaves this statement the GarbageCollector will automatically take over and dispose everything for you:
using(BitmapImage image = new BitmapImage())
{
image.BeginInit();
// image.CreateOptions = BitmapCreateOptions.n;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(path);
image.EndInit();
bitmap = image.Clone();
}
public static void CopyImage(Image picToSave, string name)
{
if (picToSave.Source != null)
{
BitmapImage src = (BitmapImage)picToSave.Source;
if (!Directory.Exists("Images"))
{
Directory.CreateDirectory("Images");
}
FileStream stream = new FileStream("Images/" + name + ".jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(stream);
stream.Close();
}
}
The problem occurs when i choose a file which already exists in the /Images directory, i guess it just cant overwrite, The exception is thrown at the "FileStream"" line (FileMode.Create I guess).
If i Choose a file which isn't in the /Images directory it works fine and copies the file to the Images Directory like it should..
Thank you :)
How did you load the image in the first place? If you didn't change the default value for CacheOption, the file is locked by the BitmapImage object. You need to specify BitmapCacheOption.OnLoad:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = imageUri;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
Assuming you created Bitmaps from all the images in the image folder this sounds like a known problem with the Bitmap class - it keeps a file lock on the file you created it from until you call dispose. Also see this thread: .NET app locks file.
Hans Passant offers the following workaround in this thread: Loading a file to a Bitmap but leaving the original file intact
public static Image LoadImageNoLock(string path) {
using (var ms = new MemoryStream(File.ReadAllBytes(path))) {
return Image.FromStream(ms);
}
}
Just thought I would post something that worked for me from #Thomas Levesque's comment.
FileStream stream = new FileStream(imageLocation, FileMode.Open);
Image image = new Image();
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = stream;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
image.Source = bi;
image.Height = 15;
btn.Content = image;
stream.Close();
stream.Dispose();
try with some modifications in your code
set the write permission to your opened file
FileStream stream = new FileStream("Images/" + name + ".jpg", FileMode.Create, FileAccess.Write);