.Net库中默认的Bitmap类是不支持读取32-bit BMP的, 注意是不支持直接读, 本身是可以处理ARGB结构的. 既然这样那就自己一个个字节读吧, 反正BMP又不需要解码一个一个像素挨个读取便是.
不过读取了每个像素的各个分量值后如果挨个setPixel()的效率是不可忍受的. 且不说双层循环本来效率就很差, setPixel()本来就不快. 还好微软自己也觉得这速度太不靠谱, 留下了用指针直接操作的方法.
主要代码如下:
using (FileStream fs = new FileStream(file, FileMode.Open))
{
//判断像素深度, 仅对32-bit BMP操作
fs.Position = 0x1C;
byte[] byteDepth = new byte[4];
fs.Read(byteDepth, 0, 4);
int depth = BitConverter.ToInt32(byteDepth, 0);
if (depth != 32)
{
Console.Write("Not 32-bit bmp file.n");
continue;
}
else
{
DateTime dt = DateTime.Now;
//Pixel数据区开始位置
fs.Position = 0xA;
byte[] bytePixelArrayStart = new byte[4];
fs.Read(bytePixelArrayStart, 0, 4);
int posPixelArrayStart = BitConverter.ToInt32(bytePixelArrayStart, 0);
//图片宽度
fs.Position = 0x12;
byte[] byteWidth = new byte[4];
fs.Read(byteWidth, 0, 4);
int width = BitConverter.ToInt32(byteWidth, 0);
//图片高度
fs.Position = 0x16;
byte[] byteHeight = new byte[4];
fs.Read(byteHeight, 0, 4);
int height = BitConverter.ToInt32(byteHeight, 0);
fs.Position = posPixelArrayStart;
//建立一个与原图等大的Bitmap对象, 设置像素格式为32-bitARGB
using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
//获取该Bitmap对象的BitmapData数据
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
//获取数据开始的指针
IntPtr ptr = bmpData.Scan0;
//计算数据区的长度
int imgLen = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] pixels = new byte[imgLen];
//逐个Byte读取值并写入对应像素的各个通道
for (int counter = 0; counter < pixels.Length; counter++)
{
byte[] color = new byte[1];
fs.Read(color, 0, 1);
pixels[counter] = color[0];
}
//直接将新的Pixel数据复制入BitmapData对象, 覆盖原有数据
System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, imgLen);
bmp.UnlockBits(bmpData);
//这样生成的图时y方向上下颠倒的, 需要做一次翻转
//这样的翻转不会导致图片质量的损失
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
//转换成PNG格式并写入新文件
string pngFile = string.Format(@"{0}{1}.png", Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file));
bmp.Save(pngFile, ImageFormat.Png);
Console.Write("Used: {0}ms.n", (DateTime.Now - dt).TotalMilliseconds);
}
}
}
测试表明使用指针直接操作Pixel数据后速度有显著提升.
另有个小工具叫AlphaConv也可以实现32-bit BMP和PNG互转, Delphi写的, 调的libpng, 效率应该不错, 可惜只有图形界面.
有空来试试直接用C调libpng来实现一下, 看能比.Net快多少><.