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.
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://learn.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;
}