Miro Bucko
Miro Bucko

Reputation: 1133

TiffBitmapEncoder, memory bug causing Insufficient Memory Exception in C#/WPF

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:

No memory exception

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

Memory exception

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:

Upvotes: 2

Views: 1659

Answers (3)

serdartu
serdartu

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

Ketchup201
Ketchup201

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

Miro Bucko
Miro Bucko

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

Related Questions