我需要一个通用函数来将图像转换为目标格式。 (在这种情况下,Format8bppIndexed)目标是能够处理合理范围的常规.NET支持的图像。我们有许多客户拥有数百TB的不同类型的图像,我打算用这些代码遍历它们。
我意识到这段代码有多个内部尝试捕获,但我想说明问题。
在下面的每个尝试中,我都有评论显示我收到的异常和错误。
public static Bitmap ConvertToFormat(this Bitmap Source, PixelFormat TargetFormat)
{
try
{
//This throws OutOfMemoryException: "Out of memory."
return Source.Clone(new Rectangle(0, 0, Source.Width, Source.Height), TargetFormat);
}
catch (OutOfMemoryException)
{
try
{
MemoryStream ResultStream = new MemoryStream();
// This throws ExternalException: "A generic error occurred in GDI+"
Source.Save(ResultStream, ImageFormat.Gif);
ResultStream.Position = 0;
return new Bitmap(ResultStream);
}
catch (ExternalException)
{
// this is just an attempt to break the process down further to try and find the cause:
ImageCodecInfo myImageCodecInfo = GetCodecInfo(ImageFormat.Gif);
EncoderParameters myEncoderParameters = new EncoderParameters(2);
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW); ;
myEncoderParameters.Param[1] = new EncoderParameter(Encoder.Quality, 0L);
MemoryStream ResultStream = new MemoryStream();
// This throws ExternalException: "A generic error occurred in GDI+"
Source.Save(ResultStream, myImageCodecInfo, myEncoderParameters);
ResultStream.Position = 0;
return new Bitmap(ResultStream);
}
}
}
private static ImageCodecInfo GetCodecInfo(ImageFormat TargetFormat)
{
return ImageCodecInfo.GetImageEncoders().ToList().Find(
delegate (ImageCodecInfo codec)
{ return codec.FormatID == TargetFormat.Guid; });
}
我知道源图像很好,因为我可以使用LockBits()读取像素。我正在考虑使用循环来逐像素地创建一个新图像,但我更喜欢使用更直接的克隆选项,因为它会自动处理调色板创建,颜色匹配和抖动。
更新
我发现导致问题的代码,但我不确定为什么。
我不希望文件被锁定,所以我使用下面的代码将图像加载到内存中然后解锁文件。显然这会导致问题,但只有在调用Clone()方法时才会出现问题。当我使用与LockBits()相同的图像时,它允许我通过内存指针访问每个像素,并且它在PictureBox中显示得很好。有谁知道为什么这个方法导致.Clone()错误,以及如何将Image文件加载到内存中然后立即释放文件?
using (Stream s = File.OpenRead(SourceFileName))
{
return (Bitmap)Bitmap.FromStream(s);
}