First, I save the frames of a tiff into a list of bitmaps:
public Bitmap SaveTiffAsTallPng(string filePathAndName)
{
byte[] tiffFile = System.IO.File.ReadAllBytes(filePathAndName);
string fileNameOnly = Path.GetFileNameWithoutExtension(filePathAndName);
string filePathWithoutFile = Path.GetDirectoryName(filePathAndName);
string filename = filePathWithoutFile + "\\" + fileNameOnly + ".png";
if (System.IO.File.Exists(filename))
{
return new Bitmap(filename);
}
else
{
List<Bitmap> bitmaps = new List<Bitmap>();
int pageCount;
using (Stream msTemp = new MemoryStream(tiffFile))
{
TiffBitmapDecoder decoder = new TiffBitmapDecoder(msTemp, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
pageCount = decoder.Frames.Count;
for (int i = 0; i < pageCount; i++)
{
System.Drawing.Bitmap bmpSingleFrame = Worker.BitmapFromSource(decoder.Frames[i]);
bitmaps.Add(bmpSingleFrame);
}
Bitmap bmp = ImgHelper.MergeImagesTopToBottom(bitmaps);
EncoderParameters eps = new EncoderParameters(1);
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 16L);
ImageCodecInfo ici = Worker.GetEncoderInfo("image/png");
bmp.Save(filename, ici, eps);
return bmp;
}
}
}
Then I pass that list of bitmaps into a separate function to do the actual combining:
public static Bitmap MergeImagesTopToBottom(IEnumerable<Bitmap> images)
{
var enumerable = images as IList<Bitmap> ?? images.ToList();
var width = 0;
var height = 0;
foreach (var image in enumerable)
{
width = image.Width > width
? image.Width
: width;
height += image.Height;
}
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);
Graphics g = Graphics.FromImage(bitmap);//gives Out of Memory Exception
var localHeight = 0;
foreach (var image in enumerable)
{
g.DrawImage(image, 0, localHeight);
localHeight += image.Height;
}
return bitmap;
}
But I usually get an Out of Memory Exception, depending on the number of frames in the tiff. Even just 5 images that are around 2550px by 3300px each are enough to cause the error. That's only around 42MB, which ends up saving as a png that is a total of 2,550px by 16,500px and is only 1.5MB on disk. I'm even using this setting in my web.config: <gcAllowVeryLargeObjects enabled=" true" />
Other details: I'm working on a 64bit Windows 7 with 16GB of RAM (and I normally run at around 65% of ram usage), and my code is running in Visual Studio 2013 in an asp.net MVC project. I'm running the project as 32 bit because I'm using Tessnet2 which is only available in 32 bit. Still, I figure I should have plenty of memory to handle much more than 5 images at a time. Is there a better way to go about this? I'd rather not have to resort to paid frameworks, if I can help it. I feel that this is something I should be able to do for free and with out-of-the-box .net code. Thanks!