2011-01-07 60 views
3

假設我從本機Windows函數獲取HBITMAP對象/句柄。我可以使用Bitmap.FromHbitmap(nativeHBitmap)將其轉換爲託管位圖,但如果本機圖像具有透明度信息(Alpha通道),則通過此轉換會丟失。在C#中使用本地HBitmap,同時保留Alpha通道/透明度

關於此問題,有幾個關於Stack Overflow的問題。使用這個問題的第一個答案(How to draw ARGB bitmap using GDI+?)中的信息,我寫了一段我已經嘗試過的代碼並且可以工作。

它基本上得到天然HBITMAP寬度,高度和使用GetObject的指針的像素數據的位置和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); 
} 
+0

像「請檢查此代碼,它適用於我的計算機...」真的不屬於問題或主題標題。 – 2011-01-07 18:01:34

+0

你說得對,我改了標題。這是一個問題,但它也包含代碼。 – AnAurelian 2011-01-07 18:04:40

回答

2

對,沒有複製。這就是爲什麼在MSDN Library的備註部分說:

調用者負責 分配和釋放由SCAN0 參數指定 內存塊,但是,內存應該 不會被釋放,直到相關的 位圖已發佈。

如果像素數據被複制,這不會成爲問題。順便說一下,這通常是一個難以解決的問題。您無法分辨客戶端代碼何時調用Dispose(),因此無法攔截該呼叫。這使得不可能使這樣一個位圖像一個位圖的替代行爲。客戶端代碼必須意識到需要額外的工作。

+0

謝謝你的好評。我做了下面列出的方法的另一個版本。你可以看看嗎?謝謝 – AnAurelian 2011-01-07 18:01:19

+0

嗯,那很好。我以爲你不想複製。 – 2011-01-07 18:36:29

1

在閱讀Hans Passant在他的回答中提出的優點之後,我改變了方法,立即將像素數據複製到託管位圖中,並釋放本地位圖。

我正在創建兩個託管位圖對象(但只有一個爲實際像素數據分配內存),並使用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; 
    } 
8

下面的代碼爲我工作,即使HBITMAP是圖標或BMP,它不會翻轉圖像時,它的圖標,並且還與不包含Alpha通道的位圖:

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; 
    } 
相關問題