diff --git a/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeAnalyzerFactory.kt b/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeAnalyzerFactory.kt index acc2f23f6c..8a1122b18d 100644 --- a/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeAnalyzerFactory.kt +++ b/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeAnalyzerFactory.kt @@ -33,14 +33,14 @@ internal fun createBarcodeAnalyzer(onResult: (String) -> Unit): ImageAnalysis.An return ImageAnalysis.Analyzer { imageProxy -> try { - val buffer: ByteBuffer = imageProxy.planes[0].buffer + val lumaPlane = imageProxy.planes[0] + val buffer: ByteBuffer = lumaPlane.buffer.duplicate() val data = ByteArray(buffer.remaining()) buffer.get(data) val width = imageProxy.width val height = imageProxy.height - - val source = PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false) + val source = PlanarYUVLuminanceSource(data, lumaPlane.rowStride, height, 0, 0, width, height, false) val binaryBitmap = BinaryBitmap(HybridBinarizer(source)) val result = reader.decodeWithState(binaryBitmap) diff --git a/core/barcode/src/testFdroid/kotlin/org/meshtastic/core/barcode/FdroidQrDecoderTest.kt b/core/barcode/src/testFdroid/kotlin/org/meshtastic/core/barcode/FdroidQrDecoderTest.kt new file mode 100644 index 0000000000..09d44135a7 --- /dev/null +++ b/core/barcode/src/testFdroid/kotlin/org/meshtastic/core/barcode/FdroidQrDecoderTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.meshtastic.core.barcode + +import com.google.zxing.BarcodeFormat +import com.google.zxing.BinaryBitmap +import com.google.zxing.MultiFormatReader +import com.google.zxing.PlanarYUVLuminanceSource +import com.google.zxing.common.HybridBinarizer +import com.google.zxing.qrcode.QRCodeWriter +import org.junit.Assert.assertEquals +import org.junit.Test + +class FdroidQrDecoderTest { + + @Test + fun `decodes Apple-compatible channel URL from padded camera plane`() { + val channelUrl = "https://meshtastic.org/e/#CgMSAQESBggBQANIAQ" + val dimension = 177 + val matrix = QRCodeWriter().encode(channelUrl, BarcodeFormat.QR_CODE, dimension, dimension) + val packed = + ByteArray(dimension * dimension) { index -> + if (matrix[index % dimension, index / dimension]) 0 else 0xFF.toByte() + } + val rowStride = dimension + 11 + val padded = ByteArray(rowStride * dimension) { 0x7F } + for (row in 0 until dimension) { + packed.copyInto(padded, row * rowStride, row * dimension, (row + 1) * dimension) + } + + val decoded = + MultiFormatReader() + .decode( + BinaryBitmap( + HybridBinarizer( + PlanarYUVLuminanceSource(padded, rowStride, dimension, 0, 0, dimension, dimension, false), + ), + ), + ) + + assertEquals(channelUrl, decoded.text) + } +}