LibQREncode 二维码转BMP

LibQREncode qrcode to BMP

我正在使用库创建二维码 qrencode.h 这个创作工作很好,但如何将 qrcode 输出到 C++ 中的 BMP 文件? 此时此刻我有这段代码:

const char*         szSourceSring = QRCODE_TEXT;
unsigned int    unWidth, x, y, l, n, unWidthAdjusted, unDataBytes;
unsigned char*  pRGBData, *pSourceData, *pDestData;
QRcode*         pQRC;
FILE*           f;
 
if (pQRC = QRcode_encodeString(szSourceSring, 4, QR_ECLEVEL_H, QR_MODE_8, 1))
        {
        unWidth = pQRC->width;
        unWidthAdjusted = unWidth * OUT_FILE_PIXEL_PRESCALER * 3;
        if (unWidthAdjusted % 4)
            unWidthAdjusted = (unWidthAdjusted / 4 + 1) * 4;
        unDataBytes = unWidthAdjusted * unWidth * OUT_FILE_PIXEL_PRESCALER;
 
            // Allocate pixels buffer
 
        if (!(pRGBData = (unsigned char*)malloc(unDataBytes)))
            {
            printf("Out of memory");
            }
           
            // Preset to white
 
        memset(pRGBData, 0xff, unDataBytes);
 
 
            // Prepare bmp headers
 
        BITMAPFILEHEADER kFileHeader;
        kFileHeader.bfType = 0x4D42;  // "BM"
        kFileHeader.bfSize =    sizeof(BITMAPFILEHEADER) +
                                sizeof(BITMAPINFOHEADER) +
                                unDataBytes;
        kFileHeader.bfReserved1 = 0;
        kFileHeader.bfReserved2 = 0;
        kFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) +
                                sizeof(BITMAPINFOHEADER);
 
        BITMAPINFOHEADER kInfoHeader;
        kInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
        kInfoHeader.biWidth = unWidth * OUT_FILE_PIXEL_PRESCALER;
        kInfoHeader.biHeight = -((int)unWidth * OUT_FILE_PIXEL_PRESCALER);
        kInfoHeader.biPlanes = 1;
        kInfoHeader.biBitCount = 24;
        kInfoHeader.biCompression = BI_RGB;
        kInfoHeader.biSizeImage = 0;
        kInfoHeader.biXPelsPerMeter = 0;
        kInfoHeader.biYPelsPerMeter = 0;
        kInfoHeader.biClrUsed = 0;
        kInfoHeader.biClrImportant = 0;
 
            // Convert QrCode bits to bmp pixels
 
        pSourceData = pQRC->data;
        for(y = 0; y < unWidth; y++)
        {
            pDestData = pRGBData + unWidthAdjusted * y * OUT_FILE_PIXEL_PRESCALER;
            for(x = 0; x < unWidth; x++)
            {
                if (*pSourceData & 1)
                {
                    for(l = 0; l < OUT_FILE_PIXEL_PRESCALER; l++)
                    {
                        for(n = 0; n < OUT_FILE_PIXEL_PRESCALER; n++)
                        {
                            *(pDestData +       n * 3 + unWidthAdjusted * l) =  PIXEL_COLOR_B;
                            *(pDestData + 1 +   n * 3 + unWidthAdjusted * l) =  PIXEL_COLOR_G;
                            *(pDestData + 2 +   n * 3 + unWidthAdjusted * l) =  PIXEL_COLOR_R;
                        }
                    }
                }
                pDestData += 3 * OUT_FILE_PIXEL_PRESCALER;
                pSourceData++;
            }
        }
 
 
            // Output the bmp file
 
        /*if (((f = fopen(OUT_FILE, "r")) != NULL))
            {*/
            f = fopen(OUT_FILE, "wb");
            fwrite(&kFileHeader, sizeof(BITMAPFILEHEADER), 14, f);
            fwrite(&kInfoHeader, sizeof(BITMAPINFOHEADER), 40, f);
            fwrite(pRGBData, sizeof(unsigned char), unDataBytes, f);
           
            fclose(f);
/*          }
        else
            {
            printf("Unable to open file");
            }
*/
            // Free data
 
        free(pRGBData);
        QRcode_free(pQRC);
        }
    else
        {
        printf("NULL returned");
        }

但不知何故,这会创建一个带有损坏 headers 的 BMP。每当我打开 bmp 文件时,它都会显示:

"BMP Image has unsupported header size"

我做错了什么? 是否可以保存为 png 而不是 BMP? 我可以访问 libPNG 图书馆

这里是一个代码示例,它转储了一个从二维码创建的 24 bpp bmp 文件。您看到的错误可能不是由 QR-Code 库引起的,而是由 bmp 文件代码引起的。

此示例创建的 bmp 文件与我的 Windows 8.1 打包的图像查看器一起工作正常。如果您也没有看到错误,则可以检查每个二进制输出中的差异以查明问题所在。如果你愿意。

这个问题被标记为"C++"和"C++11",所以这个例子使用C++ std库进行文件输出,没有使用malloc。 (但几乎同样糟糕——我在某些容器代码中使用 newdelete,其中首选 std::vector 成员……不要告诉任何人)。此外,此示例将每条数据直接写入文件,而不是使用文件大小的中间缓冲区,如 pDestData.

#include <iostream>
#include <fstream>

// A fake (or "somewhat limited") QR Code data container
struct Qrc {
    int dimsize;          // the width and height
    unsigned char* data;  // buffer which contains the elements
    Qrc() {
        static const unsigned int bin[] = { // encodes an important secret message
            0xfc8b7d7f,0xa801a83,0xd6e54d76,0xaa9eb2ed,0x43ed05db,0xb8786837,0x55555fe0,
            0x5a4c807f,0xcf315c00,0x6e8019ce,0xc7819e0d,0xd4857ba8,0x4ac5e347,0xf6f349ba,
            0xd433ccdd,0x2998361e,0x4453fab3,0x526d9085,0x81f38924,0xb4da0811,0x84b3131a,
            0x9639915e,0x3b74a4ff,0x42aa0c11,0x4127be16,0x1f4350,0xff620296,0xad54de1,
            0xd38c2272,0xa3f76155,0x5366a7ab,0x9bdd2257,0x300d5520,0x85842e7f,0 };
        dimsize = 33;
        data = new unsigned char[dimsize * dimsize];
        auto p = data;
        auto endp = p + dimsize * dimsize;
        for(unsigned int b : bin) {
            for(int i=0; i<32; ++i) {
                if(p == endp) break;
                *(p++) = b & (1 << i) ? 255 : 0;
    }   }   }
    Qrc(const Qrc&) = delete;
    Qrc& operator = (const Qrc&) = delete;
    ~Qrc() { delete [] data; }
};

struct BIH {   // a private definition of BITMAPINFOHEADER
    unsigned int   sz;
    int            width, height;
    unsigned short planes;
    short          bits;
    unsigned int   compress, szimage;
    int            xppm, yppm;
    unsigned int   clrused, clrimp;
};

void SaveBmp(const char* filename, const Qrc& qrc) {
    // Asker's Qrc struct delivered as a pointer, from a C API, but this example doesn't mimic that.
    std::ofstream ofs(filename, std::ios_base::out | std::ios_base::binary);
    if(!ofs) {
        std::cout << "Writing " << filename << " failed\n";
        return;
    }

    const int side_len            = qrc.dimsize;  // width and height of the (square) QR Code
    const int pixel_side_len      = 4; // QRC element's size in the bmp image (in pixels)
    const int bmp_line_bytes      = side_len * pixel_side_len * 3;
    const int bmp_line_pad_bytes  = (4 - bmp_line_bytes % 4) % 4;  // bmp line data padding size
    const int bmp_data_size       = side_len * (bmp_line_bytes + bmp_line_pad_bytes);

    BIH bih = { sizeof(bih) };      
    bih.width = side_len * pixel_side_len;   // element count * element size
    bih.height = -side_len * pixel_side_len; // negative height => data begins at top of image
    bih.planes = 1;
    bih.bits = 24;

    const int header_size = sizeof(bih) + 14;  // size of the bmp file header
    const int filesize    = header_size + bmp_data_size; // size of the whole file

    ofs.write("BM", 2);
    ofs.write(reinterpret_cast<const char*>(&filesize), 4);
    ofs.write("[=10=][=10=][=10=][=10=]", 4);  // 2x 16-bit reserved fields
    ofs.write(reinterpret_cast<const char*>(&header_size), 4);
    ofs.write(reinterpret_cast<const char*>(&bih), sizeof(bih));

    // pixel colors, as Blue, Green, Red char-valued triples
    // the terminating null also makes these usable as 32bpp BGRA values, with Alpha always 0.
    static const char fg_color[] = "[=10=][=10=][=10=]";
    static const char bg_color[] = "\xff\xff\xff";

    auto pd = qrc.data;

    // send pixel data directly to the bmp file
    // QRC elements are expanded into squares
    // whose sides are "pixel_side_len" in length.
    for(int y=0; y<side_len; ++y) {
        for(int j=0; j<pixel_side_len; ++j) {
            auto pdj = pd;
            for(int x=0; x<side_len; ++x) {
                for(int i=0; i<pixel_side_len; ++i) {
                    // *pdj will be 0 or 255 (from "fake" Qrc)
                    // Using "*pdj & 1" here, just to match asker's code
                    // without knowing why this was done.
                    ofs.write(*pdj & 1 ? fg_color : bg_color, 3);
                }
                ++pdj;
            }
            if(bmp_line_pad_bytes) {
                ofs.write("[=10=][=10=][=10=]", bmp_line_pad_bytes);
            }
        }
        pd += side_len;
    }
}

int main() {
    SaveBmp("MyQrCode.bmp", Qrc());
}