2011-01-07 107 views
3

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

この問題に関しては、スタックオーバーフローに関するいくつかの質問があります。この質問の最初の回答(How to draw ARGB bitmap using GDI+?)の情報を使用して、私が試したコードを書いたところ、それが動作します。

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

とおり

それは基本的に、その後ネイティブHBITMAP幅、高さ及びのGetObject用いた画素データの位置を指すポインタとBITMAP構造を取得し、管理ビットマップコンストラクタを呼び出します私は間違っていると私を訂正してください。ネイティブのHBitmapから管理されたビットマップに実際のピクセルデータをコピーするのではなく、管理されたビットマップをネイティブHBitmapのピクセルデータにポイントするだけです。

そして、特に大きいビットマップの場合、不要なメモリコピーを避けるために、別のGraphics(DC)または別のビットマップにビットマップを描画しません。

このビットマップは、ピクチャボックスコントロールまたはフォーム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); 
} 
+0

"このコードを確認してください、それは自分のコンピュータで動作します..."のようなものは、本当に質問やトピックタイトルに属していません。 –

+0

あなたは正しいです、私はタイトルを変更しました。それは質問ですが、それにもコードがあります。 – AnAurelian

答えて

2

右はコピーされません。これはMSDNライブラリの備考セクションが言う理由です:

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

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

+0

あなたの良い発言をありがとう。私は以下の方法の別のバージョンを作った。あなたはそれを見てみることができますか?ありがとう – AnAurelian

+0

まあ、それはいいです。私はあなたがコピーしたくないと思った。 –

+0

問題は、ネイティブのHBitmapがアイコン(たとえば、SIIGBF_ICONONLYでIShellItemImageFactory :: GetImageによって返されるアイコン)である場合、ビットマップが反転されていることです。この場合、単純な_graphics.DrawImage_は機能しません。 – AnAurelian

1

ハンス・パサントが答えてくれたことを読んだあと、直ちにピクセルデータをマネージビットマップにコピーし、ネイティブビットマップを解放する方法を変更しました。

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

偉大な答え、私はこの答えで提供されたコードを使用して、それが動作しますが、時にはイメージが180度反転され180度回転されます。なぜなのかご存知ですか?前もって感謝します。 –

+0

終了した後でGraphicsオブジェクトを破棄する必要があります。好ましくは、usingステートメントでラップします。 –

8

次のコードは、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; 
    } 
+1

+1すばらしい答え!この解決法はうまくいきます、ありがとう。 – OptimizePrime

+0

@DanielPeñalbaGetBitmapFromHBitmapをどのように呼び出すことができますか?私は 'GetBitmapFromHBitmap(新しいビットマップ(" fileName ")。GetHbitmap())'を実行できないと仮定しています。エクスプローラのファイルから、アルファチャンネルが保存された管理Bitmapオブジェクトを取得できますか? – test

+0

@test:このコードは、(ネイティブな)イメージハンドルを持つように呼び出されます。マネージビットマップには使用しないでください。 –

関連する問題