C-从Windows剪贴板获取32位RGBA图像



我希望我的应用程序(可与rgba8888 images一起使用)能够从Windows剪贴板粘贴图像。因此,它应该能够从剪贴板上读取来自任何常见的光栅映像应用程序,例如GIMP,Photoshop,Mspaint等。

通过在剪贴板功能上阅读,我似乎应该能够调用GetClipboardData(CF_DIBV5)以访问剪贴板上几乎所有的位图类型,因为Windows自动在该剪辑之间自动转换,并且CF_BITMAP和CF_DIB。但是,通过阅读DIB格式,我看到有大量的位深度,RGB顺序,可选压缩等可能的组合。似乎我所做的是普遍的任务,但是我不喜欢t看到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,那么我将保持良好状态。

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

这是CF_DIBV5CF_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;
    }
}

最新更新