在C#中使用本机HBitmap,同时保留alpha通道/透明度
假设我从本机Windows函数中获得了一个HBITMAP对象/句柄。我可以使用bitmap.FromHbitmap(nativeHBitmap)将其转换为托管位图,但如果本机图像具有透明度信息(alpha通道),则此转换会丢失它 关于这个问题,有几个关于堆栈溢出的问题。使用这个问题的第一个答案()中的信息,我编写了一段代码,我已经尝试过了,它可以正常工作 它基本上使用GetObject和位图结构获取本机HBitmap宽度、高度和指向像素数据位置的指针,然后调用托管位图构造函数:在C#中使用本机HBitmap,同时保留alpha通道/透明度,c#,bitmap,gdi,alpha-transparency,hbitmap,C#,Bitmap,Gdi,Alpha Transparency,Hbitmap,假设我从本机Windows函数中获得了一个HBITMAP对象/句柄。我可以使用bitmap.FromHbitmap(nativeHBitmap)将其转换为托管位图,但如果本机图像具有透明度信息(alpha通道),则此转换会丢失它 关于这个问题,有几个关于堆栈溢出的问题。使用这个问题的第一个答案()中的信息,我编写了一段代码,我已经尝试过了,它可以正常工作 它基本上使用GetObject和位图结构获取本机HBitmap宽度、高度和指向像素数据位置的指针,然后调用托管位图构造函数: Bitmap m
Bitmap managedBitmap = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight,
bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);
据我所知(如果我错了,请纠正我),这不会将实际像素数据从本机HBitmap复制到托管位图,它只是将托管位图指向本机HBitmap的像素数据
我不会在另一个图形(DC)或另一个位图上绘制位图,以避免不必要的内存复制,尤其是对于大型位图
我可以简单地将此位图分配给PictureBox控件或FormBackgroundImage属性。它工作正常,位图正确显示,使用透明度
当我不再使用位图时,我会确保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);
}
对,没有复制。这就是为什么MSDN库的备注部分说: 打电话的人负责 分配和释放数据块 由scan0指定的内存 但是,内存应该 在相关文件发布之前不得发布 位图已发布
如果复制像素数据,这不会是问题。顺便说一句,这通常是一个很难处理的问题。您无法判断客户端代码何时调用Dispose(),无法拦截该调用。这使得不可能使这样的位图像位图的替代品一样工作。客户端代码必须知道还需要额外的工作。在阅读了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;
}
以下代码适用于我,即使
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;
}
私有静态位图GetBitmapFromHBitmap(IntPtr nativeHBitmap)
{
位图bmp=位图.FromHbitmap(nativeHBitmap);
if(位图.GetPixelFormatSize(bmp.PixelFormat)<32)
返回bmp;
位图数据bmpData;
if(isalHabitMap(bmp,out bmpData))
返回getLalHabitMapFromBitMapData(bmpData);
返回bmp;
}
私有静态位图GetLalHabitMapFromBitMapData(BitmapData bmpData)
{
返回新位图(
bmpData.Width,
bmpData.高度,
bmpData.Stride,
PixelFormat.Format32bppArgb,
bmpData.Scan0);
}
专用静态布尔ISALPABITMAP(位图bmp,输出位图数据bmpData)
{
矩形bmBounds=新矩形(0,0,bmp.Width,bmp.Height);
bmpData=bmp.LockBits(bmBounds,ImageLockMode.ReadOnly,bmp.PixelFormat);
尝试
{
for(int y=0;y感谢您的好评。我制作了下面列出的方法的另一个版本。您能看一下吗?谢谢,很好。我以为您不想复制。问题是,如果本机HBitmap是一个图标(例如,由IShellItemImageFactory::GetImage with SIIGBF_Iconly返回),则位图会反转(从上到下)。因此简单的graphics.DrawImage在这种情况下不起作用。听起来像是一个bug。您可以使用Bitmap.RotateFlip()来修复它。是的,我确实使用了Bitmap.RotateFlip(),它适用于图标,但IShellItemImageFactory::GetImage可以正确获取缩略图。那么我如何检查返回的HBITMAP是否反转(图标是否反转)?类似“请检查此代码,它在我的计算机上工作…”真的不属于问题或主题标题。你是对的,我更改了标题。这是一个问题,但其中也包含代码。回答很好,我使用了此答案中提供的代码,它可以工作,但有时图像翻转180º并旋转180º。你知道为什么吗?提前谢谢。你应该在完成后处理图形对象最好用using语句包装它。+1是一个很棒的答案!这个解决方案非常适合,谢谢。@DanielPeñalba你能告诉我如何调用GetBitmapFromHBitmap吗?我假设我不能GetBitmapFromHBitmap(新位图(“文件名”).GetHbitmap()
因为这会破坏整个目的,对吗?那么从资源管理器中的文件中,我会得到一个保留alpha通道的托管位图对象吗?@test:这段代码的目的是调用一个(本机)图像句柄。不要将其用于托管位图。使用有用的可重用方法,这是一个很好的答案。它帮助我获得文件夹图标(调用具有适当透明度的IShellItemImageFactory.GetImage())之前尝试过“Bitmap.MakeTransparent()”,但这不是一个好主意。