【问题标题】:Win32 clipboard and alpha channel imagesWin32 剪贴板和 Alpha 通道图像
【发布时间】:2013-03-19 08:17:47
【问题描述】:

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

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

以下是我的观察:

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

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

  3. Firefox 将bV5AlphaMask 设置为 0xff000000,而大多数其他应用程序根本不设置它,而是将其保持为 0。这很奇怪,因为这些应用程序将 DIB 放到剪贴板上,实际上包含最高 8 位的 alpha 通道但他们仍然将bV5AlphaMask 设置为0。因此必须假设如果位深度为32,即使bV5AlphaMask 为0,也存在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 也是如此。将 Alpha 通道图像从 IE 10 粘贴到我的应用程序根本不起作用...我得到一个位深度为 24 的 DIB,即没有阿尔法通道。但是,当从 IE 10 粘贴到 Paint.NET 时,alpha 通道就在那里!所以这里肯定还有更多的东西......

【问题讨论】:

  • 当您从浏览器复制图像时,它还会将 HTML 片段复制到剪贴板。 (Internet Explorer 还会复制图像 URL。)如果 Paint.NET 从 HTML 片段中获取 URL 并直接加载图像,这将解释 1) 和 2)。请注意,这种技术有其自身的问题:如果图像 URL 不是公开的(即,如果它需要登录),那么粘贴应用程序将无法访问它。
  • 我将 PNG 上传到我的网站空间上的 .htaccess 保护文件夹,使用 Firefox 登录,将 PNG 复制到剪贴板。然后我关闭了 Firefox 并打开了 Paint.NET 并粘贴了图像……效果很好!所以它似乎没有直接使用 URL。嗯,也许我需要查看 Firefox 或 Chrome 的源代码,看看它们究竟复制了哪些内容到剪贴板?
  • 当我探索这个时,我写了一个简单的工具来显示和转储剪贴板格式。我已将来源添加到我的答案中。
  • Tks 我现在意识到可能是问题所在:我只是将 CFSTR_FILEDESCRIPTOR 设置为字符串,将 CFSTR_FILECONTENTS 设置为包含 PNG 的 IStream。但是阅读 Shell Clipboard Formats 发现这要复杂得多。 AFAIU 首先需要一个包含 STGMEDIUM 的 IDataObject,该 STGMEDIUM 包含一个包含 FILEDESCRIPTOR 的 FILEGROUPDESCRIPTOR,然后是 CFSTR_FILECONTENTS 的类似过程。我对 OLE 了解不多,所以我最好不要碰它,因为这看起来很复杂,尤其是关于谁负责释放这一切,所以我忘记了 Paint.NET :)

标签: c windows winapi 2d


【解决方案1】:

我确信有一种将 alpha 存储在 CF_DIBV5 中的正确方法,但这并不重要。应用程序已经不一致地处理它,所以如果您希望您的应用程序与其他应用程序很好地配合,您不能使用 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 控制台应用程序并将此源代码粘贴到 main 函数上。它有一个非常小的错误:如果您输入错误的格式名称,它永远不会显示“未知格式”错误。

#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;
}

【讨论】:

  • 非常感谢您的努力!我已经按照您建议的方式实现了它,但仍然存在一些问题。有关该问题的更新,请参阅我的原始帖子。再次感谢
  • Gimp 不是唯一接受带有 alpha 通道的 32bpp DIB 图像的程序。 Flash 也接受这些图像。
  • 根据docs.microsoft.com/en-us/previous-versions/windows/…,Internet Explorer 使用 'image/png' 或 'image/x-png'。
【解决方案2】:

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

原来,解决方法就是这么简单:

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

基本上,只要 alpha 被预乘,alpha 在我使用的几乎每个程序中都会保留在 CF_DIB 中。在罕见的一次性案例中,需要"PNG"

明确一点:不需要CF_DIBV5

【讨论】:

    猜你喜欢
    • 2013-07-08
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2021-01-13
    • 2011-04-19
    • 1970-01-01
    • 2011-10-14
    相关资源
    最近更新 更多