1
votes

I want to create a bitmap that has 16 bits per pixel, but when I specify any of the pixel formats:

PixelFormat.Format16bppArgb1555, PixelFormat.Format16bppGrayScale, PixelFormat.Format16bppRgb555, PixelFormat.Format16bppRgb565

They all throw a System.NotImplementedException with the error message "Not Implemented". I can't allocate 16 bits per pixel manually because Bitmap.SetPixel() only allows you to set a byte to alpha, red, green, or blue (so I need to use marshalling which requires a particular pixel format). Rather than setting a byte for each color value, I'm trying to distribute 16 bits between red, green, and blue. For example 5 bits for red, 5 bits for green, and 5 bits for blue.

Below is an example of one of the 16 bits per pixel pixel formats that throws a System.NotImplementedException:

Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppArgb1555);

My system is MacOS.

1
@mjwills I have image bytes (i.e. bytes representing only an image, no header), and I'm testing to see if the format of the image the image bytes decode to is 16 bits per pixel.bit
@harold Are you on MacOS?bit

1 Answers

1
votes

System.Drawing does not support every pixel format on all platforms. Once I collected the differences (see the Restrictions of Possible Pixel Formats on Different Platforms part). I think MacOS uses the same package as the Linux version.

As Bitmap encoders cannot save images preserving the Format16bppArgb1555 pixel format anyway I suggest you to create a Bitmap instance using the closest compatible format (for example, Format32bppArgb instead of Format16bppArgb1555), and if you really want to transform the colors into the ARGB1555 color space, you can use the Quantize method with PredefinedColorsQuantizer.Argb1555 using the same library I linked for the comparison. You can download it from NuGet.

Disclaimer: I dit not test it on MacOS but it works in Linux (Ubuntu).

Rather than setting a byte for each color value, I'm trying to distribute 16 bits between red, green, and blue. For example 5 bits for red, 5 bits for green, and 5 bits for blue.

Edit: I'm already working on the next version, which will allow using a managed bitmap data with any PixelFormat on any platform. See the public BitmapDataFactory.CreateBitmapData method (not available in a public release yet but you can already play with it if you check out the Development branch).


Update:

I have image bytes (i.e. bytes representing only an image, no header), and I'm testing to see if the format of the image the image bytes decode to is 16 bits per pixel.

Now I see why you need to set pixels in a 16-bit raw format. As in Linux/MacOS the System.Drawing.Bitmap has a limited support the easiest solution if you create managed bitmap data as I mentioned above.

Note: I published a pre-release, which makes the BitmapDataFactory.CreateBitmapData method available without checking out my development branch. And now you can turn your raw bytes of any PixelFormat to a Bitmap like this:

(of course, if pfGuess is not the actual format the result can be messy)

public Bitmap ToBitmap(byte[] rawData, PixelFormat pfGuess, Size size)
{
    // the result bitmap will be ARGB32, which works on every platform
    Bitmap result = new Bitmap(size.Width, size.Height);

    // we create an accessor for the result so we can quickly copy the translated pixels
    using var bitmapDataDst = result.GetWritableBitmapData();

    // For the managed source any pixel format will work.
    // Please note though that unlike System.Drawing.Bitmap images,
    // managed bitmap data never has padding bytes at the end of each line.
    // So if your rawData has padding (eg. when size has odd width and pf is 16bpp),
    // then maybe the best if you include the padding in width.
    // See https://docs.microsoft.com/en-us/windows/win32/medfound/image-stride for details
    using var bitmapDataSrc = BitmapDataFactory.CreateBitmapData(size, pfGuess);

    // Checking if rawData is long enough. RowSize is the width in bytes.
    if (rawData.Length < bitmapDataSrc.RowSize * size.Height)
        throw new ArgumentException("rawData is too short");

    int currentByte = 0;

    // iterating through rows
    for (int y = 0; y < size.Height; y++)
    {
        var rowSrc = bitmapDataSrc[y]; // has the specified pixel format
        var rowDst = bitmapDataDst[y]; // has ARGB32 pixel format

        // writing raw pixels
        for (int x = 0; x < bitmapDataSrc.RowSize; x++)
            rowSrc.WriteRaw<byte>(x, rawData[currentByte++]);

        // copying the pixels interpreted as pfGuess to result
        for (int x = 0; x < size.Width; x++)
            rowDst[x] = rowSrc[x];
    }

    // The result bitmap is now populated from the arbitrarily interpreted raw data.
    // As it has ARGB32 format you can save it on any platform without any issues.
    return result;
}