Win32剪贴板和Alpha通道图像

我的应用程序应该能够将32位图像(RGB + alpha通道)复制到剪贴板,并从剪贴板粘贴这些图像。 为此,我计划使用CF_DIBV5因为BITMAPV5HEADER结构有一个字段bV5AlphaMask

问题在于,如何将图像数据存储在剪贴板中似乎并没有达成共识。 在做一些testing时,我发现应用程序之间有几处不同之处,使得几乎不可能提出一个通用的解决scheme。

这是我的观察:

  1. 当我从Word 2010或XnView中将Alpha通道图像复制到剪贴板时,它的存储没有预乘像素数据。

  2. 但是,当我使用Firefox或Chrome复制图像时,像素数据似乎被Alpha通道预乘。

  3. Firefox将bV5AlphaMask设置为0xff000000,而大多数其他应用程序根本不设置它,但保持为0.这很奇怪,因为这些应用程序将DIB放到剪贴板上,实际上包含最高8位的alpha通道,但仍将bV5AlphaMask设置为0。所以必须假设,即使bV5AlphaMask为0,如果比特深度是32,那么也有一个alpha通道。

长话短说我的基本问题是:是否有一些alpha通道数据应该如何存储在剪贴板的官方信息? 我特别想知道数据是否必须预乘。 正如你所看到的,Word 2010和XnView不会预览,而Firefox和Chrome则是。 但是要知道色彩通道是否应该被预乘是非常重要的。

非常感谢您为此发表了一些看法!

更新2现在粘贴到Paint.NET工作正常。 这是由于我的代码中的错误,如果alpha通道为0,没有将颜色通道设置为0,也就是说,在这种情况下预处理没有正确完成,这似乎使Paint.NET混淆了。

仍然没有解决的是Internet Explorer 10的问题。当将带有alpha通道的PNG复制到剪贴板时,IE 10只是将24位CF_DIBV5放在剪贴板上,但Paint.NET可以将此位图粘贴到alpha通道,所以必须有另一种格式IE 10暴露给剪贴板。 也许它暴露了一个PNG使用CFSTR_FILECONTENTS和CFSTR_FILEDESCRIPTOR。

更新我现在已经用下面的arx描述的方式实现它,它工作得很好。 但是,还有两件事让我感到困惑:

1)将我的应用程序中的Alpha通道图像粘贴到Paint.NET不保留Alpha通道。 在Paint.NET中图像显示不透明。 然而,从Firefox和Chrome粘贴到Paint.NET完美的作品,alpha通道保留! 我甩掉了完整的DIBV5,它与我的应用程序完全相同,但它仍然适用于FF和Chrome,但不适用于我的应用程序,所以必须有其他的东西! Firefox和Chrome必须做别的事情,我的应用程序不做!?

2)对于Internet Explorer 10也是如此。从IE 10粘贴到我的应用程序的Alpha通道图像根本不起作用…我得到了深度为24的DIB,即没有Alpha通道所有。 当从IE 10粘贴到Paint.NET时,Alpha通道就在那里! 所以这里一定还有更多的东西

我确定在CF_DIBV5中存储一个alpha的正确方法,但这并不重要。 应用程序已经不一致地处理了,所以如果你想让你的应用程序与其他应用程序很好地协作,你就不能使用CF_DIBV5。

我前段时间研究了复制和粘贴透明位图。 我的目标是成功地将透明位图粘贴到两个版本的Office和GIMP。 我看了几种可能的格式:

CF_BITMAP

透明度总是被忽略。

CF_DIB

以通常的0xAARRGGBB格式使用32bpp BI_RGB。 GIMP支持这个,但没有别的。

CF_DIBV5

GIMP不支持这一点。

“PNG”

支持粘贴:GIMP,Word 2000,Excel 2000,Excel 2007和PowerPoint 2007。
粘贴不受支持:Word 2007和OneNote 2007。

所有这些应用程序成功导出“PNG”,如果你复制一个位图。

但是,Word和OneNote 2007 粘贴从资源管理器复制的PNG文件。 所以我想出了以下几点:

解决方案复制

将您的透明位图转换为PNG格式。

公布以下剪贴板格式:

“PNG” – 原始的PNG数据。
CF_DIB – 用于不处理透明度的应用程序(如绘画)。
CFSTR_FILEDESCRIPTOR – 使PNG看起来像一个文件。 文件描述符应该有一个“.png”扩展名的发明的文件名。
CFSTR_FILECONTENTS – 内容必须作为IStream公开; 只是使用HGLOBAL似乎没有工作。 数据与“PNG”数据相同。

完成此操作后,我可以成功地将透明位图粘贴到GIMP,Office 2000和Office 2007.您还可以将PNG直接粘贴到资源管理器文件夹中。

更新

我意识到我只回答了一半的问题。 这非常适合复制,但如果您想从仅复制CF_DIBV5的应用程序(如Firefox)粘贴,则无用。

如果可用,我建议您使用“PNG”,否则回退到CF_DIBV5,将其视为预乘。 这将正确处理Word 2010(导出“PNG”),Firefox和Chrome。 XnView只导出非乘法的CF_DIBV5,所以这将无法正常工作。 我不确定你能做得更好。

lscf – 探索剪贴板格式的工具

这是显示可用剪贴板格式列表的工具的来源。 它也可以写一个文件。 我把它叫做lscf 。 在Visual Studio中创建一个win32控制台应用程序,并将此源代码粘贴到主函数上。 它有一个非常小的错误:如果输入格式名称,它从不显示“未知格式”错误。

 #include <Windows.h> #include <stdio.h> #include <tchar.h> #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) LPCTSTR cfNames[] = { _T("CF_TEXT"), _T("CF_BITMAP"), _T("CF_METAFILEPICT"), _T("CF_SYLK"), _T("CF_DIF"), _T("CF_TIFF"), _T("CF_OEMTEXT"), _T("CF_DIB"), _T("CF_PALETTE"), _T("CF_PENDATA"), _T("CF_RIFF"), _T("CF_WAVE"), _T("CF_UNICODETEXT"), _T("CF_ENHMETAFILE"), _T("CF_HDROP"), _T("CF_LOCALE"), _T("CF_DIBV5") }; int LookupFormat(LPCTSTR name) { for (int i = 0; i != ARRAY_SIZE(cfNames); ++i) { if (_tcscmp(cfNames[i], name) == 0) return i + 1; } return RegisterClipboardFormat(name); } void PrintFormatName(int format) { if (!format) return; if ((format > 0) && (format <= ARRAY_SIZE(cfNames))) { _tprintf(_T("%s\n"), cfNames[format - 1]); } else { TCHAR buffer[100]; if (GetClipboardFormatName(format, buffer, ARRAY_SIZE(buffer))) _tprintf(_T("%s\n"), buffer); else _tprintf(_T("#%i\n"), format); } } void WriteFormats() { int count = 0; int format = 0; do { format = EnumClipboardFormats(format); if (format) { ++count; PrintFormatName(format); } } while (format != 0); if (!count) _tprintf(_T("Clipboard is empty!\n")); } void SaveFormat(int format, LPCTSTR filename) { HGLOBAL hData = (HGLOBAL)GetClipboardData(format); LPVOID data = GlobalLock(hData); HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { DWORD bytesWritten; WriteFile(hFile, data, GlobalSize(hData), &bytesWritten, 0); CloseHandle(hFile); } GlobalUnlock(hData); } int _tmain(int argc, _TCHAR* argv[]) { if (!OpenClipboard(0)) { _tprintf(_T("Cannot open clipboard\n")); return 1; } if (argc == 1) { WriteFormats(); } else if (argc == 3) { int format = LookupFormat(argv[1]); if (format == 0) { _tprintf(_T("Unknown format\n")); return 1; } SaveFormat(format, argv[2]); } else { _tprintf(_T("lscf\n")); _tprintf(_T("List available clipboard formats\n\n")); _tprintf(_T("lscf CF_NAME filename\n")); _tprintf(_T("Write format CF_NAME to file filename\n\n")); } CloseClipboard(); return 0; }