Bitmap.LockBits 中 ImageLockMode 的用途(附代码)

Xan*_*vis 3 .net c# unsafe bitmap image-processing

Bitmap.LockBits 中 ImageLockMode 的用途是什么?\n对于 ReadOnly,文档仅说明

\n\n
\n

ReadOnly:指定图像的一部分被锁定以供读取。

\n
\n\n

但下面的代码证明,这不是真的。\n我知道这个问题之前已经被问过,这次我尝试使用一些实际的代码,因为我在其他地方找不到答案。

\n\n

如果我运行以下代码,它的行为与答案中所解释的完全一样。

\n\n
using System;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Runtime.InteropServices;\n\nnamespace LockBits_Trials\n{\n   class Program\n   {\n      static readonly Random rnd = new Random(42);\n      static void Main(string[] args)\n      {\n         Bitmap bmp_fromFile = new Bitmap("example.png");\n         Bitmap bmp_fromCtor = new Bitmap(100, 100, PixelFormat.Format24bppRgb);\n         marshalCopy(bmp_fromFile, "result_marshalCopy_fromFile.png");\n         marshalCopy(bmp_fromCtor, "result_marshalCopy_fromCtor.png");\n         usePointer(bmp_fromFile, "result_usePointer_fromFile.png");\n         usePointer(bmp_fromCtor, "result_usePointer_fromCtor.png");\n      }\n\n      private static unsafe void usePointer(Bitmap bmp, string filename)\n      {\n         ImageLockMode mode = ImageLockMode.ReadOnly;\n         //code from turgay at http://csharpexamples.com/fast-image-processing-c/ \n         if (bmp.PixelFormat != PixelFormat.Format24bppRgb)\n            throw new Exception();\n         BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), mode, bmp.PixelFormat);\n         int bytesPerPixel = 3; int heightInPixels = bitmapData.Height; int widthInBytes = bitmapData.Width * bytesPerPixel;\n         byte* ptrFirstPixel = (byte*)bitmapData.Scan0;\n         for (int y = 0; y < heightInPixels; y++) {\n            byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);\n            for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) {\n               currentLine[x] = (byte)rnd.Next(0, 255);\n               currentLine[x + 1] = (byte)rnd.Next(0, 255);\n               currentLine[x + 2] = (byte)rnd.Next(0, 255);\n            }\n         }\n         bmp.UnlockBits(bitmapData);\n         bmp.Save(filename, ImageFormat.Png);\n      }\n\n      private static unsafe void marshalCopy(Bitmap bmp, string filename)\n      {\n         ImageLockMode mode = ImageLockMode.ReadOnly;\n         if (bmp.PixelFormat != PixelFormat.Format24bppRgb)\n            throw new Exception();\n         BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), mode, bmp.PixelFormat);\n         IntPtr ptrFirstPixel = bitmapData.Scan0;\n         int totalBytes = bitmapData.Stride * bitmapData.Height;\n         byte[] newData = new byte[totalBytes];\n         for (int i = 0; i < totalBytes; i++)\n            newData[i] = (byte)rnd.Next(0, 255);\n         Marshal.Copy(newData, 0, ptrFirstPixel, newData.Length);\n         bmp.UnlockBits(bitmapData);\n         bmp.Save(filename, ImageFormat.Png);\n      }\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

图片result_marshalCopy_fromFile.pngresult_usePointer_fromFile.png都包含原始图像,因此不会覆盖任何内容(并且不会引发异常!)。\n另外两张图片包含随机噪声,这些噪声是在锁定时写入的。

\n\n

我没有做进一步的测试来确认并行写入访问的行为,因为我无论如何都不会这样做。

\n\n

它不是重复的,但密切相关:\n Bitmap.LockBits \xe2\x80\x9cpin\xe2\x80\x9d 是否是内存中的位图?

\n

sau*_*rol 5

正如您所观察到的,一旦您获得了对原始数据指针的访问权限,就没有什么可以阻止您写入该内存,无论您请求的锁类型如何。锁类型仅在两种情况下真正有用:

1)如果多位代码同时请求锁,一次只允许发出一个写锁,而读锁可以共享。当然,这取决于代码是否正确使用它们来获取锁。

2) 并非所有锁都直接由 Bitmap 内存支持。在您的示例中,这是因为您创建了一个内存位图,然后请求了相同像素格式的锁定。然而,位图可以表示其他 GDI+ 对象,例如设备上下文拥有的像素。此外,如果您请求源像素格式以外的像素格式,则可能需要对其进行转换。在这两种情况下,当请求读取锁定时,GDI+ 可能必须从真实源中提取位图的副本,并以您请求的像素格式将其提供给您。如果您要修改该副本,它不会被写回源上下文。如果您请求写入锁定,一旦您释放锁定,GDI+ 就会知道将像素复制回源。