我希望我的应用程序(可与rgba8888 images一起使用)能够从Windows剪贴板粘贴图像。因此,它应该能够从剪贴板上读取来自任何常见的光栅映像应用程序,例如GIMP,Photoshop,Mspaint等。
通过在剪贴板功能上阅读,我似乎应该能够调用GetClipboardData(CF_DIBV5)
以访问剪贴板上几乎所有的位图类型,因为Windows自动在该剪辑之间自动转换,并且CF_BITMAP和CF_DIB。但是,通过阅读DIB格式,我看到有大量的位深度,RGB顺序,可选压缩等可能的组合。似乎我所做的是普遍的任务,但是我不喜欢t看到Windows API中的任何转换功能(除非我在搜索方面很差),这似乎需要一周的时间来支持所有可能的格式。所以我想知道我是否忽略了一些明显的东西。或者,如果有某种假设可以简化这一点……就像所有流行的映像应用程序碰巧以未压缩/未索引格式复制剪贴板一样。
更新:这是我到目前为止所拥有的:
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和.pngs中的数据类型。因此,如果有一种方法可以将剪贴板数据转换为位图或png文件数据而又不会丢失alpha,那么我将保持良好状态。
我发现的基本策略是检查剪贴板上是否有原始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_DIBV5
和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
添加到数据(假设源为位图),然后传递到另一个库。
这是使用CreateDIBitmap
和GetDIBits
来确保像素在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;
}
}