2011-01-07 61 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; 
    } 
相关问题