Reputation: 1133
I have a WPF application where I am saving couple of hundreds of BitmapSources by converting them to TIFF images using TiffBitmapEncoder. However, I have this strange memory consumption which is often throwing Insufficient Memory Exception.
NOTE:
Here is the code which works:
static void SaveBitmapSource(BitmapSource bitmapSource)
{
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
BitmapFrame frame = BitmapFrame.Create(bitmapSource);
encoder.Frames.Add(frame);
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
}
}
And here is the screen shot of my memory:
Now if I clone the BitmapSource (even just once) then I get this huge memory allocation causing the Insufficient Memory Exception.
static BitmapSource source2 = null;
static void SaveBitmapSource(BitmapSource bitmapSource)
{
if (source2 == null)
{
source2 = bitmapSource.Clone();
}
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
BitmapFrame frame = BitmapFrame.Create(source2);
encoder.Frames.Add(frame);
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
}
}
Here is the screenshot of my memory for the second code sample
Does anyone know what could be causing this and how to fix it?
The difference is that the BitmapSource in the first example has been rendered to the screen and the second one hasn't. My suspicions are this could be something to do with GPU and the Dispatcher that might be hardware accelerating the conversion whereas the second one is done on CPU where there is some sort of a bug...
Tried:
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
after the SaveBitmapSource()
with no luckUpvotes: 2
Views: 1659
Reputation: 41
you have to use filestream to reduce to memory usage;
BitmapDecoder decoder;
using (Stream appendToOutput = File.Open(files[0], FileMode.Open, FileAccess.Read, FileShare.Read))
{
decoder = BitmapDecoder.Create(appendToOutput, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
using (Stream output = File.Open(outputFile, FileMode.Create, FileAccess.Write))
{
TiffBitmapEncoder childEncoder = new TiffBitmapEncoder();
if(Path.GetExtension(files[0]).Replace(".", "") == ScanningImageFormat.Jpeg) {
childEncoder.Compression = TiffCompressOption.Zip;
} else {
childEncoder.Compression = TiffCompressOption.Ccitt4;
}
foreach (BitmapFrame frm in decoder.Frames)
{
childEncoder.Frames.Add(frm);
}
List<Stream> imageStreams = new List<Stream>();
try
{
for (int i = 1; i < files.Count; i++)
{
string sFile = files[i];
BitmapFrame bmp = null;
Stream original = File.Open(sFile, FileMode.Open, FileAccess.Read);
imageStreams.Add(original);
bmp = BitmapFrame.Create(original, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
childEncoder.Frames.Add(bmp);
}
childEncoder.Save(output);
}
finally
{
try
{
foreach (Stream s in imageStreams)
{
s.Close();
}
}
catch { }
}
}
}
decoder = null;
Upvotes: 2
Reputation: 156
I'm glad that resolves the issue. But I'm not convinced that's the right fix. I see that you're invoking BitmapFrame.Create() with just one parameter. You might want to look at that more carefully..
Try using the BitmapCacheOption.None flag - by default it may be caching each of the bitmaps in memory for no reason:
BitmapFrame.Create(source, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
Upvotes: 0
Reputation: 1133
I have resolved the problem by calling
GC.WaitForPendingFinalizers();
right after SaveBitmapSource()
.
So my guess is that there is some unmanaged resource inside BitmapSource
and/or BitmapEncoder
which was not being released untill the Finalize methods were run...
Upvotes: 1