Получить 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;
    }
}
Другие вопросы по тегам