c++ - LibQREncode qrcode 到 BMP

标签 c++ image c++11 qr-code bmp

我正在使用库 qrencode.h 创建一个二维码 这个创建工作很好,但是如何在 C++ 中将 qrcode 输出到 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");
        }

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

"BMP Image has unsupported header size"

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

最佳答案

这里是一个代码示例,它转储从 QR 代码创建的 24 bpp bmp 文件。您看到的错误可能不是由 QR 码库引起的,而是 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("\0\0\0\0", 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[] = "\0\0\0";
    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("\0\0\0", bmp_line_pad_bytes);
            }
        }
        pd += side_len;
    }
}

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

关于c++ - LibQREncode qrcode 到 BMP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37925701/

相关文章:

c++在不知道对象类型的情况下定义成员函数指针

c++ - 将 vector<string> 元素复制到其他 vector<string>* (1 作为指针传递)

c++ - 在 C++ 应用程序中使用 box.net、dropbox(或任何云存储)

html - WordPress 图像不一致

c++ - 使用 C++ 11 进行重构

c++ - 在编译时根据 printf 格式检查参数的可移植方式,C++

c++ - 从二进制文件读取结构数据时获取垃圾值

php - 网站中的图形偶尔会损坏

javascript - 如何使用 JS 渲染预加载的图像

c++ - 引用变量在内存中是如何表示的?