别人的代码

先放一下一份抄到的代码,一般的ZXing都是差不多这样写的

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            String content = "{\"server\":4407}";
            Integer width = 100;
            Integer height = 100;

            Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            hints.put(EncodeHintType.MARGIN, 0);

            QRCodeWriter writer = new QRCodeWriter();
            BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            MatrixToImageWriter.writeToStream(bitMatrix, "jpg", new FileOutputStream("你好.jpg"));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

结果图如下

白边问题

尺寸为100,100的时候,白边情况比较严重,左右上下都有8个像素的白边。
先考究一下为什么会产生白边,能不能去掉。

看一下encode方法。

 @Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {

    if (contents.isEmpty()) {
      throw new IllegalArgumentException("Found empty contents");
    }

    if (format != BarcodeFormat.QR_CODE) {
      throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
    }

    if (width < 0 || height < 0) {
      throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
          height);
    }

    ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
    int quietZone = QUIET_ZONE_SIZE;
    if (hints != null) {
      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
        errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
      }
      if (hints.containsKey(EncodeHintType.MARGIN)) {
        quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
      }
    }
    //没有涉及到宽高,仅仅和内容相关,那么我们的内容变成二维码就是这里
    QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
    //和宽高、以及刚刚生成的二维码相关,其中还有一个quietZone参数。
    return renderResult(code, width, height, quietZone);
  }
  // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
  // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
  // 假定初始输出的width为100,height为100
  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
      ByteMatrix input = code.getMatrix();
      //计算出来的二维码大小,假设二维码最小大小为21
      int inputWidth = input.getWidth();
      int inputHeight = input.getHeight();
      //计算留白空间,设的0会在这里生效
      int qrWidth = inputWidth + (quietZone * 2);
      int qrHeight = inputHeight + (quietZone * 2);
      //计算二维码大小和我们提供给系统的尺寸比较,取最大值。
      int outputWidth = Math.max(width, qrWidth);
      int outputHeight = Math.max(height, qrHeight);
      //计算二维码与提供尺寸的倍率关系,用于放大缩小。如100/21=4
      int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
      // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
      // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
      // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
      // handle all the padding from 100x100 (the actual QR) up to 200x160.
      //上述主要解释leftPadding和topPadding右边的计算赋值是什么意思。
      //计算左上分别空多少空间,(100-21*4)=8
      int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
      int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
      //取尺寸最大值的画布大小
      BitMatrix output = new BitMatrix(outputWidth, outputHeight);
      //从空出的第8个像素点开始,把二维码点1x1的结果放大至4x4写入画布中。
      for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
          // Write the contents of this row of the barcode
          for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
              if (input.get(inputX, inputY) == 1) {
                  output.setRegion(outputX, outputY, multiple, multiple);
              }
          }
      }

    return output;
  }

可以看到,在renderResult方法中,QRCodeWriter直接是将原本二维码像素值直接以整数倍率扩增,因为内容的大小导致对应尺寸和二维码像素值无法整除关系,为了适应用户输入的尺寸,所以才产生了白边。

所以去白边,本质上是将100x100变成84x84这种情况。

那么我们直接重新实现Writer,把其他代码基本照抄,然后把renderResult改写。

PS:为什么不是继承重写,是因为QRCodeWriter是一个被final修饰的类,不允许继承。

白边解决方案

 // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
  // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
  // 假定初始输出的width为100,height为100
  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
      ByteMatrix input = code.getMatrix();
      int inputWidth = input.getWidth();
      int inputHeight = input.getHeight();
      int qrWidth = inputWidth + (quietZone * 2);
      int qrHeight = inputHeight + (quietZone * 2);
      int outputWidth = Math.max(width, qrWidth);
      int outputHeight = Math.max(height, qrHeight);
      int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);

      outputWidth = qrWidth * multiple;// 改动点
      outputHeight = qrWidth * multiple;// 改动点

      // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
      // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
      // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
      // handle all the padding from 100x100 (the actual QR) up to 200x160.
      int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
      int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
      leftPadding = 0 ;//改动点
      topPadding = 0 ;//改动点
      BitMatrix output = new BitMatrix(outputWidth, outputHeight);
      for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
          // Write the contents of this row of the barcode
          for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
              if (input.get(inputX, inputY) == 1) {
                  output.setRegion(outputX, outputY, multiple, multiple);
              }
          }
      }

    return output;
  }

如上,添加4行代码就可以完成去白边的功能需求。

启动代码

public static void main(String[] args) {
        try {
            String content = "{\"server\":4407}";
            Integer width = 100;
            Integer height = 100;

            Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            hints.put(EncodeHintType.MARGIN, 0);

            QCCodeWriterSelf writer = new QCCodeWriterSelf();//复制QCCodeWriter实现的类,仅仅添加了上述说明的4行代码
            BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            MatrixToImageWriter.writeToStream(bitMatrix, "jpg", new FileOutputStream("你好.jpg"));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    

文件图片结果如下所示

结论

二维码的生成分为两部分:
第一部分是根据你的内容生成对应的二维码大小,如上图二维码的最小大小为21x21.
第二部分是根据用户输入的大小,如100x100就是先扩大成84x84,剩余16x16就变成4个方位的8x8白边。
因此白边是可以去除的,但是去除后白边的二维码因为是21*21,只能整数倍放大,做不到浮点值放大,因为最小单元像素点不能够再划分。

12-31 00:33