package com.peachpasswords.codex import android.graphics.Bitmap import android.graphics.Color import com.google.zxing.BarcodeFormat import com.google.zxing.BinaryBitmap import com.google.zxing.DecodeHintType import com.google.zxing.EncodeHintType import com.google.zxing.LuminanceSource import com.google.zxing.RGBLuminanceSource import com.google.zxing.aztec.AztecReader import com.google.zxing.aztec.AztecWriter import com.google.zxing.common.GlobalHistogramBinarizer import com.google.zxing.common.HybridBinarizer /** Aztec encode/decode for one Codex chunk, via ZXing. */ object CodexAztec { /** Render an envelope JSON string into a square monochrome Aztec [Bitmap]. */ fun encode(content: String, sizePx: Int = 320): Bitmap { val hints = mapOf(EncodeHintType.ERROR_CORRECTION to AZTEC_ECC_PERCENT) val matrix = AztecWriter().encode(content, BarcodeFormat.AZTEC, sizePx, sizePx, hints) val w = matrix.width val h = matrix.height val pixels = IntArray(w * h) for (y in 0 until h) { val row = y * w for (x in 0 until w) { pixels[row + x] = if (matrix[x, y]) Color.BLACK else Color.WHITE } } return Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888).apply { setPixels(pixels, 0, w, 0, 0, w, h) } } /** * Decode an Aztec code from a tightly-cropped [Bitmap]. A rendered, * cropped single code decodes most reliably with PURE_BARCODE (skip the * locate-in-scene search); fall back to the full detector for codes that * carry surrounding bleed. Null if neither reads. */ fun decode(bitmap: Bitmap): String? { val w = bitmap.width val h = bitmap.height val pixels = IntArray(w * h) bitmap.getPixels(pixels, 0, w, 0, 0, w, h) val source: LuminanceSource = RGBLuminanceSource(w, h, pixels) val pure = mapOf(DecodeHintType.PURE_BARCODE to true, DecodeHintType.TRY_HARDER to true) val scene = mapOf(DecodeHintType.TRY_HARDER to true) // Cross both binarizers (Hybrid is adaptive; Global handles flat, // evenly-lit renders) with both detector modes — marginal codes that // miss one combination usually read on another. val attempts = listOf( BinaryBitmap(HybridBinarizer(source)) to pure, BinaryBitmap(HybridBinarizer(source)) to scene, BinaryBitmap(GlobalHistogramBinarizer(source)) to pure, ) for ((binarizer, hints) in attempts) { try { return AztecReader().decode(binarizer, hints).text } catch (_: Exception) { // try next strategy } } return null } }