Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed8cda6a authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Improve QrCodeGenerator.encodeQrCode performance" into main

parents 0c552eb7 f8d17833
Loading
Loading
Loading
Loading
+86 −0
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright (C) 2022 The Android Open Source Project
 * Copyright (C) 2023 The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -14,64 +14,36 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


package com.android.settingslib.qrcode;
package com.android.settingslib.qrcode


import android.graphics.Bitmap;
import android.annotation.ColorInt
import android.graphics.Color;
import android.graphics.Bitmap

import android.graphics.Color
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType;
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.MultiFormatWriter
import com.google.zxing.WriterException;
import com.google.zxing.WriterException
import com.google.zxing.common.BitMatrix;
import java.nio.charset.StandardCharsets

import java.util.EnumMap
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public final class QrCodeGenerator {
    private static final int DEFAULT_MARGIN = -1;
    /**
     * Generates a barcode image with {@code contents}.
     *
     * @param contents The contents to encode in the barcode
     * @param size     The preferred image size in pixels
     * @return Barcode bitmap
     */
    public static Bitmap encodeQrCode(String contents, int size)
            throws WriterException, IllegalArgumentException {
        return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/false);
    }

    /**
     * Generates a barcode image with {@code contents}.
     *
     * @param contents The contents to encode in the barcode
     * @param size     The preferred image size in pixels
     * @param margin   The margin around the actual barcode
     * @return Barcode bitmap
     */
    public static Bitmap encodeQrCode(String contents, int size, int margin)
            throws WriterException, IllegalArgumentException {
        return encodeQrCode(contents, size, margin, /*invert=*/false);
    }


object QrCodeGenerator {
    /**
    /**
     * Generates a barcode image with {@code contents}.
     * Generates a barcode image with [contents].
     *
     *
     * @param contents The contents to encode in the barcode
     * @param contents The contents to encode in the barcode
     * @param size     The preferred image size in pixels
     * @param size     The preferred image size in pixels
     * @param invert   Whether to invert the black/white pixels (e.g. for dark mode)
     * @param invert   Whether to invert the black/white pixels (e.g. for dark mode)
     * @return Barcode bitmap
     * @return Barcode bitmap
     */
     */
    public static Bitmap encodeQrCode(String contents, int size, boolean invert)
    @JvmStatic
            throws WriterException, IllegalArgumentException {
    @Throws(WriterException::class, java.lang.IllegalArgumentException::class)
        return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/invert);
    fun encodeQrCode(contents: String, size: Int, invert: Boolean): Bitmap =
    }
        encodeQrCode(contents, size, DEFAULT_MARGIN, invert)

    private const val DEFAULT_MARGIN = -1


    /**
    /**
     * Generates a barcode image with {@code contents}.
     * Generates a barcode image with [contents].
     *
     *
     * @param contents The contents to encode in the barcode
     * @param contents The contents to encode in the barcode
     * @param size     The preferred image size in pixels
     * @param size     The preferred image size in pixels
@@ -79,31 +51,36 @@ public final class QrCodeGenerator {
     * @param invert   Whether to invert the black/white pixels (e.g. for dark mode)
     * @param invert   Whether to invert the black/white pixels (e.g. for dark mode)
     * @return Barcode bitmap
     * @return Barcode bitmap
     */
     */
    public static Bitmap encodeQrCode(String contents, int size, int margin, boolean invert)
    @JvmOverloads
            throws WriterException, IllegalArgumentException {
    @JvmStatic
        final Map<EncodeHintType, Object> hints = new HashMap<>();
    @Throws(WriterException::class, IllegalArgumentException::class)
    fun encodeQrCode(
        contents: String,
        size: Int,
        margin: Int = DEFAULT_MARGIN,
        invert: Boolean = false,
    ): Bitmap {
        val hints = EnumMap<EncodeHintType, Any>(EncodeHintType::class.java)
        if (!isIso88591(contents)) {
        if (!isIso88591(contents)) {
            hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
            hints[EncodeHintType.CHARACTER_SET] = StandardCharsets.UTF_8.name()
        }
        }
        if (margin != DEFAULT_MARGIN) {
        if (margin != DEFAULT_MARGIN) {
            hints.put(EncodeHintType.MARGIN, margin);
            hints[EncodeHintType.MARGIN] = margin
        }
        }

        val qrBits = MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, size, size, hints)
        final BitMatrix qrBits = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,
        @ColorInt val setColor = if (invert) Color.WHITE else Color.BLACK
                size, size, hints);
        @ColorInt val unsetColor = if (invert) Color.BLACK else Color.WHITE
        final Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
        @ColorInt val pixels = IntArray(size * size)
        int setColor = invert ? Color.WHITE : Color.BLACK;
        for (x in 0 until size) {
        int unsetColor = invert ? Color.BLACK : Color.WHITE;
            for (y in 0 until size) {
        for (int x = 0; x < size; x++) {
                pixels[x * size + y] = if (qrBits[x, y]) setColor else unsetColor
            for (int y = 0; y < size; y++) {
                bitmap.setPixel(x, y, qrBits.get(x, y) ? setColor : unsetColor);
            }
            }
        }
        }
        return bitmap;
        return Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565).apply {
            setPixels(pixels, 0, size, 0, 0, size, size)
        }
        }

    private static boolean isIso88591(String contents) {
        CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
        return encoder.canEncode(contents);
    }
    }

    private fun isIso88591(contents: String): Boolean =
        StandardCharsets.ISO_8859_1.newEncoder().canEncode(contents)
}
}