.NET中位图是如何在内存中存储的?

6
在.NET中,通常使用Bitmap类来存储图像。为了快速访问它,您需要调用lock()和unlock()来将位图的内容复制到内存中。那么,这是否意味着在锁定之前,位图未存储在紧密的内存数组中?
准确地说,什么是锁定的必要性?也就是说,除了"不安全"的内存访问考虑因素外,平台为什么不能简单地返回位图的第一个像素的指针,让您直接访问像素?
一些可能的原因:
  • 位图以其原始压缩形式存储以节省内存(PNG、JPEG等)
  • 位图以24bpp格式存储,因此访问速度比32bpp图像慢
  • 位图未存储在紧密的内存数组中,并且是分散的,因此无法快速读取/写入
  • 位图以未公开的方式存储,平台不想让您访问实际位图数据内存——强制您使用lock()在内存中创建副本
3个回答

12

位图以延迟方式读取。实际使用的算法取决于图片格式,.bmp文件很容易处理,.jpeg则不太容易。在底层,GDI+创建内存映射文件将文件数据映射到RAM中,在必要时利用操作系统的需求分页虚拟内存映射功能。这就是臭名昭著的MMF,它在同名文件保存图像时会锁定文件并导致异常。

Bitmap.LockBits() 创建一个单独的缓冲区,将像素数据从MMF映射到内存中请求的像素格式的区域。现在您可以得到以文件格式无关的明确定义格式的数据。如果有修改,则调用 UnlockBits() 将修改后的数据写回。文件中的数据如何与修改过的像素重新组合未指定且可能取决于编解码器。

当您在其他方法中使用位图时,例如使用 Graphics.DrawImage() 绘制图像和 Bitmap.GetPixel() 时,同样会进行这种锁定,但是这个过程是不可见的。由于每个像素都需要运行 GetPixel() 而不是像 DrawImage 和 LockBits() 只运行一次,因此 GetPixel() 处理速度非常慢,其中涉及了大量的开销。

显然,开销量取决于图像文件格式、即时解码像素数据的费用、解码器执行的缓存量以及 LockBits() 中请求的像素格式与文件中像素格式之间的差异。这里发生了太多的事情,无法做出可预测的性能估计,您需要进行分析。


太棒了,Hans!不过,有一个问题:如果位图必须在某个时刻渲染到屏幕上,那么将位图“锁定”到内存中一次,然后在内存上执行所有计算并将内存用于渲染是否有意义?(考虑到我正在编写自己的位图渲染器) - Robin Rodricks
1
是的,将位图的像素格式转换为与视频适配器兼容的格式是有意义的。这样,像素数据就可以直接传送到视频内存中而无需转换。在任何现代适配器上,32bppPArgb都比其他格式快十倍 - Hans Passant
32bpp ARGB是在使用C++编写的低级应用程序时最快的格式吗?“ccxvii”表示,BGRA是视频/图形卡的最快和本地格式。-http://gamedev.stackexchange.com/a/44153/15277 - Robin Rodricks
1
不,它是32bppPArgb。预乘alpha很重要,这样当发送到视频适配器时就不必应用alpha效果。BGRA不相关,它不是Bitmap类支持的像素格式。否则只是一个字节序的问题,小端还是大端。Bitmap已经将它们放在正确的顺序中了。 - Hans Passant

3

位图以未公开的方式存储,平台不希望您访问实际的位图数据内存 - 强制您使用lock()在内存中创建副本。

我认为,位图存储在GDI中,每次调用GetPixel()都会变成对gdiplus.dll中的GdipBitmapGetPixel()的调用。

Bitmap.LockBits()调用GDI中的Bitmap::Lockbits,一次性返回给定图像由GDI持有的所有像素,作为托管的BitmapData对象。

Bitmap::Lockbits的文档说明如下:

锁定位图的矩形区域并提供临时缓冲区,您可以在指定格式中使用它来读取或写入像素数据。将任何像素数据写入缓冲区时,当您调用Bitmap::UnlockBits时,它会被复制到位图对象中。在GDI文档中,我找不到其他访问“原始”位图的方法,因此我认为这是GDI设计的方式(也许是因为GDI以设备相关的方式将位图存储在(视频?)内存中),因此如果要操作“原始”位图数据,则必须调用LockBits。

1
为什么平台不能简单地返回位图的第一个像素的指针,让您直接访问像素?
只是一个想法:所有图像数据(“像素”)都可以存储在显卡的内存中(而不是CPU的内存)。因此,CPU/应用程序无法直接访问图像数据。结果,应该有一种特殊的方式通过请求从CPU获取对图像数据的访问。就像LockBits/UnockBits

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接