Как сгенерировать qr-код с наложением изображения в Gluon?

Я хочу сгенерировать QR-код, который должен поместить логотип в центр. Я проверил библиотеку zxing и сделал это с приложением Java, прочитав этот код (Как сгенерировать QR-код с логотипом внутри?). Но, насколько я знаю, мы не можем использовать свинг в android/ios. Итак, как с этим справиться?


person Sovandara LENG    schedule 11.02.2019    source источник
comment
Что ты имеешь в виду? Я не понимаю.   -  person Sovandara LENG    schedule 12.02.2019
comment
Если у вас есть BufferedImage, вы можете преобразовать его в изображение JavaFX, используя ссылку, которую я разместил в своем комментарии выше.   -  person ItachiUchiha    schedule 12.02.2019
comment
Итак, когда я генерирую штрих-код с помощью WritableImage и PixelWriter, как я могу преобразовать его в буферизованное изображение? Потому что мне нужно bufferImage для комбинирования наложения со штрих-кодом   -  person Sovandara LENG    schedule 12.02.2019
comment
Разве у вас нет BufferedImage после наложения в соответствии с вашей ссылкой Generate QR в вопросе?   -  person ItachiUchiha    schedule 12.02.2019
comment
Когда я генерирую QR, я использовал другой подход, поэтому я получил WritableImage (вы меняете эту ссылку stackoverflow.com/questions/ 54445369/). Поэтому, когда мне нужно добавить наложение, мне нужен тип bufferedImage.   -  person Sovandara LENG    schedule 12.02.2019


Ответы (2)


На основании ссылки, на которую вы ссылаетесь, принятый ответ указал на этот сообщение, где вы можете видеть, что трюк для создания QR, который позволяет скрыть его центральную часть, не влияя на его читаемость сканером QR, может быть выполнен путем увеличения ошибки уровень:

30% (H) исправления ошибок, где исправление ошибок уровня H должно привести к QRCode, который все еще действителен, даже если он скрыт на 30%

В качестве продолжения этого вопрос, вы можете просто включить подсказку при кодировании текста:

public Image generateQR(int width, int height) {
    File root = Services.get(StorageService.class)
            .flatMap(StorageService::getPrivateStorage)
            .orElseThrow(() -> new RuntimeException("Storage Service not found"));

    String uuid = Services.get(DeviceService.class)
            .map(DeviceService::getUuid)
            .orElse("123456789");

    MultiFormatWriter codeWriter = new MultiFormatWriter();

    // Add maximum correction
    Map<EncodeHintType, ErrorCorrectionLevel> hints = new HashMap<>();
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

    try {
        BarcodeFormat format = BarcodeFormat.QR_CODE;
        BitMatrix bitMatrix = codeWriter.encode(uuid, format, 
               width, height, hints);  // <--- add hints
        ...
        return writableImage;
    }
}

Вы получите изображение QR-кода без наложения.

Следующим шагом является объединение этого изображения с изображением вашего логотипа без использования Swing. Но на самом деле нам этого делать не нужно: мы можем просто использовать два узла ImageView!

ImageView imageView = new ImageView();
imageView.setFitWidth(256);
imageView.setFitHeight(256);
imageView.setImage(service.generateQR(256, 256));

ImageView overlay = new ImageView(
     new Image(getClass().getResourceAsStream("/icon.png")));
overlay.setFitWidth(80);
overlay.setFitHeight(80);

StackPane combinedQR = new StackPane(imageView, overlay);
...

Полученный код по-прежнему читабелен.

Вы также можете поиграть с результатом, добавив к изображениям эффект смешивания, например:

overlay.setBlendMode(BlendMode.ADD);

но это будет зависеть от того, как изображение вашего логотипа сочетается с QR.

Наконец, если вам все еще нужно одно комбинированное изображение, вы можете создать снимок панели стека:

WritableImage snapshot = combinedQR.snapshot(new SnapshotParameters(), null);
person José Pereda    schedule 13.02.2019
comment
Он работает идеально. Пожалуйста, объясните мне, как вы нашли решение. Это может помочь мне в будущем. Спасибо заранее - person Sovandara LENG; 13.02.2019
comment
Я отредактировал свой ответ, ссылаясь на пост, в котором объяснялось использование подсказок. - person José Pereda; 13.02.2019
comment
Для намеков, я понимаю. но я хочу знать, почему вы знаете, как использовать два ImageView. Я пытаюсь решить эту проблему, пытаясь найти способ рисования изображения независимо от Swing (подумайте о WritableImage и PixelWriter и т. д.). Я также пытаюсь использовать BufferedImage. Но не удача - person Sovandara LENG; 13.02.2019
comment
С помощью JavaFX вы можете сделать наложение с любым количеством узлов и StackPane, так как они складываются в кучу, сохраняя их по центру, поэтому, если вам нужно скомпоновать два изображения, естественным решением является использование двух узлов ImageView в StackPane. Swing вообще не нужен. Имеет ли это смысл? - person José Pereda; 13.02.2019
comment
Ага, большое спасибо за объяснение - person Sovandara LENG; 13.02.2019

Вдохновленный ответом Хосе, я написал собственное решение на Kotlin для устройств Android. Во-первых, добавьте ZXing в свой проект:

implementation "com.google.zxing:core:3.4.0"

Создайте фиктивный рисунок и поместите его в свою папку res/drawable:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/colorAccent" />
    <size
        android:width="48dp"
        android:height="48dp" />
</shape>

Затем добавьте в проект следующие расширения:

String.encodeAsQrCodeBitmap преобразует строку в растровое изображение QR-кода. Он также принимает overlayBitmap, если ноль, то только строка преобразуется в QR-код, и вы можете настроить цвета QR-кода.

Bitmap.addOverlayToCenter объединяет два растровых изображения в одно и помещает наложенное растровое изображение в центр.

Int.dpToPx() преобразует DP в пиксели.

@Throws(WriterException::class)
fun String.encodeAsQrCodeBitmap(
    dimension: Int,
    overlayBitmap: Bitmap? = null,
    @ColorInt color1: Int = Color.BLACK,
    @ColorInt color2: Int = Color.WHITE
): Bitmap? {

    val result: BitMatrix
    try {
        result = MultiFormatWriter().encode(
            this,
            BarcodeFormat.QR_CODE,
            dimension,
            dimension,
            hashMapOf(EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.H)
        )
    } catch (e: IllegalArgumentException) {
        // Unsupported format
        return null
    }

    val w = result.width
    val h = result.height
    val pixels = IntArray(w * h)
    for (y in 0 until h) {
        val offset = y * w
        for (x in 0 until w) {
            pixels[offset + x] = if (result.get(x, y)) color1 else color2
        }
    }
    val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
    bitmap.setPixels(pixels, 0, dimension, 0, 0, w, h)

    return if (overlayBitmap != null) {
        bitmap.addOverlayToCenter(overlayBitmap)
    } else {
        bitmap
    }
}

fun Bitmap.addOverlayToCenter(overlayBitmap: Bitmap): Bitmap {

    val bitmap2Width = overlayBitmap.width
    val bitmap2Height = overlayBitmap.height
    val marginLeft = (this.width * 0.5 - bitmap2Width * 0.5).toFloat()
    val marginTop = (this.height * 0.5 - bitmap2Height * 0.5).toFloat()
    val canvas = Canvas(this)
    canvas.drawBitmap(this, Matrix(), null)
    canvas.drawBitmap(overlayBitmap, marginLeft, marginTop, null)
    return this
}

fun Int.dpToPx(): Int {
    return (this * Resources.getSystem().displayMetrics.density).toInt()
}

Затем выполните в своем фрагменте/действии следующее: получите наложение из ресурсов, преобразуйте строку в QR-код и отобразите ее в ImageView:

try {
     val displayMetrics = DisplayMetrics()
     activity?.windowManager?.defaultDisplay?.getMetrics(displayMetrics)

     val size = displayMetrics.widthPixels.coerceAtMost(displayMetrics.heightPixels)

     val overlay = ContextCompat.getDrawable(requireContext(), R.drawable.dummy_round)
                ?.toBitmap(72.dpToPx(), 72.dpToPx())

     val bitmap = "https://www.example.com".encodeAsQrCodeBitmap(size, overlay)

     imageView.setImageBitmap(bitmap)
} catch (e: Exception) {
      // handle Errors here
}

Результат:

введите здесь описание изображения

person Paul Spiesberger    schedule 28.11.2019