6

ロックされていないビットマップ アンマネージ メモリから直接読み書きしてもよろしいですか?

Bitmap の UnlockBits 後、BitmapData を使い続けることはできますか? 別のスレッドが同じビットマップにピクセルを書き込んでいる間に、マウス位置で PictureBox のビットマップのピクセルを読み取ることができるテスト アプリを作成しました。

編集 1:Boing彼の回答で指摘されているように、「Scan0 は Bitmap オブジェクトの実際のピクセル データを指していません。むしろ、Bitmap オブジェクトのピクセル データの一部を表す一時バッファを指しています。」MSDNから。

しかし、Scan0 を取得すると、Lockbits や UnlockBits を必要とせずに Bitmap を読み書きできるようになります。私はこれをスレッドで何度もやっています。MSDN によれば、Scan0 は Bitmap データの COPY を指しているため、これは発生しないはずです。C# では、すべてのテストでコピーではないことが示されます。C++ では、正常に動作するかどうかわかりません。

編集 2:回転メソッドを使用すると、OS がビットマップ ピクセル データのコピーを解放することがあります。結論、it is not safe to read/write an unlocked Bitmap Scan0。Boing さん、回答とコメントをありがとうございます。

以下は、BitmapData を取得し、ピクセル値を読み書きする方法です。

    /// <summary>
    /// Locks and unlocks the Bitmap to get the BitmapData.
    /// </summary>
    /// <param name="bmp">Bitmap</param>
    /// <returns>BitmapData</returns>
    public static BitmapData GetBitmapData(Bitmap bmp)
    {
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
        bmp.UnlockBits(bmpData);
        return bmpData;
    }

    /// <summary>
    /// Get pixel directly from unamanged pixel data based on the Scan0 pointer.
    /// </summary>
    /// <param name="bmpData">BitmapData of the Bitmap to get the pixel</param>
    /// <param name="p">Pixel position</param>
    /// <param name="channel">Channel</param>
    /// <returns>Pixel value</returns>
    public static byte GetPixel(BitmapData bmpData, Point p, int channel)
    {
        if ((p.X > bmpData.Width - 1) || (p.Y > bmpData.Height - 1))
            throw new ArgumentException("GetPixel Point p is outside image bounds!");

        int bitsPerPixel = ((int)bmpData.PixelFormat >> 8) & 0xFF;
        int bpp = bitsPerPixel / 8;
        byte data;
        int id = p.Y * bmpData.Stride + p.X * bpp;
        unsafe
        {
            byte* pData = (byte*)bmpData.Scan0;
            data = pData[id + channel];
        }
        return data;
    }

    //Non UI Thread
    private void DrawtoBitmapLoop()
    {
        while (_drawBitmap)
        {
            _drawPoint = new Point(_drawPoint.X + 10, _drawPoint.Y + 10);
            if (_drawPoint.X > _backImageData.Width - 20)
                _drawPoint.X = 0;
            if (_drawPoint.Y > _backImageData.Height - 20)
                _drawPoint.Y = 0;

            DrawToScan0(_backImageData, _drawPoint, 1);

            Thread.Sleep(10);
        }
    }

    private static void DrawToScan0(BitmapData bmpData, Point start, int channel = 0)
    {
        int x = start.X;
        int y = start.Y;
        int bitsPerPixel = ((int)bmpData.PixelFormat >> 8) & 0xFF;
        int bpp = bitsPerPixel / 8;
        for (int i = 0; i < 10; i++)
        {
            unsafe
            {
                byte* p = (byte*)bmpData.Scan0;
                int id = bmpData.Stride * y + channel + (x + i) * bpp;
                p[id] = 255;
            }
        }
    }
4

1 に答える 1