{
  "lead": "二维码把文字编码成黑白方格，即使被遮挡或弄脏一部分也能读出来，秘诀在于 Reed-Solomon 纠错码。这个示例按照 ISO/IEC 18004 标准从零实现了完整的二维码编码流程：数据编码、纠错码计算、分块交织、模块排布、掩码选择和格式信息，支持全部 40 个版本和 4 个纠错等级。",
  "formulas": [["码字 = 数据 ‖ (数据 · x<sup>n</sup> mod g(x))", "Reed-Solomon：g(x) = ∏(x − α<sup>i</sup>)，在 GF(2⁸) 上计算"]],
  "terms": [
    ["数据编码", "文字先按 UTF-8 转成字节，前面加上 4 位模式标识（0100 = 字节模式）和字节数，结尾补 4 位终止符并对齐到整字节，剩余容量用 <code>0xEC</code>、<code>0x11</code> 交替填满。选择能装下这些数据的最小版本（21×21 到 177×177）。"],
    ["Reed-Solomon 纠错", "把数据看作有限域 GF(2⁸) 上的多项式，除以生成多项式 g(x) 得到的余数就是纠错码字。n 个纠错码字最多能纠正 n/2 个出错的码字。纠错等级 L / M / Q / H 分别能恢复约 7% / 15% / 25% / 30% 的损坏。"],
    ["分块与交织", "较大的版本把数据分成多个块，每块各自计算纠错码，再按列交错排列。这样一块局部污渍损坏的是分散在多个块里的少量码字，每块都还在纠错能力之内。"],
    ["模块排布", "先画出固定图案：三个角上的定位图案、时序线、对齐图案，版本 7 以上还有版本信息；然后把码字比特从右下角开始，两列一组，像蛇一样上下来回填进剩余格子。"],
    ["掩码与评分", "大片同色、类似定位图案的排列会让扫码器误判，所以要用 8 种掩码图案之一与数据区做异或。每种掩码按 4 条规则打分（连续同色、2×2 色块、假定位图案、黑白比例），选分数最低的那个，并把掩码编号和纠错等级写进两份 BCH 编码的格式信息里。"]
  ],
  "terms_notes": ["页面上的柱状图就是 8 种掩码的惩罚分，绿色是最低分，橙色是实际使用的掩码。手动指定掩码后，二维码依然能扫出来，只是可读性可能稍差。"],
  "flow": [
    ["js", "输入框每次变化，JS 调用 <code>qr_encode(文字, 纠错等级, 掩码)</code>（掩码为 −1 表示自动选择）。"],
    ["rust", "Rust 完成编码、纠错、排布和掩码，返回 <code>[边长, 版本, 掩码, 模块…]</code>，每个模块 1 字节（1 = 黑）。<code>qr_penalties()</code> 返回 8 种掩码各自的惩罚分。"],
    ["js", "JS 在四周留出 4 个模块宽的空白（标准要求的「静区」）后按整数倍放大绘制；下载时另外按 10 倍重新绘制成清晰的 PNG。"]
  ],
  "perf": [
    "测试时用独立实现 segno 生成的二维码做对照：400 个随机输入（含中文和 emoji，覆盖 29 个版本、全部纠错等级和掩码）逐个模块完全一致，自动掩码生成的 120 个二维码全部能被 ZXing 解码还原。",
    "对照过程中发现 segno 在比特流恰好对齐字节时会多补一个 0 字节（不符合标准但不影响扫码）；两者自动选掩码的时机也不同：segno 在写入格式信息之前评分，本实现对完成后的整个符号评分（与 Nayuki、ZXing 相同），两种做法生成的二维码都有效。",
    "有限域乘法用逐位移位异或实现，无需查表；即使是 177×177 的版本 40，编码一次也只需几毫秒，其中大部分时间花在 8 次掩码评分上。"
  ],
  "src": [["crates/tools/qrcode/src/lib.rs", ""], ["www/qrcode/index.js", ""]]
}
