Как скопировать изображение RGBA в буфер обмена Windows

Как можно скопировать 32-битное (на пиксель) изображение RGBA в буфер обмена Windows? Я пришел к этой функции после долгих проб, но мне не повезло с тем, что мои данные изображения «вставляются» вообще. Он также не отображается в истории буфера обмена.

Немного отредактировав его, чтобы использовать CF_DIB и заголовок BITMAPINFOHEADER, мы получили запись «копировать» в этой истории и изображение правильного размера при вставке, хотя вставка png на обратной стороне CF_DIB вызвала сбои программ в невероятно интересном и не щадящие способы.

Моя цель - скопировать изображение с альфа-каналом в буфер обмена и не допустить умножения цветов на этот альфа-канал во время передачи. Что я делаю не так..?

bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
    //  this section is my code for creating a png file
    StreamWrite stream = StreamWrite::asBufferCreate();
    in.savePng(stream);
    uint64 bufSize = 0;
    char * buf = stream._takeBuffer(bufSize, false);
    // "buf"      <-- contains the PNG payload
    // "bufSize"  <-- is the size of this payload

    // beyond this point, it's just standard windows' stuff that doesn't rely on my code
    BITMAPV5HEADER header;
    header.bV5Size          = sizeof(BITMAPV5HEADER);
    header.bV5Width         = in.getX(); // <-- size of the bitmap in pixels, width and height
    header.bV5Height        = in.getY();
    header.bV5Planes        = 1;
    header.bV5BitCount      = 0;
    header.bV5Compression   = BI_PNG;
    header.bV5SizeImage     = bufSize;
    header.bV5XPelsPerMeter = 0;
    header.bV5YPelsPerMeter = 0;
    header.bV5ClrUsed       = 0;
    header.bV5ClrImportant  = 0;
    header.bV5RedMask       = 0xFF000000;
    header.bV5GreenMask     = 0x00FF0000;
    header.bV5BlueMask      = 0x0000FF00;
    header.bV5AlphaMask     = 0x000000FF;
    header.bV5CSType        = LCS_sRGB;
    header.bV5Endpoints;    // ignored
    header.bV5GammaRed      = 0;
    header.bV5GammaGreen    = 0;
    header.bV5GammaBlue     = 0;
    header.bV5Intent        = 0;
    header.bV5ProfileData   = 0;
    header.bV5ProfileSize   = 0;
    header.bV5Reserved      = 0;

    HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER) + bufSize);
    if (gift == NULL) 
        return false;

    HWND win = window.getWindowHandle();
    if (!OpenClipboard(win)) {
        GlobalFree(gift);
        return false;
    }
    EmptyClipboard();

    void * giftLocked = GlobalLock(gift);
    if (giftLocked) {
        memcpy(giftLocked, &header, sizeof(BITMAPV5HEADER));
        memcpy((char*)giftLocked + sizeof(BITMAPV5HEADER), buf, bufSize);
    }
    GlobalUnlock(gift);

    SetClipboardData(CF_DIBV5, gift);

    CloseClipboard();
    return true;
}
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
0
37
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По крайней мере, по моему опыту, попытка передать данные png с помощью BITMAPV5HEADER — это почти полная потеря, если только вы не планируете использовать его строго как внутренний формат.

Одна из стратегий, которая работает, по крайней мере, для значительного числа приложений, заключается в регистрации формата буфера обмена PNG и простом помещении содержимого файла PNG в буфер обмена (без другого заголовка). Код будет выглядеть примерно так:

bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
    //  this section is my code for creating a png file
    StreamWrite stream = StreamWrite::asBufferCreate();
    in.savePng(stream);
    uint64 bufSize = 0;
    char * buf = stream._takeBuffer(bufSize, false);
    // "buf"      <-- contains the PNG payload
    // "bufSize"  <-- is the size of this payload

    HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, bufSize);
    if (gift == NULL) 
        return false;

    HWND win = window.getWindowHandle();
    if (!OpenClipboard(win)) {
        GlobalFree(gift);
        return false;
    }
    EmptyClipboard();

    auto fmt = RegisterClipboardFormat("PNG"); // or `L"PNG", as applicable

    void * giftLocked = GlobalLock(gift);
    if (giftLocked) {
        memcpy((char*)giftLocked, buf, bufSize);
    }
    GlobalUnlock(gift);

    SetClipboardData(fmt, gift);

    CloseClipboard();
    return true;
}

Я использовал подобный код и успешно вставил содержимое как минимум в последние версии LibreOffice Write and Calc, MS Word и Paint.Net.

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

С другой стороны, FireFox производит целую кучу форматов, но не этот. Он выдаст CF_DIBV5, но, по крайней мере, если память не изменяет, он имеет предварительно умноженную альфу (или, может быть, он полностью теряет альфу - я точно не помню. Все равно не сохраняет ее так, как вам бы хотелось).

Gimp примет 32-битный формат DIB RGB с альфой в оставшемся байте и будет использовать эту альфу. К лучшему или к худшему, насколько я понял, речь идет о Только, который работает, чтобы вставить что-то в Gimp с сохраненной альфой (без предварительного умножения).

Примечания

  • По мере обновления версий форматы, которые они поддерживают, вполне могут измениться, поэтому, хотя (например) PNG не работал с Gimp в последний раз, когда я пытался, теперь это возможно.

  • Вы можете добавлять одни и те же данные в буфер обмена в разных форматах. Вы хотите начать с «лучшего» формата (тот, который сохраняет данные наиболее точно) и двигаться вниз к худшему. Поэтому, когда вы делаете копию, вы можете сделать PNG, затем RGB с альфа-каналом, затем CF_BITMAP (который будет предварительно умножать альфа-канал, но все же может быть лучше, чем ничего).

Другие вопросы по теме