1package com.peachpasswords.ui.pairing23import androidx.camera.core.ImageProxy4import com.google.zxing.BinaryBitmap5import com.google.zxing.DecodeHintType6import com.google.zxing.LuminanceSource7import com.google.zxing.PlanarYUVLuminanceSource8import com.google.zxing.common.GlobalHistogramBinarizer9import com.google.zxing.common.HybridBinarizer10import com.google.zxing.qrcode.QRCodeReader111213internal object PairingQrFrame {14 fun decode(image: ImageProxy): String? {15 val plane = image.planes.getOrNull(0) ?: return null16 val buffer = plane.buffer17 val data = ByteArray(buffer.remaining())18 buffer.get(data)19 val width = image.width20 val height = image.height21 val luma = if (plane.rowStride == width) {22 data23 } else {24 ByteArray(width * height).also { packed ->25 for (row in 0 until height) {26 System.arraycopy(27 data,28 row * plane.rowStride,29 packed,30 row * width,31 width,32 )33 }34 }35 }36 val source = planar(luma, width, height)3738 val hints = mapOf(39 DecodeHintType.TRY_HARDER to true,40 DecodeHintType.POSSIBLE_FORMATS to listOf(com.google.zxing.BarcodeFormat.QR_CODE),41 )42 43 44 45 46 val attempts = sequenceOf<() -> BinaryBitmap>(47 { BinaryBitmap(HybridBinarizer(source)) },48 { BinaryBitmap(GlobalHistogramBinarizer(source)) },49 { BinaryBitmap(HybridBinarizer(rotatedCounterClockwise(luma, width, height))) },50 )51 for (attempt in attempts) {52 try {53 return QRCodeReader().decode(attempt(), hints).text54 } catch (_: Exception) {55 56 }57 }58 return null59 }6061 private fun planar(luma: ByteArray, width: Int, height: Int): LuminanceSource =62 PlanarYUVLuminanceSource(luma, width, height, 0, 0, width, height, false)6364 65666768697071 private fun rotatedCounterClockwise(72 luma: ByteArray,73 width: Int,74 height: Int,75 ): LuminanceSource {76 val rotated = ByteArray(width * height)77 for (y in 0 until height) {78 val row = y * width79 for (x in 0 until width) {80 rotated[(width - 1 - x) * height + y] = luma[row + x]81 }82 }83 return planar(rotated, height, width)84 }85}