.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快多少><.
One thought on “32-bit BMP转PNG之C#版”