最近在做一個二維碼的相關的應用,順帶整理下相關的知識。今天我們來看看二維碼的白邊如何去除,以及如何生透明底的二維碼。

前篇回顧:【android應用】基于ZXing生成的二維碼,文末附帶源碼---添加logo、彩色、背景、文字、水印在這裡都能找到
在上篇中我們已經知道了彩色二維碼如何生成,以及logo、背景、文字、水印的添加,但裡面那個白邊一直占據着我們的視線,今天就來講講如何去除掉這個白邊。
目錄
何去除白邊
1、MultiFormatWriter類的encode方法
2、QRCodeWriter類的encode方法
3、方法renderResult
4、解決方案
如何生成透明底的二維碼
1、再來看下生成二維碼圖的函數
2、色彩模式ARGB
3、解決方案
4、效果圖
如何添加半透明的水印和logo
1、了解下drawBitmap方法
2、看下添加水印的代碼
3、解決方案
4、效果圖
源碼
結束語
何去除白邊
下面我們來跟一下二維碼生成的源碼流程。首先是調用位置:
BitMatrix matrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, width, height, hints);
1、MultiFormatWriter類的encode方法
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException {
Writer writer;
switch (format) {
case EAN_8:
writer = new EAN8Writer();
break;
case EAN_13:
writer = new EAN13Writer();
break;
case UPC_A:
writer = new UPCAWriter();
break;
case QR_CODE:
//用到的是這個模式
writer = new QRCodeWriter();
break;
case CODE_39:
writer = new Code39Writer();
break;
case CODE_128:
writer = new Code128Writer();
break;
case ITF:
writer = new ITFWriter();
break;
case PDF_417:
writer = new PDF417Writer();
break;
case CODABAR:
writer = new CodaBarWriter();
break;
default:
throw new IllegalArgumentException("No encoder available for format " + format);
}
return writer.encode(contents, format, width, height, hints);
}
}
實際上這個方法就是依據format來選擇一種編碼方式,我們這裡用的是QR_CODE的方式了,然後我們再看QRCodeWriter的encode方法。
2、QRCodeWriter類的encode方法
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException {
if(contents.length() == 0) {
throw new IllegalArgumentException("Found empty contents");
} else if(format != BarcodeFormat.QR_CODE) {
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
} else if(width >= 0 && height >= 0) {
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
if(hints != null) {
ErrorCorrectionLevel code = (ErrorCorrectionLevel)hints.get(EncodeHintType.ERROR_CORRECTION);
if(code != null) {
errorCorrectionLevel = code;
}
}
// 前面的都是做編碼前的準備,安全檢驗,糾錯級别設定等
QRCode code1 = new QRCode();
// 這裡才是真正的将contents轉換成code
Encoder.encode(contents, errorCorrectionLevel, hints, code1);
// return的時候将code轉換成BitMatrix,并加入白邊
return renderResult(code1, width, height);
} else {
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' + height);
}
}
下面再看将code轉換成BitMatrix,并加入白邊的方法renderResult
3、方法renderResult
private static BitMatrix renderResult(QRCode code, int width, int height) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
// 這裡qrWidth就是原始的二維碼的寬度了,包含8機關寬度的白邊
int qrWidth = inputWidth + 8;
int qrHeight = inputHeight + 8;
// 依據使用者的輸入寬高,計算最後的輸出寬高
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
//計算縮放比例
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
// 計算白邊的寬度
int leftPadding = (outputWidth - inputWidth * multiple) / 2;
int topPadding = (outputHeight - inputHeight * multiple) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
int inputY = 0;
// 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
for (int outputY = topPadding; inputY < inputHeight; outputY += multiple) {
int inputX = 0;
for (int outputX = leftPadding; inputX < inputWidth; outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
inputX++;
}
inputY++;
}
return output;
}
4、解決方案
這個方法裡的代碼不難讀懂,是以要去掉白邊實際上就很簡單了,自定義一個QRCodeWriter類,完全把Zxing包的QRCodeWriter類複制過來,然後将renderResult方法裡的padding去掉就可以了(為什麼不繼承QRCodeWriter,因為它是final類~~)。
修改後如下:
private static BitMatrix renderResult(QRCode code, int width, int height) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
// 依據使用者的輸入寬高,計算最後的輸出寬高
int outputWidth = Math.max(width, inputWidth);
int outputHeight = Math.max(height, inputHeight);
//計算縮放比例
int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
int inputY = 0;
// 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
for (int outputY = 0; inputY < inputHeight; outputY += multiple) {
int inputX = 0;
for (int outputX = 0; inputX < inputWidth; outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
inputX++;
}
inputY++;
}
return output;
}
但是你還是會發現,還是有一點點小小的白邊。而且整體往上偏了效果圖如下:
在renderResult方法中加上調試log在看下:
可以看出29*8 != 240;
解決方案1:
嘗試修改inputWidth和inputHeight為29的倍數是可以解決的,但是每次都需要計算傳值這樣很是麻煩。
解決方案2:
在renderResult方法得到multiple後重新指派一次給inputWidth和inputHeight,這樣比較友善不需要每次計算傳值。
最終renderResult方法修改如下:
private static BitMatrix renderResult(QRCode code, int width, int height) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
// 依據使用者的輸入寬高,計算最後的輸出寬高
int outputWidth = Math.max(width, inputWidth);
int outputHeight = Math.max(height, inputHeight);
//計算縮放比例
int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
//重新寫一次outputWidth和inputHeight
outputWidth = multiple * inputWidth;
outputHeight = multiple * inputHeight;
Log.d(TAG, "renderResult: outputWidth " +outputWidth +" outputHeight "+outputHeight);
Log.d(TAG, "renderResult: inputHeight " +inputWidth +" inputHeight "+inputHeight);
Log.d(TAG, "renderResult: multiple " + multiple);
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
int inputY = 0;
// 嵌套循環,将ByteMatrix的内容計算padding後轉換成BitMatrix
for (int outputY = 0; inputY < inputHeight; outputY += multiple) {
int inputX = 0;
for (int outputX = 0; inputX < inputWidth; outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
inputX++;
}
inputY++;
}
return output;
}
這樣就能完美的去除白邊了效果圖如下:
後面我會把源碼貼上,如果對你有用,希望能給我一個贊。謝謝!!!
如何生成透明底的二維碼
1、再來看下生成二維碼圖的函數
private static Bitmap bitMatrix2Bitmap(BitMatrix matrix) {
int WIDTH = matrix.getWidth();
int HEIGHT = matrix.getHeight();
int[] pixels = new int[WIDTH * HEIGHT];
for (int y = 0; y < WIDTH; y++) {
for (int x = 0; x < HEIGHT; x++) {
int color = Color.WHITE;
if (matrix.get(x, y)) {
// 有内容的部分,顔色設定為漸變,當然這裡可以自己修改成喜歡的顔色
color = 0xFF0094FF + y/2;// 藍色
// if (x < WIDTH / 2 && y < HEIGHT / 2) {
// color = 0xFF0094FF;// 藍色
// //Integer.toHexString(new Random().nextInt());
// } else if (x < WIDTH / 2 && y > HEIGHT / 2) {
// color = 0xFFFED545;// 黃色
// } else if (x > WIDTH / 2 && y > HEIGHT / 2) {
// color = 0xFF5ACF00;// 綠色
// } else {
// color = 0xFF000000;// 黑色
// }
}
pixels[x + (y * WIDTH)] = color;
}
}
Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.RGB_565);
bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
return bitmap;
}
我們隻需要将color的初始值設定成我們我們想要的顔色,就能得到我們想要的底色。比如藍色(0xFF0094FF):
是不是感覺自己馬上就能搞出透明底的隻需要将FF改成8F就木有問題了,可結果不是這樣,不要慌。我們再來回顧下色彩模式。你就知道哪不對了。
2、色彩模式ARGB
可以檢視我轉載的一篇部落格,飛機票:常用色彩模式ARGB詳解
3、解決方案
半透明:
- 将0xFF0094FF改成0x800094FF
- 将Bitmap.Config.RGB_565模式修改成Bitmap.Config.ARGB_8888模式
private static Bitmap bitMatrix2Bitmap(BitMatrix matrix) {
int WIDTH = matrix.getWidth();
int HEIGHT = matrix.getHeight();
int[] pixels = new int[WIDTH * HEIGHT];
for (int y = 0; y < WIDTH; y++) {
for (int x = 0; x < HEIGHT; x++) {
//int color = 0xFFFFFFFF;
int color = 0x800094FF;
if (matrix.get(x, y)) {
// 有内容的部分,顔色設定為黑色,當然這裡可以自己修改成喜歡的顔色
color = 0xFF000000;
}
pixels[x + (y * WIDTH)] = color;
}
}
Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
return bitmap;
}
4、效果圖
透明的隻需要在透明上修改顔色即可(将0xFF0094FF改成0x000094FF),這裡就不貼效果圖了。
如何添加半透明的水印和logo
1、了解下drawBitmap方法
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint);
Rect src: 是對圖檔進行裁截,若是空null則顯示整個圖檔
RectF dst:是圖檔在Canvas畫布中顯示的區域,
大于src則把src的裁截區放大,
小于src則把src的裁截區縮小。
Paint paint :該類儲存了繪制幾何圖形、文本和位圖的樣式和顔色資訊。
2、看下添加水印的代碼
public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) {
if (srcBMP == null) {
return null;
}
// 建立一個新的和SRC長度寬度一樣的位圖
Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(),
srcBMP.getHeight(), Bitmap.Config.ARGB_8888);
Canvas cv = new Canvas(newb);
// 在 0,0坐标開始畫入原圖
cv.drawBitmap(srcBMP, 0, 0, null);
// 在原圖的右下角畫入水印
cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth() * 3 / 5,
srcBMP.getHeight() * 3 / 7, null);
// 儲存
cv.save(Canvas.ALL_SAVE_FLAG);
// 存儲
cv.restore();
return newb;
}
Paint 表示畫筆 Canvas 表示畫布,畫闆。可以看出Paint 傳入的null,說明使用預設的。我們可以通過畫筆Paint來設定,比如:畫筆大小,顔色值,透明度,填充樣式等等。這樣就簡單了。
3、解決方案
public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) {
if (srcBMP == null) {
return null;
}
// 建立一個新的和SRC長度寬度一樣的位圖
Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(),
srcBMP.getHeight(), Bitmap.Config.ARGB_8888);
//擷取透明度
Paint vPaint = selectAlpha(0);
Canvas cv = new Canvas(newb);
// 在 0,0坐标開始畫入原圖
cv.drawBitmap(srcBMP, 0, 0, null);
// 在原圖的右下角畫入水印
cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth() * 3 / 5,
srcBMP.getHeight() * 3 / 7, vPaint);
// 儲存
cv.save(Canvas.ALL_SAVE_FLAG);
// 存儲
cv.restore();
return newb;
}
/**
* 設定透明度
*
* @param alpha 透明度
* @return Paint 畫筆
*/
private static Paint setAlpha(int alpha) {
// 建立Paint 物件
Paint vPaint = new Paint();
vPaint .setStyle( Paint.Style.STROKE ); //空心
vPaint .setAlpha(alpha); //0—255
return vPaint;
}
/**
* 選擇透明度
*
* @param alpha 透明度 1 不透明 0 半透明
* @return Paint 畫筆
*/
private static Paint selectAlpha(int alpha) {
if(alpha == 0){
return setAlpha(75);
}else{
return setAlpha(255);
}
}
4、效果圖
源碼
飛機票:【源碼】二維碼透明底和去除白邊功能
結束語
以上就是跟二維碼去除白邊和透明底的相關接口總結,希望對你有用。歡迎大家關注我們微信公衆号,來交流程式員的技術。如果能留言或者點個贊,我也是很開心的,非常感謝!
部分圖檔來自網際網路
參考:https://blog.csdn.net/pxr1989104/article/details/51283585