为了改进this answer,我正在寻找一种方法来确定通过HBITMAP
引用的位图是否包含Alpha通道。
我理解,我可以致电GetObject
,并检索BITMAP
结构:
BITMAP bm = { 0 };
::GetObject(hbitmap, sizeof(bm), &bm);
但这只能得到存储像素颜色所需的位数。它没有告诉我,实际使用了哪些位,或者它们与各个通道的关系。例如,16bpp位图可以编码5-6-5 BGR图像,或1-5-5-5 ABGR图像。同样,32bpp位图可以存储ABGR或xBGR数据。
我可以更进一步,探索DIBSECTION而不是(如果可用):
bool is_dib = false;
BITMAP bm = { 0 };
DIBSECTION ds = { 0 };
if ( sizeof(ds) == ::GetObject(hbitmap, sizeof(ds), &ds ) {
is_dib = true;
} else {
::GetObject(hbitmap, sizeof(bm), &bm );
}
虽然这可以消除16bpp案例的歧义(使用dsBitfields
成员),但在32bpp图像的情况下仍然无法确定是否存在alpha通道。
有没有办法找出,通过HBITMAP
引用的位图是否包含alpha通道(以及哪些位用于它),或者这些信息根本不可用?
答案 0 :(得分:3)
你无法明确地知道,但如果你愿意迭代像素,你可以做出良好的教育猜测..
(暂时忽略带有1位alpha通道的16位颜色。)
对于存在alpha通道,有必要(但不充分)将位图作为DIB部分并且每像素具有32位。如问题中所述,您可以检查这些要求。
我们也知道Windows只处理预乘的alpha。这意味着,对于每个像素A >= max(R, G, B)
。因此,如果您愿意扫描所有像素,则可以排除一堆24位图像。如果该条件适用于所有像素和,如果任何A的非零,则几乎肯定会有alpha通道(或图像损坏)。
基本上,唯一的不确定性是全透明图像与全黑图像,两者都包含所有通道设置为零的像素。也许在这种情况下采取有根据的猜测是足够的。我猜是的,因为拥有32位DIB部分是一个非常强大的信号。如果你有一个32位设备相关的位图,那么它没有alpha,如果你有一个没有alpha的设备无关位图,你可能每个像素使用24位来节省空间。
一些更详细的位图信息标题可以告诉您是否为alpha通道保留了位。例如,请参阅BITMAPV5HEADER,其中有一个掩码,指示哪些位是alpha通道(尽管文档中说明了一些相互矛盾的事情)。同样适用于BITMAPV4HEADER。不幸的是,我认为没有办法从HBITMAP获得此版本的标题。 (而且我确定那里有支持alpha的位图文件没有正确设置这些字段。)
众所周知,GDI不处理alpha通道(AlphaBlend除外,它不接受HBITMAP,而是访问一个选择的内存DC)。有一些用户API,如UpdateLayeredWindow,可以处理带有alpha通道的图像,但是,像AlphaBlend一样,将位图数据从所选信息中取出到内存DC中。如果传递正确的标志,LoadImage将在加载要由HBITMAP访问的DIB时保留alpha通道。
如果使用适当的标志创建图像列表,则采用HBITMAP的ImageList_Add将保留alpha通道。但是,在所有这些情况下,调用者必须知道位图数据包含正确的alpha数据并为API设置正确的标志。这表明位图句柄无法提供信息。
如果您可以访问从中加载图像的位图资源或文件,则可以查看原始标头是否使用BI_BITFIELDS并指定了alpha通道,但是您无法从HBITMAP获取该标头在所有情况下。 (并且仍然存在没有正确填写标题的担忧。)
答案 1 :(得分:0)
您可以使用GetObject函数间接获取它。在文档中隐藏了一条注释:
如果 hgdiobj 是通过调用CreateDIBSection创建的位图的句柄,并且指定的缓冲区足够大,则 GetObject 函数将返回{{3 }} 结构体。此外, DIBSECTION 中包含的DIBSECTION结构的 bmBits 成员将包含一个指向位图位值的指针。
如果 hgdiobj 是通过任何其他方式创建的位图的句柄,则 GetObject 仅返回位图的宽度,高度和颜色格式信息。 您可以通过调用BITMAP或GetDIBits函数来获取位图的位值。
(重点是我的)
换句话说:如果您尝试解码它是DIBSECTION,但它只是一个BITMAP,那么
dibSection.BitmapInfoHeader
将不会更新。 (例如,保留为零)
记住BITMAP和DIBSECTION有何不同是很有帮助的:
| BITMAP | DIBSECTION |
|------------------------|--------------------------|
| bmType: Longint | bmType: Longint |
| bmWidth: Longint | bmWidth: Longint |
| bmHeight: Longint | bmHeight: Longint |
| bmWidthBytes: Longint | bmWidthBytes: Longint |
| bmPlanes: Word | bmPlanes: Word |
| bmBitsPixel: Word | bmBitsPixel: Word |
| bmBits: Pointer | bmBits: Pointer |
| | |
| |BITMAPINFOHEADER | <-- will remain unchanged for BITMAPs
| | biSize: DWORD |
| | biWidth: Longint |
| | biHeight: Longint |
| | biPlanes: Word |
| | biBitCount: Word |
| | biCompression: DWORD |
| | biSizeImage: DWORD |
| | biXPelsPerMeter: Longint |
| | biYPelsPerMeter: Longint |
| | biClrUsed: DWORD |
| | biClrImportant: DWORD |
| | |
| | dsBitfields: DWORD[3] |
| | dshSection: HANDLE |
| | dsOffset: DWORD |
如果您尝试获取BITMAP作为分区,则 GetObject 函数将不会填写 BITMAPINFOHEADER 的任何字段。
因此,请检查所有这些值是否为空,如果是,则为空:您知道它不是DIBSECTION(必须是BITMAP)。
function IsDibSection(bmp: HBITMAP): Boolean
{
Result := True; //assume that it is a DIBSECTION.
var ds: DIBSECTION = Default(DIBSECTION); //initialize everything to zeros
var res: Integer;
//Try to decode hbitmap as a DIBSECTION
res := GetObject(bmp, sizeof(ds), ref ds);
if (res = 0)
ThrowLastWin32Error();
//If the bitmap actually was a BITMAP (and not a DIBSECTION),
//then BitmapInfoHeader values will remain zeros
if ((ds.Bmih.biSize = 0)
and (ds.Bmih.biWidth = 0)
and (ds.Bmih.biHeight= 0)
and (ds.Bmih.biPlanes= 0)
and (ds.Bmih.biBitCount= 0)
and (ds.Bmih.biCompression= 0)
and (ds.Bmih.biSizeImage= 0)
and (ds.Bmih.biXPelsPerMeter= 0)
and (ds.Bmih.biYPelsPerMeter= 0)
and (ds.Bmih.biClrUsed= 0)
and (ds.Bmih.biClrImportant= 0))
Result := False; //it's not a dibsection
}