用到的库

opencv【v4.8.0】:用于解码剪贴板中的位图数据

openssl【v1.1.1】:用于对图片数据进行 base64 编码

代码

#include <Windows.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/lib.h>

#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

static std::string base64_encode(const std::string &input) {
    BIO *bio, *b64;
    BUF_MEM *bufferPtr;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);

    BIO_write(bio, input.data(), input.size());
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bufferPtr);
    BIO_set_close(bio, BIO_NOCLOSE);

    std::string output(bufferPtr->data, bufferPtr->length);

    BIO_free_all(bio);
    return output;
}

static std::string base64_decode(const std::string &input) {
    BIO *bio, *b64;
    char *buffer = (char *) malloc(input.size());
    memset(buffer, 0, input.size());

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new_mem_buf(input.data(), input.size());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);

    int decoded_size = BIO_read(bio, buffer, input.size());
    std::string output(buffer, decoded_size);

    BIO_free_all(bio);
    free(buffer);

    return output;
}

INT GetPixelDataOffsetForPackedDIB(const BITMAPINFOHEADER *BitmapInfoHeader) {
    INT OffsetExtra = 0;
    if (BitmapInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) {
        if (BitmapInfoHeader->biBitCount > 8) {
            if (BitmapInfoHeader->biCompression == BI_BITFIELDS) {
                OffsetExtra += 3 * sizeof(RGBQUAD);
            } else if (BitmapInfoHeader->biCompression == 6) {
                OffsetExtra += 4 * sizeof(RGBQUAD);
            }
        }
    }
    if (BitmapInfoHeader->biClrUsed > 0) {
        OffsetExtra += BitmapInfoHeader->biClrUsed * sizeof(RGBQUAD);
    } else {
        if (BitmapInfoHeader->biBitCount <= 8) {
            OffsetExtra += sizeof(RGBQUAD) << BitmapInfoHeader->biBitCount;
        }
    }
    return BitmapInfoHeader->biSize + OffsetExtra;
}

bool GetBitmapDataFromPackedDIB(const HGLOBAL ClipboardDataHandle, std::vector<uchar> &bmpData) {
    const auto bitmapInfoHeader = static_cast<BITMAPINFOHEADER *>(GlobalLock(ClipboardDataHandle));
    if (!bitmapInfoHeader) {
        CloseClipboard();
        return false;
    }
    const SIZE_T clipboardDataSize = GlobalSize(ClipboardDataHandle);
    const INT pixelDataOffset = GetPixelDataOffsetForPackedDIB(bitmapInfoHeader);
    const size_t totalBitmapFileSize = sizeof(BITMAPFILEHEADER) + clipboardDataSize;
    // 构建 BMP 文件头
    BITMAPFILEHEADER BitmapFileHeader{};
    BitmapFileHeader.bfType = 0x4D42;
    BitmapFileHeader.bfSize = static_cast<DWORD>(totalBitmapFileSize);
    BitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + pixelDataOffset;
    // 将 BMP 数据写入内存缓冲区
    bmpData.clear();
    bmpData.resize(totalBitmapFileSize);
    memcpy(bmpData.data(), &BitmapFileHeader, sizeof(BITMAPFILEHEADER));
    memcpy(bmpData.data() + sizeof(BITMAPFILEHEADER), bitmapInfoHeader, clipboardDataSize);

    GlobalUnlock(ClipboardDataHandle);
    return true;
}

bool ConvertBmpDataToPng(const std::vector<uchar> &bmpData, std::vector<uchar> &pngData, int compression = 3,
                         int flags = cv::IMREAD_COLOR) {
    const auto &bmp = cv::imdecode(bmpData, flags);
    if (bmp.empty()) {
        return false;
    }
    std::vector<int> compressionParams;
    compressionParams.push_back(cv::IMWRITE_PNG_COMPRESSION);
    compressionParams.push_back(compression);

    pngData.clear();
    if (!cv::imencode(".png", bmp, pngData, compressionParams)) {
        return false;
    }
    return true;
}

int wmain(int argc, wchar_t *argv[]) {
    if (!OpenClipboard(NULL)) {
        return 1;
    }
    HGLOBAL ClipboardDataHandle = GetClipboardData(CF_DIB);
    if (!ClipboardDataHandle) {
        CloseClipboard();
        return 0;
    }
    std::vector<uchar> bmpData{};
    if (!GetBitmapDataFromPackedDIB(ClipboardDataHandle, bmpData)) {
        CloseClipboard();
        return 0;
    }
    // 释放剪贴板资源
    GlobalUnlock(ClipboardDataHandle);
    CloseClipboard();

    std::vector<uchar> pngData;
    if (!ConvertBmpDataToPng(bmpData, pngData)) {
        return 0;
    }
    auto imgBase64 = base64_encode(std::string(pngData.begin(), pngData.end()));
    std::cout << "imgBase64: " << imgBase64 << std::endl;
    return 0;
}