3

ネイティブ Windows 関数から HBITMAP オブジェクト/ハンドルを取得するとします。Bitmap.FromHbitmap(nativeHBitmap)を使用してマネージド ビットマップに変換できますが、ネイティブ イメージに透明度情報 (アルファ チャネル) がある場合、この変換によって失われます。

この問題に関して、Stack Overflow でいくつか質問があります。この質問の最初の回答 ( GDI+ を使用して ARGB ビットマップを描画する方法は? ) の情報を使用して、試したコードを書きましたが、動作します。

これは基本的に、 GetObjectBITMAP構造体を使用して、ネイティブの HBitmap の幅、高さ、およびピクセル データの場所へのポインターを取得し、マネージド Bitmap コンストラクターを呼び出します。

Bitmap managedBitmap = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight,
    bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

私が理解しているように (間違っている場合は修正してください)、これは実際のピクセル データをネイティブ HBitmap からマネージド ビットマップにコピーするのではなく、マネージド ビットマップをネイティブ HBitmap のピクセル データにポイントするだけです。

ここでは、別のグラフィックス (DC) または別のビットマップにビットマップを描画しません。これは、特に大きなビットマップの場合に不要なメモリ コピーを避けるためです。

このビットマップを PictureBox コントロールまたは Form BackgroundImage プロパティに割り当てるだけです。そしてそれは機能し、透明度を使用してビットマップが正しく表示されます。

ビットマップを使用しなくなったら、BackgroundImage プロパティがビットマップを指していないことを確認し、マネージ ビットマップとネイティブ HBitmap の両方を破棄します。

質問:この推論とコードが正しいかどうか教えてください。予期しない動作やエラーが発生しないことを願っています。そして、すべてのメモリとオブジェクトを正しく解放していることを願っています。

    private void Example()
    {
        IntPtr nativeHBitmap = IntPtr.Zero;

        /* Get the native HBitmap object from a Windows function here */

        // Create the BITMAP structure and get info from our nativeHBitmap
        NativeMethods.BITMAP bitmapStruct = new NativeMethods.BITMAP();
        NativeMethods.GetObjectBitmap(nativeHBitmap, Marshal.SizeOf(bitmapStruct), ref bitmapStruct);

        // Create the managed bitmap using the pointer to the pixel data of the native HBitmap
        Bitmap managedBitmap = new Bitmap(
            bitmapStruct.bmWidth, bitmapStruct.bmHeight, bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

        // Show the bitmap
        this.BackgroundImage = managedBitmap;

        /* Run the program, use the image */
        MessageBox.Show("running...");

        // When the image is no longer needed, dispose both the managed Bitmap object and the native HBitmap
        this.BackgroundImage = null;
        managedBitmap.Dispose();
        NativeMethods.DeleteObject(nativeHBitmap);
    }

internal static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAP
    {
        public int bmType;
        public int bmWidth;
        public int bmHeight;
        public int bmWidthBytes;
        public ushort bmPlanes;
        public ushort bmBitsPixel;
        public IntPtr bmBits;
    }

    [DllImport("gdi32", CharSet = CharSet.Auto, EntryPoint = "GetObject")]
    public static extern int GetObjectBitmap(IntPtr hObject, int nCount, ref BITMAP lpObject);

    [DllImport("gdi32.dll")]
    internal static extern bool DeleteObject(IntPtr hObject);
}
4

3 に答える 3

13

次のコードHBITMAPは、がアイコンまたはbmpであっても機能し、アイコンの場合は画像を反転せず、アルファチャネルを含まないビットマップでも機能します。

    private static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
    {
        Bitmap bmp = Bitmap.FromHbitmap(nativeHBitmap);

        if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
            return bmp;

        BitmapData bmpData;

        if (IsAlphaBitmap(bmp, out bmpData))
            return GetlAlphaBitmapFromBitmapData(bmpData);

        return bmp;
    }

    private static Bitmap GetlAlphaBitmapFromBitmapData(BitmapData bmpData)
    {
        return new Bitmap(
                bmpData.Width,
                bmpData.Height,
                bmpData.Stride,
                PixelFormat.Format32bppArgb,
                bmpData.Scan0);
    }

    private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
    {
        Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);

        bmpData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);

        try
        {
            for (int y = 0; y <= bmpData.Height - 1; y++)
            {
                for (int x = 0; x <= bmpData.Width - 1; x++)
                {
                    Color pixelColor = Color.FromArgb(
                        Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));

                    if (pixelColor.A > 0 & pixelColor.A < 255)
                    {
                        return true;
                    }
                }
            }
        }
        finally
        {
            bmp.UnlockBits(bmpData);
        }

        return false;
    }
于 2012-02-15T09:53:05.753 に答える
2

そうです、コピーは作成されません。これが、MSDN ライブラリの備考セクションに次のように記載されている理由です。

呼び出し元は、scan0 パラメータで指定されたメモリ ブロックの割り当てと解放を担当しますが、メモリは、関連するビットマップが解放されるまで解放されるべきではありません。

ピクセル データがコピーされていれば、これは問題になりません。ちなみに、これは通常、対処が難しい問題です。クライアント コードがいつ Dispose() を呼び出したかはわかりません。その呼び出しをインターセプトする方法はありません。これにより、そのようなビットマップを Bitmap の代わりのように動作させることができなくなります。クライアント コードは、追加の作業が必要であることを認識している必要があります。

于 2011-01-07T16:50:58.483 に答える
1

Hans Passant の回答の良い点を読んだ後、メソッドを変更して、ピクセル データをマネージ ビットマップにすぐにコピーし、ネイティブ ビットマップを解放しました。

2 つのマネージ ビットマップ オブジェクトを作成し (ただし、実際のピクセル データ用にメモリを割り当てるのは 1 つだけです)、graphics.DrawImageを使用して画像をコピーします。これを達成するためのより良い方法はありますか?または、これは十分に高速ですか?

    public static Bitmap CopyHBitmapToBitmap(IntPtr nativeHBitmap)
    {
        // Get width, height and the address of the pixel data for the native HBitmap
        NativeMethods.BITMAP bitmapStruct = new NativeMethods.BITMAP();
        NativeMethods.GetObjectBitmap(nativeHBitmap, Marshal.SizeOf(bitmapStruct), ref bitmapStruct);

        // Create a managed bitmap that has its pixel data pointing to the pixel data of the native HBitmap
        // No memory is allocated for its pixel data
        Bitmap managedBitmapPointer = new Bitmap(
            bitmapStruct.bmWidth, bitmapStruct.bmHeight, bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

        // Create a managed bitmap and allocate memory for pixel data
        Bitmap managedBitmapReal = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight, PixelFormat.Format32bppArgb);

        // Copy the pixels of the native HBitmap into the canvas of the managed bitmap
        Graphics graphics = Graphics.FromImage(managedBitmapReal);
        graphics.DrawImage(managedBitmapPointer, 0, 0);

        // Delete the native HBitmap object and free memory
        NativeMethods.DeleteObject(nativeHBitmap);

        // Return the managed bitmap, clone of the native HBitmap, with correct transparency
        return managedBitmapReal;
    }
于 2011-01-07T18:00:19.840 に答える