用C++完成QRCode(快速响应码)的基本逻辑实现

QRCode(快速响应码)基本逻辑实现

事前说明

本文档基于GB/T 18284-2000

本文档仅仅实现快速响应码(又称二维码、QR Code)的基本功能,重点在于完成最基本的核心业务逻辑。

更多扩展内容还待自行查看国标完成补充。

该文档属于实现部分,更多请查看国标内容

正文

思路

二维码如其名所示,是一个二维矩阵。而每一个元素有黑与白两个值,因此在c/c++中就可以一个二维数组表示(在不特意说明之下,“(二维)矩阵”、“(二维)数组”都表示二维码的bitmap),然后对该数组进行渲染(true为黑,false为白)。

本实现以由简到难,需要啥才用啥的为理念
并且使用类与对象的编程思想,函数式编程方式,因为好维护也好讲解

正式步骤

0.提前说明
以下讲解的函数等,都默认为QRCode类的内部函数,去除前缀仅仅为了易看,完整版参考附录Z

QRCode::QRCode(/*params...*/)
{
    //steps...
}
  1. 基本大小
  • length( l e n len len)

    在我们看到的许多二维码中,我们可以很明显的看到不同二维之间有着不同的大小。其长度

    l e n = 4 × ( v e r − 1 ) + 21 len = 4\times(ver-1)+21 len=4×(ver1)+21

    其中ver代表二维码的版本

  • version( v e r ∈ { 1 , 2 , . . . , 40 } ver\in \{1,2,...,40\} ver{1,2,...,40})

    版本在许多部分是有用途的,而且版本7是一个比较特殊的分界线,这里先埋一个伏笔。

    至此,得到了一个等边矩阵,设置绘画和获得bitmap函数

        typedef struct _QRC_XY
        {
            int16_t x;
            int16_t y;
            _QRC_XY(int16_t x, int16_t y):x(x),y(y) {}
        }QRC_XY;
        
        QRCode::QRCode(/*params...*/uint8_t ver/*params..*/)
        {
            //step1:size
            this->len = 4*(ver-1)+21;
            this->bitmap = new bool[len*len];
            this->ver = ver;
            // 一定要设置,因为有空白区域(画不到)的存在
            for (uint16_t i = 0; i < len * len; i++) this->bitmap[i] = false;
    
            //steps...
        }
    
        bool get(uint8_t x,uint8_t y)
        {
            return this->bitmap[x + y*this->len];
        }
        bool get(QRC_XY xy)
        {
            return this->get(xy.x,xy.y);
        }
        void set(uint8_t x,uint8_t y,bool flag)
        {
            this->bitmap[x + y*this->len] = flag;
        }
        void set(QRC_XY xy,bool flag)
        {
            this->set(xy.x,xy.y,flag);
        }
    
  1. 固定图案

在确定大小之后,就可以画图案了。各个图案查看国标图2

  • 位置探测图形及其分隔符

    查看图9,可以看到图案结构,同时还可以在图2看到其分隔符,位置根据探测图像的不同而不同

    那么以图案探测图形左上为起始,画图有

    void drawPosDectPatt(uint8_t startX,uint8_t startY)
    {
        uint8_t i/*delta_y*/,j/*delta_x | y*/;
        
        // 图案
        // 1、2、6、7行
        for (i = 0; i < 2; i++)
        {
            // 2、6行 i*4+1 in [1,5]
            j = startY + i * 4 + 1;
            this->set(startX    ,j,true);
            this->set(startX + 1,j,false);
            this->set(startX + 2,j,false);
            this->set(startX + 3,j,false);
            this->set(startX + 4,j,false);
            this->set(startX + 5,j,false);
            this->set(startX + 6,j,true);
            // 1、7行 i*6 in [0,6]
            for (j = 0; j < 7; j++)
                this->set(startX + j,startY + i * 6,true);
        }
        // 3、4、5行
        for (i = 0; i < 3; i++)
        {
            this->set(startX + 0, startY + i + 2,true);
            this->set(startX + 1, startY + i + 2,false);
            this->set(startX + 2, startY + i + 2,true);
            this->set(startX + 3, startY + i + 2,true);
            this->set(startX + 4, startY + i + 2,true);
            this->set(startX + 5, startY + i + 2,false);
            this->set(startX + 6, startY + i + 2,true);
        }
    
        // 分隔符
        if (startX == 0)
        {
            if (startY == 0) // 左上
            {
                for (i = 0; i < 8; i++) this->set(7, i, false);
                for (j = 0; j < 7; j++) this->set(j, 7, false);
            }
            else // 左下
            {
                for (i = 0; i < 8; i++) this->set(7, startY + i - 1, false);
                for (j = 0; j < 7; j++) this->set(j, startY - 1, false);
            }
        }
        else // 右上
        {
            for(i=0;i<8;i++) this->set(startX - 1,i,false);
            for(j=0;j<7;j++) this->set(startX + j,7,false);
        }
    }
    

    数量固定为3个,位置的确定也比较简单分别为

    x 1 = 0 , y 1 = 0 x_1 = 0,y_1 = 0 x1=0,y1=0

    x 2 = 0 , y 2 = l e n − 7 x_2 = 0,y_2 = len-7 x2=0,y2=len7

    x 3 = l e n − 7 , y 3 = 0 x_3 = len-7,y_3 = 0 x3=len7,y3=0

  • 定位图形

    水平和垂直定位图形分别为一个模块宽的一行和一列,由深色与浅色模块交替组成,其开始和结尾都是深色模块(如图 2 所示)。水平定位图形位于符号上部的两个位置探测图形之间,在第 6 行垂直定位图形位于符号左侧的两个位探测图形之间,在第 6 列,它们的作用是确定符号的密度和版本,提供决定模块坐标的基准位置

    void drawFixPatt(void)
    {
        uint8_t printLen = (this->len - 17)/2,i,j;
        // 行
        for(i=0;i<printLen;i++)
        {
            this->set(9 + 2 * i,6,false);
            this->set(10+ 2 * i,6,true);
        }
    
        // 列
        for(i=0;i<printLen;i++)
        {
            this->set(6,9 + 2 * i,false);
            this->set(6,10+ 2 * i,true);
        }
        // 不能忘记这一点
        this->set(8,this->len - 8,true);
    }
    
  • 校正图像

    整个校正图形可看作是 3 个重叠的同心正方形,由 5 X 5 个的深色模块,3 X 3 个的浅色模块以及位于中心的一个深色模块组成(如图 2 所示)八校正图形的数量视符号的版本号而定,版本 2 以上(含版本2)的符号均有校正图形

    那么以图像中心为位置,有

    void drawAlignPatt(uint8_t centerX,uint8_t centerY)
    {
        uint8_t i,j;
        // 1、2、4、5行
        for(i=0;i<2;i++)
        {
            // 1、5行
            for(j=0;j<5;j++) this->set(centerX - 2 + j,centerY - 2 + 4 * i,true);
            //2、4行
            this->set(centerX - 2,centerY - 1 + 2 * i,true);
            this->set(centerX - 1,centerY - 1 + 2 * i,false);
            this->set(centerX    ,centerY - 1 + 2 * i,false);
            this->set(centerX + 1,centerY - 1 + 2 * i,false);
            this->set(centerX + 2,centerY - 1 + 2 * i,true);
        }
        // 3行
        this->set(centerX - 2,centerY,true);
        this->set(centerX - 1,centerY,false);
        this->set(centerX    ,centerY,true);
        this->set(centerX + 1,centerY,false);
        this->set(centerX + 2,centerY,true);
    }
    

    数量和位置可以查看国标附录E
    不过这有一套算法可以得到不同版本的矫正图像数量和位置,如下

    std::list<QRC_XY> getAliData(void)
    {
        std::list<QRC_XY> xy;
        if(this->ver == 1) return xy;
    
        uint8_t
        multy = (this->ver / 7 + 2),
        mulmin = (multy - 1),
        distance = (this->len - 13) / mulmin,
        delta = (this->len - 13) % mulmin,
        x, y;
    
    // 当进入循环时,mulmin > 1,因为mulmin=0,1时,distance一定为偶数
        if (distance & 0x1)
        while (--distance)
        {
        delta += mulmin;
        if (delta % (mulmin - 1) == 0)
        {
            delta = delta / (mulmin - 1);
            break;
        }
        }
        for (uint8_t i = 0; i < multy; i++)
            for (uint8_t j = 0; j < multy; j++)
            {
                if ((i == 0 && j == 0) ||
                    (i == 0 && j == mulmin) ||
                    (i == mulmin && j == 0))
                    continue;
                x = 6 + j * distance;
                y = 6 + i * distance;
                if (i > 1)
                    y += (i - 1) * delta;
                if (j > 1)
                    x += (j - 1) * delta;
                xy.push_back(QRC_XY(x,y));
            }
    
        return xy;
    }
    
    

至此,固定图案已经介绍完毕,那么进行绘画

QRCode::QRCode(/*params...*/)
{
    //steps:...
    //step2:fixed pattern
    drawPosDectPatt(0,0);
    drawPosDectPatt(0,this->len - 7);
    drawPosDectPatt(this->len - 7,0);
    
    drawFixPatt();

    this->alCenterxys = getAliData();
    for(QRC_XY& xy : this->alCenterxys) drawAlignPatt(xy.x,xy.y);
    //steps:...
}
  1. 格式信息与版本信息
  • 格式信息

    与分隔符类似,在分隔符之外再“铺上一层”,其长度为15位,即"bool[15]"

    其本身数值来源于两个参数:掩码图形和纠错等级。关于这两个的介绍将于4小节出现,现在只需知道纠错等级占2位(有四个, c e l ∈ { 0 , 1 , 2 , 3 } cel\in\{0,1,2,3\} cel{0,1,2,3}),掩码图形占3位(有8个 m a s k ∈ { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } mask\in\{0,1,2,3,4,5,6,7\} mask{0,1,2,3,4,5,6,7}

    那么这共5位的二进制数是怎么以及为什么转化成15位数的呢?

    很简单,经过BCH纠错以及异或掩膜得到。但是这些不需要去学习纠错码以及实现算法,已经预先计算了,只需要调用全局变量g_15Bit索引( i = 8 × c e l + m a s k i=8\times cel+mask i=8×cel+mask)就行了,其在附录A中得到。

    同时可以在国标图19中看到格式信息所在位置,则有

    uint16_t get15Bit(uint8_t cel,uint8_t mask)
    {
        return g_15Bit[cel<<3|mask];
    }
    
    void drawFormPatt(uint16_t bch15_5)
    {
        bool tof;
        // 0-5、9-14位
        for(uint8_t i = 0;i<6;i++) 
        {
            tof = (0x1 << i) & bch15_5;
            this->set(this->len - i - 1,8,tof);
            this->set(8                ,i,tof);
            tof = (0x1 << (14 - i)) & bch15_5;
            this->set(i                ,8,tof);
            this->set(8,this->len - i - 1,tof);
        }
        // 6、7、8位
        tof = (0x1 << 6) & bch15_5;
        this->set(8,7,tof);
        this->set(this->len - 7,8,tof);
        tof = (0x1 << 7) & bch15_5;
        this->set(8,8,tof);
        this->set(this->len - 8,8,tof);
        tof = (0x1 << 8) & bch15_5;
        this->set(7,8,tof);
        this->set(8,this->len - 7,tof);
    }
    
  • 版本信息

    还记得留下的伏笔吗?就是以版本7作为分界线的伏笔。实际上,如果当版本大于等于7时,就会在右上和左下分别留出18( 3 × 6 3\times6 3×6)位填充,具体数值也是通过BCH纠错和异或计算得到。同样也无需花费精力,已经预先计算,调用g_verBit索引( i = v e r − 7 i=ver-7 i=ver7),详见国标附录D,表D1或者本文档附录B。

    查看国标图20、21,得到位置和排序,那么有

    uint32_t getVerBit(void)
    {
        return g_verBit[this->ver-7];
    }
    
    void drawVerPatt(uint32_t verBit)
    {
        for(uint8_t i = 0;i<6;i++)
            for (uint8_t j = 0; j < 3; j++)
            {
                bool tof = verBit & (0x1 << (i * 3 + j));
                this->set(this->len - 11 + j,i,tof);
                this->set(i,this->len - 11 + j,tof);
            }
    }
    

至此得到版本和格式信息,进行绘画


QRCode::QRCode(/*params...*/uint8_t ecl,uint8_t mask/*params...*/)
{
    //steps...
    //step3:version and format pattern
    this->ecl = ecl;
    this->mask = mask;
    drawFormPatt();
    if(this->ver >= 7) drawVerPatt();
    //steps:...
}

  1. 字符串编码

字符串,是二维码的核心,是产生二维码最重要的因素,因此也会花费2个小节介绍和实现,分为编码部分和绘画部分。而在编码时,我们可以使用vector作为容器,以uint8_t作为基本数据类型

  • 数据编码

    二维码提供了不同的数据编码格式,而且还提供了自行拓展的实现方法,但这里仅仅为了实现基本功能以及能够简单理解原理,所以仅采用单一格式的“8位字节”格式,如有需求可以自行实现。

    enumclass DataCodeT
    {
        // ...
        bit8 = 4 // 8位字节指示符,占4位
        // ...
    }
    

    在预设编码格式中,作为位流仅仅需要使用模式指示符和字符计数指示符,而8位字节的模式指示符为“0100”(二进制),计数指示符可以查国标表3。之后连续填充字符串。而且若最后得到的数据大小小于国标表7的数据数据编码数 c a p B i t capBit capBit(见附录表C,之后也会遇到),那么需要连续填充国标表 7 所定义的版本和纠错等级交替添加填充码字“11101100” 和 “00010001”(二进制),见附录D。

    因此可以得到

    uint16_t getCapBit(void)
    {
        return g_capBit[this->ver - 1][this->ecl];
    }
    uint16_t getStuff(uint16_t index)
    {
        return g_stuffing[index & 1];
    }
    const std::vector<uint8_t>& getDataCode(const std::string& str)
    {
        std::vector<uint8_t> data;
        uint16_t len = str.length(),index = 0,i;
        data.push_back(this->type<<4);
        switch(this->type)
        {
        case bit8:{
            if(this->ver <= 9) // 8
            {
                // 前4位
                data[0] |= len>>4;
                // 后4位
                data.push_back((0xf & len)<<4);
                index = 2;
            }
            else // 16
            {
                // 前4位
                data[0] |= len>>12;
                // 中8位
                data.push_back(0xff & (len >> 4));
                // 后4位
                data.push_back((0xf & len)<<4);
                index = 3;
            }
    
            for(i = 0;i<len;i++)
            {
                // 前4位
                data[index + i] |= str[i]>>4;
                // 后4位
                data.push_back((0xf & str[i])<<4);
            }
        }break;
        //case ...
        }
    
        // 填充
        len = data.size();
        // 一定要确保大小小于标准长度(查看表7),不然会减出一个非常大的值,后果很严重
        index = this->getCapBit(this->ver,this->ecl) - len;
        if(index != 0) 
            for(i = 0;i < index;i++)
            {
                uint16_t stf = this->getStuff(i);
                // 前4位
                data[len + i - 1] |= stf>>4;
                // 后4位
                data.push_back((0xf & stf)<<4);
            }
    
        return data;
    }
    

    另外,国标中的剩余位实际上就是为了适应8位大小的数据结构而设置的,而本文使用的数据类型刚好契合,所以无需考虑剩余位问题,如果有,那么就是在vector的最后一个元素中。

    而且,如果有其他的编码类型需求,还需要自行构建一个类。这里提供一下参考

    class StringDataBit
    {
    public:
    private:
        uint8_t* bit_;
        uint32_t length_, arrSize_;
        // 为0是代表有多种,此时下方没意义
        uint8_t version_, insBit_;// 版本和指示符所占长度
        DataCodeT mode_;
    public:
        friend class QRCode;
        StringDataBit(StringDataBit& _strDataBit);
        StringDataBit(DataCodeT _mode, uint8_t _version,const std::string& _string);
        uint8_t* getBit();
        uint32_t getLength();
        uint32_t getSize();
        StringDataBit operator+(const StringDataBit& _strDataBit)const;
        void operator=(const StringDataBit& _strDataBit);
        void operator+=(const StringDataBit& _strDataBit);
        ~StringDataBit();
    
    private:
        StringDataBit(uint32_t _arrSize);
        inline void setBitMapByByte_t(uint16_t _byte);
    
        inline void setValue(size_t _beginIndex, uint16_t _len, uint32_t _value);
        inline std::string str2numChar(std::string _string);
    };
    
  • 纠错编码

    • 纠错等级

      符号及其二进制指示符见国标表13,表格越右边,越代表能够纠错的数量也就越多,但与此同时纠错码的大小就越大,这是一个需要根据不同应用场景而选择不同的等级,一般使用“M”。而在获得等级之后,就可以根据这些字符编码以及版本而得到纠错编码。这里提供了枚举类型供参考(注意M为第二等级但指示符为0,L为第一等级指示符为1)。

         typedef enum _COR_LEV
         {
             M = 0,
             L = 1,
             Q = 2,
             H = 3
         }COR_LEV;
      
    • 块大小(以1字节(8bit)为单位)

      在单个块中,纠错码块数的大小一般来说是固定的(仅仅保证在统一版本和纠错等级内),而数据编码块数大小有时会因为编码大小(整除不了)而进行微调从而多出一些,导致分块的时候后面一边的块往往比前面的块多出1字节(8位)。为了方便处理,需要以一个固定而不会出现变数的数为基准,那么就选取该纠错码块的大小为基准值为 b l o c k E r r S i z e blockErrSize blockErrSize,即

      b l o c k E r r S i z e = c o r C a p × 2 blockErrSize = corCap \times 2 blockErrSize=corCap×2

      其中 c o r C a p corCap corCap(和 c a p B i t capBit capBit有点像,但要注意分别)也可以在表9查,为表9最右边的“r”,到而我也在附录E设置了不同版本和纠错等级的纠错字数(在单个块内)。至于为什么要求乘2,因为这为了不同的纠错等级而定下的大小。

    • 块数

      在我们得到所谓的字符串编码之后,为了获得其纠错码,是需要通过将这些编码生成一个个的块,再按特定顺序排列好之后,对这些一个个块单独进行错误编码得到错误编码。具体的分块的数量和单个块的大小是根据版本和前文提到的纠错等级而不同的。

      具体大小我们可以查国标表9,就是从右边数的第二个,或者可以根据其本身规律获得大小。具体来说,由于数据编码的大小会因有时无法整除而出现变数,所有尝试着手于固定的纠错码块大小的部分。即若要获得块数,则要使总的纠错码大小除于单个纠错块大小,即

      b l o c k N u m = e r r L e n ÷ b l o c k S i z e blockNum = errLen \div blockSize blockNum=errLen÷blockSize

      注意这里用div,即整除除法,因为前文也提过,数据编码有时会得不到整除结果,但为了得到整数的块大小,这里使用整数除法;

      e r r L e n errLen errLen为表9的编码总数减去表7数据码总数的值(注意区别,数据码单指从字符串得到的编码,而编码代表数据与数据纠错码的结合,也就是二维码整个的编码),即

      e r r L e n = a l l C a p − c a p B i t errLen = allCap - capBit errLen=allCapcapBit

      其中 c a p B i t capBit capBit前文已提,不再赘述; a l l C a p allCap allCap为编码总数,具体大小查表9的编码总数,或者在附录F查到。

    • 小块和大块

      在块大小部分就已经提到,块会因为数据部分无法被整除而不得不在后面多分出1字节来保证囊括所有的数据。方便的做法是把整个块分为两个部分,即大块和小块。大块会比小块多出一个字节,这种做法既可以囊括所有的数据,也可以让我们方便地得到大块的数量,从而得到小块的数量,即

      b l o c k N u m b i g = c a p B i t m o d    b l o c k N u m blockNum_{big} = capBit \mod blockNum blockNumbig=capBitmodblockNum

      b l o c k N u m s m a l l = b l o c k N u m − b l o c k N u m b i g blockNum_{small} = blockNum - blockNum_{big} blockNumsmall=blockNumblockNumbig

      mod代表取余,在C++中的运算符为“%”,这样我们就可以得到大块和小块的数量,从而对这两个块分别进行处理,再合并得到所有的纠错编码。

      另外,既然定下了小块大块,那么其各自的大小也就确定了。不妨设小块大小为 b l o c k D a t a S i z e blockDataSize blockDataSize,则大块大小为 b l o c k D a t a S i z e + 1 blockDataSize+1 blockDataSize+1,而有

      b l o c k D a t a S i z e = c a p B i t ÷ b l o c k N u m blockDataSize = capBit \div blockNum blockDataSize=capBit÷blockNum

    • Reed-Solomon编码

      具体原理不在这里赘述,自行看相关书籍即可。而实现在附录G中可以查看,其实现了一个Reed_Solomon函数,这里仅调用了该函数,有兴趣可以自行观看。

      最后可以得到这样一个获得纠错编码的过程

      uint16_t getAllBit(void)
      {
          return g_allBit[this->ver-1];
      }
      uint16_t getCorCap(void)
      {
          return static_cast<uint16_t>((this->ver < 27)?g_corCap[this->ver-1][this->ecl]:g_corCap[26][this->ecl]);
      }
      
      const std::vector<uint8_t>& getErrDataCode(const std::vector<uint8_t>& data)
      {
          uint16_t 
          allBit = getAllBit(),
          corCap = getCorCap(),
          capBit = getCapBit(),
          errLen = allBit - capBit,
          blockErrSize = 2 * corCap,
          blockNum = errLen / blockErrSize,
          blockDataSize = capBit / blockNum,
          blockNum_big = capBit % blockNum,
          blockNum_small = blockNum - blockNum_big;
          
          const std::vector<uint8_t>& errdata = QRC_Reed_Solomon(data,blockNum_big,blockNum_small,blockErrSize,blockDataSize);
      
          return errdata;
      }
      
      

至此,我们了解了所有字符编码步骤,接下来在构造函数中实现。

QRCode::QRCode(/*params...*/const std::string& str,DataCodeT type/*params...*/)
{
    //steps:...
    //step4:string encode
    this->type = type;
    const std::vetor<uint8_t>& data = getDataCode(str),& errdata = getErrDataCode(data);
    //steps:...
}
  1. 字符串绘画
  • 布置块

    • 块的排列

      见国标6.7小节,不难看出块的排列方式,就是先数据码后纠错码。然后块中各个位所在的位置可以在图14、15看到。但实际上,不必过分关注其某个块在什么位置上。究其本质,就是类似以表10的列的顺序(国标6.6小节可以看到具体步骤)排布,然后从0位到8位的方式读取块的编码,然后以特定的方式在“绘画”。

    • 特定方式

      这里用一个不太严谨的状态图表示,在某一状态代表在二维码图中的矩阵中要执行某一方向的图画方向

      四种方向,以左上为原点

      • 方向

        以最右下角为初始位置,以最中间的方向为初始方向,以向左为图的下一方向,再回到最中间的方向、然后循环向左,上图的横代表在普通情况之下画图的方向,回旋代表遇到“墙”(边界、固定位置如格式信息、版本信息图形等)之后就代表前进失败,回到最中间并到另一边继续循环下去。

      • 步长(Δ的绝对值)
        在默认情况之下,左边步长默认为(1,1),而中间为(1,0),右边为(1,1)。但是在遇到“障碍”(定位图形、校正图形等)时,会在y方向上的步长加一,直至跳过“障碍”;在遇到“墙”的时候,一般情况下会转换到中间,设置y步长为0,但在特殊情况下(即方向向左无法回到规定的数据绘画区域内,例如左边为定位符时遇到边界),就不得不在y上增加步长(为7,为了到达定位符上方),使得方向呈现向左上,然后回到普通状态。

      // 障碍
      typedef enum _InPositionPat
      {
          not_in,
          left,
          normal,
          normal_left,
      }InPositionPat;
      
      InPositionPat QRCode::inPositionPattern(QRC_XY xy)
      {
          for (auto cxy : this->alCenterxys)
              if (cxy.y - 3 < xy.y && xy.y < cxy.y + 3)
              {
                  if (cxy.x - 2 < xy.x && xy.x < x + 3)
                      return (cxy.x == 6) ? left : normal;
                  if (xy.x == cxy.x - 2)
                      return normal_left;
              }
      
          return not_in;
      }
      QRC_XY QRCode::getDeltaByXY(QRC_XY xy)
      {
          const bool inRight = (xy.x <= 6) ? (xy.x % 2) : ((xy.x - 1) % 2), blockUp = (xy.x <= 6) ? (xy.x / 2) % 2 : !(((xy.x - 7) / 2) % 2);
          QRC_XY delta = { inRight ? -1 : 1, blockUp ? (inRight ? 0 : -1) : (inRight ? 0 : 1) };
          InPositionPat position = this->inPositionPattern(QRC_XY(xy.x + delta.x, xy.y + delta.y));
          
          switch (position)
          {
          case left:
          case normal:delta.y += (blockUp) ? -5 : 5; break;
          case normal_left:delta.x = 0; break;
          case not_in:break;
          }
      
          // 上面一条杠
          if ( xy.y + delta.y == 6) delta.y += (blockUp) ? -1 : 1;
      
          // 格式消息下方
          if ( xy.y + delta.y == 8 && ( xy.x + delta.x <= 8 ||  xy.x + delta.x >= this->len_ - 8))
          {
              delta = {( xy.x + delta.x == 8) ? -2 : -1,0};
              goto RETURN;
          }
      
          // 向下上没位子
          if ( xy.y + delta.y == this->len_ ||  xy.y + delta.y == -1)
          {
              delta = { -1,(xy.x + delta.x == 10) ? -8 : 0};
              goto RETURN;
          }
      
          // 版本块 >=7
          if (this->ver_>= 7)
          {
              // -7-4 // 左下版本消息
              if (xy.y + delta.y == this->len_ - 11 && xy.x + delta.x == 5)
              {
                  delta = { -1,0 };
              }
              // 右上
              else if (xy.x + delta.x == this->len_ - 9 && xy.y + delta.y == 5)
              {
                  delta = { -2,-7 };
              }
              // 右边是右上的版本信息块
              else if (xy.x + delta.x == this->len_ - 11)
              {
                  if (xy.y + delta.x < 6)
                  {
                      delta = { 0,1 };
                  }
                  else if (xy.y + delta.x == 6) 
                  { 
                      delta = { 1,2 };
                  }
              }
      
              goto RETURN;
          }
          // 左下
          // 右上和平时无差别
          else
          {
              if ((xy.y + delta.y) == this->len_ - 8 && xy.x + delta.x <= 6)
              {
                  delta = { -1,0 };
                  goto RETURN;
              }
          }
          // 无特殊情况
      RETURN:
          return delta;
      }
      
  • 掩模

这个概念早在之前就可以看到,其实这代表了对二维矩阵的异或所采用的不同的格式,仅仅是为了避免获得大块黑或白色的块。使得在扫描的时候更便于得到结果。其具体情况可在6.8.1小节看到。

```C++
// 各种模可以在图17看到
bool maskFunc0(QRC_XY _xy){return (_xy.x + _xy.y) % 2 == 0;}
bool maskFunc1(QRC_XY _xy){return _xy.x % 2 == 0;}
bool maskFunc2(QRC_XY _xy){return _xy.y % 3 == 0;}
bool maskFunc3(QRC_XY _xy){return (_xy.x + _xy.y) % 3 == 0;}
bool maskFunc4(QRC_XY _xy){return ((_xy.x/2) + (_xy.y/3)) % 2 == 0;}
bool maskFunc5(QRC_XY _xy){return (_xy.x * _xy.y) % 2 + (_xy.x * _xy.y) % 3 == 0;}
bool maskFunc6(QRC_XY _xy){return ((_xy.x * _xy.y) % 2 + (_xy.x * _xy.y) % 3) % 2 == 0;}
bool maskFunc7(QRC_XY _xy){return ((_xy.x + _xy.y) % 2 + (_xy.x * _xy.y) % 3) % 2 == 0;}
// 获得函数指针
bool (*getMaskFunc(void))(QRC_XY xy)
{
    bool (*func)(QRC_XY xy) = nullptr;
    switch(this->mask)
    {
    case 0:func = maskFunc0;break;
    case 1:func = maskFunc2;break;
    case 2:func = maskFunc2;break;
    case 3:func = maskFunc3;break;
    case 4:func = maskFunc4;break;
    case 5:func = maskFunc5;break;
    case 6:func = maskFunc6;break;
    case 7:func = maskFunc7;break;
    default:func = maskFunc0;break;
    }
    return func;
}
bool getMask(QRC_XY xy)
{
    return getMaskFunc()(xy);
}
bool getMask(uint8_t x.uint8_t y)
{
    return getMask(QRC_XY(x,y));
}
void drawMask()
{
    bool (*maskFunc)(QRC_XY xy) = getMaskFunc();
    for(uint8_t i = 0;i<len;i++)
        for(uint8_t j = 0;j<len;j++)
            this->set(i,j,this->get(i,j) != maskFunc(QRC_XY(i,j)));
}
```

至此得到

QRCode::QRCode(/*params...*/)
{
    //steps:...
    //step5:string pattern
    drawStrPatt(data,errdata);
    drawMask();
    //steps:...
}
  1. 结构介绍

到了这里,所有的步骤都介绍完毕了。但由于掩码的存在,因此需要先进行字符串绘画,之后进行固定绘画,即

QRCode::QRCode(/*params...*/)
{
    //step1:size (including the assignment of attribute)
    //step4:string encode
    //step5:string pattern
    //step2:fixed pattern
    //step3:version and format pattern
}

完整代码在附录Z中,可以自行查看

附录

  • A BCH15_5索引
// g_15Bit[cel << 3 | mask] 
const uint16_t g_15Bit[32] = {
    21522,20773,24188,23371,17913,16590,20375,
    19104,30660,29427,32170,30877,26159,25368,
    27713,26998,5769,5054,7399,6608,1890,597,3340,
    2107,13663,12392,16177,14854,9396,8579,11994,11245
};
  • B BCH18_6索引
   // g_verBit[ver-7]
const uint32_t g_verBit[34] = {
   0x7c94,0x85bc,0x9a99,0xa4d3,0xbbf6,0xc762,0xd847,0xe60d,0xf928,
   0x10b78,0x1145d,0x12a17,0x13532,0x149a6,0x15683,0x168c9,0x177ec,
   0x18ec4,0x191e1,0x1afab,0x1b08e,0x1cc1a,0x1d33f,0x1ed75,0x1f250,
   0x209d5,0x216f0,0x228ba,0x2379f,0x24b0b,0x2542e,0x26a64,0x27541,0x28c69
};
  • C 标准数据编码大小
// g_capBit[ver - 1][mask]
const uint16_t g_capBit[40][4] = {
   {16,19,9,13},{28,34,16,22},{44,55,26,34},{64,80,36,48},
   {86,108,46,62},{108,136,60,76},{124,156,66,88},{154,194,86,110},
   {182,232,100,132},{216,274,122,154},{254,324,140,180},{290,370,158,206},
   {334,428,180,244},{365,461,197,261},{415,523,223,295},{453,589,253,325},
   {507,647,283,367},{563,721,313,397},{627,795,341,445},{669,861,385,485},
   {714,932,406,512},{782,1006,442,568},{860,1094,464,614},{914,1174,514,664},
   {1000,1276,538,718},{1062,1370,596,754},{1128,1468,628,808},{1193,1531,661,871},
   {1267,1631,701,911},{1373,1735,745,985},{1455,1843,793,1033},{1541,1955,845,1115},
   {1631,2071,901,1171},{1725,2191,961,1231},{1812,2306,986,1286},{1914,2434,1054,1354},
   {1992,2566,1096,1426},{2102,2702,1142,1502},{2216,2812,1222,1582},{2334,2956,1276,1666}
  };
  • D 填充码
const uint8_t g_stuffing[2] = { 0xec , 0x11 };
  • E 纠错码字数
// (ver < 27)? g_corCap[ver-1][ecl]:g_corCap[26][ecl]
const uint8_t g_corCap[27][4] = {
   {4,2,8,6},{8,4,14,11},{13,7,11,9},{9,10,8,13},
   {12,13,11,9},{8,9,14,12},{9,10,13,9},{11,12,13,11},
   {11,15,12,10},{13,9,14,12},{15,10,12,14},{11,12,14,13},
   {11,13,11,12},{12,15,12,10},{12,11,12,15},{14,12,15,12},
   {14,14,14,14},{13,15,14,14},{13,14,13,13},{13,14,14,15},
   {13,14,15,14},{14,14,12,15},{14,15,15,15},{14,15,15,15},
   {14,13,15,15},{14,14,15,14},{14,15,15,15}
  };
  • F 码字总数
// g_allBit[ver - 1]
const uint16_t g_allBit[40] = {
   26,44,70,100,134,172,196,242,292,346,
   404,466,532,581,655,733,815,901,991,1085,
   1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,
   2323,2465,2611,2761,2876,3034,3196,3362,3532,370};
  • G 生成多项式与所罗门编码
// &g_generator_polynomial_exp[index]
const uint8_t g_generator_polynomial_exp[259] = {/* 纠错字数 在此数组上的索引*/
   0,87,229,146,149,238,102,21,/* 7 0*/
   0,251,67,46,61,118,70,64,94,32,45, /* 10 8*/
   0,74,152,176,100,86,100,106,104,130,218,206,140,78,/* 13 19*/
   0,8,183,61,91,202,37,51,58,58,237,140,124,5,99,105,/* 15 33*/
   0,120,104,107,109,102,161,76,3,91,191,147,169,182,194,225,120,/* 16 49*/
   0,43,139,206,78,43,239,123,206,214,147,24,99,150,39,243,163,136,/* 17 66*/
   0,215,234,158,94,184,97,118,170,79,187,152,148,252,179,5,98,96,153,/* 18 84*/
   0,17,60,79,50,61,163,26,187,202,180,221,225,83,239,156,164,212,212,188,190,/* 20 103*/
   0,210,171,247,242,93,230,14,109,221,53,200,74,8,172,98,80,219,134,160,105,165,231,/* 22 124*/
   0,229,121,135,48,211,117,251,126,159,180,169,152,192,226,228,218,111,0,117,232,87,96,227,21,/* 24 147*/
   0,173,125,158,2,103,182,118,17,145,201,111,28,165,53,161,21,245,142,13,102,48,227,153,145,218,70,/* 26 172*/
   0,168,223,200,104,224,234,108,180,110,190,195,147,205,27,232,201,21,43,245,87,42,195,212,119,242,37,9,123,/* 28 */
   0,41,173,145,152,216,31,179,182,50,48,110,86,239,96,222,125,42,173,226,193,224,130,156,37,251,216,238,40,192,180,/* 30 */
};
// https://www.thonky.com/qr-code-tutorial/log-antilog-table 预先算出来的
// g_alpha_int_is_index[inte - 1]
const uint8_t g_alpha_int_is_index[255] = { 0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,
    4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,
    225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,
    201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,
    179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,
    56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,
    155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,
    99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,
    146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,
    252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,
    216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,
    170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,
    122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175 
};
    
const uint8_t* getGenPolyExpByBlockErrSize(uint16_t blockErrSize)
{
    // 为了获得多项式在全局变量的位置,多项式长度就是blockSize
    uint8_t index = 0;
    // 就是要这么做,不用break
    switch (blockErrSize)
    {
    case 30:index += 29;
    case 28:index += 27;
    case 26:index += 25;
    case 24:index += 23;
    case 22:index += 21;
    case 20:index += 19;
    case 18:index += 18;
    case 17:index += 17;
    case 16:index += 16;
    case 15:index += 14;
    case 13:index += 11;
    case 10:index += 8;
    case 7:break;
    }
    return &g_generator_polynomial_exp[index];
}
uint8_t getAlphaByExp(size_t exp)
{
    uint16_t result = 1,i;
    exp %= 255;
    for (i = 0; i < exp; i++) 
    {
        result *= 2;
        if (result > 255)
        result ^= 285;
    }
    return (uint8_t)result;
}
uint8_t getAlphaByInt(uint8_t inte)
{
 return g_alpha_int_is_index[inte - 1];
}
const std::vector<uint8_t>& QRC_Reed_Solomon(const std::vector<uint8_t>& data,uint16_t blockNum_big,uint16_t blockNum_small,uint16_t blockErrSize,uint16_t blockDataSize)
{
    std::vector<uint8_t> errdata;
    // 生成多项式
    const uint8_t* poly = getGenPolyExpByBlockErrSize(blockErrSize);
    // 一个大小为大块大小的块
    uint8_t* blockData = new uint8_t[blockErrSize + blockDataSize + 1];
    uint16_t i,j,k;

    // 小块
    for (i = 0; i < blockNum_small; i++)
    {
        // 进位,补零
        for (j = 0; j < blockDataSize; j++) blockData[j] = data[j + i * blockDataSize];
        for (j = 0; j < blockErrSize; j++)  blockData[j + blockDataSize] = 0;

        // 除法
        for (j = 0; j < blockDataSize; j++)
            if (blockData[j] != 0)
            {
                uint8_t deltaAlpha = this->getAlphaByInt(blockData[j]);
                for (k = 0; k < blockErrSize + 1; k++) blockData[j + k] ^= this->getAlphaByExp(static_cast<size_t>(poly[k] + deltaAlpha));
            }

        for (j = 0; j < blockErrSize; j++) errdata.push_back(blockData[blockDataSize + j]);
    }
    // 大块
    for (i = 0; i < blockNum_big; i++)
    {
        for (j = 0; j < blockDataSize + 1; j++) blockData[j] = data[j + i * (blockDataSize + 1) + blockNum_small * blockDataSize];
        for (j = 0; j < blockErrSize; j++) blockData[j + blockDataSize + 1] = 0;

        // 除法
        for (j = 0; j < blockDataSize + 1; j++)
            if (blockData[j] != 0)
            {
                uint8_t deltaAlpha = this->getAlphaByInt(blockData[j]);
                for (k = 0; k < blockErrSize + 1; k++) blockData[k + j] ^= this->getAlphaByExp(static_cast<size_t>(poly[k] + deltaAlpha));
            }
        for (j = 0; j < blockErrSize; j++) errdata.push_back(blockData[blockDataSize + j + 1]);
    }
    delete[] blockData;

    return errdata;
}
  • Z 完整代码
    • qrcode.h

      #include<vector>
      #include<list>
      #include<string>
      
      const uint8_t g_stuffing[2] = { 0xec , 0x11 };
      // g_15Bit[cel << 3 | mask]
      const uint16_t g_15Bit[32] = {
          21522,20773,24188,23371,17913,16590,20375,
          19104,30660,29427,32170,30877,26159,25368,
          27713,26998,5769,5054,7399,6608,1890,597,3340,
          2107,13663,12392,16177,14854,9396,8579,11994,11245,
      };
      // g_verBit[ver-7]
      const uint32_t g_verBit[34] = {
          0x7c94,0x85bc,0x9a99,0xa4d3,0xbbf6,0xc762,0xd847,0xe60d,0xf928,
          0x10b78,0x1145d,0x12a17,0x13532,0x149a6,0x15683,0x168c9,0x177ec,
          0x18ec4,0x191e1,0x1afab,0x1b08e,0x1cc1a,0x1d33f,0x1ed75,0x1f250,
          0x209d5,0x216f0,0x228ba,0x2379f,0x24b0b,0x2542e,0x26a64,0x27541,0x28c69
      };
      // g_capBit[ver - 1][mask]
      const uint16_t g_capBit[40][4] = {
          {16,19,9,13},{28,34,16,22},{44,55,26,34},{64,80,36,48},
          {86,108,46,62},{108,136,60,76},{124,156,66,88},{154,194,86,110},
          {182,232,100,132},{216,274,122,154},{254,324,140,180},{290,370,158,206},
          {334,428,180,244},{365,461,197,261},{415,523,223,295},{453,589,253,325},
          {507,647,283,367},{563,721,313,397},{627,795,341,445},{669,861,385,485},
          {714,932,406,512},{782,1006,442,568},{860,1094,464,614},{914,1174,514,664},
          {1000,1276,538,718},{1062,1370,596,754},{1128,1468,628,808},{1193,1531,661,871},
          {1267,1631,701,911},{1373,1735,745,985},{1455,1843,793,1033},{1541,1955,845,1115},
          {1631,2071,901,1171},{1725,2191,961,1231},{1812,2306,986,1286},{1914,2434,1054,1354},
          {1992,2566,1096,1426},{2102,2702,1142,1502},{2216,2812,1222,1582},{2334,2956,1276,1666}
      };
      // (ver < 27)? g_corCap[ver-1][ecl]:g_corCap[26][ecl]
      const uint8_t g_corCap[27][4] = {
          {4,2,8,6},{8,4,14,11},{13,7,11,9},{9,10,8,13},
          {12,13,11,9},{8,9,14,12},{9,10,13,9},{11,12,13,11},
          {11,15,12,10},{13,9,14,12},{15,10,12,14},{11,12,14,13},
          {11,13,11,12},{12,15,12,10},{12,11,12,15},{14,12,15,12},
          {14,14,14,14},{13,15,14,14},{13,14,13,13},{13,14,14,15},
          {13,14,15,14},{14,14,12,15},{14,15,15,15},{14,15,15,15},
          {14,13,15,15},{14,14,15,14},{14,15,15,15}
      };
      // g_allBit[ver - 1]
      const uint16_t g_allBit[40] = {
          26,44,70,100,134,172,196,242,292,346,
          404,466,532,581,655,733,815,901,991,1085,
          1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,
          2323,2465,2611,2761,2876,3034,3196,3362,3532,3706
      };
      // &g_generator_polynomial_exp[index]
      const uint8_t g_generator_polynomial_exp[259] = {/*纠错字数 在此数组上的索引*/
          0,87,229,146,149,238,102,21,/*7 0*/
          0,251,67,46,61,118,70,64,94,32,45, /*10 8*/
          0,74,152,176,100,86,100,106,104,130,218,206,140,78,/*13 19*/
          0,8,183,61,91,202,37,51,58,58,237,140,124,5,99,105,/*15 33*/
          0,120,104,107,109,102,161,76,3,91,191,147,169,182,194,225,120,/*16 49*/
          0,43,139,206,78,43,239,123,206,214,147,24,99,150,39,243,163,136,/*17 66*/
          0,215,234,158,94,184,97,118,170,79,187,152,148,252,179,5,98,96,153,/*18 84*/
          0,17,60,79,50,61,163,26,187,202,180,221,225,83,239,156,164,212,212,188,190,/*20 103*/
          0,210,171,247,242,93,230,14,109,221,53,200,74,8,172,98,80,219,134,160,105,165,231,/*22 124*/
          0,229,121,135,48,211,117,251,126,159,180,169,152,192,226,228,218,111,0,117,232,87,96,227,21,/*24 147*/
          0,173,125,158,2,103,182,118,17,145,201,111,28,165,53,161,21,245,142,13,102,48,227,153,145,218,70,/*26 172*/
          0,168,223,200,104,224,234,108,180,110,190,195,147,205,27,232,201,21,43,245,87,42,195,212,119,242,37,9,123,/*28*/
          0,41,173,145,152,216,31,179,182,50,48,110,86,239,96,222,125,42,173,226,193,224,130,156,37,251,216,238,40,192,180,/*30*/
      };
      // <https://www.thonky.com/qr-code-tutorial/log-antilog-table> 预先算出来的
      // g_alpha_int_is_index[inte - 1]
      const uint8_t g_alpha_int_is_index[255] = { 0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,
          4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,
          225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,
          201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,
          179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,
          56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,
          155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,
          99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,
          146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,
          252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,
          216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,
          170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,
          122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175
      };
      
      // 障碍
      typedef enum _InPositionPat
      {
          not_in,
          left,
          normal,
          normal_left,
      }InPositionPat;
      
      typedef enum _COR_LEV
      {
          M = 0,
          L = 1,
          Q = 2,
          H = 3
      }COR_LEV;
      
      typedef enum  _DataCodeT
      {
          // ...
          bit8 = 4 // 8位字节指示符,占4位
          // ...
      }DataCodeT;
      
      struct QRC_XY
      {
          int16_t x;
          int16_t y;
          QRC_XY(int16_t x, int16_t y) :x(x), y(y) {};
          QRC_XY operator+=(const QRC_XY _other)
          {
              this->x += _other.x;
              this->y += _other.y;
              return *this;
          }
      };
      
      class QRCode
      {
      public:
      private:
          std::list<QRC_XY> alCenterxys_;
          bool* bitmap_;
          uint8_t ver_;
          uint8_t len_;
          uint8_t mask_;
          COR_LEV ecl_;
          DataCodeT type_;
      public:
          QRCode(const std::string& _str,uint8_t _ver = 7, COR_LEV _ecl = M,uint8_t _mask = 5, DataCodeT type_ = bit8);
          bool get(uint8_t _x,uint8_t _y);
          bool get(QRC_XY _xy);
          void set(uint8_t _x,uint8_t _y,bool _flag);
          void set(QRC_XY _xy,bool _flag);
          uint8_t getLen(void);
      private:
          void drawPosDectPatt(uint8_t _startX,uint8_t startY);
          void drawFixPatt(void);
          void drawAlignPatt(uint8_t centerX,uint8_t centerY);
          std::list<QRC_XY> getAliData(void);
          uint16_t get15Bit(void);
          void drawFormPatt(void);
          uint32_t getVerBit(void);
          void drawVerPatt(void);
          uint16_t getCapBit(void);
          uint8_t getStuff(uint16_t _index);
          std::vector<uint8_t> getDataCode(const std::string& _str);
          uint16_t getAllBit(void);
          uint16_t getCorCap(void);
          std::vector<uint8_t> getErrDataCode(const std::vector<uint8_t>& _data);
          InPositionPat inPositionPattern(QRC_XY _xy);
          QRC_XY getDeltaByXY(QRC_XY _xy);
          void drawStrPatt(const std::vector<uint8_t> _data,const std::vector<uint8_t> _errdata);
          static bool maskFunc0(QRC_XY _xy);
          static bool maskFunc1(QRC_XY _xy);
          static bool maskFunc2(QRC_XY _xy);
          static bool maskFunc3(QRC_XY _xy);
          static bool maskFunc4(QRC_XY _xy);
          static bool maskFunc5(QRC_XY _xy);
          static bool maskFunc6(QRC_XY _xy);
          static bool maskFunc7(QRC_XY _xy);
          bool (*getMaskFunc(void))(QRC_XY _xy);
          bool getMask(uint8_t _x,uint8_t _y);
          bool getMask(QRC_XY _xy);
          void drawMask(void);
          const uint8_t* getGenPolyExpByBlockErrSize(uint16_t _blockErrSize);
          uint8_t getAlphaByExp(size_t _exp);
          uint8_t getAlphaByInt(uint8_t _int);
          std::vector<uint8_t> QRC_Reed_Solomon(const std::vector<uint8_t>& _data,uint16_t _blockNum_big,uint16_t _blockNum_small,uint16_t _blockErrSize,uint16_t _blockDataSize);
          
      };
      
      
    • qrcode.cpp

      #include "qrcode.h"
      QRCode::QRCode(const std::string& _str,uint8_t _ver,COR_LEV _ecl, uint8_t _mask, DataCodeT _type)
      :ver_(_ver),len_(4*(this->ver_-1)+21),mask_(_mask),ecl_(_ecl),bitmap_(nullptr),type_(_type)
      {
          //step1:size
          this->bitmap_ = new bool[len_ * len_];
          this->alCenterxys_ = this->getAliData();
          // 一定要设置,因为有空白区域(画不到)的存在
          for (uint16_t i = 0; i < len_ * len_; i++) this->bitmap_[i] = false;
          //step4:string encode
          const std::vector<uint8_t>& data = this->getDataCode(_str);
          const std::vector<uint8_t>& errdata = this->getErrDataCode(data);
          //step5:string pattern
          this->drawStrPatt(data,errdata);
          this->drawMask();
          //step2:fixed pattern
          this->drawPosDectPatt(0,0);
          this->drawPosDectPatt(0,this->len_ - 7);
          this->drawPosDectPatt(this->len_ - 7,0);
          
          this->drawFixPatt();
      
          for(QRC_XY& xy : this->alCenterxys_) this->drawAlignPatt(xy.x,xy.y);
          //step3:version and format pattern
          this->drawFormPatt();
          if(this->ver_ >= 7) this->drawVerPatt();
      }
      
      bool QRCode::get(uint8_t _x,uint8_t _y)
      {
          return this->bitmap_[_x + _y*this->len_];
      }
      bool QRCode::get(QRC_XY _xy)
      {
          return this->get(_xy.x,_xy.y);
      }
      void QRCode::set(uint8_t _x,uint8_t _y,bool _flag)
      {
          this->bitmap_[_x + _y * this->len_] = _flag;
      }
      void QRCode::set(QRC_XY _xy,bool _flag)
      {
          this->set(_xy.x,_xy.y,_flag);
      }
      
      void QRCode::drawPosDectPatt(uint8_t _startX,uint8_t _startY)
      {
          uint8_t i/*delta_y*/,j/*delta_x | y*/;
          
          // 图案
          // 1、2、6、7行
          for (i = 0; i < 2; i++)
          {
              // 2、6行 i*4+1 in [1,5]
              j = _startY + i * 4 + 1;
              this->set(_startX    ,j,true);
              this->set(_startX + 1,j,false);
              this->set(_startX + 2,j,false);
              this->set(_startX + 3,j,false);
              this->set(_startX + 4,j,false);
              this->set(_startX + 5,j,false);
              this->set(_startX + 6,j,true);
              // 1、7行 i*6 in [0,6]
              for (j = 0; j < 7; j++)
                  this->set(_startX + j,_startY + i * 6,true);
          }
          // 3、4、5行
          for (i = 0; i < 3; i++)
          {
              this->set(_startX + 0, _startY + i + 2,true);
              this->set(_startX + 1, _startY + i + 2,false);
              this->set(_startX + 2, _startY + i + 2,true);
              this->set(_startX + 3, _startY + i + 2,true);
              this->set(_startX + 4, _startY + i + 2,true);
              this->set(_startX + 5, _startY + i + 2,false);
              this->set(_startX + 6, _startY + i + 2,true);
          }
      
          // 分隔符
          if (_startX == 0)
          {
              if (_startY == 0) // 左上
              {
                  for (i = 0; i < 8; i++) this->set(7, i, false);
                  for (j = 0; j < 7; j++) this->set(j, 7, false);
              }
              else // 左下
              {
                  for (i = 0; i < 8; i++) this->set(7, _startY + i - 1, false);
                  for (j = 0; j < 7; j++) this->set(j, _startY - 1, false);
              }
          }
          else // 右上
          {
              for(i=0;i<8;i++) this->set(_startX - 1,i,false);
              for(j=0;j<7;j++) this->set(_startX + j,7,false);
          }
      }
      
      void QRCode::drawFixPatt(void)
      {
          uint8_t printLen = (this->len_ - 17)/2,i,j;
          // 行
          for(i=0;i<printLen;i++)
          {
              this->set(9 + 2 * i,6,false);
              this->set(10+ 2 * i,6,true);
          }
      
          // 列
          for(i=0;i<printLen;i++)
          {
              this->set(6,9 + 2 * i,false);
              this->set(6,10+ 2 * i,true);
          }
          // 不能忘记这一点
          this->set(8,this->len_ - 8,true);
      }
      
      void QRCode::drawAlignPatt(uint8_t _centerX,uint8_t _centerY)
      {
          uint8_t i,j;
          // 1、2、4、5行
          for(i=0;i<2;i++)
          {
              // 1、5行
              for(j=0;j<5;j++) this->set(_centerX - 2 + j,_centerY - 2 + 4 * i,true);
              //2、4行
              this->set(_centerX - 2,_centerY - 1 + 2 * i,true);
              this->set(_centerX - 1,_centerY - 1 + 2 * i,false);
              this->set(_centerX    ,_centerY - 1 + 2 * i,false);
              this->set(_centerX + 1,_centerY - 1 + 2 * i,false);
              this->set(_centerX + 2,_centerY - 1 + 2 * i,true);
          }
          // 3行
          this->set(_centerX - 2,_centerY,true);
          this->set(_centerX - 1,_centerY,false);
          this->set(_centerX    ,_centerY,true);
          this->set(_centerX + 1,_centerY,false);
          this->set(_centerX + 2,_centerY,true);
      }
      
      std::list<QRC_XY> QRCode::getAliData(void)
      {
          std::list<QRC_XY> xy;
          if(this->ver_ == 1) return xy;
      
          uint8_t
              multy = (this->ver_ / 7 + 2),
              mulmin = (multy - 1),
              distance = (this->len_ - 13) / mulmin,
              delta = (this->len_ - 13) % mulmin,
              x, y;
      
          // 当进入循环时,mulmin > 1,因为mulmin=0,1时,distance一定为偶数
          if (distance & 0x1)
              while (--distance)
              {
                  delta += mulmin;
                  if (delta % (mulmin - 1) == 0)
                  {
                      delta = delta / (mulmin - 1);
                      break;
                  }
              }
          for (uint8_t i = 0; i < multy; i++)
              for (uint8_t j = 0; j < multy; j++)
              {
                  if ((i == 0 && j == 0) ||
                      (i == 0 && j == mulmin) ||
                      (i == mulmin && j == 0))
                      continue;
                  x = 6 + j * distance;
                  y = 6 + i * distance;
                  if (i > 1)
                      y += (i - 1) * delta;
                  if (j > 1)
                      x += (j - 1) * delta;
                  xy.push_back(QRC_XY(x,y));
              }
      
          return xy;
      }
      
      uint16_t QRCode::get15Bit(void)
      {
          return g_15Bit[this->ecl_<<3|this->mask_];
      }
      
      void QRCode::drawFormPatt(void)
      {
          uint16_t bch15_5 = this->get15Bit();
          bool tof;
          // 0-5、9-14位
          for(uint8_t i = 0;i<6;i++) 
          {
              tof = (0x1 << i) & bch15_5;
              this->set(this->len_ - i - 1,8,tof);
              this->set(8                ,i,tof);
              tof = (0x1 << (14 - i)) & bch15_5;
              this->set(i                ,8,tof);
              this->set(8,this->len_ - i - 1,tof);
          }
          // 6、7、8位
          tof = (0x1 << 6) & bch15_5;
          this->set(8,7,tof);
          this->set(this->len_ - 7,8,tof);
          tof = (0x1 << 7) & bch15_5;
          this->set(8,8,tof);
          this->set(this->len_ - 8,8,tof);
          tof = (0x1 << 8) & bch15_5;
          this->set(7,8,tof);
          this->set(8,this->len_ - 7,tof);
      }
      
      uint32_t QRCode::getVerBit(void)
      {
          return g_verBit[this->ver_-7];
      }
      
      void QRCode::drawVerPatt(void)
      {
          uint32_t verBit = this->getVerBit();
          for(uint8_t i = 0;i<6;i++)
              for (uint8_t j = 0; j < 3; j++)
              {
                  bool tof = verBit & (0x1 << (i * 3 + j));
                  this->set(this->len_ - 11 + j,i,tof);
                  this->set(i,this->len_ - 11 + j,tof);
              }
      }
      
      uint16_t QRCode::getCapBit(void)
      {
          return g_capBit[this->ver_ - 1][this->ecl_];
      }
      
      uint8_t QRCode::getStuff(uint16_t _index)
      {
          return g_stuffing[_index & 1];
      }
      
      std::vector<uint8_t> QRCode::getDataCode(const std::string& _str)
      {
          std::vector<uint8_t> data;
          uint16_t len_ = _str.length(),index = 0,i;
          data.push_back(this->type_<<4);
          switch(this->type_)
          {
          case bit8:{
              if(this->ver_ <= 9) // 8
              {
                  // 前4位
                  data[0] |= len_>>4;
                  // 后4位
                  data.push_back((0xf & len_)<<4);
                  index = 1;
              }
              else // 16
              {
                  // 前4位
                  data[0] |= len_>>12;
                  // 中8位
                  data.push_back(0xff & (len_ >> 4));
                  // 后4位
                  data.push_back((0xf & len_)<<4);
                  index = 2;
              }
      
              for(i = 0;i<len_;i++)
              {
                  // 前4位
                  data[index + i] |= _str[i]>>4;
                  // 后4位
                  data.push_back((0xf & _str[i])<<4);
              }
          }break;
          //case ...
          }
      
          // 填充
          len_ = data.size();
          // 一定要确保大小小于标准长度(查看表7),不然会减出一个非常大的值,后果很严重
          index = this->getCapBit() - len_;
          if(index != 0) 
              for(i = 0;i < index;i++)
                  data.push_back(this->getStuff(i));
      
          return data;
      }
      
      uint16_t QRCode::getAllBit(void)
      {
          return g_allBit[this->ver_-1];
      }
      
      uint16_t QRCode::getCorCap(void)
      {
          return static_cast<uint16_t>((this->ver_ < 27)?g_corCap[this->ver_-1][this->ecl_]:g_corCap[26][this->ecl_]);
      }
      
      std::vector<uint8_t> QRCode::getErrDataCode(const std::vector<uint8_t>& _data)
      {
          uint16_t 
          allBit = getAllBit(),
          corCap = getCorCap(),
          capBit = getCapBit(),
          errLen = allBit - capBit,
          blockErrSize = 2 * corCap,
          blockNum = errLen / blockErrSize,
          blockDataSize = capBit / blockNum,
          blockNum_big = capBit % blockNum,
          blockNum_small = blockNum - blockNum_big;
          
          const std::vector<uint8_t>& errdata = QRC_Reed_Solomon(_data,blockNum_big,blockNum_small,blockErrSize,blockDataSize);
      
          return errdata;
      }
      
      InPositionPat QRCode::inPositionPattern(QRC_XY _xy)
      {
          for (auto cxy : this->alCenterxys_)
              if (cxy.y - 3 < _xy.y && _xy.y < cxy.y + 3)
              {
                  if (cxy.x - 2 < _xy.x && _xy.x < cxy.x + 3)
                      return (cxy.x == 6) ? left : normal;
                  if (_xy.x == cxy.x - 2)
                      return normal_left;
              }
      
          return not_in;
      }
      
      QRC_XY QRCode::getDeltaByXY(QRC_XY _xy)
      {
          const bool inRight = (_xy.x <= 6) ? (_xy.x % 2) : ((_xy.x - 1) % 2), blockUp = (_xy.x <= 6) ? (_xy.x / 2) % 2 : !(((_xy.x - 7) / 2) % 2);
          QRC_XY delta = { inRight ? -1 : 1, blockUp ? (inRight ? 0 : -1) : (inRight ? 0 : 1) };
          InPositionPat position = this->inPositionPattern(QRC_XY(_xy.x + delta.x, _xy.y + delta.y));
          
          switch (position)
          {
          case left:
          case normal:delta.y += (blockUp) ? -5 : 5; break;
          case normal_left:delta.x = 0; break;
          case not_in:break;
          }
      
          // 上面一条杠
          if ( _xy.y + delta.y == 6) delta.y += (blockUp) ? -1 : 1;
      
          // 格式消息下方
          if ( _xy.y + delta.y == 8 && ( _xy.x + delta.x <= 8 ||  _xy.x + delta.x >= this->len_ - 8))
          {
              delta = {( _xy.x + delta.x == 8) ? -2 : -1,0};
              goto RETURN;
          }
      
          // 向下上没位子
          if ( _xy.y + delta.y == this->len_ ||  _xy.y + delta.y == -1)
          {
              delta = { -1,(_xy.x + delta.x == 10) ? -8 : 0};
              goto RETURN;
          }
      
          // 版本块 >=7
          if (this->ver_>= 7)
          {
              // -7-4 // 左下版本消息
              if (_xy.y + delta.y == this->len_ - 11 && _xy.x + delta.x == 5)
              {
                  delta = { -1,0 };
              }
              // 右上
              else if (_xy.x + delta.x == this->len_ - 9 && _xy.y + delta.y == 5)
              {
                  delta = { -2,-7 };
              }
              // 右边是右上的版本信息块
              else if (_xy.x + delta.x == this->len_ - 11)
              {
                  if (_xy.y + delta.x < 6)
                  {
                      delta = { 0,1 };
                  }
                  else if (_xy.y + delta.x == 6) 
                  { 
                      delta = { 1,2 };
                  }
              }
      
              goto RETURN;
          }
          // 左下
          // 右上和平时无差别
          else
          {
              if ((_xy.y + delta.y) == this->len_ - 8 && _xy.x + delta.x <= 6)
              {
                  delta = { -1,0 };
                  goto RETURN;
              }
          }
          // 无特殊情况
      RETURN:
          return delta;
      }
      
      void QRCode::drawStrPatt(const std::vector<uint8_t> _data,const std::vector<uint8_t> _errdata)
      {
          uint16_t 
          allBit = getAllBit(),
          corCap = getCorCap(),
          capBit = getCapBit(),
          errLen = allBit - capBit,
          blockErrSize = 2 * corCap,
          blockNum = errLen / blockErrSize,
          blockDataSize = capBit / blockNum,
          blockNum_big = capBit % blockNum,
          blockNum_small = blockNum - blockNum_big;
          uint8_t i,j,k,block;
          QRC_XY delta = { 0,0 }, xy = { this->len_ - 1 ,this->len_ - 1 };
          uint8_t blcokDelta = 0;
      
          // 数据块绘图
              // Data 1
          for (i = 0; i < blockDataSize; i++)
              for (j = 0; j < blockNum; j++)
              {
                  if (j > blockNum_small)
                      blcokDelta = j - blockNum_small;
                  else blcokDelta = 0;
      
                  block = _data[i + j * blockDataSize + blcokDelta];
                  for (k = 0; k < 8; k++)
                  {
                      this->set(xy, block & (0x1 << (7 - k)));
                      delta = getDeltaByXY(xy);
                      xy += delta;
                  }
              }
      
              // Data 2 (if existed)
          for (i = 0; i < blockNum_big; i++)
          {
              block = _data[i * (blockDataSize + 1) + (blockNum_small + 1) * blockDataSize];
              for (k = 0; k < 8; k++)
              {
                  this->set(xy, block & (0x1 << (7 - k)));
                  delta = getDeltaByXY(xy);
                  xy += delta;
              }
          }
          // 纠错块绘图
          for (i = 0; i < blockErrSize; i++)
              for (j = 0; j < blockNum; j++)
              {
                  block = _errdata[i + j * blockErrSize];
                  for (k = 0; k < 8; k++)
                  {
                      this->set(xy, block & (0x1 << (7 - k)));
                      delta = getDeltaByXY(xy);
                      xy += delta;
                  }
              }
          return;
      }
      
      bool QRCode::maskFunc0(QRC_XY _xy){return (_xy.x + _xy.y) % 2 == 0;}
      bool QRCode::maskFunc1(QRC_XY _xy){return _xy.x % 2 == 0;}
      bool QRCode::maskFunc2(QRC_XY _xy){return _xy.y % 3 == 0;}
      bool QRCode::maskFunc3(QRC_XY _xy){return (_xy.x + _xy.y) % 3 == 0;}
      bool QRCode::maskFunc4(QRC_XY _xy){return ((_xy.x/2) + (_xy.y/3)) % 2 == 0;}
      bool QRCode::maskFunc5(QRC_XY _xy){return (_xy.x * _xy.y) % 2 + (_xy.x * _xy.y) % 3 == 0;}
      bool QRCode::maskFunc6(QRC_XY _xy){return ((_xy.x * _xy.y) % 2 + (_xy.x * _xy.y) % 3) % 2 == 0;}
      bool QRCode::maskFunc7(QRC_XY _xy){return ((_xy.x + _xy.y) % 2 + (_xy.x * _xy.y) % 3) % 2 == 0;}
      // 获得函数指针
      bool (*QRCode::getMaskFunc(void))(QRC_XY _xy)
      {
          bool (*func)(QRC_XY _xy) =  maskFunc0;
          switch(this->mask_)
          {
          case 0:func = maskFunc0;break;
          case 1:func = maskFunc2;break;
          case 2:func = maskFunc2;break;
          case 3:func = maskFunc3;break;
          case 4:func = maskFunc4;break;
          case 5:func = maskFunc5;break;
          case 6:func = maskFunc6;break;
          case 7:func = maskFunc7;break;
          }
          return func;
      }
      
      bool QRCode::getMask(QRC_XY _xy)
      {
          return this->getMaskFunc();
      }
      
      bool QRCode::getMask(uint8_t _x,uint8_t _y)
      {
          return this->getMask(QRC_XY(_x,_y));
      }
      
      void QRCode::drawMask(void)
      {
          bool (*maskFunc)(QRC_XY xy) =  getMaskFunc();
          for(uint8_t i = 0;i<len_;i++)
              for(uint8_t j = 0;j<len_;j++)
                  this->set(i,j,this->get(i,j) != maskFunc(QRC_XY(i,j)));
      }
      
      const uint8_t* QRCode::getGenPolyExpByBlockErrSize(uint16_t _blockErrSize)
      {
          // 为了获得多项式在全局变量的位置,多项式长度就是blockSize
          uint8_t index = 0;
          switch (_blockErrSize)
          {
          case 30:index += 29;
          case 28:index += 27;
          case 26:index += 25;
          case 24:index += 23;
          case 22:index += 21;
          case 20:index += 19;
          case 18:index += 18;
          case 17:index += 17;
          case 16:index += 16;
          case 15:index += 14;
          case 13:index += 11;
          case 10:index += 8;
          case 7:break;
          default:break;
          }
          return &g_generator_polynomial_exp[index];
      }
      
      uint8_t QRCode::getAlphaByExp(size_t _exp)
      {
          uint16_t result = 1,i;
          _exp %= 255;
          for (i = 0; i < _exp; i++) 
          {
              result *= 2;
              if (result > 255)
                  result ^= 285;
          }
          return (uint8_t)result;
      }
      
      uint8_t QRCode::getAlphaByInt(uint8_t _int)
      {
          return g_alpha_int_is_index[_int - 1];
      }
      
      std::vector<uint8_t> QRCode::QRC_Reed_Solomon(const std::vector<uint8_t>& _data,uint16_t _blockNum_big,uint16_t _blockNum_small,uint16_t _blockErrSize,uint16_t _blockDataSize)
      {
          std::vector<uint8_t> errdata;
          // 生成多项式
          const uint8_t* poly = getGenPolyExpByBlockErrSize(_blockErrSize);
          // 一个大小为大块大小的块
          uint8_t* blockData = new uint8_t[_blockErrSize + _blockDataSize + 1];
          uint16_t i,j,k;
      
          // 小块
          for (i = 0; i < _blockNum_small; i++)
          {
              // 进位,补零
              for (j = 0; j < _blockDataSize; j++) blockData[j] = _data[j + i * _blockDataSize];
              for (j = 0; j < _blockErrSize; j++) blockData[j + _blockDataSize] = 0;
      
              // 除法
              for (j = 0; j < _blockDataSize; j++)
                  if (blockData[j] != 0)
                  {
                      uint8_t deltaAlpha = this->getAlphaByInt(blockData[j]);
                      for (k = 0; k < _blockErrSize + 1; k++) blockData[j + k] ^= this->getAlphaByExp(static_cast<size_t>(poly[k] + deltaAlpha));
                  }
      
              for (j = 0; j < _blockErrSize; j++) errdata.push_back(blockData[_blockDataSize + j]);
          }
          // 大块
          for (i = 0; i < _blockNum_big; i++)
          {
              for (j = 0; j < _blockDataSize + 1; j++) blockData[j] = _data[j + i * (_blockDataSize + 1) + _blockNum_small * _blockDataSize];
              for (j = 0; j < _blockErrSize; j++) blockData[j + _blockDataSize + 1] = 0;
      
              // 除法
              for (j = 0; j < _blockDataSize + 1; j++)
                  if (blockData[j] != 0)
                  {
                      uint8_t deltaAlpha = this->getAlphaByInt(blockData[j]);
                      for (k = 0; k < _blockErrSize + 1; k++) blockData[k + j] ^= this->getAlphaByExp(static_cast<size_t>(poly[k] + deltaAlpha));
                  }
              for (j = 0; j < _blockErrSize; j++) errdata.push_back(blockData[_blockDataSize + j + 1]);
          }
          delete[] blockData;
      
          return errdata;
      }
      
      uint8_t QRCode::getLen(void)
      {
          return this->len_;
      }
      
      
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值