1469 lines
53 KiB
GDScript
1469 lines
53 KiB
GDScript
extends RefCounted
|
||
|
||
const BitStream := preload("res://addons/kenyoni/qr_code/bit_stream.gd")
|
||
const ReedSolomon := preload("res://addons/kenyoni/qr_code/reed_solomon.gd")
|
||
const ShiftJIS := preload("res://addons/kenyoni/qr_code/shift_jis.gd")
|
||
|
||
## Encoding Mode
|
||
enum Mode {
|
||
## 0001
|
||
NUMERIC = 1,
|
||
## 0010
|
||
ALPHANUMERIC = 2,
|
||
## 0100
|
||
BYTE = 4,
|
||
## 1000
|
||
KANJI = 8
|
||
}
|
||
|
||
## Error Correction
|
||
enum ErrorCorrection {
|
||
LOW = 1,
|
||
MEDIUM = 0,
|
||
QUARTILE = 3,
|
||
HIGH = 2
|
||
}
|
||
|
||
## Extended Channel Interpretation
|
||
enum ECI {
|
||
CODE_PAGE_437 = 2,
|
||
ISO_8859_1 = 3,
|
||
ISO_8859_2 = 4,
|
||
ISO_8859_3 = 5,
|
||
ISO_8859_4 = 6,
|
||
ISO_8859_5 = 7,
|
||
ISO_8859_6 = 8,
|
||
ISO_8859_7 = 9,
|
||
ISO_8859_8 = 10,
|
||
ISO_8859_9 = 11,
|
||
ISO_8859_10 = 12,
|
||
ISO_8859_11 = 13,
|
||
ISO_8859_12 = 14,
|
||
ISO_8859_13 = 15,
|
||
ISO_8859_14 = 16,
|
||
ISO_8859_15 = 17,
|
||
ISO_8859_16 = 18,
|
||
SHIFT_JIS = 20,
|
||
WINDOWS_1250 = 21,
|
||
WINDOWS_1251 = 22,
|
||
WINDOWS_1252 = 23,
|
||
WINDOWS_1256 = 24,
|
||
UTF_16 = 25,
|
||
UTF_8 = 26,
|
||
US_ASCII = 27,
|
||
BIG_5 = 28,
|
||
GB_18030 = 29,
|
||
EUC_KR = 30
|
||
}
|
||
|
||
const _DATA_CAPACITY: Array[Dictionary] = [
|
||
# 1
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 41, Mode.ALPHANUMERIC: 25, Mode.BYTE: 17, Mode.KANJI: 10},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 34, Mode.ALPHANUMERIC: 20, Mode.BYTE: 14, Mode.KANJI: 8},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 27, Mode.ALPHANUMERIC: 16, Mode.BYTE: 11, Mode.KANJI: 7},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 17, Mode.ALPHANUMERIC: 10, Mode.BYTE: 7, Mode.KANJI: 4},
|
||
},
|
||
# 2
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 77, Mode.ALPHANUMERIC: 47, Mode.BYTE: 32, Mode.KANJI: 20},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 63, Mode.ALPHANUMERIC: 38, Mode.BYTE: 26, Mode.KANJI: 16},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 48, Mode.ALPHANUMERIC: 29, Mode.BYTE: 20, Mode.KANJI: 12},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 34, Mode.ALPHANUMERIC: 20, Mode.BYTE: 14, Mode.KANJI: 8},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 127, Mode.ALPHANUMERIC: 77, Mode.BYTE: 53, Mode.KANJI: 32},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 101, Mode.ALPHANUMERIC: 61, Mode.BYTE: 42, Mode.KANJI: 26},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 77, Mode.ALPHANUMERIC: 47, Mode.BYTE: 32, Mode.KANJI: 20},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 58, Mode.ALPHANUMERIC: 35, Mode.BYTE: 24, Mode.KANJI: 15},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 187, Mode.ALPHANUMERIC: 114, Mode.BYTE: 78, Mode.KANJI: 48},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 149, Mode.ALPHANUMERIC: 90, Mode.BYTE: 62, Mode.KANJI: 38},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 111, Mode.ALPHANUMERIC: 67, Mode.BYTE: 46, Mode.KANJI: 28},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 82, Mode.ALPHANUMERIC: 50, Mode.BYTE: 34, Mode.KANJI: 21},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 255, Mode.ALPHANUMERIC: 154, Mode.BYTE: 106, Mode.KANJI: 65},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 202, Mode.ALPHANUMERIC: 122, Mode.BYTE: 84, Mode.KANJI: 52},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 144, Mode.ALPHANUMERIC: 87, Mode.BYTE: 60, Mode.KANJI: 37},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 106, Mode.ALPHANUMERIC: 64, Mode.BYTE: 44, Mode.KANJI: 27},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 322, Mode.ALPHANUMERIC: 195, Mode.BYTE: 134, Mode.KANJI: 82},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 255, Mode.ALPHANUMERIC: 154, Mode.BYTE: 106, Mode.KANJI: 65},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 178, Mode.ALPHANUMERIC: 108, Mode.BYTE: 74, Mode.KANJI: 45},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 139, Mode.ALPHANUMERIC: 84, Mode.BYTE: 58, Mode.KANJI: 36},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 370, Mode.ALPHANUMERIC: 224, Mode.BYTE: 154, Mode.KANJI: 95},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 293, Mode.ALPHANUMERIC: 178, Mode.BYTE: 122, Mode.KANJI: 75},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 207, Mode.ALPHANUMERIC: 125, Mode.BYTE: 86, Mode.KANJI: 53},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 154, Mode.ALPHANUMERIC: 93, Mode.BYTE: 64, Mode.KANJI: 39},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 461, Mode.ALPHANUMERIC: 279, Mode.BYTE: 192, Mode.KANJI: 118},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 365, Mode.ALPHANUMERIC: 221, Mode.BYTE: 152, Mode.KANJI: 93},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 259, Mode.ALPHANUMERIC: 157, Mode.BYTE: 108, Mode.KANJI: 66},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 202, Mode.ALPHANUMERIC: 122, Mode.BYTE: 84, Mode.KANJI: 52},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 552, Mode.ALPHANUMERIC: 335, Mode.BYTE: 230, Mode.KANJI: 141},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 432, Mode.ALPHANUMERIC: 262, Mode.BYTE: 180, Mode.KANJI: 111},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 312, Mode.ALPHANUMERIC: 189, Mode.BYTE: 130, Mode.KANJI: 80},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 235, Mode.ALPHANUMERIC: 143, Mode.BYTE: 98, Mode.KANJI: 60},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 652, Mode.ALPHANUMERIC: 395, Mode.BYTE: 271, Mode.KANJI: 167},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 513, Mode.ALPHANUMERIC: 311, Mode.BYTE: 213, Mode.KANJI: 131},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 364, Mode.ALPHANUMERIC: 221, Mode.BYTE: 151, Mode.KANJI: 93},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 288, Mode.ALPHANUMERIC: 174, Mode.BYTE: 119, Mode.KANJI: 74},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 772, Mode.ALPHANUMERIC: 468, Mode.BYTE: 321, Mode.KANJI: 198},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 604, Mode.ALPHANUMERIC: 366, Mode.BYTE: 251, Mode.KANJI: 155},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 427, Mode.ALPHANUMERIC: 259, Mode.BYTE: 177, Mode.KANJI: 109},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 331, Mode.ALPHANUMERIC: 200, Mode.BYTE: 137, Mode.KANJI: 85},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 883, Mode.ALPHANUMERIC: 535, Mode.BYTE: 367, Mode.KANJI: 226},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 691, Mode.ALPHANUMERIC: 419, Mode.BYTE: 287, Mode.KANJI: 177},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 489, Mode.ALPHANUMERIC: 296, Mode.BYTE: 203, Mode.KANJI: 125},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 374, Mode.ALPHANUMERIC: 227, Mode.BYTE: 155, Mode.KANJI: 96},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1022, Mode.ALPHANUMERIC: 619, Mode.BYTE: 425, Mode.KANJI: 262},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 796, Mode.ALPHANUMERIC: 483, Mode.BYTE: 331, Mode.KANJI: 204},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 580, Mode.ALPHANUMERIC: 352, Mode.BYTE: 241, Mode.KANJI: 149},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 427, Mode.ALPHANUMERIC: 259, Mode.BYTE: 177, Mode.KANJI: 109},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1101, Mode.ALPHANUMERIC: 667, Mode.BYTE: 458, Mode.KANJI: 282},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 871, Mode.ALPHANUMERIC: 528, Mode.BYTE: 362, Mode.KANJI: 223},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 621, Mode.ALPHANUMERIC: 376, Mode.BYTE: 258, Mode.KANJI: 159},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 468, Mode.ALPHANUMERIC: 283, Mode.BYTE: 194, Mode.KANJI: 120},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1250, Mode.ALPHANUMERIC: 758, Mode.BYTE: 520, Mode.KANJI: 320},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 991, Mode.ALPHANUMERIC: 600, Mode.BYTE: 412, Mode.KANJI: 254},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 703, Mode.ALPHANUMERIC: 426, Mode.BYTE: 292, Mode.KANJI: 180},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 530, Mode.ALPHANUMERIC: 321, Mode.BYTE: 220, Mode.KANJI: 136},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1408, Mode.ALPHANUMERIC: 854, Mode.BYTE: 586, Mode.KANJI: 361},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1082, Mode.ALPHANUMERIC: 656, Mode.BYTE: 450, Mode.KANJI: 277},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 775, Mode.ALPHANUMERIC: 470, Mode.BYTE: 322, Mode.KANJI: 198},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 602, Mode.ALPHANUMERIC: 365, Mode.BYTE: 250, Mode.KANJI: 154},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1548, Mode.ALPHANUMERIC: 938, Mode.BYTE: 644, Mode.KANJI: 397},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1212, Mode.ALPHANUMERIC: 734, Mode.BYTE: 504, Mode.KANJI: 310},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 876, Mode.ALPHANUMERIC: 531, Mode.BYTE: 364, Mode.KANJI: 224},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 674, Mode.ALPHANUMERIC: 408, Mode.BYTE: 280, Mode.KANJI: 173},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1725, Mode.ALPHANUMERIC: 1046, Mode.BYTE: 718, Mode.KANJI: 442},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1346, Mode.ALPHANUMERIC: 816, Mode.BYTE: 560, Mode.KANJI: 345},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 948, Mode.ALPHANUMERIC: 574, Mode.BYTE: 394, Mode.KANJI: 243},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 746, Mode.ALPHANUMERIC: 452, Mode.BYTE: 310, Mode.KANJI: 191},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 1903, Mode.ALPHANUMERIC: 1153, Mode.BYTE: 792, Mode.KANJI: 488},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1500, Mode.ALPHANUMERIC: 909, Mode.BYTE: 624, Mode.KANJI: 384},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1063, Mode.ALPHANUMERIC: 644, Mode.BYTE: 442, Mode.KANJI: 272},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 813, Mode.ALPHANUMERIC: 493, Mode.BYTE: 338, Mode.KANJI: 208},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 2061, Mode.ALPHANUMERIC: 1249, Mode.BYTE: 858, Mode.KANJI: 528},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1600, Mode.ALPHANUMERIC: 970, Mode.BYTE: 666, Mode.KANJI: 410},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1159, Mode.ALPHANUMERIC: 702, Mode.BYTE: 482, Mode.KANJI: 297},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 919, Mode.ALPHANUMERIC: 557, Mode.BYTE: 382, Mode.KANJI: 235},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 2232, Mode.ALPHANUMERIC: 1352, Mode.BYTE: 929, Mode.KANJI: 572},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1708, Mode.ALPHANUMERIC: 1035, Mode.BYTE: 711, Mode.KANJI: 438},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1224, Mode.ALPHANUMERIC: 742, Mode.BYTE: 509, Mode.KANJI: 314},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 969, Mode.ALPHANUMERIC: 587, Mode.BYTE: 403, Mode.KANJI: 248},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 2409, Mode.ALPHANUMERIC: 1460, Mode.BYTE: 1003, Mode.KANJI: 618},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 1872, Mode.ALPHANUMERIC: 1134, Mode.BYTE: 779, Mode.KANJI: 480},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1358, Mode.ALPHANUMERIC: 823, Mode.BYTE: 565, Mode.KANJI: 348},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1056, Mode.ALPHANUMERIC: 640, Mode.BYTE: 439, Mode.KANJI: 270},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 2620, Mode.ALPHANUMERIC: 1588, Mode.BYTE: 1091, Mode.KANJI: 672},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2059, Mode.ALPHANUMERIC: 1248, Mode.BYTE: 857, Mode.KANJI: 528},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1468, Mode.ALPHANUMERIC: 890, Mode.BYTE: 611, Mode.KANJI: 376},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1108, Mode.ALPHANUMERIC: 672, Mode.BYTE: 461, Mode.KANJI: 284},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 2812, Mode.ALPHANUMERIC: 1704, Mode.BYTE: 1171, Mode.KANJI: 721},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2188, Mode.ALPHANUMERIC: 1326, Mode.BYTE: 911, Mode.KANJI: 561},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1588, Mode.ALPHANUMERIC: 963, Mode.BYTE: 661, Mode.KANJI: 407},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1228, Mode.ALPHANUMERIC: 744, Mode.BYTE: 511, Mode.KANJI: 315},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 3057, Mode.ALPHANUMERIC: 1853, Mode.BYTE: 1273, Mode.KANJI: 784},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2395, Mode.ALPHANUMERIC: 1451, Mode.BYTE: 997, Mode.KANJI: 614},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1718, Mode.ALPHANUMERIC: 1041, Mode.BYTE: 715, Mode.KANJI: 440},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1286, Mode.ALPHANUMERIC: 779, Mode.BYTE: 535, Mode.KANJI: 330},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 3283, Mode.ALPHANUMERIC: 1990, Mode.BYTE: 1367, Mode.KANJI: 842},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2544, Mode.ALPHANUMERIC: 1542, Mode.BYTE: 1059, Mode.KANJI: 652},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1804, Mode.ALPHANUMERIC: 1094, Mode.BYTE: 751, Mode.KANJI: 462},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1425, Mode.ALPHANUMERIC: 864, Mode.BYTE: 593, Mode.KANJI: 365},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 3514, Mode.ALPHANUMERIC: 2132, Mode.BYTE: 1465, Mode.KANJI: 902},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2701, Mode.ALPHANUMERIC: 1637, Mode.BYTE: 1125, Mode.KANJI: 692},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 1933, Mode.ALPHANUMERIC: 1172, Mode.BYTE: 805, Mode.KANJI: 496},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1501, Mode.ALPHANUMERIC: 910, Mode.BYTE: 625, Mode.KANJI: 385},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 3669, Mode.ALPHANUMERIC: 2223, Mode.BYTE: 1528, Mode.KANJI: 940},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 2857, Mode.ALPHANUMERIC: 1732, Mode.BYTE: 1190, Mode.KANJI: 732},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2085, Mode.ALPHANUMERIC: 1263, Mode.BYTE: 868, Mode.KANJI: 534},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1581, Mode.ALPHANUMERIC: 958, Mode.BYTE: 658, Mode.KANJI: 405},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 3909, Mode.ALPHANUMERIC: 2369, Mode.BYTE: 1628, Mode.KANJI: 1002},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 3035, Mode.ALPHANUMERIC: 1839, Mode.BYTE: 1264, Mode.KANJI: 778},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2181, Mode.ALPHANUMERIC: 1322, Mode.BYTE: 908, Mode.KANJI: 559},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1677, Mode.ALPHANUMERIC: 1016, Mode.BYTE: 698, Mode.KANJI: 430},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 4158, Mode.ALPHANUMERIC: 2520, Mode.BYTE: 1732, Mode.KANJI: 1066},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 3289, Mode.ALPHANUMERIC: 1994, Mode.BYTE: 1370, Mode.KANJI: 843},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2358, Mode.ALPHANUMERIC: 1429, Mode.BYTE: 982, Mode.KANJI: 604},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1782, Mode.ALPHANUMERIC: 1080, Mode.BYTE: 742, Mode.KANJI: 457},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 4417, Mode.ALPHANUMERIC: 2677, Mode.BYTE: 1840, Mode.KANJI: 1132},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 3486, Mode.ALPHANUMERIC: 2113, Mode.BYTE: 1452, Mode.KANJI: 894},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2473, Mode.ALPHANUMERIC: 1499, Mode.BYTE: 1030, Mode.KANJI: 634},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 1897, Mode.ALPHANUMERIC: 1150, Mode.BYTE: 790, Mode.KANJI: 486},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 4686, Mode.ALPHANUMERIC: 2840, Mode.BYTE: 1952, Mode.KANJI: 1201},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 3693, Mode.ALPHANUMERIC: 2238, Mode.BYTE: 1538, Mode.KANJI: 947},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2670, Mode.ALPHANUMERIC: 1618, Mode.BYTE: 1112, Mode.KANJI: 684},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2022, Mode.ALPHANUMERIC: 1226, Mode.BYTE: 842, Mode.KANJI: 518},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 4965, Mode.ALPHANUMERIC: 3009, Mode.BYTE: 2068, Mode.KANJI: 1273},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 3909, Mode.ALPHANUMERIC: 2369, Mode.BYTE: 1628, Mode.KANJI: 1002},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2805, Mode.ALPHANUMERIC: 1700, Mode.BYTE: 1168, Mode.KANJI: 719},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2157, Mode.ALPHANUMERIC: 1307, Mode.BYTE: 898, Mode.KANJI: 553},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 5253, Mode.ALPHANUMERIC: 3183, Mode.BYTE: 2188, Mode.KANJI: 1347},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 4134, Mode.ALPHANUMERIC: 2506, Mode.BYTE: 1722, Mode.KANJI: 1060},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 2949, Mode.ALPHANUMERIC: 1787, Mode.BYTE: 1228, Mode.KANJI: 756},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2301, Mode.ALPHANUMERIC: 1394, Mode.BYTE: 958, Mode.KANJI: 590},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 5529, Mode.ALPHANUMERIC: 3351, Mode.BYTE: 2303, Mode.KANJI: 1417},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 4343, Mode.ALPHANUMERIC: 2632, Mode.BYTE: 1809, Mode.KANJI: 1113},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3081, Mode.ALPHANUMERIC: 1867, Mode.BYTE: 1283, Mode.KANJI: 790},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2361, Mode.ALPHANUMERIC: 1431, Mode.BYTE: 983, Mode.KANJI: 605},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 5836, Mode.ALPHANUMERIC: 3537, Mode.BYTE: 2431, Mode.KANJI: 1496},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 4588, Mode.ALPHANUMERIC: 2780, Mode.BYTE: 1911, Mode.KANJI: 1176},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3244, Mode.ALPHANUMERIC: 1966, Mode.BYTE: 1351, Mode.KANJI: 832},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2524, Mode.ALPHANUMERIC: 1530, Mode.BYTE: 1051, Mode.KANJI: 647},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 6153, Mode.ALPHANUMERIC: 3729, Mode.BYTE: 2563, Mode.KANJI: 1577},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 4775, Mode.ALPHANUMERIC: 2894, Mode.BYTE: 1989, Mode.KANJI: 1224},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3417, Mode.ALPHANUMERIC: 2071, Mode.BYTE: 1423, Mode.KANJI: 876},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2625, Mode.ALPHANUMERIC: 1591, Mode.BYTE: 1093, Mode.KANJI: 673},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 6479, Mode.ALPHANUMERIC: 3927, Mode.BYTE: 2699, Mode.KANJI: 1661},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 5039, Mode.ALPHANUMERIC: 3054, Mode.BYTE: 2099, Mode.KANJI: 1292},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3599, Mode.ALPHANUMERIC: 2181, Mode.BYTE: 1499, Mode.KANJI: 923},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2735, Mode.ALPHANUMERIC: 1658, Mode.BYTE: 1139, Mode.KANJI: 701},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 6743, Mode.ALPHANUMERIC: 4087, Mode.BYTE: 2809, Mode.KANJI: 1729},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 5313, Mode.ALPHANUMERIC: 3220, Mode.BYTE: 2213, Mode.KANJI: 1362},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3791, Mode.ALPHANUMERIC: 2298, Mode.BYTE: 1579, Mode.KANJI: 972},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 2927, Mode.ALPHANUMERIC: 1774, Mode.BYTE: 1219, Mode.KANJI: 750},
|
||
},
|
||
{
|
||
ErrorCorrection.LOW: {Mode.NUMERIC: 7089, Mode.ALPHANUMERIC: 4296, Mode.BYTE: 2953, Mode.KANJI: 1817},
|
||
ErrorCorrection.MEDIUM: {Mode.NUMERIC: 5596, Mode.ALPHANUMERIC: 3391, Mode.BYTE: 2331, Mode.KANJI: 1435},
|
||
ErrorCorrection.QUARTILE: {Mode.NUMERIC: 3993, Mode.ALPHANUMERIC: 2420, Mode.BYTE: 1663, Mode.KANJI: 1024},
|
||
ErrorCorrection.HIGH: {Mode.NUMERIC: 3057, Mode.ALPHANUMERIC: 1852, Mode.BYTE: 1273, Mode.KANJI: 784}
|
||
},
|
||
]
|
||
|
||
const _ALPHANUMERIC_CHARACTERS: Dictionary[String, int] = {
|
||
"0": 0,
|
||
"1": 1,
|
||
"2": 2,
|
||
"3": 3,
|
||
"4": 4,
|
||
"5": 5,
|
||
"6": 6,
|
||
"7": 7,
|
||
"8": 8,
|
||
"9": 9,
|
||
"A": 10,
|
||
"B": 11,
|
||
"C": 12,
|
||
"D": 13,
|
||
"E": 14,
|
||
"F": 15,
|
||
"G": 16,
|
||
"H": 17,
|
||
"I": 18,
|
||
"J": 19,
|
||
"K": 20,
|
||
"L": 21,
|
||
"M": 22,
|
||
"N": 23,
|
||
"O": 24,
|
||
"P": 25,
|
||
"Q": 26,
|
||
"R": 27,
|
||
"S": 28,
|
||
"T": 29,
|
||
"U": 30,
|
||
"V": 31,
|
||
"W": 32,
|
||
"X": 33,
|
||
"Y": 34,
|
||
"Z": 35,
|
||
" ": 36,
|
||
"$": 37,
|
||
"%": 38,
|
||
"*": 39,
|
||
"+": 40,
|
||
"-": 41,
|
||
".": 42,
|
||
"/": 43,
|
||
":": 44,
|
||
}
|
||
|
||
## https://www.thonky.com/qr-code-tutorial/error-correction-table
|
||
## [total data codewords, EC codewords per block, number of blocks in group 1, number of data codewords in group 1 blocks, number of blocks in group 2, number of data codewords in group 2 blocks]
|
||
const _ERROR_CORRECTION: Array[Dictionary] = [
|
||
# 1
|
||
{
|
||
ErrorCorrection.LOW: [19, 7, 1, 19, 0, 0],
|
||
ErrorCorrection.MEDIUM: [16, 10, 1, 16, 0, 0],
|
||
ErrorCorrection.QUARTILE: [13, 13, 1, 13, 0, 0],
|
||
ErrorCorrection.HIGH: [9, 17, 1, 9, 0, 0],
|
||
},
|
||
# 2
|
||
{
|
||
ErrorCorrection.LOW: [34, 10, 1, 34, 0, 0],
|
||
ErrorCorrection.MEDIUM: [28, 16, 1, 28, 0, 0],
|
||
ErrorCorrection.QUARTILE: [22, 22, 1, 22, 0, 0],
|
||
ErrorCorrection.HIGH: [16, 28, 1, 16, 0, 0],
|
||
},
|
||
# 3
|
||
{
|
||
ErrorCorrection.LOW: [55, 15, 1, 55, 0, 0],
|
||
ErrorCorrection.MEDIUM: [44, 26, 1, 44, 0, 0],
|
||
ErrorCorrection.QUARTILE: [34, 18, 2, 17, 0, 0],
|
||
ErrorCorrection.HIGH: [26, 22, 2, 13, 0, 0],
|
||
},
|
||
# 4
|
||
{
|
||
ErrorCorrection.LOW: [80, 20, 1, 80, 0, 0],
|
||
ErrorCorrection.MEDIUM: [64, 18, 2, 32, 0, 0],
|
||
ErrorCorrection.QUARTILE: [48, 26, 2, 24, 0, 0],
|
||
ErrorCorrection.HIGH: [36, 16, 4, 9, 0, 0],
|
||
},
|
||
# 5
|
||
{
|
||
ErrorCorrection.LOW: [108, 26, 1, 108, 0, 0],
|
||
ErrorCorrection.MEDIUM: [86, 24, 2, 43, 0, 0],
|
||
ErrorCorrection.QUARTILE: [62, 18, 2, 15, 2, 16],
|
||
ErrorCorrection.HIGH: [46, 22, 2, 11, 2, 12],
|
||
},
|
||
# 6
|
||
{
|
||
ErrorCorrection.LOW: [136, 18, 2, 68, 0, 0],
|
||
ErrorCorrection.MEDIUM: [108, 16, 4, 27, 0, 0],
|
||
ErrorCorrection.QUARTILE: [76, 24, 4, 19, 0, 0],
|
||
ErrorCorrection.HIGH: [60, 28, 4, 15, 0, 0],
|
||
},
|
||
# 7
|
||
{
|
||
ErrorCorrection.LOW: [156, 20, 2, 78, 0, 0],
|
||
ErrorCorrection.MEDIUM: [124, 18, 4, 31, 0, 0],
|
||
ErrorCorrection.QUARTILE: [88, 18, 2, 14, 4, 15],
|
||
ErrorCorrection.HIGH: [66, 26, 4, 13, 1, 14],
|
||
},
|
||
# 8
|
||
{
|
||
ErrorCorrection.LOW: [194, 24, 2, 97, 0, 0],
|
||
ErrorCorrection.MEDIUM: [154, 22, 2, 38, 2, 39],
|
||
ErrorCorrection.QUARTILE: [110, 22, 4, 18, 2, 19],
|
||
ErrorCorrection.HIGH: [86, 26, 4, 14, 2, 15],
|
||
},
|
||
# 9
|
||
{
|
||
ErrorCorrection.LOW: [232, 30, 2, 116, 0, 0],
|
||
ErrorCorrection.MEDIUM: [182, 22, 3, 36, 2, 37],
|
||
ErrorCorrection.QUARTILE: [132, 20, 4, 16, 4, 17],
|
||
ErrorCorrection.HIGH: [100, 24, 4, 12, 4, 13],
|
||
},
|
||
# 10
|
||
{
|
||
ErrorCorrection.LOW: [274, 18, 2, 68, 2, 69],
|
||
ErrorCorrection.MEDIUM: [216, 26, 4, 43, 1, 44],
|
||
ErrorCorrection.QUARTILE: [154, 24, 6, 19, 2, 20],
|
||
ErrorCorrection.HIGH: [122, 28, 6, 15, 2, 16],
|
||
},
|
||
# 11
|
||
{
|
||
ErrorCorrection.LOW: [324, 20, 4, 81, 0, 0],
|
||
ErrorCorrection.MEDIUM: [254, 30, 1, 50, 4, 51],
|
||
ErrorCorrection.QUARTILE: [180, 28, 4, 22, 4, 23],
|
||
ErrorCorrection.HIGH: [140, 24, 3, 12, 8, 13],
|
||
},
|
||
# 12
|
||
{
|
||
ErrorCorrection.LOW: [370, 24, 2, 92, 2, 93],
|
||
ErrorCorrection.MEDIUM: [290, 22, 6, 36, 2, 37],
|
||
ErrorCorrection.QUARTILE: [206, 26, 4, 20, 6, 21],
|
||
ErrorCorrection.HIGH: [158, 28, 7, 14, 4, 15],
|
||
},
|
||
# 13
|
||
{
|
||
ErrorCorrection.LOW: [428, 26, 4, 107, 0, 0],
|
||
ErrorCorrection.MEDIUM: [334, 22, 8, 37, 1, 38],
|
||
ErrorCorrection.QUARTILE: [244, 24, 8, 20, 4, 21],
|
||
ErrorCorrection.HIGH: [180, 22, 12, 11, 4, 12],
|
||
},
|
||
# 14
|
||
{
|
||
ErrorCorrection.LOW: [461, 30, 3, 115, 1, 116],
|
||
ErrorCorrection.MEDIUM: [365, 24, 4, 40, 5, 41],
|
||
ErrorCorrection.QUARTILE: [261, 20, 11, 16, 5, 17],
|
||
ErrorCorrection.HIGH: [197, 24, 11, 12, 5, 13],
|
||
},
|
||
# 15
|
||
{
|
||
ErrorCorrection.LOW: [523, 22, 5, 87, 1, 88],
|
||
ErrorCorrection.MEDIUM: [415, 24, 5, 41, 5, 42],
|
||
ErrorCorrection.QUARTILE: [295, 30, 5, 24, 7, 25],
|
||
ErrorCorrection.HIGH: [223, 24, 11, 12, 7, 13],
|
||
},
|
||
# 16
|
||
{
|
||
ErrorCorrection.LOW: [589, 24, 5, 98, 1, 99],
|
||
ErrorCorrection.MEDIUM: [453, 28, 7, 45, 3, 46],
|
||
ErrorCorrection.QUARTILE: [325, 24, 15, 19, 2, 20],
|
||
ErrorCorrection.HIGH: [253, 30, 3, 15, 13, 16],
|
||
},
|
||
# 17
|
||
{
|
||
ErrorCorrection.LOW: [647, 28, 1, 107, 5, 108],
|
||
ErrorCorrection.MEDIUM: [507, 28, 10, 46, 1, 47],
|
||
ErrorCorrection.QUARTILE: [367, 28, 1, 22, 15, 23],
|
||
ErrorCorrection.HIGH: [283, 28, 2, 14, 17, 15],
|
||
},
|
||
# 18
|
||
{
|
||
ErrorCorrection.LOW: [721, 30, 5, 120, 1, 121],
|
||
ErrorCorrection.MEDIUM: [563, 26, 9, 43, 4, 44],
|
||
ErrorCorrection.QUARTILE: [397, 28, 17, 22, 1, 23],
|
||
ErrorCorrection.HIGH: [313, 28, 2, 14, 19, 15],
|
||
},
|
||
# 19
|
||
{
|
||
ErrorCorrection.LOW: [795, 28, 3, 113, 4, 114],
|
||
ErrorCorrection.MEDIUM: [627, 26, 3, 44, 11, 45],
|
||
ErrorCorrection.QUARTILE: [445, 26, 17, 21, 4, 22],
|
||
ErrorCorrection.HIGH: [341, 26, 9, 13, 16, 14],
|
||
},
|
||
# 20
|
||
{
|
||
ErrorCorrection.LOW: [861, 28, 3, 107, 5, 108],
|
||
ErrorCorrection.MEDIUM: [669, 26, 3, 41, 13, 42],
|
||
ErrorCorrection.QUARTILE: [485, 30, 15, 24, 5, 25],
|
||
ErrorCorrection.HIGH: [385, 28, 15, 15, 10, 16],
|
||
},
|
||
# 21
|
||
{
|
||
ErrorCorrection.LOW: [932, 28, 4, 116, 4, 117],
|
||
ErrorCorrection.MEDIUM: [714, 26, 17, 42, 0, 0],
|
||
ErrorCorrection.QUARTILE: [512, 28, 17, 22, 6, 23],
|
||
ErrorCorrection.HIGH: [406, 30, 19, 16, 6, 17],
|
||
},
|
||
# 22
|
||
{
|
||
ErrorCorrection.LOW: [1006, 28, 2, 111, 7, 112],
|
||
ErrorCorrection.MEDIUM: [782, 28, 17, 46, 0, 0],
|
||
ErrorCorrection.QUARTILE: [568, 30, 7, 24, 16, 25],
|
||
ErrorCorrection.HIGH: [442, 24, 34, 13, 0, 0],
|
||
},
|
||
# 23
|
||
{
|
||
ErrorCorrection.LOW: [1094, 30, 4, 121, 5, 122],
|
||
ErrorCorrection.MEDIUM: [860, 28, 4, 47, 14, 48],
|
||
ErrorCorrection.QUARTILE: [614, 30, 11, 24, 14, 25],
|
||
ErrorCorrection.HIGH: [464, 30, 16, 15, 14, 16],
|
||
},
|
||
# 24
|
||
{
|
||
ErrorCorrection.LOW: [1174, 30, 6, 117, 4, 118],
|
||
ErrorCorrection.MEDIUM: [914, 28, 6, 45, 14, 46],
|
||
ErrorCorrection.QUARTILE: [664, 30, 11, 24, 16, 25],
|
||
ErrorCorrection.HIGH: [514, 30, 30, 16, 2, 17],
|
||
},
|
||
# 25
|
||
{
|
||
ErrorCorrection.LOW: [1276, 26, 8, 106, 4, 107],
|
||
ErrorCorrection.MEDIUM: [1000, 28, 8, 47, 13, 48],
|
||
ErrorCorrection.QUARTILE: [718, 30, 7, 24, 22, 25],
|
||
ErrorCorrection.HIGH: [538, 30, 22, 15, 13, 16],
|
||
},
|
||
# 26
|
||
{
|
||
ErrorCorrection.LOW: [1370, 28, 10, 114, 2, 115],
|
||
ErrorCorrection.MEDIUM: [1062, 28, 19, 46, 4, 47],
|
||
ErrorCorrection.QUARTILE: [754, 28, 28, 22, 6, 23],
|
||
ErrorCorrection.HIGH: [596, 30, 33, 16, 4, 17],
|
||
},
|
||
# 27
|
||
{
|
||
ErrorCorrection.LOW: [1468, 30, 8, 122, 4, 123],
|
||
ErrorCorrection.MEDIUM: [1128, 28, 22, 45, 3, 46],
|
||
ErrorCorrection.QUARTILE: [808, 30, 8, 23, 26, 24],
|
||
ErrorCorrection.HIGH: [628, 30, 12, 15, 28, 16],
|
||
},
|
||
# 28
|
||
{
|
||
ErrorCorrection.LOW: [1531, 30, 3, 117, 10, 118],
|
||
ErrorCorrection.MEDIUM: [1193, 28, 3, 45, 23, 46],
|
||
ErrorCorrection.QUARTILE: [871, 30, 4, 24, 31, 25],
|
||
ErrorCorrection.HIGH: [661, 30, 11, 15, 31, 16],
|
||
},
|
||
# 29
|
||
{
|
||
ErrorCorrection.LOW: [1631, 30, 7, 116, 7, 117],
|
||
ErrorCorrection.MEDIUM: [1267, 28, 21, 45, 7, 46],
|
||
ErrorCorrection.QUARTILE: [911, 30, 1, 23, 37, 24],
|
||
ErrorCorrection.HIGH: [701, 30, 19, 15, 26, 16],
|
||
},
|
||
# 30
|
||
{
|
||
ErrorCorrection.LOW: [1735, 30, 5, 115, 10, 116],
|
||
ErrorCorrection.MEDIUM: [1373, 28, 19, 47, 10, 48],
|
||
ErrorCorrection.QUARTILE: [985, 30, 15, 24, 25, 25],
|
||
ErrorCorrection.HIGH: [745, 30, 23, 15, 25, 16],
|
||
},
|
||
# 31
|
||
{
|
||
ErrorCorrection.LOW: [1843, 30, 13, 115, 3, 116],
|
||
ErrorCorrection.MEDIUM: [1455, 28, 2, 46, 29, 47],
|
||
ErrorCorrection.QUARTILE: [1033, 30, 42, 24, 1, 25],
|
||
ErrorCorrection.HIGH: [793, 30, 23, 15, 28, 16],
|
||
},
|
||
# 32
|
||
{
|
||
ErrorCorrection.LOW: [1955, 30, 17, 115, 0, 0],
|
||
ErrorCorrection.MEDIUM: [1541, 28, 10, 46, 23, 47],
|
||
ErrorCorrection.QUARTILE: [1115, 30, 10, 24, 35, 25],
|
||
ErrorCorrection.HIGH: [845, 30, 19, 15, 35, 16],
|
||
},
|
||
# 33
|
||
{
|
||
ErrorCorrection.LOW: [2071, 30, 17, 115, 1, 116],
|
||
ErrorCorrection.MEDIUM: [1631, 28, 14, 46, 21, 47],
|
||
ErrorCorrection.QUARTILE: [1171, 30, 29, 24, 19, 25],
|
||
ErrorCorrection.HIGH: [901, 30, 11, 15, 46, 16],
|
||
},
|
||
# 34
|
||
{
|
||
ErrorCorrection.LOW: [2191, 30, 13, 115, 6, 116],
|
||
ErrorCorrection.MEDIUM: [1725, 28, 14, 46, 23, 47],
|
||
ErrorCorrection.QUARTILE: [1231, 30, 44, 24, 7, 25],
|
||
ErrorCorrection.HIGH: [961, 30, 59, 16, 1, 17],
|
||
},
|
||
# 35
|
||
{
|
||
ErrorCorrection.LOW: [2306, 30, 12, 121, 7, 122],
|
||
ErrorCorrection.MEDIUM: [1812, 28, 12, 47, 26, 48],
|
||
ErrorCorrection.QUARTILE: [1286, 30, 39, 24, 14, 25],
|
||
ErrorCorrection.HIGH: [986, 30, 22, 15, 41, 16],
|
||
},
|
||
# 36
|
||
{
|
||
ErrorCorrection.LOW: [2434, 30, 6, 121, 14, 122],
|
||
ErrorCorrection.MEDIUM: [1914, 28, 6, 47, 34, 48],
|
||
ErrorCorrection.QUARTILE: [1354, 30, 46, 24, 10, 25],
|
||
ErrorCorrection.HIGH: [1054, 30, 2, 15, 64, 16],
|
||
},
|
||
# 37
|
||
{
|
||
ErrorCorrection.LOW: [2566, 30, 17, 122, 4, 123],
|
||
ErrorCorrection.MEDIUM: [1992, 28, 29, 46, 14, 47],
|
||
ErrorCorrection.QUARTILE: [1426, 30, 49, 24, 10, 25],
|
||
ErrorCorrection.HIGH: [1096, 30, 24, 15, 46, 16],
|
||
},
|
||
# 38
|
||
{
|
||
ErrorCorrection.LOW: [2702, 30, 4, 122, 18, 123],
|
||
ErrorCorrection.MEDIUM: [2102, 28, 13, 46, 32, 47],
|
||
ErrorCorrection.QUARTILE: [1502, 30, 48, 24, 14, 25],
|
||
ErrorCorrection.HIGH: [1142, 30, 42, 15, 32, 16],
|
||
},
|
||
# 39
|
||
{
|
||
ErrorCorrection.LOW: [2812, 30, 20, 117, 4, 118],
|
||
ErrorCorrection.MEDIUM: [2216, 28, 40, 47, 7, 48],
|
||
ErrorCorrection.QUARTILE: [1582, 30, 43, 24, 22, 25],
|
||
ErrorCorrection.HIGH: [1222, 30, 10, 15, 67, 16],
|
||
},
|
||
# 40
|
||
{
|
||
ErrorCorrection.LOW: [2956, 30, 19, 118, 6, 119],
|
||
ErrorCorrection.MEDIUM: [2334, 28, 18, 47, 31, 48],
|
||
ErrorCorrection.QUARTILE: [1666, 30, 34, 24, 34, 25],
|
||
ErrorCorrection.HIGH: [1276, 30, 20, 15, 61, 16],
|
||
},
|
||
]
|
||
|
||
## sorted by version
|
||
const _ALIGNMENT_PATTERN_POSITIONS: Array[Array] = [
|
||
[],
|
||
[6, 18],
|
||
[6, 22],
|
||
[6, 26],
|
||
[6, 30],
|
||
[6, 34],
|
||
[6, 22, 38],
|
||
[6, 24, 42],
|
||
[6, 26, 46],
|
||
[6, 28, 50],
|
||
[6, 30, 54],
|
||
[6, 32, 58],
|
||
[6, 34, 62],
|
||
[6, 26, 46, 66],
|
||
[6, 26, 48, 70],
|
||
[6, 26, 50, 74],
|
||
[6, 30, 54, 78],
|
||
[6, 30, 56, 82],
|
||
[6, 30, 58, 86],
|
||
[6, 34, 62, 90],
|
||
[6, 28, 50, 72, 94],
|
||
[6, 26, 50, 74, 98],
|
||
[6, 30, 54, 78, 102],
|
||
[6, 28, 54, 80, 106],
|
||
[6, 32, 58, 84, 110],
|
||
[6, 30, 58, 86, 114],
|
||
[6, 34, 62, 90, 118],
|
||
[6, 26, 50, 74, 98, 122],
|
||
[6, 30, 54, 78, 102, 126],
|
||
[6, 26, 52, 78, 104, 130],
|
||
[6, 30, 56, 82, 108, 134],
|
||
[6, 34, 60, 86, 112, 138],
|
||
[6, 30, 58, 86, 114, 142],
|
||
[6, 34, 62, 90, 118, 146],
|
||
[6, 30, 54, 78, 102, 126, 150],
|
||
[6, 24, 50, 76, 102, 128, 154],
|
||
[6, 28, 54, 80, 106, 132, 158],
|
||
[6, 32, 58, 84, 110, 136, 162],
|
||
[6, 26, 54, 82, 110, 138, 166],
|
||
[6, 30, 58, 86, 114, 142, 170],
|
||
]
|
||
|
||
## remainder bits after structured data bits
|
||
const _REMAINDER_BITS: Array[int] = [0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0]
|
||
|
||
const _DARK: int = 1
|
||
const _LIGHT: int = 0
|
||
|
||
static var _number_rx: RegEx = RegEx.create_from_string("[^\\d]*")
|
||
static var _alphanumeric_rx: RegEx = RegEx.create_from_string("[^0-9A-Z $%*+\\-.\\/:]*")
|
||
|
||
## this can be either a String or PackedByteArray, based on the current encoding mode
|
||
var _input_data: Variant = "":
|
||
get = get_input_data
|
||
## Encoding Mode
|
||
var mode: Mode = Mode.NUMERIC:
|
||
set = set_mode
|
||
## Error Correction
|
||
var error_correction: ErrorCorrection = ErrorCorrection.LOW:
|
||
set = set_error_correction
|
||
## Set to true if you want to specify an ECI value.
|
||
var use_eci: bool = false:
|
||
set = set_use_eci
|
||
## Extended Channel Interpretation (ECI) Value. Is only used if `use_eci` is true.
|
||
var eci_value: int = ECI.ISO_8859_1:
|
||
set = set_eci_value
|
||
## Use automatically the smallest version
|
||
var auto_version: bool = true:
|
||
set = set_auto_version
|
||
## Version
|
||
## Will be changed on encoding to the used version if auto_version is true
|
||
var version: int = 1:
|
||
set = set_version
|
||
var auto_mask_pattern: bool = true:
|
||
set = set_auto_mask_pattern
|
||
## Will be changed on encoding to the used mask pattern if auto_mask_pattern is true
|
||
var mask_pattern: int = 0:
|
||
set = set_mask_pattern
|
||
|
||
func set_auto_version(new_auto_version: bool) -> void:
|
||
if new_auto_version == auto_version:
|
||
return
|
||
auto_version = new_auto_version
|
||
|
||
func set_version(new_version: int) -> void:
|
||
if new_version == version:
|
||
return
|
||
version = clampi(new_version, 1, 40)
|
||
|
||
func set_error_correction(new_error_correction: ErrorCorrection) -> void:
|
||
if new_error_correction == error_correction:
|
||
return
|
||
error_correction = new_error_correction
|
||
|
||
func set_mode(new_mode: Mode) -> void:
|
||
if new_mode == mode:
|
||
return
|
||
mode = new_mode
|
||
|
||
match mode:
|
||
Mode.NUMERIC, Mode.ALPHANUMERIC, Mode.KANJI:
|
||
self._input_data = ""
|
||
Mode.BYTE:
|
||
self._input_data = PackedByteArray()
|
||
|
||
func set_use_eci(new_use_eci: bool) -> void:
|
||
if new_use_eci == use_eci:
|
||
return
|
||
use_eci = new_use_eci
|
||
|
||
func set_eci_value(new_eci_value: int) -> void:
|
||
if new_eci_value == eci_value:
|
||
return
|
||
if !ECI.values().has(new_eci_value):
|
||
return
|
||
eci_value = new_eci_value
|
||
|
||
func set_auto_mask_pattern(new_auto_mask_pattern: bool) -> void:
|
||
if new_auto_mask_pattern == auto_mask_pattern:
|
||
return
|
||
auto_mask_pattern = new_auto_mask_pattern
|
||
|
||
func set_mask_pattern(new_mask_pattern: int) -> void:
|
||
if new_mask_pattern == mask_pattern:
|
||
return
|
||
mask_pattern = clampi(new_mask_pattern, 0, 7)
|
||
|
||
## return the data which was put in
|
||
func get_input_data() -> Variant:
|
||
return _input_data
|
||
|
||
## returns the dimension for the current version.
|
||
func get_dimension() -> int:
|
||
return _calc_dimension(self.version)
|
||
|
||
## returns ONE minimum version which fits the data
|
||
## the returned version is just an approach
|
||
## returns -1 if too huge
|
||
func calc_min_version() -> int:
|
||
var input_size: int = self._get_input_data_size()
|
||
for idx: int in range(_DATA_CAPACITY.size()):
|
||
var cap: int = _DATA_CAPACITY[idx][ self.error_correction][ self.mode]
|
||
if self.use_eci:
|
||
# subtract roughly eci header size
|
||
match self.mode:
|
||
Mode.NUMERIC:
|
||
cap -= 4
|
||
Mode.ALPHANUMERIC:
|
||
cap -= 3
|
||
Mode.BYTE:
|
||
cap -= 2
|
||
Mode.KANJI:
|
||
cap -= 1
|
||
if input_size <= cap:
|
||
return idx + 1
|
||
return -1
|
||
|
||
static func _get_alphanumeric_number(chr: String) -> int:
|
||
return _ALPHANUMERIC_CHARACTERS[chr]
|
||
|
||
# functions are adapted to our starting point 0, 0
|
||
static func _mask_pattern_fns() -> Array[Callable]:
|
||
return [
|
||
func(pos: Vector2i) -> bool: return (pos.x + pos.y) % 2 == 0,
|
||
func(pos: Vector2i) -> bool: return pos.y % 2 == 0,
|
||
func(pos: Vector2i) -> bool: return pos.x % 3 == 0,
|
||
func(pos: Vector2i) -> bool: return (pos.x + pos.y) % 3 == 0,
|
||
func(pos: Vector2i) -> bool: return (pos.x / 3 + pos.y / 2) % 2 == 0,
|
||
func(pos: Vector2i) -> bool: return (pos.x * pos.y % 2) + (pos.x * pos.y) % 3 == 0,
|
||
func(pos: Vector2i) -> bool: return ((pos.x * pos.y % 2) + (pos.x * pos.y) % 3) % 2 == 0,
|
||
func(pos: Vector2i) -> bool: return (((pos.x + pos.y) % 2) + (pos.x * pos.y) % 3) % 2 == 0,
|
||
]
|
||
|
||
# helper function check if a bit is set
|
||
static func _get_state(value: int, idx: int) -> bool:
|
||
return value & (1 << idx) != 0
|
||
|
||
func _get_data_codeword_count() -> int:
|
||
return _ERROR_CORRECTION[ self.version - 1][ self.error_correction][0]
|
||
|
||
func _get_ec_codeword_count() -> int:
|
||
return _ERROR_CORRECTION[ self.version - 1][ self.error_correction][1]
|
||
|
||
func _get_ec_block_count(group: int) -> int:
|
||
return _ERROR_CORRECTION[ self.version - 1][ self.error_correction][2 + (group - 1) * 2]
|
||
|
||
func _get_ec_block_codeword_count(group: int) -> int:
|
||
return _ERROR_CORRECTION[ self.version - 1][ self.error_correction][3 + (group - 1) * 2]
|
||
|
||
static func _calc_dimension(version: int) -> int:
|
||
return 21 + 4 * (version - 1)
|
||
|
||
func _get_alignment_pattern_positions() -> Array[Vector2i]:
|
||
var dimension: int = self.get_dimension()
|
||
var positions: Array[Vector2i] = []
|
||
for row: int in _ALIGNMENT_PATTERN_POSITIONS[ self.version - 1]:
|
||
for col: int in _ALIGNMENT_PATTERN_POSITIONS[ self.version - 1]:
|
||
# do not overlap finder positions
|
||
if (row - 2 < 8 && col - 2 < 8) || \
|
||
(row - 2 < 8 && col + 2 > dimension - 8) || \
|
||
(row + 2 >= dimension - 8 && col - 2 < 8):
|
||
continue
|
||
positions.append(Vector2i(row, col))
|
||
return positions
|
||
|
||
static func _get_remainder_bits(version: int) -> int:
|
||
return _REMAINDER_BITS[version - 1]
|
||
|
||
func _get_input_data_size() -> int:
|
||
match typeof(self._input_data):
|
||
TYPE_STRING:
|
||
return self._input_data.length()
|
||
TYPE_PACKED_BYTE_ARRAY:
|
||
return self._input_data.size()
|
||
return 0
|
||
|
||
func _get_char_count_size() -> int:
|
||
if self.version < 10:
|
||
match self.mode:
|
||
Mode.NUMERIC:
|
||
return 10
|
||
Mode.ALPHANUMERIC:
|
||
return 9
|
||
Mode.BYTE:
|
||
return 8
|
||
Mode.KANJI:
|
||
return 8
|
||
elif self.version < 27:
|
||
match self.mode:
|
||
Mode.NUMERIC:
|
||
return 12
|
||
Mode.ALPHANUMERIC:
|
||
return 11
|
||
Mode.BYTE:
|
||
return 16
|
||
Mode.KANJI:
|
||
return 10
|
||
else:
|
||
match self.mode:
|
||
Mode.NUMERIC:
|
||
return 14
|
||
Mode.ALPHANUMERIC:
|
||
return 13
|
||
Mode.BYTE:
|
||
return 16
|
||
Mode.KANJI:
|
||
return 12
|
||
return 0
|
||
|
||
# TODO: TEST IF STATIC VAR WORKS
|
||
static func _static_init() -> void:
|
||
# TODO: static init is not called in editor if not @tool
|
||
if _number_rx == null:
|
||
_number_rx = RegEx.create_from_string("[^\\d]*")
|
||
# TODO: static init is not called in editor if not @tool
|
||
if _alphanumeric_rx == null:
|
||
_alphanumeric_rx = RegEx.create_from_string("[^0-9A-Z $%*+\\-.\\/:]*")
|
||
|
||
func _init(error_correction_: ErrorCorrection = ErrorCorrection.LOW) -> void:
|
||
self.error_correction = error_correction_
|
||
|
||
# TODO: static init is not called in editor if not @tool
|
||
if Engine.is_editor_hint():
|
||
_static_init()
|
||
|
||
## generate an QR code image
|
||
static func generate_image(qr_data: PackedByteArray, module_size: int = 1, light_module_color: Color = Color.WHITE, dark_module_color: Color = Color.BLACK, quiet_zone_size: int = 4) -> Image:
|
||
module_size = maxi(1, module_size)
|
||
quiet_zone_size = maxi(0, quiet_zone_size)
|
||
|
||
var dimension: int = sqrt(qr_data.size())
|
||
var image_size: int = (dimension + 2 * quiet_zone_size) * module_size
|
||
var image: Image = Image.create_empty(image_size, image_size, false, Image.FORMAT_RGB8)
|
||
image.fill(light_module_color)
|
||
|
||
for y: int in range(dimension):
|
||
for x: int in range(dimension):
|
||
var color: Color = Color.PINK
|
||
match qr_data[x + y * dimension]:
|
||
_LIGHT:
|
||
color = light_module_color
|
||
_DARK:
|
||
color = dark_module_color
|
||
for offset_x: int in range(module_size):
|
||
for offset_y: int in range(module_size):
|
||
image.set_pixel((x + quiet_zone_size) * module_size + offset_x, (y + quiet_zone_size) * module_size + offset_y, color)
|
||
|
||
return image
|
||
|
||
func put_numeric(number: String) -> void:
|
||
self.mode = Mode.NUMERIC
|
||
self._input_data = _number_rx.sub(number, "", true)
|
||
|
||
func put_alphanumeric(text: String) -> void:
|
||
self.mode = Mode.ALPHANUMERIC
|
||
self._input_data = _alphanumeric_rx.sub(text, "", true)
|
||
|
||
func put_byte(data: PackedByteArray) -> void:
|
||
self.mode = Mode.BYTE
|
||
self._input_data = data
|
||
|
||
func put_kanji(data: String) -> void:
|
||
self.mode = Mode.KANJI
|
||
# clear invalid characters
|
||
self._input_data = ShiftJIS.get_string_from_shift_jis_2004(ShiftJIS.to_shift_jis_2004_buffer(data))
|
||
|
||
## returns row by row
|
||
## to get row size use get_dimension
|
||
func encode() -> PackedByteArray:
|
||
if self.auto_version:
|
||
self.version = self.calc_min_version()
|
||
|
||
var data_stream: BitStream = self._encode_data()
|
||
var err_correction: Array[PackedByteArray] = self._error_correction(data_stream)
|
||
var structured_data: BitStream = self._structure_data(data_stream, err_correction)
|
||
var module_data: PackedByteArray = self._place_modules(structured_data)
|
||
|
||
return self._mask_qr(module_data)
|
||
|
||
func _encode_data() -> BitStream:
|
||
var stream: BitStream = BitStream.new()
|
||
|
||
# add ECI header
|
||
if self.use_eci:
|
||
stream.append(0b0111, 4)
|
||
if self.eci_value <= 127:
|
||
stream.append(0, 1)
|
||
stream.append(self.eci_value, 7)
|
||
elif self.eci_value <= 16383:
|
||
stream.append(0b10, 2)
|
||
stream.append(self.eci_value, 14)
|
||
else:
|
||
stream.append(0b110, 3)
|
||
stream.append(self.eci_value, 21)
|
||
|
||
# add mode
|
||
stream.append(int(self.mode), 4)
|
||
|
||
# add character count indicator
|
||
stream.append(self._get_input_data_size(), self._get_char_count_size())
|
||
|
||
# add encoded data
|
||
match self.mode:
|
||
Mode.NUMERIC:
|
||
self._encode_numeric(stream)
|
||
Mode.ALPHANUMERIC:
|
||
self._encode_alphanumeric(stream)
|
||
Mode.BYTE:
|
||
self._encode_byte(stream)
|
||
Mode.KANJI:
|
||
self._encode_kanji(stream)
|
||
|
||
# add terminator
|
||
var required_bytes: int = self._get_data_codeword_count()
|
||
var terminator_size: int = mini(8 * required_bytes - stream.size(), 4)
|
||
stream.append(0, terminator_size)
|
||
|
||
# add bits to multiple of 8
|
||
stream.append(0, (8 - stream.size() % 8) % 8)
|
||
|
||
# pad bytes to capacity
|
||
var missing_bytes = required_bytes - stream.size() / 8
|
||
for idx: int in range(missing_bytes):
|
||
if idx % 2 == 0:
|
||
stream.append(236, 8)
|
||
else:
|
||
stream.append(17, 8)
|
||
|
||
return stream
|
||
|
||
func _encode_numeric(stream: BitStream) -> void:
|
||
assert(typeof(self._input_data) == TYPE_STRING)
|
||
const GROUP_SIZE: int = 3
|
||
|
||
for idx: int in range(ceili(self._input_data.length() / float(GROUP_SIZE))):
|
||
var chars: String = self._input_data.substr(idx * GROUP_SIZE, GROUP_SIZE)
|
||
var number: int = chars.to_int()
|
||
var bit_count: int = 0
|
||
match chars.length():
|
||
3:
|
||
bit_count = 10
|
||
2:
|
||
bit_count = 7
|
||
1:
|
||
bit_count = 4
|
||
stream.append(number, bit_count)
|
||
|
||
func _encode_alphanumeric(stream: BitStream) -> void:
|
||
assert(typeof(self._input_data) == TYPE_STRING)
|
||
const GROUP_SIZE: int = 2
|
||
|
||
for idx: int in range(ceili(self._input_data.length() / float(GROUP_SIZE))):
|
||
var chars: String = self._input_data.substr(idx * GROUP_SIZE, GROUP_SIZE)
|
||
var number: int = _get_alphanumeric_number(chars[0])
|
||
if chars.length() == 2:
|
||
number = 45 * number + _get_alphanumeric_number(chars[1])
|
||
stream.append(number, 11)
|
||
else:
|
||
stream.append(number, 6)
|
||
|
||
func _encode_byte(stream: BitStream) -> void:
|
||
assert(typeof(self._input_data) == TYPE_PACKED_BYTE_ARRAY)
|
||
|
||
for val: int in self._input_data:
|
||
stream.append(val, 8)
|
||
|
||
func _encode_kanji(stream: BitStream) -> void:
|
||
assert(typeof(self._input_data) == TYPE_STRING)
|
||
|
||
var jis_bytes: PackedByteArray = ShiftJIS.to_shift_jis_2004_buffer(self._input_data)
|
||
for idx: int in range(jis_bytes.size() / 2):
|
||
var value = jis_bytes.decode_u16(idx * 2)
|
||
if value >= 0x8140 && value <= 0x9FFC:
|
||
value = value - 0x8140
|
||
elif value >= 0xE040 && value <= 0xEBBF:
|
||
value = value - 0xC140
|
||
value = ((value & 0xFF00) >> 8) * 0xC0 + (value & 0x00FF)
|
||
stream.append(value, 13)
|
||
|
||
# returns an array of PackedByteArray's, structured as Group, Block [G1B1, G1B2, G1B3, G2B1, G2B2, ...]
|
||
func _error_correction(stream: BitStream) -> Array[PackedByteArray]:
|
||
var data: PackedByteArray = stream.to_byte_array()
|
||
|
||
var ec_words: int = self._get_ec_codeword_count()
|
||
var group_blocks: PackedByteArray = [
|
||
self._get_ec_block_count(1),
|
||
self._get_ec_block_count(2),
|
||
]
|
||
var group_codewords: PackedByteArray = [
|
||
self._get_ec_block_codeword_count(1),
|
||
self._get_ec_block_codeword_count(2),
|
||
]
|
||
var groups: int = 1
|
||
if group_blocks[1] > 0:
|
||
groups += 1
|
||
|
||
var err_corr: Array[PackedByteArray] = []
|
||
for group_idx: int in range(groups):
|
||
var block_size: int = group_codewords[group_idx]
|
||
for block_idx: int in range(group_blocks[group_idx]):
|
||
var start_idx: int = 0
|
||
# add offset to current group
|
||
for group_off: int in range(group_idx):
|
||
start_idx += group_blocks[group_off] * group_codewords[group_off]
|
||
start_idx = start_idx + block_idx * block_size
|
||
var cur_data: PackedByteArray = data.slice(start_idx, start_idx + block_size)
|
||
err_corr.append(ReedSolomon.encode(cur_data, ec_words))
|
||
|
||
return err_corr
|
||
|
||
func _structure_data(data_stream: BitStream, err_correction: Array[PackedByteArray]) -> BitStream:
|
||
if err_correction.size() == 1:
|
||
var res: BitStream = data_stream.duplicate()
|
||
res.append_byte_array(err_correction[0])
|
||
# append remainder bits
|
||
res.append(0, _get_remainder_bits(self.version))
|
||
return res
|
||
|
||
var res: BitStream = BitStream.new()
|
||
var data_arr: PackedByteArray = data_stream.to_byte_array()
|
||
var group_blocks: PackedByteArray = [
|
||
self._get_ec_block_count(1),
|
||
self._get_ec_block_count(2),
|
||
]
|
||
var group_codewords: Array[int] = [
|
||
self._get_ec_block_codeword_count(1),
|
||
self._get_ec_block_codeword_count(2),
|
||
]
|
||
var groups: int = 1
|
||
if group_blocks[1] > 0:
|
||
groups += 1
|
||
|
||
# interleave data code words
|
||
var max_code_words: int = group_codewords.max()
|
||
for codeword_idx: int in range(max_code_words):
|
||
for group_idx: int in range(groups):
|
||
# if current group/block has not this much codewords skip
|
||
if codeword_idx >= group_codewords[group_idx]:
|
||
continue
|
||
var group_offset: int = 0
|
||
for group_off: int in range(group_idx):
|
||
group_offset += group_blocks[group_off] * group_codewords[group_off]
|
||
for block_idx: int in range(group_blocks[group_idx]):
|
||
var idx: int = group_offset + codeword_idx + block_idx * group_codewords[group_idx]
|
||
res.append(data_arr[idx], 8)
|
||
|
||
# interleave error code words
|
||
for word_idx: int in range(self._get_ec_codeword_count()):
|
||
for block: int in range(err_correction.size()):
|
||
res.append(err_correction[block][word_idx], 8)
|
||
|
||
# append remainder bits
|
||
res.append(0, _get_remainder_bits(self.version))
|
||
|
||
return res
|
||
|
||
# pos is upper left black corner
|
||
# 7 x 7 size
|
||
static func _place_finder(data: PackedByteArray, dimension: int, pos: Vector2i) -> void:
|
||
for row: int in range(7):
|
||
for col: int in range(7):
|
||
data[(pos.x + row) + (pos.y + col) * dimension] = _DARK
|
||
for idx: int in range(5):
|
||
data[(pos.x + 1 + idx) + (pos.y + 1) * dimension] = _LIGHT
|
||
data[(pos.x + 1 + idx) + (pos.y + 5) * dimension] = _LIGHT
|
||
for idx: int in range(3):
|
||
data[(pos.x + 1) + (pos.y + 2 + idx) * dimension] = _LIGHT
|
||
data[(pos.x + 5) + (pos.y + 2 + idx) * dimension] = _LIGHT
|
||
|
||
# pos is center
|
||
# 5 x 5 size
|
||
static func _place_align_pattern(data: PackedByteArray, dimension: int, pos: Vector2i) -> void:
|
||
for row: int in range(5):
|
||
for col: int in range(5):
|
||
data[(pos.x - 2 + row) + (pos.y - 2 + col) * dimension] = _DARK
|
||
for idx: int in range(3):
|
||
data[(pos.x - 1 + idx) + (pos.y - 1) * dimension] = _LIGHT
|
||
data[(pos.x - 1 + idx) + (pos.y + 1) * dimension] = _LIGHT
|
||
data[(pos.x - 1) + (pos.y) * dimension] = _LIGHT
|
||
data[(pos.x + 1) + (pos.y) * dimension] = _LIGHT
|
||
|
||
static func _place_separators(data: PackedByteArray, dimension: int) -> void:
|
||
for idx: int in range(8):
|
||
# upper left
|
||
data[idx + 7 * dimension] = _LIGHT
|
||
data[7 + idx * dimension] = _LIGHT
|
||
# lower left
|
||
data[idx + (dimension - 8) * dimension] = _LIGHT
|
||
data[(dimension - 8) + idx * dimension] = _LIGHT
|
||
# upper right
|
||
data[(dimension - idx - 1) + 7 * dimension] = _LIGHT
|
||
data[7 + (dimension - idx - 1) * dimension] = _LIGHT
|
||
|
||
static func _place_timing_patterns(data: PackedByteArray, dimension: int) -> void:
|
||
for idx: int in range(dimension - 6 * 2):
|
||
data[6 + idx + 6 * dimension] = (idx + 1) % 2
|
||
data[6 + (6 + idx) * dimension] = (idx + 1) % 2
|
||
|
||
static func _is_data_module(dimension: int, alignment_pattern_pos: Array[Vector2i], pos: Vector2i) -> bool:
|
||
# finder patterns with separators and format information area
|
||
if (pos.x <= 8 && pos.y <= 8) || \
|
||
(pos.x >= dimension - 8 && pos.y <= 8) || \
|
||
(pos.x <= 8 && pos.y >= dimension - 8):
|
||
return false
|
||
|
||
# timing patterns (run through middle, already excluded in finder areas)
|
||
if (pos.x == 6 && pos.y >= 8 && pos.y < dimension - 8) || \
|
||
(pos.y == 6 && pos.x >= 8 && pos.x < dimension - 8):
|
||
return false
|
||
|
||
# version information area (version >= 7 only)
|
||
if dimension >= 45:
|
||
# Bottom-left: 6 rows × 3 columns
|
||
if pos.x <= 5 && pos.y >= dimension - 11 && pos.y <= dimension - 9:
|
||
return false
|
||
# Top-right: 3 rows × 6 columns
|
||
if pos.y <= 5 && pos.x >= dimension - 11 && pos.x <= dimension - 9:
|
||
return false
|
||
|
||
# alignment patterns
|
||
for align_pos: Vector2i in alignment_pattern_pos:
|
||
if pos.x >= align_pos.x - 2 && pos.x <= align_pos.x + 2 && \
|
||
pos.y >= align_pos.y - 2 && pos.y <= align_pos.y + 2:
|
||
return false
|
||
|
||
return true
|
||
|
||
static func _place_data(data: PackedByteArray, dimension: int, alignment_pattern_pos: Array[Vector2i], structured_data: BitStream) -> void:
|
||
var data_idx: int = 0
|
||
# base column where to go up or down
|
||
var base_col: int = dimension - 1
|
||
var upwards: bool = true
|
||
|
||
while base_col > 0:
|
||
# skip vertical timing pattern
|
||
if base_col == 6:
|
||
base_col -= 1
|
||
|
||
for row: int in range(dimension):
|
||
if upwards:
|
||
row = dimension - 1 - row
|
||
for offset: int in range(2):
|
||
var pos: Vector2i = Vector2i(base_col - offset, row)
|
||
if _is_data_module(dimension, alignment_pattern_pos, pos):
|
||
data[pos.x + pos.y * dimension] = int(structured_data.get_bit(data_idx))
|
||
data_idx += 1
|
||
|
||
base_col -= 2
|
||
upwards = !upwards
|
||
|
||
# all data modules placed
|
||
assert(data_idx == structured_data.size(), "failed to place all data (%d of %d)" % [data_idx, structured_data.size()])
|
||
|
||
static func _calc_mask_rating(data: PackedByteArray, dimension: int) -> int:
|
||
var rating: int = 0
|
||
|
||
# condition 1
|
||
# horizontal
|
||
for y: int in range(dimension):
|
||
var count: int = 0
|
||
var block_value: int = 0
|
||
for x: int in range(dimension):
|
||
var cur_value: int = data[x + y * dimension]
|
||
if cur_value == block_value:
|
||
count += 1
|
||
else:
|
||
if count >= 5:
|
||
rating += count - 2
|
||
count = 1
|
||
block_value = cur_value
|
||
if count >= 5:
|
||
rating += count - 2
|
||
# vertical
|
||
for x: int in range(dimension):
|
||
var count: int = 0
|
||
var block_value: int = 0
|
||
for y: int in range(dimension):
|
||
var cur_value: int = data[x + y * dimension]
|
||
if cur_value == block_value:
|
||
count += 1
|
||
else:
|
||
if count >= 5:
|
||
rating += count - 2
|
||
count = 1
|
||
block_value = cur_value
|
||
if count >= 5:
|
||
rating += count - 2
|
||
|
||
# condition 2
|
||
for x: int in range(dimension - 1):
|
||
for y: int in range(dimension - 1):
|
||
var val: int = data[x + y * dimension] + data[x + 1 + y * dimension] + data[x + (y + 1) * dimension] + data[x + 1 + (y + 1) * dimension]
|
||
if val == 0 || val == 4:
|
||
rating += 3
|
||
|
||
# condition 3
|
||
for y: int in range(dimension):
|
||
for x: int in range(dimension - 6):
|
||
var start_idx: int = x + y * dimension
|
||
if (data[start_idx]
|
||
&& !data[start_idx + 1]
|
||
&& data[start_idx + 2]
|
||
&& data[start_idx + 3]
|
||
&& data[start_idx + 4]
|
||
&& !data[start_idx + 5]
|
||
&& data[start_idx + 6]):
|
||
if x >= 4 && !data[start_idx - 1] && !data[start_idx - 2] && !data[start_idx - 3] && !data[start_idx - 4]:
|
||
rating += 40
|
||
if x <= (dimension - 11) && !data[start_idx + 7] && !data[start_idx + 8] && !data[start_idx + 9] && !data[start_idx + 10]:
|
||
rating += 40
|
||
|
||
for x: int in range(dimension):
|
||
for y: int in range(dimension - 6):
|
||
if (data[x + y * dimension]
|
||
&& !data[x + (y + 1) * dimension]
|
||
&& data[x + (y + 2) * dimension]
|
||
&& data[x + (y + 3) * dimension]
|
||
&& data[x + (y + 4) * dimension]
|
||
&& !data[x + (y + 5) * dimension]
|
||
&& data[x + (y + 6) * dimension]):
|
||
if y >= 4 && !data[x + (y - 1) * dimension] && !data[x + (y - 2) * dimension] && !data[x + (y - 3) * dimension] && !data[x + (y - 4) * dimension]:
|
||
rating += 40
|
||
if y <= (dimension - 11) && !data[x + (y + 7) * dimension] && !data[x + (y + 8) * dimension] && !data[x + (y + 9) * dimension] && !data[x + (y + 10) * dimension]:
|
||
rating += 40
|
||
|
||
# condition 4
|
||
var dark_mods: int = data.count(_DARK)
|
||
var ratio: float = dark_mods / float(dimension * dimension)
|
||
var percent: int = int((ratio * 100) - 50)
|
||
rating += absi(percent) / 5 * 10
|
||
return rating
|
||
|
||
static func _place_format(qr_data: PackedByteArray, dimension: int, error_corr: ErrorCorrection, mask_pattern_val: int) -> void:
|
||
var base_code: int = (int(error_corr) << 3) | mask_pattern_val
|
||
|
||
var code: int = base_code
|
||
for _idx: int in range(10):
|
||
code = (code << 1) ^ ((code >> 9) * 0x537)
|
||
code = (base_code << 10 | code) ^ 0x5412
|
||
|
||
# upper left finder
|
||
for idx: int in range(8):
|
||
# skip timing pattern
|
||
var pos: int = idx
|
||
if idx > 5:
|
||
pos += 1
|
||
# horizontal
|
||
qr_data[pos + 8 * dimension] = int(_get_state(code, 14 - idx))
|
||
# vertical
|
||
qr_data[8 + pos * dimension] = int(_get_state(code, idx))
|
||
# lower left finder
|
||
for idx: int in range(7):
|
||
qr_data[8 + (dimension - 1 - idx) * dimension] = int(_get_state(code, 14 - idx))
|
||
# upper right finder
|
||
for idx: int in range(8):
|
||
qr_data[(dimension - 1 - idx) + 8 * dimension] = int(_get_state(code, idx))
|
||
|
||
static func _place_version(qr_data: PackedByteArray, version: int) -> void:
|
||
if version < 7:
|
||
return
|
||
|
||
var code: int = version
|
||
for _idx: int in range(12):
|
||
code = (code << 1) ^ ((code >> 11) * 0x1F25)
|
||
code = version << 12 | code
|
||
|
||
var dimension: int = _calc_dimension(version)
|
||
for idx: int in range(18):
|
||
var x: int = idx / 3
|
||
var y: int = dimension - 11 + idx % 3
|
||
qr_data[x + y * dimension] = int(_get_state(code, idx))
|
||
qr_data[y + x * dimension] = int(_get_state(code, idx))
|
||
|
||
# returns qr module data, ordered by rows
|
||
# (col/x, row/y) | index
|
||
# (0, 0) (1, 0) (2, 0) | 0, 1, 2
|
||
# (0, 1) (1, 1) (2, 1) | 3, 4, 5
|
||
# (0, 2) (1, 2) (2, 2) | 6, 7, 8
|
||
func _place_modules(structured_data: BitStream) -> PackedByteArray:
|
||
var qr_data: PackedByteArray = PackedByteArray()
|
||
var dimension: int = self.get_dimension()
|
||
qr_data.resize(dimension * dimension)
|
||
|
||
# place upper left finder
|
||
_place_finder(qr_data, dimension, Vector2i(0, 0))
|
||
# place lower left finder
|
||
_place_finder(qr_data, dimension, Vector2i(0, dimension - 7))
|
||
# place upper right finder
|
||
_place_finder(qr_data, dimension, Vector2i(dimension - 7, 0))
|
||
_place_separators(qr_data, dimension)
|
||
|
||
var alignment_pattern_pos: Array[Vector2i] = self._get_alignment_pattern_positions()
|
||
for pos: Vector2i in alignment_pattern_pos:
|
||
_place_align_pattern(qr_data, dimension, pos)
|
||
|
||
_place_timing_patterns(qr_data, dimension)
|
||
|
||
# dark module
|
||
qr_data[8 + (dimension - 8) * dimension] = _DARK
|
||
|
||
# place data
|
||
_place_data(qr_data, dimension, alignment_pattern_pos, structured_data)
|
||
|
||
return qr_data
|
||
|
||
static func _mask(qr_data: PackedByteArray, dimension: int, alignment_pattern_pos: Array[Vector2i], mask_pattern: int) -> void:
|
||
var mask_fn: Callable = _mask_pattern_fns()[mask_pattern]
|
||
|
||
for x: int in range(dimension):
|
||
for y: int in range(dimension):
|
||
var pos: Vector2i = Vector2i(x, y)
|
||
if _is_data_module(dimension, alignment_pattern_pos, pos):
|
||
var idx: int = x + y * dimension
|
||
qr_data[idx] = int(mask_fn.call(pos)) ^ qr_data[idx]
|
||
|
||
# return mask pattern number
|
||
func _get_best_qr_mask(masked_qrs: Array[PackedByteArray], dimension: int) -> int:
|
||
var min_idx: int = 0
|
||
# integer max
|
||
var cur_min_value: int = 9223372036854775807
|
||
for idx: int in range(masked_qrs.size()):
|
||
var rating: int = _calc_mask_rating(masked_qrs[idx], dimension)
|
||
if rating < cur_min_value:
|
||
min_idx = idx
|
||
cur_min_value = rating
|
||
|
||
return min_idx
|
||
|
||
func _mask_qr(qr_data: PackedByteArray) -> PackedByteArray:
|
||
var dimension: int = self.get_dimension()
|
||
var alignment_pattern_pos: Array[Vector2i] = self._get_alignment_pattern_positions()
|
||
|
||
# apply mask pattern
|
||
if ! self.auto_mask_pattern:
|
||
_mask(qr_data, dimension, alignment_pattern_pos, self.mask_pattern)
|
||
_place_format(qr_data, dimension, self.error_correction, self.mask_pattern)
|
||
_place_version(qr_data, self.version)
|
||
|
||
return qr_data
|
||
|
||
# get best mask pattern
|
||
var masked_qr: Array[PackedByteArray] = []
|
||
var mask_fns: Array[Callable] = _mask_pattern_fns()
|
||
|
||
for pattern_idx: int in range(mask_fns.size()):
|
||
var cur_qr: PackedByteArray = qr_data.duplicate()
|
||
_mask(cur_qr, dimension, alignment_pattern_pos, pattern_idx)
|
||
# normally the format version is applied AFTER getting the best pattern, but will produce worse qr codes
|
||
_place_format(cur_qr, dimension, self.error_correction, pattern_idx)
|
||
_place_version(cur_qr, self.version)
|
||
masked_qr.append(cur_qr)
|
||
var best_mask: int = _get_best_qr_mask(masked_qr, dimension)
|
||
self.mask_pattern = best_mask
|
||
qr_data = masked_qr[best_mask]
|
||
|
||
return qr_data
|
||
|
||
#### DEVEL TOOLS
|
||
|
||
static func _print_qr(data: PackedByteArray, dimension: int) -> void:
|
||
for y: int in range(dimension):
|
||
var row: String = ""
|
||
for x: int in range(dimension):
|
||
var value: int = data[y * dimension + x]
|
||
match value:
|
||
0:
|
||
row += "⬜"
|
||
1:
|
||
row += "⬛"
|
||
2:
|
||
row += "🟨"
|
||
3:
|
||
row += "🟦"
|
||
_:
|
||
row += "🟥"
|
||
print(row)
|
||
|
||
static func _bin_to_string(value: int, bits: int = 8) -> String:
|
||
var val: String = ""
|
||
for idx: int in range(bits):
|
||
if idx % 4 == 0:
|
||
val = " " + val
|
||
val = str(int(bool(value & (1 << idx)))) + val
|
||
return val.strip_edges()
|
||
|
||
static func _arr_to_string(arr: PackedByteArray) -> String:
|
||
var val: String = ""
|
||
for byte: int in arr:
|
||
val += "[" + _bin_to_string(byte, 8) + "] "
|
||
return val.strip_edges()
|