在.NET中,您通常使用Bitmap类来存储图像。要快速访问它,您需要调用lock() and unlock()将位图的内容复制到内存中。那么,这是否意味着在锁定之前位图没有存储在打包的内存数组中?
究竟是什么需要锁定?,即为什么平台不能简单地返回指向位图第一个像素的指针并让你直接访问像素? (除了“不安全”的内存访问注意事项)
一些可能的原因:
答案 0 :(得分:12)
以懒惰的方式读取位图。使用的实际算法在很大程度上取决于图像格式,.bmp文件很容易,.jpeg并不那么容易。在引擎盖下,GDI +创建了一个内存映射文件,以便在必要时将文件数据映射到RAM中,从而利用操作系统的按需分页虚拟内存映射功能。正是这个MMF臭名昭着地创建了一个锁定文件,并且负责在尝试将图像保存回具有相同名称的文件时获得的稀有异常。
Bitmap.LockBits()创建一个单独的缓冲区,用于将MMF中的像素数据映射到内存中具有所请求像素格式的区域。现在,您可以使用定义良好的格式的数据,该格式与文件中的格式无关。调用UnlockBits()会将修改后的数据写回(如果有)。确切地说,如何将文件中的数据与修改后的像素重新组合,并且可能在很大程度上取决于编解码器。
当您在其他方法中使用位图时,看不到这种完全相同的锁定。就像用Graphics.DrawImage()绘制图像一样。当你使用Bitmap.GetPixel()时臭名昭着。正如您现在可以猜到的那样,在锁定调用中获取像素数据所涉及的开销相当大,因为GetPixel()因为您对位图中的每个像素执行而不是仅仅执行一次,因此速度非常慢,因为DrawImage和LockBits()可以。
还应该清楚的是,开销量在很大程度上取决于图像文件格式,即时解码像素数据的费用,解码器完成的缓存量,像素格式文件与您在LockBits()中要求的像素格式。这里有太多的东西可以做出可预测的猜测,你必须对它进行分析。
答案 1 :(得分:3)
位图以未公开的方式存储,平台不希望您访问实际的位图数据存储器 - 强制您使用lock()
在内存中创建它的副本
我想。位图存储在GDI中,每次调用GetPixel()
都会调用gdiplus.dll
中的GdipBitmapGetPixel()
。
Bitmap::Lockbits
的 Bitmap.LockBits()
将立即返回GDI为给定图像保留的所有像素,作为托管BitmapData
对象。
Bitmap::Lockbits
的文档说:
锁定此位图的矩形部分,并提供一个临时缓冲区,您可以使用该缓冲区以指定格式读取或写入像素数据。当您调用Bitmap :: UnlockBits时,您写入缓冲区的任何像素数据都将复制到Bitmap对象。
在GDI文档中,我找不到任何其他方式来访问'原始'位图,所以我认为这是因为GDI的设计方式(可能是因为GDI存储位图的设备依赖方式(视频?)内存?),如果你想对“原始”位图数据进行操作,你有来调用LockBits
。
答案 2 :(得分:1)
为什么平台不能简单地返回指向位图第一个像素的指针,让你直接访问像素?
只是一个想法:所有图像数据(“像素”)都可以存储在视频卡的内存中(而不是CPU的内存中)。因此,无法直接访问CPU /应用程序中的图像数据。因此,应该有一种特殊的方法可以通过请求从CPU访问图像数据。就像LockBits/UnockBits
。