0

I have presumably tried over 15 different techniques to convert a TIFF to a Bitmap, yet all already succed with a 99% chance. In 1% of all cases the Bitmap will be locked and I will face an Exception due to Bitmap.Save execution right after the conversion. At least I'm suspecting a lock on the Bitmap, as I can't even create a new Bitmap from it, using "new Bitmap(bitmap).

The Idea is simple: Take a multipage tiff, parse it into different pages/frames, convert them and add them to a list of bitmaps.

This is the code so far:

public static List<Bitmap> ConvertTiffToBitmaps(Image tiffImage)
    {
        Image tempTiffImage = tiffImage;
        List<Bitmap> bitmaps = new List<Bitmap>();

        ImageCodecInfo bmpImageCodecInfo;
        EncoderParameters parmsEncoder;
        EncoderParameter parm;
        System.Drawing.Imaging.Encoder enc;
        long modeEncoder;

        // Image Settings
        Guid imageGuid = tempTiffImage.FrameDimensionsList[0];
        FrameDimension imageFrameDimension = new FrameDimension(imageGuid);
        // Built in BMP-Codec
        bmpImageCodecInfo = GetEncoderInfo("image/bmp");

        // uncompressed -> Encoder Parameters                        
        enc = Encoder.Compression;
        modeEncoder = (long)EncoderValue.CompressionNone;
        parm = new EncoderParameter(enc, modeEncoder);
        parmsEncoder = new EncoderParameters(1);
        parmsEncoder.Param[0] = parm;
        int pageCount = tempTiffImage.GetFrameCount(imageFrameDimension);

        Bitmap convertedBitmap = null;
        for (int i = 0; i < pageCount; i++)
        {
            tempTiffImage.SelectActiveFrame(imageFrameDimension, i);                
            convertedBitmap = LoadBitmapFromTiff_AvoidLock(tempTiffImage, bmpImageCodecInfo, parmsEncoder);

// climacteric:
// here I can neither do use new Bitmap or Save:
// Bitmap temp = new Bitmap(convertedBitmap)
// converterdBitmap.Save(somePath);
// -> GDI+ Exception
// however I am able to clone the bitmap, but it is no use for the usage of save

            bitmaps.Add(convertedBitmap);
        }           
        return bitmaps;
    }

Trying to avoid the lock with "drawing a new bitmap" and letting the Garbage Collector do its work was so far the best way. Yet it also doesnt succeed always.

        public static Bitmap LoadBitmapFromTiff_AvoidLock(Image tempTiffImage, ImageCodecInfo bmpImageCodecInfo, EncoderParameters parmsEncoder)
        {
        Bitmap returnBitmap;

        MemoryStream ms = new MemoryStream();            
        tempTiffImage.Save(ms, bmpImageCodecInfo, parmsEncoder);

        using (Bitmap tempStreamBitmap = new Bitmap(ms))
        {
            returnBitmap = new Bitmap(tempStreamBitmap.Width, tempStreamBitmap.Height);

            using (Graphics g = Graphics.FromImage(returnBitmap))
            {
                g.DrawImage(tempStreamBitmap, 0, 0, returnBitmap.Width, returnBitmap.Height);
            }
            // msdn: "You must not destroy the stream"
            //ms.Dispose();
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return returnBitmap;
    }

According to the msdn documentation one mustn't dispose the memorystream in which the bitmap is allocated in. As a result I have not made use of the "using" expression or disposed the MemoryStream itself ("LoadBitmapFromTiff_AvoidLock" method).

debugging environment:

In order to preclude diffrent issues in my code I have solely run this code with always the same TIFF Image again and again. Also have I "reseted" the enviroment the tiff is converted to exclude actual memory issues.

how it is undoable:

If I add a ~Thread.Sleep(30000) right after the conversion it does work 100%. I feel like some Bitmap GDI+ functionalities had been asyncronly implemented. I also face this issue only with very large tiff with again lead to very large bitmaps. But it can't be a memory issue as it does work often(yet not always...)

postscript

I know there are many ways to convert a tiff, yet this is the only one which has been always working, atleast in the conversion process itself.

Update 1

The very common hint of using double new Bitmap constructor led also to an "Generic GDI+ Exception"

Bitmap bClone = null;
using (Bitmap b = new Bitmap(ms))
{
    bClone = new Bitmap(b);
    // Genereic GDI+ exception
}
bClone.Save("image.bmp");
bClone.Dispose();

Update 2 Calling GC.Collect() a second time triggered by a OutOfMemoryException wouldn't help either as a "Generic GDI+" exception is thrown.

        public static Bitmap LoadBitmapFromTiff_AvoidLock(Image tempTiffImage, ImageCodecInfo bmpImageCodecInfo, EncoderParameters parmsEncoder)
        {
        Bitmap returnBitmap;

        MemoryStream ms = new MemoryStream();            
        tempTiffImage.Save(ms, bmpImageCodecInfo, parmsEncoder);

        using (Bitmap tempStreamBitmap = new Bitmap(ms))
        {
            returnBitmap = new Bitmap(tempStreamBitmap.Width, tempStreamBitmap.Height, tempStreamBitmap.PixelFormat);

            using (Graphics g = Graphics.FromImage(returnBitmap))
            {
                g.DrawImage(tempStreamBitmap, 0, 0, returnBitmap.Width, returnBitmap.Height);
            }
            // msdn: "You must not destroy the stream itself"
            //ms.Dispose();
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        try
        {
            using(Bitmap tempOOM = new Bitmap(returnBitmap))
            {
                tempOOM.Save(Path.GetTempPath() + Guid.NewGuid() + @"\" + Guid.NewGuid() + ".bmp");
            }                
        }
        catch (OutOfMemoryException)
        {
            GC.Collect();
        }
        return returnBitmap;
        }

Update 3 Calling "LoadBitmapFromTiff_AvoidLock" async and awaiting it didn't help either. I reckon for await .NET will only await the method called, not the nested async methods in it.

Nex
  • 421
  • 1
  • 4
  • 17
  • If the exception is an out of memory exception, try to add a second GC.Collect() after GC.WaitForPendingFinalizers(). – Graffito Aug 08 '15 at 23:37
  • Could you explain the logic behind it, please? – Nex Aug 08 '15 at 23:39
  • I have no logic behind it, it is just a recipe got from internet that I systematically apply and fixed some out of memory exceptions. – Graffito Aug 08 '15 at 23:42
  • If there is a lot of image, systematic garbage collection may compromise performance. In that case, you may test if garbage collection is required, by trying to temporarily allocate a large array. a piece of code [there](http://stackoverflow.com/questions/30968016/out-of-memory-exception-in-iis). – Graffito Aug 08 '15 at 23:45
  • "I read in the msdn documentation that I mustn't dispose the memorystream the bitmap is allocated it. " Can you provide a link? – Hamlet Hakobyan Aug 08 '15 at 23:47
  • Here is jst one as an example: https://support.microsoft.com/en-us/kb/814675 Trying to fix this bug for weeks I have found dozens of threads claiming this to be the issue. However It didnt matter as both ways were not working. – Nex Aug 09 '15 at 00:00

0 Answers0