/**
* 二維碼相關工具類
*/
public final class QRCodeUtil {
private QRCodeUtil() {
throw new UnsupportedOperationException("cannot be instantiated");
}
/***********************************************************************************************
***** 生成二維碼
**********************************************************************************************/
/**
* 生成二維碼的 Bitmap
*
* @param content 二維碼中的內容
* @param width 二維碼的寬
* @param height 二維碼的高
* @return 二維碼圖片
*/
public static Bitmap createQRCode(String content, int width, int height) {
return createQRCode(content, width, height, 2);
}
/**
* 生成二維碼的 Bitmap
*
* @param content 二維碼中的內容
* @param width 二維碼的寬
* @param height 二維碼的高
* @param height 二維碼空白邊距的寬度
* @return 二維碼圖片
*/
public static Bitmap createQRCode(String content, int width, int height, int border) {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
// 配置引數
Map hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 容錯級別
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 設定空白邊距的寬度,default is 4
hints.put(EncodeHintType.MARGIN, border);
try {
// 影象資料轉換,使用了矩陣轉換
BitMatrix encode = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
// 下面這裡按照二維碼的演算法,逐個生成二維碼的圖片,
// 兩個 for 迴圈是圖片橫列掃描的結果
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (encode.get(j, i)) {
pixels[i * width + j] = Color.BLACK;
} else {
pixels[i * width + j] = Color.WHITE;
}
}
}
// 生成二維碼圖片的格式,使用 ARGB_8888
return Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 在二維碼中間新增 Logo 圖案
*
* @param qrBitmap 二維碼圖片
* @param logoBitmap logo 圖片
* @return 新增了 Logo 的二維碼圖片
*/
public static Bitmap addLogoToQRCode(Bitmap qrBitmap, Bitmap logoBitmap) {
if (qrBitmap == null) return null;
if (logoBitmap == null) return qrBitmap;
int qrBitmapWidth = qrBitmap.getWidth();
int qrBitmapHeight = qrBitmap.getHeight();
int logoBitmapWidth = logoBitmap.getWidth();
int logoBitmapHeight = logoBitmap.getHeight();
Bitmap blankBitmap = Bitmap.createBitmap(qrBitmapWidth, qrBitmapHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(blankBitmap);
canvas.drawBitmap(qrBitmap, 0, 0, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
float scaleSize = 1.0f;
while ((logoBitmapWidth / scaleSize) > (qrBitmapWidth / 5) || (logoBitmapHeight / scaleSize) > (qrBitmapHeight / 5)) {
scaleSize *= 2;
}
float sx = 1.0f / scaleSize;
canvas.scale(sx, sx, qrBitmapWidth / 2, qrBitmapHeight / 2);
canvas.drawBitmap(logoBitmap, (qrBitmapWidth - logoBitmapWidth) / 2, (qrBitmapHeight - logoBitmapHeight) / 2, null);
canvas.restore();
return blankBitmap;
}
/***********************************************************************************************
**** 解析二維碼
**********************************************************************************************/
/**
* 解析二維碼(使用解析RGB編碼資料的方式)
*
* @param path 二維碼圖片所在路徑
* @return 解析結果
*/
public static Result decodeQRCodeRGB(String path) {
if (TextUtils.isEmpty(path)) return null;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 1;
Bitmap qrcode = BitmapFactory.decodeFile(path, opts);
Result result = decodeQRCodeRGB(qrcode);
qrcode.recycle();
qrcode = null;
return result;
}
/**
* 解析二維碼 (使用解析 RGB 編碼資料的方式)
*
* @param qrcode 二維碼圖片
* @return 解析結果
*/
public static Result decodeQRCodeRGB(Bitmap qrcode) {
if (qrcode == null) return null;
int width = qrcode.getWidth();
int height = qrcode.getHeight();
int[] data = new int[width * height];
qrcode.getPixels(data, 0, width, 0, 0, width, height);
RGBLuminanceSource source = new RGBLuminanceSource(width, height, data);
BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
Result result = null;
try {
result = reader.decode(bitmap1);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (ChecksumException e) {
e.printStackTrace();
} catch (FormatException e) {
e.printStackTrace();
}
qrcode.recycle();
qrcode = null;
return result;
}
/**
* 解析二維碼(使用解析 YUV 編碼資料的方式)
*
* @param path 二維碼圖片所在路徑
* @return 解析結果
*/
public static Result decodeQRCodeYUV(String path) {
if (TextUtils.isEmpty(path)) return null;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 1;
Bitmap barcode = BitmapFactory.decodeFile(path, opts);
Result result = decodeQRCodeYUV(barcode);
barcode.recycle();
barcode = null;
return result;
}
/**
* 解析二維碼(使用解析 YUV 編碼資料的方式)
*
* @param qrcode 二維碼圖片
* @return 解析結果
*/
public static Result decodeQRCodeYUV(Bitmap qrcode) {
if (qrcode == null) return null;
int width = qrcode.getWidth();
int height = qrcode.getHeight();
// 以 argb 方式存放圖片的畫素
int[] argb = new int[width * height];
qrcode.getPixels(argb, 0, width, 0, 0, width, height);
// 將 argb 轉換為 yuv
byte[] yuv = new byte[width * height * 3 / 2];
encodeYUV420SP(yuv, argb, width, height);
// 解析 YUV 編碼方式的二維碼
Result result = decodeQRCodeYUV(yuv, width, height);
qrcode.recycle();
qrcode = null;
return result;
}
/**
* 解析二維碼(使用解析 YUV 編碼資料的方式)
*
* @param yuv 二維碼圖片
* @param width 二維碼圖片的寬
* @param height 二維碼圖片的高
* @return 解析結果
*/
private static Result decodeQRCodeYUV(byte[] yuv, int width, int height) {
MultiFormatReader multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(null);
Result rawResult = null;
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(yuv, width, height, 0, 0,
width, height, false);
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
re.printStackTrace();
} finally {
multiFormatReader.reset();
multiFormatReader = null;
}
}
return rawResult;
}
/**
* RGB 轉 YUV 的公式是:
* Y=0.299R + 0.587G + 0.114B;
* U=-0.147R - 0.289G + 0.436B;
* V=0.615R - 0.515G - 0.1B;
*
* @param yuv
* @param argb
* @param width
* @param height
*/
private static void encodeYUV420SP(byte[] yuv, int[] argb, int width, int height) {
// 幀圖片的畫素大小
final int frameSize = width * height;
// ---YUV 資料---
int Y, U, V;
// Y 的 index 從0開始
int yIndex = 0;
// UV 的 index 從 frameSize 開始
int uvIndex = frameSize;
// ---顏色資料---
int R, G, B;
int rgbIndex = 0;
// ---迴圈所有畫素點,RGB 轉 YUV---
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
R = (argb[rgbIndex] & 0xff0000) >> 16;
G = (argb[rgbIndex] & 0xff00) >> 8;
B = (argb[rgbIndex] & 0xff);
//
rgbIndex++;
// 已知的 RGB 轉 YUV 演算法
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
Y = Math.max(0, Math.min(Y, 255));
U = Math.max(0, Math.min(U, 255));
V = Math.max(0, Math.min(V, 255));
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
// pixel AND every other scan line.
// ---Y---
yuv[yIndex++] = (byte) Y;
// ---UV---
if ((j % 2 == 0) && (i % 2 == 0)) {
yuv[uvIndex++] = (byte) V;
yuv[uvIndex++] = (byte) U;
}
}
}
}
/***********************************************************************************************
***** 條形碼相關
**********************************************************************************************/
/**
* 生成條形碼
*
* @param contents 需要生成的內容
* @param desiredWidth 生成條形碼的寬度
* @param desiredHeight 生成條形碼的高度
* @param displayCode 是否在條形碼下方顯示內容
* @return
*/
public static Bitmap createBarCode(String contents, int desiredWidth, int desiredHeight, boolean displayCode) {
Bitmap ruseltBitmap = null;
/**
* 圖片兩端所保留的空白的寬度
*/
int marginW = 20;
/**
* 條形碼的編碼型別
*/
BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;
if (displayCode) {
Bitmap barcodeBitmap = encodeAsBitmap(contents, barcodeFormat,
desiredWidth, desiredHeight);
Bitmap codeBitmap = createCodeBitmap(contents, desiredWidth + 2
* marginW, desiredHeight, SUtils.getApp());
ruseltBitmap = mixtureBitmap(barcodeBitmap, codeBitmap, new PointF(
0, desiredHeight));
} else {
ruseltBitmap = encodeAsBitmap(contents, barcodeFormat,
desiredWidth, desiredHeight);
}
return ruseltBitmap;
}
/**
* 生成條形碼的 Bitmap
*
* @param contents 需要生成的內容
* @param format 編碼格式
* @param desiredWidth 生成條形碼的寬度
* @param desiredHeight 生成條形碼的高度
* @return
* @throws WriterException
*/
private static Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int desiredWidth, int desiredHeight) {
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result = null;
try {
result = writer.encode(contents, format, desiredWidth, desiredHeight, null);
} catch (WriterException e) {
e.printStackTrace();
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
// 預設情況下,所有的都是 0 或黑色
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
/**
* 生成顯示編碼的 Bitmap
*
* @param contents
* @param width
* @param height
* @param context
* @return
*/
private static Bitmap createCodeBitmap(String contents, int width, int height, Context context) {
TextView tv = new TextView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(layoutParams);
tv.setText(contents);
tv.setHeight(height);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setWidth(width);
tv.setDrawingCacheEnabled(true);
tv.setTextColor(Color.BLACK);
tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
tv.buildDrawingCache();
Bitmap bitmapCode = tv.getDrawingCache();
return bitmapCode;
}
/**
* 將兩個 Bitmap 合併成一個
*
* @param first
* @param second
* @param fromPoint 第二個Bitmap開始繪製的起始位置(相對於第一個 Bitmap)
* @return 合併後的 bitmap
*/
private static Bitmap mixtureBitmap(Bitmap first, Bitmap second, PointF fromPoint) {
if (first == null || second == null || fromPoint == null) {
return null;
}
int marginW = 20;
Bitmap newBitmap = Bitmap.createBitmap(
first.getWidth() + second.getWidth() + marginW,
first.getHeight() + second.getHeight(), Bitmap.Config.ARGB_4444);
Canvas cv = new Canvas(newBitmap);
cv.drawBitmap(first, marginW, 0, null);
cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
cv.save(Canvas.ALL_SAVE_FLAG);
cv.restore();
return newBitmap;
}
}