鉴于HBITMAP,有没有办法找出它是否包含alpha通道?

问题描述 投票:4回答:2

为了改善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通道(以及哪些位用于它),或者这些信息是否根本不可用?

winapi gdi
2个回答
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获取该标头。 (并且仍然存在标题未正确填写的担忧。)


0
投票

您可以使用GetObject函数间接获取它。隐藏在文档中的是一个注释:

如果hgdiobj是通过调用CreateDIBSection创建的位图的句柄,并且指定的缓冲区足够大,则GetObject函数返回DIBSECTION结构。此外,DIBSECTION中包含的BITMAP结构的bmBits成员将包含指向位图位值的指针。

如果hgdiobj是通过任何其他方式创建的位图的句柄,则GetObject仅返回位图的宽度,高度和颜色格式信息。您可以通过调用GetDIBitsGetBitmapBits函数来获取位图的位值。

(强调我的)

换句话说:如果你试图解码它是一个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作为DIBSECTION,则GetObject函数将不会填充BITMAPINFOHEADER的任何字段。

因此,检查所有这些值是否为空,如果是,那么:您知道它不是DIBSECTION(并且必须是BITMAP)。

Sample

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
}
© www.soinside.com 2019 - 2024. All rights reserved.