1

我希望我的应用程序(适用于 RGBA8888 图像)能够从 Windows 剪贴板粘贴图像。所以它应该能够从剪贴板上读取来自任何常见光栅图像应用程序(如 Gimp、Photoshop、MSPaint 等)的图像。

通过阅读剪贴板功能,看来我应该可以调用GetClipboardData(CF_DIBV5)几乎可以访问剪贴板上的任何位图类型,因为 Windows 会自动在它与 CF_BITMAP 和 CF_DIB 之间进行转换。但是通过阅读 DIB 格式,我发现位深度、RGB 顺序、可选压缩等的可能组合有很多。看起来我正在做的事情将是一项常见的任务,但我没有在 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,那么我的状态会很好。

4

2 回答 2

2

这是 和 的用法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到数据(假设源是位图)然后传递给另一个库。

这是一个使用CreateDIBitmapGetDIBits确保像素为 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;
    }
}
于 2016-06-21T23:09:25.117 回答
2

我发现的基本策略是检查剪贴板上是否有原始 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 的位数可能意味着索引颜色,我还没有工作。

于 2016-06-27T01:01:55.630 回答