potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# BitmapData.Scan0の操作でAccessViolationException例外が発生する理由と対処

本文

Image.FromHbitmap関数で作成したビットマップのような一部のビットマップではBitmap.LockBitsメソッドが返すBitmapDataのStrideが負の値になります。このようなビットマップはボトムアップ形式と呼ばれ、Scan0はビットマップの最終列の最初のピクセルを指しています*1。Strideが正のビットマップのようにScan0を使用したり、BitmapクラスのコンストラクタにScan0を指定した場合はメモリアクセス例外が発生します。

Strideが負のRGBビットマップを正のARGBビットマップに移す場合は以下のコードが使用可能です*2

bmp1:事前に作成されたPixelFormat.Format32bppRgb形式のビットマップ。

    var bmp2 = default(Bitmap);
    try
    {
        bmp2 = new Bitmap(bmp1.Width, bmp1.Height, PixelFormat.Format32bppArgb);

        var bound = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
        var data1 = bmp1.LockBits(bound, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
        var data2 = bmp2.LockBits(bound, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

        if (data1.Stride > 0)
        {
            NativeMethod.RtlMoveMemory(data2.Scan0, data1.Scan0, data1.Stride * data1.Height);
        }
        else
        {
            var stride = -data1.Stride;
            var p1 = data1.Scan0 + data1.Stride * (data1.Height - 1);
            var p2 = data2.Scan0 + stride * (data1.Height - 1);
            for (var y = 0; y < data1.Height; y++)
            {
                NativeMethod.RtlMoveMemory(p2, p1, stride);

                p1 += stride;
                p2 -= stride;
            }
        }

        bmp2.UnlockBits(data2);
        bmp1.UnlockBits(data1);

        // TODO:ここでbmp2を処理します。

    }
    catch
    {
        bmp2?.Dispose();
        throw;
    }
}
private static class NativeMethods
{
    [DllImport("Kernel32.dll")]
    public static extern void RtlMoveMemory(
        IntPtr dest,
        IntPtr src,
        [MarshalAs(UnmanagedType.U4)]int size);
}

参考

stackoverflow.com

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。

*1:間違っているかもしれません。

*2:間違っているかもしれません。