聊聊zxing的qrcode
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
序
本文主要研究下zxing的qrcode的一些代碼。
maven
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.1</version></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.3.1</version></dependency>QRCodeWriter
core-3.3.1-sources.jar!/com/google/zxing/qrcode/QRCodeWriter.java QRCodeWriter的encode方法進(jìn)行編碼,轉(zhuǎn)換為BitMatrix
@Overridepublic 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);return renderResult(code, width, height, quietZone);}QRCode
core-3.3.1-sources.jar!/com/google/zxing/qrcode/encoder/QRCode.java
public final class QRCode {public static final int NUM_MASK_PATTERNS = 8;private Mode mode;private ErrorCorrectionLevel ecLevel;private Version version;private int maskPattern;private ByteMatrix matrix;public QRCode() {maskPattern = -1;}public Mode getMode() {return mode;}public ErrorCorrectionLevel getECLevel() {return ecLevel;}public Version getVersion() {return version;}public int getMaskPattern() {return maskPattern;}public ByteMatrix getMatrix() {return matrix;}@Overridepublic String toString() {StringBuilder result = new StringBuilder(200);result.append("<<\n");result.append(" mode: ");result.append(mode);result.append("\n ecLevel: ");result.append(ecLevel);result.append("\n version: ");result.append(version);result.append("\n maskPattern: ");result.append(maskPattern);if (matrix == null) {result.append("\n matrix: null\n");} else {result.append("\n matrix:\n");result.append(matrix);}result.append(">>\n");return result.toString();}public void setMode(Mode value) {mode = value;}public void setECLevel(ErrorCorrectionLevel value) {ecLevel = value;}public void setVersion(Version version) {this.version = version;}public void setMaskPattern(int value) {maskPattern = value;}public void setMatrix(ByteMatrix value) {matrix = value;}// Check if "mask_pattern" is valid.public static boolean isValidMaskPattern(int maskPattern) {return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;}}是由Encoder類(lèi)encode而來(lái)
Encoder
core/3.3.1/core-3.3.1-sources.jar!/com/google/zxing/qrcode/encoder/Encoder.java
public static QRCode encode(String content,ErrorCorrectionLevel ecLevel,Map<EncodeHintType,?> hints) throws WriterException {// Determine what character encoding has been specified by the caller, if anyString encoding = DEFAULT_BYTE_MODE_ENCODING;boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET);if (hasEncodingHint) {encoding = hints.get(EncodeHintType.CHARACTER_SET).toString();}// Pick an encoding mode appropriate for the content. Note that this will not attempt to use// multiple modes / segments even if that were more efficient. Twould be nice.Mode mode = chooseMode(content, encoding);// This will store the header information, like mode and// length, as well as "header" segments like an ECI segment.BitArray headerBits = new BitArray();// Append ECI segment if applicableif (mode == Mode.BYTE && (hasEncodingHint || !DEFAULT_BYTE_MODE_ENCODING.equals(encoding))) {CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);if (eci != null) {appendECI(eci, headerBits);}}// (With ECI in place,) Write the mode markerappendModeInfo(mode, headerBits);// Collect data within the main segment, separately, to count its size if needed. Don't add it to// main payload yet.BitArray dataBits = new BitArray();appendBytes(content, mode, dataBits, encoding);Version version;if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) {int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString());version = Version.getVersionForNumber(versionNumber);int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);if (!willFit(bitsNeeded, version, ecLevel)) {throw new WriterException("Data too big for requested version");}} else {version = recommendVersion(ecLevel, mode, headerBits, dataBits);}BitArray headerAndDataBits = new BitArray();headerAndDataBits.appendBitArray(headerBits);// Find "length" of main segment and write itint numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();appendLengthInfo(numLetters, version, mode, headerAndDataBits);// Put data together into the overall payloadheaderAndDataBits.appendBitArray(dataBits);Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();// Terminate the bits properly.terminateBits(numDataBytes, headerAndDataBits);// Interleave data bits with error correction code.BitArray finalBits = interleaveWithECBytes(headerAndDataBits,version.getTotalCodewords(),numDataBytes,ecBlocks.getNumBlocks());QRCode qrCode = new QRCode();qrCode.setECLevel(ecLevel);qrCode.setMode(mode);qrCode.setVersion(version);// Choose the mask pattern and set to "qrCode".int dimension = version.getDimensionForVersion();ByteMatrix matrix = new ByteMatrix(dimension, dimension);int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);qrCode.setMaskPattern(maskPattern);// Build the matrix and set it to "qrCode".MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);qrCode.setMatrix(matrix);return qrCode;}這里重點(diǎn)看Version的這段
Version version;if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) {int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString());version = Version.getVersionForNumber(versionNumber);int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);if (!willFit(bitsNeeded, version, ecLevel)) {throw new WriterException("Data too big for requested version");}} else {version = recommendVersion(ecLevel, mode, headerBits, dataBits);}這里計(jì)算version,同時(shí)判斷content的大小,是否超出qrcode的容量,超出的話(huà),拋出WriterException("Data too big for requested version")
- willFit計(jì)算方法
QRCodeWriter.renderResult
// 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).private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {ByteMatrix input = code.getMatrix();if (input == null) {throw new IllegalStateException();}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);// 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;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 barcodefor (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {if (input.get(inputX, inputY) == 1) {output.setRegion(outputX, outputY, multiple, multiple);}}}return output;}這個(gè)renderResult根據(jù)QRCode信息來(lái)構(gòu)造BitMatrix。可以看到這里重新計(jì)算了輸出的寬度和高度,是取了qrcode的寬高+兩邊的quietZone與輸入?yún)?shù)的目標(biāo)寬高取最大值。也就是說(shuō)如果qrcode的寬高大于目標(biāo)的寬高,則以qrcode的寬高為準(zhǔn),這種情況下的quietZone基本跟輸出的一致。
一般而言qrcode的寬高小于目標(biāo)寬高的話(huà),這種情況下quietZone就跟輸出的不一致,需要經(jīng)過(guò)重新放大,得到的才是最后輸出的padding。
doc
- 二維碼最大能包含多少信息量?
- 使用zxing生成和識(shí)別二維碼
轉(zhuǎn)載于:https://my.oschina.net/go4it/blog/1556912
總結(jié)
以上是生活随笔為你收集整理的聊聊zxing的qrcode的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 3.2. @RestController
- 下一篇: vc升级失败恢复快照后数据不一致问题