Winapi 给定HBITMAP,有没有办法确定它是否包含alpha通道?

Winapi 给定HBITMAP,有没有办法确定它是否包含alpha通道?,winapi,gdi,Winapi,Gdi,为了改进,我正在寻找一种方法来确定通过HBITMAP引用的位图是否包含alpha通道 我了解,我可以调用并检索结构: 位图bm={0}; ::GetObject(hbitmap、sizeof(bm)和bm); 但这只能得到存储像素颜色所需的位数。它没有告诉我实际使用了哪些位,或者它们与各个通道的关系。例如,16bpp位图可以对5-6-5 BGR图像或1-5-5-5 ABGR图像进行编码。同样,32bpp位图可以存储ABGR或xBGR数据 我可以更进一步,转而探索(如果有): 虽然这可以消除16

为了改进,我正在寻找一种方法来确定通过
HBITMAP
引用的位图是否包含alpha通道

我了解,我可以调用并检索结构:

位图bm={0}; ::GetObject(hbitmap、sizeof(bm)和bm); 但这只能得到存储像素颜色所需的位数。它没有告诉我实际使用了哪些位,或者它们与各个通道的关系。例如,16bpp位图可以对5-6-5 BGR图像或1-5-5-5 ABGR图像进行编码。同样,32bpp位图可以存储ABGR或xBGR数据

我可以更进一步,转而探索(如果有):

虽然这可以消除16bpp情况的歧义(使用
dsBitfields
成员),但它仍然无法确定32bpp图像中是否存在alpha通道


是否有任何方法可以确定通过
HBITMAP
引用的位图是否包含alpha通道(以及使用了哪些位),或者该信息根本不可用?

您无法确定,但如果您愿意迭代像素,您可以做出有根据的猜测

(不管怎样,现在还是忽略带有1位alpha通道的16位颜色。)

为了有一个alpha通道,位图必须是一个DIB部分,并且每个像素有32位。如问题中所述,您可以检查这些要求

我们还知道Windows只处理预乘的alpha。这意味着,对于每个像素
A>=max(R,G,B)
。所以,如果你想扫描所有的像素,你可以排除一堆24位的图像。若该条件适用于所有像素,并且A中的任何一个非零,那个么几乎可以肯定您有一个alpha通道(或损坏的图像)

基本上,剩下的唯一不确定性是全透明图像与全黑图像,两者都由所有通道设置为零的像素组成。在这种情况下,也许只需进行有根据的猜测就足够了。我想是的,因为32位DIB段是一个非常强的信号。如果你有一个32位的设备相关位图,那么它没有alpha,如果你有一个没有alpha的设备无关位图,你可能会使用每像素24位来节省空间

一些更详细的位图信息标题可以告诉您是否为alpha通道保留了位。例如,请参阅,其中有一个掩码,指示哪些位是alpha通道(尽管文档中说了一些相互矛盾的事情)。对于BitMapv4标头也是如此。不幸的是,我认为没有办法从HBITMAP中获取此版本的标题。(我敢肯定,有些启用alpha的位图文件无法正确设置这些字段。)

众所周知,GDI不处理alpha通道(AlphaBlend除外,它不接受HBITMAP,而是访问选定的内存DC)。有一些用户API,比如UpdateLayeredWindow,可以处理带有alpha通道的图像,但是,像AlphaBlend一样,将位图数据从所选信息中提取到内存DC中。如果传递了正确的标志,则LoadImage将在加载HBITMAP访问的DIB时保留alpha通道。
如果使用正确的标志创建图像列表,则ImageList_Add将保留HBITMAP和alpha通道。但是,在所有这些情况下,调用方必须知道位图数据包含正确的alpha数据,并为API设置正确的标志。这表明位图句柄中没有现成的信息


如果您可以访问从中加载图像的位图资源或文件,则可以查看原始标头是否使用BI_位字段并指定了alpha通道,但无法在所有情况下从HBITMAP访问该标头。(还有一个问题是页眉没有正确填写。)

您可以使用函数间接获得它。文档中隐藏着一个注释:

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

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

(强调矿山)

换句话说:如果你试图解码它是一个DIBSECTION,但它只是一个位图,那么

dibSection.BitmapInfoHeader
不会被更新。(例如,保留为零)

记住位图和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          |

我们是否有任何API函数支持带有
HBITMAP
s的alpha通道?没有API,位布局就没有多大意义。现有的定义位布局的方法是,但它与
stretchibits
等函数一起使用,这些函数接受位图数据参数,而不是位图句柄。也就是说,当您处理老式位图句柄时,它非常简单,假设没有alpha通道,并且位布局是固定的。@RomanR.-我们是否有任何API函数支持带有HBITMAP的alpha通道-
AlphaBlend
。假设唯一支持的alpha格式是ARGB,则可以使用试探法确定是否存在alpha通道。迭代所有像素,如果我们找到一个A字节大于0的像素,我们可以假设它是一个实际的alpha通道。当然,这对于所有透明/全黑图像都是失败的。“我们有任何支持HBITMAPs的alpha通道的API函数吗?”是的,除了AlphaBlend,还有ImageList t的API
| 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          |
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
}