Получить 32-битное изображение RGBA из буфера обмена Windows
Я хочу, чтобы мое приложение (которое работает с изображениями 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-файла, не теряя альфа, тогда я буду в хорошей форме.
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, вероятно, означает индексированный цвет, который у меня пока не работает.
Вот пример использования для CF_DIBV5
а также CF_DIB
, Лучше всего использовать CF_DIB
в качестве резервного варианта. Обратите внимание, что этот код не будет работать для изображений на основе палитры (если он не гарантирован, 32-битный, то см. Метод ниже)
Ты можешь использовать SetDIBitsToDevice
рисовать прямо на HDC
или используйте SetDIBits
Функции GDI не поддерживают альфа-прозрачность (за исключением нескольких функций, таких как 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
к данным (при условии, что источником является растровое изображение), а затем перейти к другой библиотеке.
Это пример использования CreateDIBitmap
а также GetDIBits
чтобы убедиться, что пиксели в 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;
}
}