C语言结合stb库实现任意尺寸QR码PNG图像编码导出
问题描述
使用QR-Code-generator 1.8.0与stb_image_write库将QR码渲染为PNG、SVG格式图像时,参照官方demo示例可以正确生成QR码,但输出图像尺寸过小,不清楚缩放实现方式。经判断缩放逻辑应在图像写入环节实现,但不知道如何调整像素数据构造逻辑,实现通过函数参数传入目标尺寸,输出任意自定义大小的QR码图像。
初始问题代码
#include "qrcodegen.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define Border 4 void qr_make_image_data(const uint8_t *data, uint8_t **result) { int size = qrcodegen_getSize(data); int border = Border; *result = calloc(1, (size + border*2)*(size + border*2)*2); int count = 0; for (int y = -border; y < size + border; y++) for (int x = -border; x < size + border; x++) (*result)[count++] = qrcodegen_getModule(data, x, y) ? 0xff : 0x00; } int qr_make(char *text, char *file_out) { uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; uint8_t temp_buf[qrcodegen_BUFFER_LEN_MAX]; bool ok = qrcodegen_encodeText(text, temp_buf, qrcode, qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); if (!ok) return -1; int size = qrcodegen_getSize(qrcode); uint8_t *data = NULL; qr_make_image_data(qrcode, &data); int image_side = size + Border * 2; int result = stbi_write_png(file_out, image_side, image_side, 1, data, 0); free(data); return result; } int main(void) { char *sample_text_1 = "Donec diam neque, vestibulum eget, " "vulputate ut, ultrices vel, augue. Donec posuere metus " "vitae ipsum. Mauris sit amet eros. Vestibulum ac est " "lacinia nisi venenatis tristique. Integer pede justo, " "lacinia eget, tincidunt eget, tempus vel, pede."; char *sample_text_2 = "www.stackoverflow.com"; qr_make(sample_text_1, "qr1.png"); qr_make(sample_text_2, "qr2.png"); }
解决方案
修复原有代码中像素值反转的错误,引入stb_image_resize库实现输出图像缩放,支持传入缩放因子调整输出尺寸。
- 修正点1:原代码中QR码模块颜色逻辑写反,黑色模块对应像素值
0x00、白色背景对应0xff - 修正点2:移除原像素缓冲区多余的2倍内存分配,单通道灰度图每个像素仅占1字节
- 修正点3:新增
resize_factor缩放因子参数,使用盒式滤镜缩放保证QR码边缘锐利无模糊 - 修正点4:缩放完成后及时释放原始像素缓冲区与缩放后缓冲区,避免内存泄漏
修正后完整代码
#include "qrcodegen.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" #define Border 4 void qr_make_image_data(const uint8_t *data, uint8_t **result) { int size = qrcodegen_getSize(data); int border = Border; *result = calloc(1, (size + border*2)*(size + border*2)); int count = 0; for (int y = -border; y < size + border; y++) for (int x = -border; x < size + border; x++) { (*result)[count++] = qrcodegen_getModule(data, x, y) ? 0x00 : 0xff; } } int qr_make(char *text, char *file_out, int resize_factor) { uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; uint8_t temp_buf[qrcodegen_BUFFER_LEN_MAX]; // 编码文本生成QR码 bool ok = qrcodegen_encodeText(text, temp_buf, qrcode, qrcodegen_Ecc_QUARTILE, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, false); if (!ok) return -1; int size = qrcodegen_getSize(qrcode); uint8_t *data = NULL; qr_make_image_data(qrcode, &data); // 计算原始尺寸与缩放后尺寸 int image_side = size + Border * 2; int image_side_resized = image_side * resize_factor; // 分配缩放后图像的内存缓冲区 uint8_t *resized_data = malloc(image_side_resized * image_side_resized * 1); stbir_resize(data, image_side, image_side, 0, resized_data, image_side_resized, image_side_resized, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, NULL); // 将像素数据编码为PNG文件写入磁盘 int result = stbi_write_png(file_out, image_side_resized, image_side_resized, 1, resized_data, 0); free(data); free(resized_data); return result; } int main(void) { char *sample_text_1 = "Donec diam neque, vestibulum eget, " "vulputate ut, ultrices vel, augue. Donec posuere metus " "vitae ipsum. Mauris sit amet eros. Vestibulum ac est " "lacinia nisi venenatis tristique. Integer pede justo, " "lacinia eget, tincidunt eget, tempus vel, pede."; char *sample_text_2 = "www.stackoverflow.com"; // 传入缩放因子4,输出4倍放大的QR码 qr_make(sample_text_1, "QR1.png", 4); qr_make(sample_text_2, "QR2.png", 4); }
内容的提问来源于stack exchange,提问作者anewb33
相关产品推荐
相关产品推荐

