GDI+中常见的几个问题(4)2011-04-15 博客园 Hotcan5.读图是快了,处理怎么还是慢?GDI+的Bitmap类提供了两个罪恶的函数GetPixel, SetPixel,用来获取某个像素点的颜色 值。这个2个函数如果只调用一次两次也就罢了,万一我想把整张图片加红一点,用下面的代 码,我估计你等到黄花菜都凉了,还没有算完呢。 看看下面的代码是怎么写的。1 FileStream fs = new FileStream(image, FileMode.Open, FileAccess.Read);
2 Image img = Image.FromStream(fs, false, false);
3 Bitmap bmp = new Bitmap(img);
4 img.Dispose();
5 fs.Close();
6
7 for (int j = 0; j < bmp.Height; j++)
8 {
9 for (int i = 0; i < bmp.Width; i++)
10 {
11 Color color = bmp.GetPixel(i, j);
12 color = Color.FromArgb(color.R + 20, color.G, color.B);
13 bmp.SetPixel(i, j, color);
14 }
15 }
代码逻辑很清楚,第1到第5行,写得很好,用了我们在前几节里面的方法,读图速度飞快 且不锁文件。当然如果不用覆盖原始文件,不用复制都可以,速度就更快了。接下来我们对 图像做一个循环,一行一行更新图像的数据。殊不知GetPixel和SetPixel是GDI里面耗费最大 的函数之一,此外bmp.Height和bmp.Width也是慢得够呛,如果处理一张500M像素的照片,您 可以去喝杯茶,睡一觉再回来了。Bitmap有个方法叫LockBits,就是把图像的内存区域根据格式锁定,拿到那块内存的首地 址。这样就可以直接改写这段内存了。这个方法的设计是挺好,可惜都是C++作为源泉来的, .NET Framework里面根本就不推荐用指针,VB里面根本也就没有指针。最后设计了一个鸡肋 的IntPtr,将就掉了这个问题。C#其实还好,可以用unsafe code,也算是间接用了指针, VB.NET就痛苦了,需要用Marshal.Copy把内容Copy到一个byte数组里面,然后处理完了再 Copy回去。所以结论就是,要用GDI+做图像处理,最好别用VB.NET,否则内存翻倍。让我们来看看快速的写法,注意在编译的时候加上unsafe开关,允许C#使用指针:1 FileStream fs = new FileStream(image, FileMode.Open, FileAccess.Read);
2 Image img = Image.FromStream(fs, false, false);
3 Bitmap bmp = new Bitmap(img);
4 img.Dispose();
5 fs.Close();
6
7 int width = bmp.Width;
8 int height = bmp.Height;
9 BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
10 byte* p = (byte*)bmData.Scan0;
11 int offset = bmData.Stride - width * 3; //only correct when PixelFormat is Format24bppRgb
12
13 for (int j = 0; j < height; j++)
14 {
15 for (int i = 0; i < width; i++)
16 {
17 p[2] += 20; //should check boundary
18 p += 3;
19 }
20 p += offset;
21 }
22
23 bmp.UnlockBits(bmData);
24 bmp.Dispose();