从Windows剪贴板获取32位RGBA图像

我想我的应用程序(它与RGBA8888图像一起工作)能够从Windows剪贴板粘贴图像。 所以它应该能够从剪贴板中读取来自任何常见的光栅图像应用程序的图像,如Gimp,Photoshop,MSPaint等。

从阅读剪贴板function,似乎我应该能够调用GetClipboardData(CF_DIBV5)来访问几乎所有位于剪贴板上的位图types,因为Windows会自动在它和CF_BITMAP和CF_DIB之间进行转换。 但从DIB格式的阅读中,我发现有大量的位深度,RGB顺序,可选压缩等等的可能组合。看起来我正在做的事情是一个常见的任务,没有看到在Windows API中的任何转换函数(除非我穷人search),这似乎是需要一个星期来写支持所有可能的格式。 所以我想知道如果我忽略了一些明显的东西。 或者,如果有一种假设,我可以简化这个…就像所有stream行的图像应用程序碰巧将图像以未压缩/未索引的格式复制到剪贴板一样。

更新:这是我迄今为止:

 HGLOBAL clipboard = GetClipboardData(CF_DIBV5); exists = clipboard != NULL; int dataLength = GlobalSize(clipboard); exists = dataLength != 0; if (exists) { LPTSTR lockedClipboard = GlobalLock(clipboard); exists = lockedClipboard != NULL; if (exists) { BITMAPV5HEADER *header = (BITMAPV5HEADER*)lockedClipboard; LONG width = header->bV5Width; LONG height = header->bV5Height; BYTE *bits = header + sizeof(header) + header->bV5ClrUsed * sizeof(RGBQUAD); //Now what? Need function to convert the bits to something uncompressed. GlobalUnlock(clipboard); } } 

更新2:

为了澄清,我需要从字面上解压缩的32位图像数据(RRGGBBAA),我可以操纵,但我喜欢在跨平台的应用程序。 我没有必要使用Windows API来绘制这个图像来屏幕。

我知道一个名为stdb_image.h第三方库,它可以将.bmps,.jpgs和.png加载到我需要的数据types中。 所以如果有办法,我可以将剪贴板数据转换成位图或PNG文件数据,而不会丢失alpha,那么我将会保持良好状态。

我发现的基本策略是检查剪贴板上是否存在原始PNG,如果可用,请使用第一个PNG。 这是最简单的。 某些应用程序(如GIMP)会将图像作为PNG复制到剪贴板。

然后检查CF_DIBV5 。 实际位的位置取决于“压缩”是否为BI_BITFIELDS

 int offset = bitmapV5Header->bV5Size + bitmapV5Header->bV5ClrUsed * (bitmapV5Header->bV5BitCount > 24 ? sizeof(RGBQUAD) : sizeof(RGBTRIPLE)); if (compression == BI_BITFIELDS) offset += 12; //bit masks follow the header BYTE *bits = (BYTE*)bitmapV5Header + offset; 

如果标题说压缩是BI_BITFIELDS ,那么数据已经是我所需要的了。

如果标题说压缩是BI_RGB和位数是24或32,那么我可以解压缩字节。 24字节意味着行大小可能不会在DWORD边界上,所以您必须注意这一点。

最后,低于24的位数可能意味着索引颜色,我还没有工作。

以下是CF_DIBV5CF_DIB的使用CF_DIB 。 最好使用CF_DIB作为备份选项。 请注意,此代码不适用于基于调色板的图像(如果不能保证32位,则可以进一步查看方法)

您可以使用SetDIBitsToDevice直接在HDC上绘图,或使用SetDIBits

GDI函数不支持alpha透明(除了像TransparentBlt这样的函数外),通常你必须使用GDI +等库。

 void foo(HDC hdc) { if (!OpenClipboard(NULL)) return; HANDLE handle = GetClipboardData(CF_DIBV5); if (handle) { BITMAPV5HEADER* header = (BITMAPV5HEADER*)GlobalLock(handle); if (header) { BITMAPINFO bmpinfo; memcpy(&bmpinfo.bmiHeader, header, sizeof(BITMAPINFOHEADER)); bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFO); //(use `header` to access other BITMAPV5HEADER information) int w = bmpinfo.bmiHeader.biWidth; int h = bmpinfo.bmiHeader.biHeight; const char* bits = (char*)(header) + header->bV5Size; //draw using SetDIBitsToDevice SetDIBitsToDevice(hdc,0,0,w,h,0,0,0,h,bits,&bmpinfo,DIB_RGB_COLORS); } } else { handle = GetClipboardData(CF_DIB); if (handle) { BITMAPINFO* bmpinfo = (BITMAPINFO*)GlobalLock(handle); if (bmpinfo) { int w = bmpinfo->bmiHeader.biWidth; int h = bmpinfo->bmiHeader.biHeight; const char* bits = (char*)(bmpinfo)+bmpinfo->bmiHeader.biSize; SetDIBitsToDevice(hdc, 0, 0, w, h, 0, 0, 0, h, bits, bmpinfo, 0); } } } CloseClipboard(); } 

如果原始图像是基于调色板的,则必须转换为32位。 或者,您可以将BITMAPFILEHEADER添加到数据(假定源是位图),然后传递给其他库。

这是一个使用CreateDIBitmapGetDIBits来确保像素位于32位的示例:

 HANDLE handle = GetClipboardData(CF_DIB); if (handle) { BITMAPINFO* bmpinfo = (BITMAPINFO*)GlobalLock(handle); if (bmpinfo) { int offset = (bmpinfo->bmiHeader.biBitCount > 8) ? 0 : sizeof(RGBQUAD) * (1 << bmpinfo->bmiHeader.biBitCount); const char* bits = (const char*)(bmpinfo)+bmpinfo->bmiHeader.biSize + offset; HBITMAP hbitmap = CreateDIBitmap(hdc, &bmpinfo->bmiHeader, CBM_INIT, bits, bmpinfo, DIB_RGB_COLORS); //convert to 32 bits format (if it's not already 32bit) BITMAP bm; GetObject(hbitmap, sizeof(bm), &bm); int w = bm.bmWidth; int h = bm.bmHeight; char *bits32 = new char[w*h*4]; BITMAPINFOHEADER bmpInfoHeader = { sizeof(BITMAPINFOHEADER), w, h, 1, 32 }; HDC hdc = GetDC(0); GetDIBits(hdc, hbitmap, 0, h, bits32, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS); ReleaseDC(0, hdc); //use bits32 for whatever purpose... //cleanup delete[]bits32; } }