最近在做一个二维码的相关的应用,顺带整理下相关的知识。今天我们来看看二维码的白边如何去除,以及如何生透明底的二维码。
前篇回顾:【android应用】基于ZXing生成的二维码,文末附带源码---添加logo、彩色、背景、文字、水印在这里都能找到
在上篇中我们已经知道了彩色二维码如何生成,以及logo、背景、文字、水印的添加,但里面那个白边一直占据着我们的视线,今天就来讲讲如何去除掉这个白边。
目录
何去除白边
下面我们来跟一下二维码生成的源码流程。首先是调用位置:
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