2016年3月9日水曜日

C#のPixelFormat変換

前回はPixelFormat8に対するエラーの対策として、画像の保存方法を変更するという手段で対応した。しかし今回、プログラム内で対処する必要に迫られたので変換するメソッドを作ってみた。

static Bitmap convert(Bitmap Src)
{
    if (Src.PixelFormat != PixelFormat.Format8bppIndexed)
    {
        throw (new Exception());
    }
            
    ColorPalette palette = Src.Palette;
    Bitmap Dst = new Bitmap(Src.Width, Src.Height, PixelFormat.Format32bppArgb);

    BitmapData SrcData = Src.LockBits(new Rectangle(0, 0, Src.Width, Src.Height),
        ImageLockMode.ReadOnly, Src.PixelFormat);

    BitmapData DstData = Dst.LockBits(new Rectangle(0, 0, Dst.Width, Dst.Height),
            ImageLockMode.WriteOnly, Dst.PixelFormat);

    IntPtr SrcAdr = SrcData.Scan0;
    IntPtr DstAdr = DstData.Scan0;
            
    for(int y = 0; y < Src.Height; y++)
    {
        for(int x = 0; x < Src.Width; x++)
        {
            Color color;

            {
                int pos = x + SrcData.Stride * y;

                byte index = Marshal.ReadByte(SrcAdr, pos);
                color = palette.Entries[index];
            }

            {
                int pos = x * 4 + DstData.Stride * y;

                Marshal.WriteByte(DstAdr, pos + 0, color.B);
                Marshal.WriteByte(DstAdr, pos + 1, color.G);
                Marshal.WriteByte(DstAdr, pos + 2, color.R);
                Marshal.WriteByte(DstAdr, pos + 3, color.A);
            }
        }
    }
            
    Src.UnlockBits(SrcData);
    Dst.UnlockBits(DstData);

    return (Dst);
}

コードを読めばわかると思うが、入力はFormat8bppIndexedのみで、出力はFormat32bppArgbとなる。違うフォーマットに対応するには2段forをそれぞれに作る必要があり、FormatNNに対応するだけでも少なくとも196種類の分岐が必要となる。さすがに使う用もないのに実装するのも面倒なので今回は8bppIndexedから32bppArgbのみの実装とした。
ピクセルフォーマットの変換って地味に重要なのでC#に付属してても良さそうな気がするんだけど。特に8bppは今でも使われる部分はあるだろうし、Grapicsに使うには8bppは不可だし。

1 件のコメント:

  1. Bitmap.Clone でフォーマットを指定すると、32bppArgbに変換できますよ!
    https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.bitmap.clone?view=dotnet-plat-ext-3.1

    返信削除