CodexAztec.kt

2.7 KB · raw

1package com.peachpasswords.codex23import android.graphics.Bitmap4import android.graphics.Color5import com.google.zxing.BarcodeFormat6import com.google.zxing.BinaryBitmap7import com.google.zxing.DecodeHintType8import com.google.zxing.EncodeHintType9import com.google.zxing.LuminanceSource10import com.google.zxing.RGBLuminanceSource11import com.google.zxing.aztec.AztecReader12import com.google.zxing.aztec.AztecWriter13import com.google.zxing.common.GlobalHistogramBinarizer14import com.google.zxing.common.HybridBinarizer1516/** Aztec encode/decode for one Codex chunk, via ZXing. */17object CodexAztec {1819    /** Render an envelope JSON string into a square monochrome Aztec [Bitmap]. */20    fun encode(content: String, sizePx: Int = 320): Bitmap {21        val hints = mapOf(EncodeHintType.ERROR_CORRECTION to AZTEC_ECC_PERCENT)22        val matrix = AztecWriter().encode(content, BarcodeFormat.AZTEC, sizePx, sizePx, hints)23        val w = matrix.width24        val h = matrix.height25        val pixels = IntArray(w * h)26        for (y in 0 until h) {27            val row = y * w28            for (x in 0 until w) {29                pixels[row + x] = if (matrix[x, y]) Color.BLACK else Color.WHITE30            }31        }32        return Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888).apply { setPixels(pixels, 0, w, 0, 0, w, h) }33    }3435    /**36     * Decode an Aztec code from a tightly-cropped [Bitmap]. A rendered,37     * cropped single code decodes most reliably with PURE_BARCODE (skip the38     * locate-in-scene search); fall back to the full detector for codes that39     * carry surrounding bleed. Null if neither reads.40     */41    fun decode(bitmap: Bitmap): String? {42        val w = bitmap.width43        val h = bitmap.height44        val pixels = IntArray(w * h)45        bitmap.getPixels(pixels, 0, w, 0, 0, w, h)46        val source: LuminanceSource = RGBLuminanceSource(w, h, pixels)47        val pure = mapOf(DecodeHintType.PURE_BARCODE to true, DecodeHintType.TRY_HARDER to true)48        val scene = mapOf(DecodeHintType.TRY_HARDER to true)49        // Cross both binarizers (Hybrid is adaptive; Global handles flat,50        // evenly-lit renders) with both detector modes — marginal codes that51        // miss one combination usually read on another.52        val attempts = listOf(53            BinaryBitmap(HybridBinarizer(source)) to pure,54            BinaryBitmap(HybridBinarizer(source)) to scene,55            BinaryBitmap(GlobalHistogramBinarizer(source)) to pure,56        )57        for ((binarizer, hints) in attempts) {58            try {59                return AztecReader().decode(binarizer, hints).text60            } catch (_: Exception) {61                // try next strategy62            }63        }64        return null65    }66}