2013-03-28 79 views
9

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

问题是,关于图像数据应该如何存储在剪贴板中,似乎并没有达成共识。在进行一些测试时,我发现应用程序之间存在若干差异,使得几乎不可能提出一个通用解决方案。

这里是我的意见:

  1. 当我从Word 2010或XnView的alpha通道图像复制到剪贴板,它存储不左乘的像素数据。

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

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

为了削减长话短说我的基本问题是alpha通道:是有关alpha通道数据如何存储在剪贴板上的官方信息?我特别想知道数据是否必须预乘。正如您在上面看到的那样,Word 2010和XnView不会预览,而Firefox和Chrome则可以。但是了解色彩通道是否应该预乘是非常重要的。

非常感谢你为此付出了一些光芒!

UPDATE 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。

UPDATE 我现在按照arx下面描述的方式实现它,它工作得很好。但是,仍然有两件事让我感到困惑:

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

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

+0

当你从一个浏览器,它还会复制一个HTML片段复制到剪贴板的图像。 (Internet Explorer额外复制图像URL。)如果Paint.NET从HTML片段中获取URL并直接加载图像,这将解释1)和2)。请注意,这种技术有其自身的问题:如果图像URL不公开(即,如果它需要登录),那么粘贴应用程序将无法访问它。 – arx

+0

我将PNG上传到我的网站空间上的.htaccess保护文件夹,使用Firefox登录,将PNG复制到剪贴板。然后我关闭Firefox并打开Paint.NET并粘贴图像......它工作的很好!所以它似乎没有直接使用URL。嗯,也许我需要查看Firefox或Chrome源代码,看看它们到剪贴板的内容到底是什么? – Andreas

+0

当我探索这个时,我写了一个简单的工具来显示和转储剪贴板格式。我已将源添加到我的答案中。 – arx

回答

13

我确定在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; 
} 
+2

+1多么令人愉快的答案! –

+0

非常感谢您的努力!我按照您的建议实施了它,但仍然存在一些问题。查看我的原始帖子,了解有关此问题的更新。再次感谢 – Andreas

+0

Gimp不是唯一接受带alpha通道的32bpp DIB图像的程序。 Flash也接受这些图像。 – Dwedit