准确地说,什么是锁定的必要性?也就是说,除了"不安全"的内存访问考虑因素外,平台为什么不能简单地返回位图的第一个像素的指针,让您直接访问像素?
一些可能的原因:
- 位图以其原始压缩形式存储以节省内存(PNG、JPEG等)
- 位图以24bpp格式存储,因此访问速度比32bpp图像慢
- 位图未存储在紧密的内存数组中,并且是分散的,因此无法快速读取/写入
- 位图以未公开的方式存储,平台不想让您访问实际位图数据内存——强制您使用lock()在内存中创建副本
位图以延迟方式读取。实际使用的算法取决于图片格式,.bmp文件很容易处理,.jpeg则不太容易。在底层,GDI+创建内存映射文件将文件数据映射到RAM中,在必要时利用操作系统的需求分页虚拟内存映射功能。这就是臭名昭著的MMF,它在同名文件保存图像时会锁定文件并导致异常。
Bitmap.LockBits() 创建一个单独的缓冲区,将像素数据从MMF映射到内存中请求的像素格式的区域。现在您可以得到以文件格式无关的明确定义格式的数据。如果有修改,则调用 UnlockBits() 将修改后的数据写回。文件中的数据如何与修改过的像素重新组合未指定且可能取决于编解码器。
当您在其他方法中使用位图时,例如使用 Graphics.DrawImage() 绘制图像和 Bitmap.GetPixel() 时,同样会进行这种锁定,但是这个过程是不可见的。由于每个像素都需要运行 GetPixel() 而不是像 DrawImage 和 LockBits() 只运行一次,因此 GetPixel() 处理速度非常慢,其中涉及了大量的开销。
显然,开销量取决于图像文件格式、即时解码像素数据的费用、解码器执行的缓存量以及 LockBits() 中请求的像素格式与文件中像素格式之间的差异。这里发生了太多的事情,无法做出可预测的性能估计,您需要进行分析。
位图以未公开的方式存储,平台不希望您访问实际的位图数据内存 - 强制您使用lock()在内存中创建副本。
我认为,位图存储在GDI中,每次调用GetPixel()
都会变成对gdiplus.dll
中的GdipBitmapGetPixel()
的调用。
Bitmap.LockBits()
调用GDI中的Bitmap::Lockbits
,一次性返回给定图像由GDI持有的所有像素,作为托管的BitmapData
对象。
Bitmap::Lockbits
的文档说明如下:
LockBits/UnockBits
。