Structure for holding a 16bits grayscale image

Issue

I need to mask image from memory buffers (rectangle area filled with black). So I naively re-use the Bitmap class with ImageFormat.MemoryBmp for my API. This works quite well on my local machine:

public static void MaskBitmap(Bitmap input, Rectangle maskRegion)
{
    var bytesPerPixel = Image.GetPixelFormatSize(input.PixelFormat) / 8;
    var row = new byte[maskRegion.Width * bytesPerPixel];

    var maskData = input.LockBits(maskRegion, ImageLockMode.WriteOnly, input.PixelFormat);
    for (var i = 0; i < maskData.Height; ++i)
    {
        Marshal.Copy(row, 0, maskData.Scan0 + (i * maskData.Stride), row.Length);
    }
    input.UnlockBits(maskData);
}

However when deploying to production it turns out that the following throw a NotImplementedException:

var image16 = new Bitmap(512, 512, PixelFormat.Format16bppGrayScale);

I eventually tracked it down to here:

So my question is: is there any existing class in c# that I can re-use to hold images of pixelformat type:

  • PixelFormat.Format8bppIndexed:
  • PixelFormat.Format16bppGrayScale:
  • PixelFormat.Format24bppRgb:

I know GDI+ does not support saving/displaying 16bits image, I simply need a memory structure with image-style access.


Just for reference, I tried the following hack:

var image = new Bitmap(512,512,PixelFormat.Format24bppRgb);
image.Flags = ImageFlags.ColorSpaceGray;

But Flags is read-only.

Solution

As you could see, the GDI+ Bitmap does not support the 16bpp grayscale pixel format on Linux at all, and actually its support is quite limited on Windows, too. Once I collected the limitations for both platforms, see the table under the Restrictions of Possible Pixel Formats on Different Platforms section here.

I need to mask image from memory buffers

To use completely managed in-memory representation of a bitmap both on Linux and Windows, you can use this library (disclaimer: written by me). You can create a 16bpp grayscale bitmap data by the BitmapDataFactory.CreateBitmapData method, which returns an IReadWriteBitmapData that allows a lot of managed operations (see the link that enlists the usable extension methods). You can even convert it to an actual Bitmap by the ToBitmap extension, but on Linux this converts the result to a Bitmap with 24bpp RGB pixel format.

Example:

var image16 = BitmapDataFactory.CreateBitmapData(new Size(512, 512), PixelFormat.Format16bppGrayScale);
var row = image16.FirstRow;
do
{
    for (int x = 0; x < image16.Width; x++)
    {
        // row[x] uses a Color32 structure (8 bit per color) but raw access
        // enables you to use the full 16-bit color range:
        row.WriteRaw<ushort>(x, 32768); // 50% gray
    }
} while (row.MoveNextRow());


As for the 8bpp indexed and 24bpp RGB formats, these are supported by the native Bitmap also on Linux, but please note that starting with version .NET 6 System.Drawing will be supported only on Windows by default. Microsoft recommends using other libraries instead, but you can still enable the Unix support by adding "System.Drawing.EnableUnixSupport": true to runtimeconfig.json. Or, if you decide to use my library I mentioned above, just call DrawingModule.Initialize() before anything else, which enables the Unix support without editing any config files.

Answered By – György Kőszeg

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published