二維碼相關工具類

weixin_34194087發表於2019-01-22
/**
 * 二維碼相關工具類
 */
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;
    }
}

相關文章