Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are Bitmaps stored in memory in .NET?

In .NET you usually use the Bitmap class to store images. To access it quickly, you need to call lock() and unlock() to copy the contents of the bitmap into memory. So, does it mean that the bitmap was not stored in a packed memory array before locking?

Exactly what is the need for locking?, i.e. why couldn't the platform simply return a pointer to the first pixel of the bitmap and let you access the pixels directly? (apart from the "unsafe" memory access considerations)

Some possible reasons:

  • The bitmap is stored in its original compressed form to save memory (PNG, JPEG, etc)
  • The bitmap is stored in 24-bpp format so its slower to access than a 32-bpp image
  • The bitmap is not stored in a packed memory array and is fragmented so can't be read/written fast
  • The bitmap is stored in an undisclosed manner, and the platform does not want you to access the actual bitmap data memory - forcing you to create a copy of it in memory using lock()
like image 326
Robin Rodricks Avatar asked Nov 21 '12 14:11

Robin Rodricks


2 Answers

Bitmaps are read in a lazy fashion. The actual algorithm used depends a great deal on the image format, a .bmp file is easy, a .jpeg is not so easy. Under the hood, GDI+ creates a memory mapped file to map the file data into RAM, when necessary, taking advantage of the demand-paged virtual memory mapping feature of the operating system. It is this MMF that infamously creates a lock on the file and is responsible for the ratty exceptions you get when trying to save an image back to a file with the same name.

Bitmap.LockBits() creates a separate buffer that maps the pixel data from the MMF to an area in memory that has the requested pixel format. Now you have the data in a well defined format that's independent from the format in the file. Calling UnlockBits() writes the modified data back, if any. Exactly how the data from the file gets re-combined with the modified pixels is not specified and is likely to depend a great deal on the codec.

This exact same locking happens, unseen, when you use the bitmap in other methods. Like when you draw the image with Graphics.DrawImage(). And infamously when you use Bitmap.GetPixel(). As you can guess by now, there's a fair amount of overhead involved in getting the pixel data converted at the lock call, the reason that GetPixel() is so slow since you do it for every pixel in the bitmap rather than just once, as DrawImage and LockBits() do.

It should be also clear that the amount of overhead depends a great deal on the image file format, the expense of decoding the pixel data on-the-fly, the amount of caching done by the decoder, the pixel format of the file versus the pixel format you ask for in LockBits(). There's way too much going on here to make predictable perf guesses, you'll have to profile it.

like image 84
Hans Passant Avatar answered Sep 22 '22 17:09

Hans Passant


The bitmap is stored in an undisclosed manner, and the platform does not want you to access the actual bitmap data memory - forcing you to create a copy of it in memory using lock()

This, I think. The bitmap is stored in GDI, and every call to GetPixel() will become a call to GdipBitmapGetPixel() in gdiplus.dll.

Bitmap.LockBits(), which calls Bitmap::Lockbits in GDI, will return all pixels held by GDI for the given image at once, as a managed BitmapData object.

Documentation for Bitmap::Lockbits says:

locks a rectangular portion of this bitmap and provides a temporary buffer that you can use to read or write pixel data in a specified format. Any pixel data that you write to the buffer is copied to the Bitmap object when you call Bitmap::UnlockBits.

In the GDI documentation I cannot find any other way to access the 'raw' bitmap, so I think this is because of the way GDI was designed (perhaps because of the device-dependant way GDI stores bitmaps in (video?) memory?), so you have to call LockBits if you want to operate on the 'raw' bitmap data.

like image 30
CodeCaster Avatar answered Sep 22 '22 17:09

CodeCaster