1.libqr
libqr源码地址:https://github.com/rsky/qrcode,该库自带输出bitmap和png格式图片二维码的功能,所以依赖的第三方库较少(只依赖libz)。
-
编译步骤(交叉编译):
1. 下载源码:git clone https://github.com/rsky/qrcode
2. 进入编译目录:cd qrcode/libqr
3. libqr使用cmake方式编译,交叉编译具体如下:
1) 修改CMakeLists:vi CMakeLists.txt,文件开头添加如下内容(交叉编译器根据实际情况自行修改)
# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
2) 生成Makefile:cmake .
3) 编译:make
-
使用方法:
#include "qr.h"
#include <fstream>
using namespace std;
int qrencode(char *string, char *path)
{
int errcode = QR_ERR_NONE;
QRCode* p = qrInit(8, QR_EM_8BIT, 1, -1, &errcode);
if (p == NULL) {
printf("error\n");
return -1;
}
qrAddData(p, (const qr_byte_t*)string, strlen(string));
if (!qrFinalize(p)) {
printf("finalize error\n");
return -1;
}
int size = 0;
// width = height = qr_vertable[version] * mag + sep * mag * 2
qr_byte_t * buffer = qrSymbolToPNG(p, 5, 5, &size);
if (buffer == NULL) {
printf("error %s", qrGetErrorInfo(p));
return -1;
}
ofstream f(path);
if (f.fail()) {
return -1;
}
f.write((const char *)buffer, size);
f.close();
return 0;
}
该库的优点是编译和使用简单,但个人测试,待编码的字符串超过152个字符时,生成的二维码无法被正常解析,具体原因未深入分析。
2.libqrencode
libqrencode项目地址:https://fukuchi.org/works/qrencode/。该库依赖libpthread、libz、libpng和libiconv,其中libpthread和libz一般编译器都有自带,而libpng和libiconv不一定有,如有依赖需自行编译。该库目前最新版本为4.0.2,本文以该版本编译为例:
-
编译步骤(交叉编译):
1. 下载源码:wget https://fukuchi.org/works/qrencode/qrencode-4.0.2.tar.gz
2.解压源码:tar zxvf qrencode-4.0.2.tar.gz
3.进入编译目录:cd qrencode-4.0.2/
4. libqrencode同时提供cmake和configure方式编译,但configure方式,工程提供的config.sub和config.guess太旧,没有支持我所使用的androideabi(可找新的config.sub和config.guess替换),故本文只介绍cmake编译方式,具体如下:
1)修改CMakeLists:vi CMakeLists.txt ,文件开头添加如下内容 (交叉编译器根据实际情况自行修改,默认配置编译会生成qrencode命令行工具,依赖libpng等第三库)
# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
2)生成Makefile:cmake .
3)编译:make
-
使用方法(可参考源码中的qrenc.c文件):
#include "qrencode.h"
#include "png/png.h"
#include <errno.h>
#include <stdlib.h>
enum imageType {
PNG_TYPE,
PNG32_TYPE,
};
#define INCHES_PER_METER (100.0/2.54)
static int dpi = 72;
static int size = 5;
static int eightbit = 0; // encode entire data in 8-bit mode
static int version = 0; // specify the minimum version of the symbol.
static int margin = 5; // specify the width of the margins. (default=4 (2 for Micro QR)))
static QRecLevel level = QR_ECLEVEL_L;
static QRencodeMode hint = QR_MODE_8;
static unsigned char fg_color[4] = {0, 0, 0, 255};
static unsigned char bg_color[4] = {255, 255, 255, 255};
static void fillRow(unsigned char *row, int num, const unsigned char color[])
{
int i;
for(i = 0; i < num; i++) {
memcpy(row, color, 4);
row += 4;
}
}
static int writePNG(const QRcode *qrcode, const char *outfile, enum imageType type)
{
static FILE *fp; // avoid clobbering by setjmp.
png_structp png_ptr;
png_infop info_ptr;
png_colorp palette = NULL;
png_byte alpha_values[2];
unsigned char *row, *p, *q;
int x, y, xx, yy, bit;
int realwidth;
realwidth = (qrcode->width + margin * 2) * size;
if(type == PNG_TYPE) {
row = (unsigned char *)malloc((realwidth + 7) / 8);
} else if(type == PNG32_TYPE) {
row = (unsigned char *)malloc(realwidth * 4);
} else {
fprintf(stderr, "Internal error.\n");
exit(EXIT_FAILURE);
}
if(row == NULL) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(EXIT_FAILURE);
}
if(outfile[0] == '-' && outfile[1] == '\0') {
fp = stdout;
} else {
fp = fopen(outfile, "wb");
if(fp == NULL) {
fprintf(stderr, "Failed to create file: %s\n", outfile);
perror(NULL);
exit(EXIT_FAILURE);
}
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(png_ptr == NULL) {
fprintf(stderr, "Failed to initialize PNG writer.\n");
exit(EXIT_FAILURE);
}
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL) {
fprintf(stderr, "Failed to initialize PNG write.\n");
exit(EXIT_FAILURE);
}
if(setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fprintf(stderr, "Failed to write PNG image.\n");
exit(EXIT_FAILURE);
}
if(type == PNG_TYPE) {
palette = (png_colorp) malloc(sizeof(png_color) * 2);
if(palette == NULL) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(EXIT_FAILURE);
}
palette[0].red = fg_color[0];
palette[0].green = fg_color[1];
palette[0].blue = fg_color[2];
palette[1].red = bg_color[0];
palette[1].green = bg_color[1];
palette[1].blue = bg_color[2];
alpha_values[0] = fg_color[3];
alpha_values[1] = bg_color[3];
png_set_PLTE(png_ptr, info_ptr, palette, 2);
png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL);
}
png_init_io(png_ptr, fp);
if(type == PNG_TYPE) {
png_set_IHDR(png_ptr, info_ptr,
realwidth, realwidth,
1,
PNG_COLOR_TYPE_PALETTE,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
} else {
png_set_IHDR(png_ptr, info_ptr,
realwidth, realwidth,
8,
PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
}
png_set_pHYs(png_ptr, info_ptr,
dpi * INCHES_PER_METER,
dpi * INCHES_PER_METER,
PNG_RESOLUTION_METER);
png_write_info(png_ptr, info_ptr);
if(type == PNG_TYPE) {
/* top margin */
memset(row, 0xff, (realwidth + 7) / 8);
for(y = 0; y < margin * size; y++) {
png_write_row(png_ptr, row);
}
/* data */
p = qrcode->data;
for(y = 0; y < qrcode->width; y++) {
memset(row, 0xff, (realwidth + 7) / 8);
q = row;
q += margin * size / 8;
bit = 7 - (margin * size % 8);
for(x = 0; x < qrcode->width; x++) {
for(xx = 0; xx < size; xx++) {
*q ^= (*p & 1) << bit;
bit--;
if(bit < 0) {
q++;
bit = 7;
}
}
p++;
}
for(yy = 0; yy < size; yy++) {
png_write_row(png_ptr, row);
}
}
/* bottom margin */
memset(row, 0xff, (realwidth + 7) / 8);
for(y = 0; y < margin * size; y++) {
png_write_row(png_ptr, row);
}
} else {
/* top margin */
fillRow(row, realwidth, bg_color);
for(y = 0; y < margin * size; y++) {
png_write_row(png_ptr, row);
}
/* data */
p = qrcode->data;
for(y = 0; y < qrcode->width; y++) {
fillRow(row, realwidth, bg_color);
for(x = 0; x < qrcode->width; x++) {
for(xx = 0; xx < size; xx++) {
if(*p & 1) {
memcpy(&row[((margin + x) * size + xx) * 4], fg_color, 4);
}
}
p++;
}
for(yy = 0; yy < size; yy++) {
png_write_row(png_ptr, row);
}
}
/* bottom margin */
fillRow(row, realwidth, bg_color);
for(y = 0; y < margin * size; y++) {
png_write_row(png_ptr, row);
}
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
free(row);
free(palette);
return 0;
}
static QRcode *encode(const unsigned char *intext, int length)
{
QRcode *code;
if(eightbit) {
code = QRcode_encodeData(length, intext, version, level);
} else {
code = QRcode_encodeString((char *)intext, version, level, hint, true);
}
return code;
}
void qrencode(char *intext, int length, const char *outfile)
{
QRcode *qrcode;
qrcode = encode((const unsigned char *)intext, length);
if(qrcode == NULL) {
if(errno == ERANGE) {
fprintf(stderr, "Failed to encode the input data: Input data too large\n");
} else {
perror("Failed to encode the input data");
}
exit(EXIT_FAILURE);
}
writePNG(qrcode, outfile, PNG_TYPE);
QRcode_free(qrcode);
}
3. zint
zint项目地址:http://zint.github.io/,该库依赖libpng和libz第三方库,编译前需保证已安装。
-
编译步骤(交叉编译):
1. 下载源码:git clone https://github.com/zint/zint.git
2. 进入编译目录:cd zint/;mkdir build;cd build
3. zint也是使用cmake方式编译,交叉编译具体如下:
1) 修改CMakeLists:vi CMakeLists.txt,文本开头添加如下内容
# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
2)生成Makefile:cmake ..
3)编译:make (编译过程有出现multiple definition of 'concat'错误,通过注释掉backend/common.c文件中的concat和itoc函数,即可正常编译)
- 使用方法: