This commit is contained in:
2025-12-24 22:15:47 +08:00
parent 356154909b
commit 47af32aec2
10 changed files with 236 additions and 0 deletions

View File

@@ -23,6 +23,8 @@ kotlin {
} }
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
explicitApi()
sourceSets { sourceSets {
commonMain.dependencies { commonMain.dependencies {

View File

@@ -0,0 +1,29 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
public class QRMatrix(
public val version: Int,
) {
private val size = 21 + (version - 1) * 4
private val finderTL = 0 to 0
private val finderTR = 0 to size - 7
private val finderBL = size - 7 to 0
private fun drawTimingPatterns(matrix: Array<IntArray>) {
val size = matrix.size
for (col in 8 until size - 8) {
matrix[6][col] = if (col % 2 == 0) 1 else 0
}
for (row in 8 until size - 8) {
matrix[row][6] = if (row % 2 == 0) 1 else 0
}
}
public fun generate() {
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
internal val FINDER_MATRIX = arrayOf(
Array(9) { 0 },
arrayOf(0, 1, 1, 1, 1, 1, 1, 1, 0),
arrayOf(0, 1, 0, 0, 0, 0, 0, 1, 0),
arrayOf(0, 1, 0, 1, 1, 1, 0, 1, 0),
arrayOf(0, 1, 0, 1, 1, 1, 0, 1, 0),
arrayOf(0, 1, 0, 1, 1, 1, 0, 1, 0),
arrayOf(0, 1, 0, 0, 0, 0, 0, 1, 0),
arrayOf(0, 1, 1, 1, 1, 1, 1, 1, 0),
Array(9) { 0 },
)

View File

@@ -0,0 +1,12 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
public enum class ECLevel(public val ecPercent: Int) {
L(7), M(15), Q(25), H(30)
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
internal object QRCodeEncoder {
const val NumericModeIndicator = 0b0100
fun Int.toBits(bitCount: Int): List<Int> {
require(this >= 0)
val bits = ArrayList<Int>(bitCount)
for (i in bitCount - 1 downTo 0) bits.add((this shr i) and 1)
return bits
}
fun encodeNumeric(s: String): IntArray {
val bits = mutableListOf<Int>()
var i = 0
while (i < s.length) {
val chunk = minOf(3, s.length - i)
val num = s.substring(i, i + chunk).toInt()
val bitLen = when (chunk) {
3 -> 10
2 -> 7
else -> 4
}
bits.addAll(num.toBits(bitLen))
i += chunk
}
return bits.toIntArray()
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
/**
* https://en.wikipedia.org/wiki/Finite_field
*/
internal fun gfAdd(a: Int, b: Int) = a xor b
/**
* eq 2 add
*/
internal fun gfMin(a: Int, b: Int) = gfAdd(a, b)
internal fun gfMul(a: Int, b: Int): Int {
if (a == 0 || b == 0) return 0
return EXP[LOG[a] + LOG[b]]
}
internal fun gfDiv(a: Int, b: Int): Int {
require(b != 0)
if (a == 0) return 0
return EXP[(LOG[a] + 255 - LOG[b]) % 255]
}
internal val EXP = IntArray(512)
internal val LOG = IntArray(256)
/**
* init galois field matrix
*/
internal fun initGF() {
var x = 1
for (i in 0 until 255) {
EXP[i] = x
LOG[x] = i
x = x shl 1
if (x and 0x100 != 0) x = x xor 0x11D
}
for (i in 255 until 512) {
EXP[i] = EXP[i - 255]
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
internal typealias Poly = IntArray
internal fun polyMul(a: Poly, b: Poly): Poly {
val result = IntArray(a.size + b.size - 1)
a.indices.forEach { i -> b.forEach { j -> result[i + j] = result[i + j] xor gfMul(a[i], b[j]) } }
return result
}
internal fun polyGenerator(ecLevel: ECLevel): Poly {
var r = intArrayOf(1)
for (i in 0 until ecLevel.ecPercent) {
val term = intArrayOf(1, EXP[i])
r = polyMul(r, term)
}
return r
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
/**
* https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction
*/
internal fun reedSolomonEncode(data: IntArray, ecLevel: ECLevel): IntArray {
val generator = polyGenerator(ecLevel)
val buffer = IntArray(data.size + ecLevel.ecPercent)
for (i in data.indices) buffer[i] = data[i]
for (i in data.indices) {
val factor = buffer[i]
if (factor != 0) for (j in generator.indices) buffer[i + j] = buffer[i + j] xor gfMul(generator[j], factor)
}
/**
* copy ec code part
*/
return buffer.copyOfRange(data.size, buffer.size)
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package cn.rtast.kqrcode
public value class QRCodeVersion(public val size: Int)
public fun Int.asQRCodeVersion(): QRCodeVersion {
require(this in 1..40) { throw IllegalArgumentException("QRCode version must in range 1(included)..40(included)") }
return QRCodeVersion(21 + (this - 1) * 4)
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/12/24
*/
package test
import cn.rtast.kqrcode.FINDER_MATRIX
import kotlin.test.Test
class TestAnchor {
@Test
fun `test anchor println`() {
println(FINDER_MATRIX.map { it.toList().apply { println(it.size) } })
println(FINDER_MATRIX.size)
}
}