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 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");
 * you may not use this file except in compliance with the License.
@@ -14,64 +14,36 @@
 * limitations under the License.
 */

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

import android.graphics.Bitmap;
import android.graphics.Color;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;

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);
    }
import android.annotation.ColorInt
import android.graphics.Bitmap
import android.graphics.Color
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter
import com.google.zxing.WriterException
import java.nio.charset.StandardCharsets
import java.util.EnumMap

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 size     The preferred image size in pixels
     * @param invert   Whether to invert the black/white pixels (e.g. for dark mode)
     * @return Barcode bitmap
     */
    public static Bitmap encodeQrCode(String contents, int size, boolean invert)
            throws WriterException, IllegalArgumentException {
        return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/invert);
    }
    @JvmStatic
    @Throws(WriterException::class, java.lang.IllegalArgumentException::class)
    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 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)
     * @return Barcode bitmap
     */
    public static Bitmap encodeQrCode(String contents, int size, int margin, boolean invert)
            throws WriterException, IllegalArgumentException {
        final Map<EncodeHintType, Object> hints = new HashMap<>();
    @JvmOverloads
    @JvmStatic
    @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)) {
            hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
            hints[EncodeHintType.CHARACTER_SET] = StandardCharsets.UTF_8.name()
        }
        if (margin != DEFAULT_MARGIN) {
            hints.put(EncodeHintType.MARGIN, margin);
            hints[EncodeHintType.MARGIN] = margin
        }

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