別人的代碼
先放一下一份抄到的代碼,一般的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,只能整數倍放大,做不到浮點值放大,因為最小單元像素點不能夠再划分。