如何在 MacOS 上创建每像素 16 位的位图?

How to create a bitmap with 16 bits per pixel on MacOS?

我想创建一个每像素 16 位的位图,但是当我指定任何像素格式时:

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

他们都抛出 System.NotImplementedException 错误信息 "Not Implemented"。我无法手动为每个像素分配 16 位,因为 Bitmap.SetPixel() 只允许您将一个字节设置为 alpha、红色、绿色或蓝色(因此我需要使用需要特定像素格式的编组)。我没有为每个颜色值设置一个字节,而是尝试在红色、绿色和蓝色之间分配 16 位。例如,红色 5 位,绿色 5 位,蓝色 5 位。

下面是抛出 System.NotImplementedException:

的每像素 16 位像素格式之一的示例
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppArgb1555);

我的系统是 MacOS。

System.Drawing 并非在所有平台上都支持所有像素格式。一旦我 collected 差异(请参阅 不同平台上可能的像素格式的限制 部分)。我认为 MacOS 使用与 Linux 版本相同的包。

由于 Bitmap 编码器无法保存保留 Format16bppArgb1555 像素格式的图像,我建议您使用最接近的兼容格式创建一个 Bitmap 实例(例如,Format32bppArgb 而不是 Format16bppArgb1555),如果你真的想将颜色转换为 ARGB1555 颜色 space,你可以使用 Quantize method with PredefinedColorsQuantizer.Argb1555 using the same library I linked for the comparison. You can download it from NuGet.

免责声明:我没有在 MacOS 上测试它,但它在 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.

编辑:我已经在开发下一个版本,它将允许在任何平台上使用任何 PixelFormat 的托管位图数据。请参阅 public BitmapDataFactory.CreateBitmapData 方法(在 public 版本中尚不可用,但如果您查看 Development 分支,您已经可以使用它了)。


更新:

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.

现在我明白为什么您需要以 16 位原始格式设置像素了。与 Linux/MacOS 一样,System.Drawing.Bitmap 的支持有限,如果您按照我上面提到的那样创建托管位图数据,这是最简单的解决方案。

注意: 我发布了一个 pre-release,它使 BitmapDataFactory.CreateBitmapData 方法在不检查我的开发分支的情况下可用。现在您可以将任何 PixelFormat 的原始字节转换为 Bitmap,如下所示:

(当然,如果 pfGuess 不是实际格式,结果可能会很乱)

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;
}