Win32 clipboard and alpha channel images

后端 未结 2 1167
臣服心动
臣服心动 2021-01-02 06:00

My application should be able to copy 32-bit images (RGB + alpha channel) to the clipboard and paste these images from the clipboard. For this I plan to use CF_DIBV5

2条回答
  •  离开以前
    2021-01-02 06:59

    I'm sure there is a right way of storing the alpha in CF_DIBV5, but it really doesn't matter. Applications already handle it inconsistently, so if you want your application to play nicely with others you can't use CF_DIBV5.

    I researched copying and pasting transparent bitmaps a while ago. My aim was to successfully paste a transparent bitmap into two versions of Office and GIMP. I looked at several possible formats:

    CF_BITMAP

    Transparency is always ignored.

    CF_DIB

    Using 32bpp BI_RGB in the usual 0xAARRGGBB format. GIMP supports this but nothing else does.

    CF_DIBV5

    GIMP doesn't support this.

    "PNG"

    Paste supported: GIMP, Word 2000, Excel 2000, Excel 2007 and PowerPoint 2007.
    Paste unsupported: Word 2007 and OneNote 2007.

    All of these applications successfully export "PNG" if you copy a bitmap.

    However, Word and OneNote 2007 will paste a PNG file copied from Explorer. So I came up with the following:

    Solution for Copying

    Convert your transparent bitmap to PNG format.

    Advertise the following clipboard formats:

    "PNG" - the raw PNG data.
    CF_DIB - for applications (like paint) that don't handle transparency.
    CFSTR_FILEDESCRIPTOR - make the PNG look like a file. The file descriptor should have an invented filename with a ".png" extension.
    CFSTR_FILECONTENTS - the contents must be exposed as an IStream; just using an HGLOBAL doesn't seem to work. The data is identical to the "PNG" data.

    Having done this I could successfully paste transparent bitmaps into GIMP, Office 2000 and Office 2007. You can also paste the PNG directly into an Explorer folder.

    Update

    I realised that I've only answered half the question. This is great for copying, but no use if you want to paste from an application that only copies CF_DIBV5 (like Firefox).

    I'd recommend that you use "PNG" if it's available, otherwise fall back to CF_DIBV5, treating it as premultiplied. This will correctly handle Word 2010 (which exports "PNG"), Firefox and Chrome. XnView only exports non-multiplied CF_DIBV5, so this won't work correctly. I'm not sure you can do any better.

    lscf - A Tool for Exploring Clipboard Formats

    This is the source of a tool for displaying a list of available clipboard formats. It can also write one to a file. I called it lscf. Create a win32 console application in Visual Studio and paste this source over the main function. It has one very minor bug: it never displays the "Unknown format" error if you mistype a format name.

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

提交回复
热议问题