Win32剪贴板和alpha通道图像

时间:2013-03-28 18:30:09

标签: c windows winapi 2d

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

问题在于,对于图像数据应该如何存储在剪贴板中似乎没有达成共识。在进行一些测试时,我发现应用程序之间存在一些差异,使其几乎无法提出通用解决方案。

以下是我的观察:

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

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

  3. Firefox将bV5AlphaMask设置为0xff000000,而大多数其他应用程序根本不设置此值但保留为0.这很奇怪,因为这些应用程序将DIB放到实际包含最高alpha通道的剪贴板上8位,但仍然将bV5AlphaMask设置为0.因此,必须假设如果位深度为32,即使bV5AlphaMask为0,也存在alpha通道。

  4. 简而言之,我的基本问题是:是否有关于如何将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通道就在那里!所以这里必须有更多的东西......

2 个答案:

答案 0 :(得分:16)

我确信在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;
}

答案 1 :(得分:0)

尽管有详细的主要答案,但我仍然在这个问题上停留了一段时间。它似乎无法保留Alpha(即使通过剪贴板查看器)。

事实证明,解决方案很简单:

  1. 使用 32位预乘alpha
  2. 导出CF_DIB(不需要V5)
  3. 并导出"PNG"格式 这样,它似乎可以粘贴到我测试过的所有应用程序(Paint.NET,GIMP,LibreOffice等)中。

从本质上讲,只要预乘alpha,就几乎在我使用的每个程序中,alpha都保存在CF_DIB中。在极少数的一次性情况下,需要"PNG"

请注意:不需要CF_DIBV5