2015-06-17 15 views
0

Я пытаюсь заставить это работать некоторое время, но я не могу понять это, и часы Googling пока не раскрывают никаких полезных результатов.Как я могу поместить независимое от устройства растровое изображение в буфер обмена Windows, используя только прямой WinAPI? (Нет MFC или других оберток)

У меня есть массив 32-разрядных пикселей в RGBA-порядке, и я хочу создать из них растровое изображение, не зависящее от устройства, и поместить его в буфер обмена с помощью SetClipboardData(CF_DIBV5, dib) или аналогичного (в идеале, я хочу сохранить альфа-канал) , Регистрация настраиваемого типа буфера обмена не является вариантом, поскольку точка его размещения в буфере обмена такова, что ее можно вставить в другую программу. Бонусные баллы, если мне не нужно вручную преобразовывать мои пиксельные данные в какой-либо другой формат (например, планарный BGRA).

Мой текущий код выглядит следующим образом (это все в пределах функции set_clipboard_img):

if(!OpenClipboard(hwnd)) return; 
BITMAPV5HEADER* info = (BITMAPV5HEADER*) GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER)); 
info->bV5Size = sizeof(BITMAPV5HEADER); 
info->bV5Width = img_width; 
info->bV5Height = -img_height; 
info->bV5Planes = 1; // The docs say this is the only valid value here. 
info->bV5BitCount = 32; 
info->bV5Compression = BI_BITFIELDS; 
info->bV5SizeImage = img_width * img_height * 4; 
info->bV5RedMask = 0xff000000; 
info->bV5GreenMask = 0x00ff0000; 
info->bV5BlueMask = 0x0000ff00; 
info->bV5AlphaMask = 0x000000ff; 
unsigned char* buf; 
// One of the sources I found said that I can pass a BITMAPV5HEADER in place of the BITMAPINFO, hence the first reinterpret_cast. 
HBITMAP dib = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(info), DIB_RGB_COLORS, reinterpret_cast<void**>(&buf), NULL, 0); 
if(dib == NULL) { 
    CloseClipboard(); 
    return; 
} 
// img_pixels_ptr is a unsigned char* to the pixels in non-planar RGBA format 
std::copy_n(img_pixels_ptr, info->bV5SizeImage, buf); 
EmptyClipboard(); 
auto result = SetClipboardData(CF_DIBV5, dib); 
if(result == NULL) { 
    char str[256]; 
    str[255] = 0; 
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, str, 255, NULL); 
    std::cerr << "Error setting clipboard: " << str << std::endl; 
    // Here I get "handle not valid". I have no idea _why_ it's not valid, though. 
} 
CloseClipboard(); 

В конце концов, я также должен быть в состоянии повернуть вспять процесс (получение потенциально-прозрачного растрового изображения от буфера обмена), но одна вещь за раз.

ответ

2

Вы не можете передать HBITMAP в SetClipboardData(). Для этого требуется HGLOBAL от GlobalAlloc(). Вот почему ошибка SetClipboardData() с ошибкой ERROR_INVALID_HANDLE.

Вы должны поместить данные BITMAPV5HEADER и пиксельные непосредственно в выделенную HGLOBAL и поставить его как есть в буфер обмена, забыть, используя CreateDIBSection() на всех:

Standard Clipboard Formats

CF_DIBV5
Объект памяти, содержащий структуру BITMAPV5HEADER, за которой следует информация о цветовом пространстве растрового изображения и битовые биты.

попробовать что-то больше, как это:

void printErr(const char *msg) 
{ 
    char str[256] = {0}; 
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, str, 255, NULL); 
    std::cerr << msg << ": " << str << std::endl; 
} 

... 

DWORD size_pixels = img_width * img_height * 4; 

HGLOBAL hMem = GlobalAlloc(GHND, sizeof(BITMAPV5HEADER) + size_pixels); 
if (!hMem) 
{ 
    printErr("Error allocating memory for bitmap data"); 
    return; 
} 

BITMAPV5HEADER* hdr = (BITMAPV5HEADER*) GlobalLock(hMem); 
if (!hdr) 
{ 
    printErr("Error accessing memory for bitmap data"); 
    GlobalFree(hMem); 
    return; 
} 

hdr->bV5Size = sizeof(BITMAPV5HEADER); 
hdr->bV5Width = img_width; 
hdr->bV5Height = -img_height; 
hdr->bV5Planes = 1; 
hdr->bV5BitCount = 32; 
hdr->bV5Compression = BI_BITFIELDS; 
hdr->bV5SizeImage = size_pixels; 
hdr->bV5RedMask = 0xff000000; 
hdr->bV5GreenMask = 0x00ff0000; 
hdr->bV5BlueMask = 0x0000ff00; 
hdr->bV5AlphaMask = 0x000000ff; 

// img_pixels_ptr is a unsigned char* to the pixels in non-planar RGBA format 
CopyMemory(hdr+1, img_pixels_ptr, size_pixels); 
GlobalUnlock(hMem); 

if (!OpenClipboard(hwnd)) 
{ 
    printErr("Error opening clipboard"); 
} 
else 
{ 
    if (!EmptyClipboard()) 
     printErr("Error emptying clipboard"); 

    else if (!SetClipboardData(CF_DIBV5, hMem)) 
     printErr("Error setting bitmap on clipboard"); 

    else 
     hMem = NULL; // clipboard now owns the memory 

    CloseClipboard(); 
} 

if (hMem) 
    GlobalFree(hMem); 
+0

Спасибо! С небольшим изменением, это, кажется, работает очень хорошо (мне просто пришлось изменить порядок четырех параметров маски). К сожалению, похоже, что многие программы не читают альфа-канал при получении данных из буфера обмена (и GIMP даже не распознает изображение). Ну что ж! – celticminstrel

+0

Однако я могу скопировать-вставить с альфой из Paint.net в GIMP, так что должен быть способ. Мне придется продолжать искать. – celticminstrel