Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30103925
  • 博文数量: 230
  • 博客积分: 2868
  • 博客等级: 少校
  • 技术积分: 2223
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-08 21:48
个人简介

Live & Learn

文章分类

全部博文(230)

文章存档

2022年(2)

2019年(5)

2018年(15)

2017年(42)

2016年(24)

2015年(13)

2014年(1)

2012年(5)

2011年(58)

2010年(56)

2009年(9)

我的朋友

分类: Windows平台

2017-12-25 15:45:36

现在生活中,二维码可以说是无处不在,微信扫码支付,支付宝扫码支付,就连贴小广告的都带上了二维码了。之前一直想去了解一下,还是太懒了,就没去,现在项目中需要用到这东西,正好借此机会了解一下。

 

 

上网一查,原来二维码的还有很多种。下表是一个简单的介绍:



二维码的优点突出,所以大有取代条形码的趋势,二维码的特点:

1、高密度,容量大,可容纳多达1850个大写字母(字符)或2710个数字,支持最高1108个字节的数据存储,比一维码信息容量高几十倍。

2、 范围广,支持对图片、声音、文字、签字、指纹等各类可以数字化信息的编码,还可以表示多种语言文字和图像数据。

3、 容错能力强,具有纠错功能,当二维码因穿孔、污损等引起局部损坏时,照样可以正常识别,损毁面积达50%仍可恢复。

4、 成本低,易制作,持久耐用

 

 

本次开发使用的是QRcode,因为它目前使用的较为广泛,微信支付,扫码加好友等等都可以是QRcode

 

下面对QR Code做个详细的了解:

QR Code码,是由Denso公司于19949月研制的一种符号,它具有一维条码及其它二维条码所具有的信息容量大、可靠性高、可表示汉字及图象多种文字信息、保密防伪性强等优点,是目前较为常用的二维条码。

基本特性

符号规格

21×21模块(版本1-177×177 模块(版本40

(每一规格:每边增加4个模块)

容量(指最大规格符号版本40-L级)


· 8位字节数据 :2,953个字符

· 汉字数据 :1,817个字符

数据表示方法

深色模块表示二进制“1”,浅色模块表示二进制“0”

纠错能力

· L级:约可纠错7%的数据码字

· M级:约可纠错15%的数据码字

· Q级:约可纠错25%的数据码字

· H级:约可纠错30%的数据码字

掩模(固有)

可以使符号中深色与浅色模块的比例接近11,使因相邻模块的排列造成译码困难的可能性降为最小。

模式

1、数字模式(numeric mode ): 0001(数字09

2、混合字符模式(alphanumeric mode) : 0010(数字09;大写字母AZ9个其他:space ,$, %, *, +, -, ., /, :);

3、8bit byte mode: 0100

4、日本汉字(KANJI mode) : 1000

5、中国汉字(GB2312):1101GB 2312对应的汉字和非汉字字符)

 


 

为了美观,可以在二维码上添加一张logo图片,一般放在中间。我在网上查了好久,没有找到logo的大小和二维码的等级和容错性的关系,如果大家有找到的,请告知,谢谢,我这边自己选择了长宽是二维码的20%,测试不影响识别。

 

编码阶段:

在网上找了一个第三方的库,qrencode,提供了制作二维码的API,接口很简单,

extern QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);

这个API可以直接设置要编码的字符串内容,以及对二维码的设置,如版本(即大小等级1~40)、容错等级、模式等,返回的是一个结构体指针,QRcode,

typedef struct {
         int version;         ///< version of the symbol
         int width;           ///< width of the symbol
         unsigned char *data; ///< symbol data
} QRcode;

它的数据data就是二维码的内容,1对应深色块,0对应浅色块。借助一些其他的画图的API就可以绘制出二维码。哭的详细介绍请参考:

 

我使用的是QT+VS 和QT+Linux,做的是嵌入式开发,所以需要涉及到一些交叉编译等。

 

由于qrencode 在我们项目中作为一个第三方库,只需要它的基础功能,后面不会持续去更新,所以,决定将其编译成静态库。如果想编译成动态库,可以参考http://blog.csdn.net/u010977122/article/details/52959098

这个是我开始编译成动态库遇到的一些问题。

静态库的编译参照了 http://blog.csdn.net/liyuanbhu/article/details/44647139 博主的配置,在此感谢。

将博主的一些东西搬过来,从而让本文更加完整,下面是编译windows下的静态库的过程。


建立一个 win32 项目,选择生成静态库,不使用预编译头。将 qrencode 的源文件(.c 和 .h)全部拷到vc 的项目目录中,除了 qrenc.c 。编译 qrencode 时还需要有个 config.h 文件(源码中的config.h.in文件修改成config.h),这个文件主要是配置库中的一些宏开关,可以用我下面提供的这个。

[cpp] view plain copy
  1. /* config.h.  Generated from config.h.in by configure.  */    
  2. /* config.h.in.  Generated from configure.ac by autoheader.  */    
  3.     
  4. /* Define to 1 if you have the  header file. */    
  5. #define HAVE_INTTYPES_H 1    
  6.     
  7. /* Define to 1 if using pthread is enabled. */    
  8. #undef HAVE_LIBPTHREAD    
  9.     
  10. /* Define to 1 if you have the  header file. */    
  11. #define HAVE_MEMORY_H 1    
  12.     
  13. /* Define to 1 if you have the  header file. */    
  14. #define HAVE_STDINT_H 1    
  15.     
  16. /* Define to 1 if you have the  header file. */    
  17. #define HAVE_STDLIB_H 1    
  18.     
  19. /* Define to 1 if you have the  header file. */    
  20. #define HAVE_STRINGS_H 1    
  21.     
  22. /* Define to 1 if you have the  header file. */    
  23. #define HAVE_STRING_H 1    
  24.     
  25. /* Define to 1 if you have the `strdup' function. */    
  26. #define HAVE_STRDUP 1    
  27.     
  28. /* Define to 1 if you have the  header file. */    
  29. #define HAVE_SYS_STAT_H 1    
  30.     
  31. /* Define to 1 if you have the  header file. */    
  32. #define HAVE_SYS_TYPES_H 1    
  33.     
  34. /* Define to 1 if you have the  header file. */    
  35. #define HAVE_UNISTD_H 1    
  36.     
  37.     
  38. /* Major version number */    
  39. #define MAJOR_VERSION 3    
  40.     
  41. /* Micro version number */    
  42. #define MICRO_VERSION 4    
  43.     
  44. /* Minor version number */    
  45. #define MINOR_VERSION 4    
  46.     
  47. /* Name of package */    
  48. #define PACKAGE "qrencode"    
  49.     
  50. /* Define to the address where bug reports for this package should be sent. */    
  51. #define PACKAGE_BUGREPORT ""    
  52.     
  53. /* Define to the full name of this package. */    
  54. #define PACKAGE_NAME "QRencode"    
  55.     
  56. /* Define to the full name and version of this package. */    
  57. #define PACKAGE_STRING "QRencode 3.4.4"    
  58.     
  59. /* Define to the one symbol short name of this package. */    
  60. #define PACKAGE_TARNAME "qrencode"    
  61.     
  62. /* Define to the home page for this package. */    
  63. #define PACKAGE_URL ""    
  64.     
  65. /* Define to the version of this package. */    
  66. #define PACKAGE_VERSION "3.4.4"    
  67.     
  68. /* Define to 1 if you have the ANSI C header files. */    
  69. #define STDC_HEADERS 1    
  70.     
  71. /* Version number of package */    
  72. #define VERSION "3.4.4"    
  73.     
  74. #define inline    
  75.     
  76. /* Define to 'static' if no test programs will be compiled. */    
  77. #define __STATIC static    
  78. /* #undef WITH_TESTS */    
  79.        
然后在项目属性中添加预处理定义:HAVE_CONFIG_H(项目->属性->配置属性->C/C++->预处理器->预处理器定义)


config.h 中有一行:#define inline 

是因为rscode.c 文件中有个modnn的定义如下:

[cpp] view plain copy
  1. static inline int modnn(RS *rs, int x){    
  2.     while (x >= rs->nn) {    
  3.         x -= rs->nn;    
  4.         x = (x >> rs->mm) + (x & rs->nn);    
  5.     }    
  6.     return x;    
  7. }    
在用VS2012编译的时候,在split.c文件中,strdup编译不过,提示 error C4996: 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _strdup. See online help for details. 将strdup改成_strdup,具体参考我的博客:http://blog.csdn.net/u010977122/article/details/53020475

点击打开链接

qrencode 本身是不依赖于 libpng 库的。所以不存在什么缺少 png.h 的问题。按照本文介绍的方法按部就班的做就能生成静态库,不存在任何问题。编译时一定要排除 qrenc.c 这个文件。这个文件是 qrencode 的一个使用例子,与这个库本身无关。


生成了libqrencode.a的一个静态库,再配qrencode.h这个头文件,就可以在项目中使用了。

在QT中使用的具体代码如下:


[cpp] view plain copy
  1. void HQrencodePCtrl::PaintQRcode::draw(QPainter &painter, int width, int height)    
  2. {    
  3.     QColor foreground(Qt::black);  
  4.     painter.setBrush(foreground);  
  5.     const int qr_width = qr->width > 0 ? qr->width : 1;    
  6.     double scale_x = width / qr_width;    
  7.     double scale_y = height / qr_width;    
  8.     forint y = 0; y < qr_width; y ++)    
  9.     {    
  10.         for(int x = 0; x < qr_width; x++)    
  11.         {    
  12.             unsigned char b = qr->data[y * qr_width + x];    
  13.             if(b & 0x01)    
  14.             {    
  15.                 QRectF r(int(x * scale_x), int(y * scale_y), int(scale_x), int(scale_y));    
  16.                 painter.drawRects(&r, 1);    
  17.             }    
  18.         }    
  19.     }   
  20.     if(bhavelogo)     //添加Logo的图片,绘制在二维码的中间  
  21.     {  
  22.         QPixmap picture(logoaddr.c_str());  
  23.         int logoWidth = width*0.2;  
  24.         int logoHeight = height * 0.2;  
  25.         painter.drawPixmap(width/2-logoWidth/2,height/2-logoHeight/2,logoWidth,logoHeight,picture);  
  26.     }     
  27. }    
[cpp] view plain copy
  1. "code" class="cpp">void HQrencodePCtrl::PaintQRcode::setString(HString str)    
  2. {    
  3.     priv_string = str;    
  4.     if(qr != NULL)    
  5.     {    
  6.         QRcode_free(qr);    
  7.     }    
  8.     qr = QRcode_encodeString(priv_string.c_str(),    
  9.         1,    
  10.         QR_ECLEVEL_L,    
  11.         QR_MODE_8,    
  12.         1);    
  13.     update();    
  14. }    
[cpp] view plain copy
  1. void HQrencodePCtrl::PaintQRcode::paint(QPainter *painter)  
  2. {  
  3.     QColor background(Qt::white);    
  4.     painter->setBrush(background);    
  5.     painter->setPen(Qt::NoPen);    
  6.     //painter->drawRect(0, 0, width(), height());    
  7.   
  8.   
  9.     if(qr != NULL)    
  10.     {  
  11.         int paintWidth = int(width()/qr->width)*qr->width ;  
  12.         int paintHeight = int(height()/qr->width)*qr->width;  
  13.         painter->drawRect(0, 0, paintWidth, paintHeight);   
  14.         draw(*painter, paintWidth,paintHeight);    
  15.     }    
  16. }  





在linux下编译成库,主要是写Makefile 文件,除了将strdup改成_strdup再改回去,其他不变。下面是我的makefile文件:

[java] view plain copy
  1. TARGET = libqrencode  
  2. TARGET_DIR = ../../../lib/linux/debug/  
  3. #OBJ_DIR = ./objs  
  4. #$(CC) -o $(TARGET_DIR)/$(TARGET) $(OBJ_FILES) $(LDFLAGS)  
  5.   
  6. INCLUDE = -I../../../src/ -I../../../inc/  
  7. SOURCES = ../../../src/bitstream.c ../../../src/mask.c ../../../src/mmask.c ../../../src/mqrspec.c ../../../src/qrencode.c ../../../src/qrinput.c ../../../src/qrspec.c ../../../src/rscode.c ../../../src/split.c  
  8.   
  9. LIBS = -lpthread  
  10. OBJ_FILES = $(patsubst %.c, %.o, $(SOURCES))  
  11.   
  12. CC = arm-poky-linux-gnueabi-g++ -march=armv7-a -mthumb-interwork -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=/opt/poky/1.7/sysroots/cortexa9hf-vfp-neon-poky-linux-gnueabi -std=c++11 -ggdb  
  13. AR = arm-poky-linux-gnueabi-ar   #这里将编译器替换成自己需要的编译器  
  14. #CC = gcc  
  15. #AR = ar  
  16.   
  17. LDFLAGS += $(LIBS)   
  18. CPPFLAGS += $(INCLUDE)  
  19.   
  20. all: check_env compile link  
  21.   
  22. check_env:  
  23.     @echo "[$(TARGET)]: Build Start..."  
  24.     @mkdir -p $(TARGET_DIR)  
  25.   
  26. compile: $(OBJ_FILES)  
  27.   
  28. link:  
  29.     @echo "[$(TARGET)]: Linking ..."  
  30.     @$(AR) rcs $(TARGET_DIR)/$(TARGET).a $(OBJ_FILES)  
  31.     @echo "[$(TARGET)]: Build done!"      
  32.   
  33. #complie step 1:  
  34. $(OBJ_FILES): %.o: %.c  
  35.     @echo "[$(TARGET)]: Compiling  $(notdir $^) ..."  
  36.     @$(CC) -DHAVE_CONFIG_H=1 -fPIC -c $(CPPFLAGS) $< -o $@     #这里在编译的时候加上"font-family: 'Times New Roman';">HAVE_CONFIG_H=1的宏定义  
  37.   
  38.   
  39. clean:  
  40.     @rm -rf $(OBJ_FILES) $(TARGET_DIR)/$(TARGET).a  
  41.     @echo "[$(TARGET)]: Clean Finish!"  

谢谢~
阅读(10471) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~