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
I was stuck on this problem for a while despite the detailed main answer. It would not seem to preserve alpha (even through a clipboard viewer).
It turns out, the solution is as simple as this:
CF_DIB
(no need for V5) with 32-bit pre-multiplied alpha"PNG"
format
With that, it seemed to be able to paste in all applications I tested (Paint.NET, GIMP, LibreOffice, and so forth).Essentially, as long as alpha was pre-multiplied, alpha was preserved in CF_DIB
in almost every program I used. In a rare one-off case, "PNG"
was needed.
To be clear: CF_DIBV5
was not needed.
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 <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;
}