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.