4
0
Fork 0
WolfBox/addons/qr_code/qr_code.gd

1478 lines
54 KiB
GDScript

extends RefCounted
const BitStream := preload("res://addons/qr_code/bit_stream.gd")
const ReedSolomon := preload("res://addons/qr_code/reed_solomon.gd")
const ShiftJIS := preload("res://addons/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 = {
"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 = [
# 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 = [
[],
[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 = [ 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 ]
static var _number_rx: RegEx = RegEx.create_from_string("[^\\d]*")
static var _alphanumeric_rx: RegEx = RegEx.create_from_string("[^0-9A-Z $%*+\\-.\\/:]*")
## cached qr data
var _cached_qr: PackedByteArray = []
## 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
self._clear_cache()
func set_version(new_version: int) -> void:
if new_version == version:
return
version = clampi(new_version, 1, 40)
self._clear_cache()
func set_error_correction(new_error_correction: ErrorCorrection) -> void:
if new_error_correction == error_correction:
return
error_correction = new_error_correction
self._clear_cache()
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()
self._clear_cache()
func set_use_eci(new_use_eci: bool) -> void:
if new_use_eci == use_eci:
return
use_eci = new_use_eci
self._clear_cache()
func set_eci_value(new_eci_value: int) -> void:
if new_eci_value == eci_value:
return
eci_value = new_eci_value
self._clear_cache()
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
self._clear_cache()
func set_mask_pattern(new_mask_pattern: int) -> void:
if new_mask_pattern == mask_pattern:
return
mask_pattern = clampi(new_mask_pattern, 0, 7)
self._clear_cache()
## return the data which was put in
func get_input_data() -> Variant:
return _input_data
## get module count of one axis
func get_module_count() -> int:
return _calc_module_count(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.eci_value != ECI.ISO_8859_1:
# subtract roughly eci header size
cap -= 4
if input_size <= cap:
return idx + 1
return -1
static func _get_alphanumeric_number(char: String) -> int:
return _ALPHANUMERIC_CHARACTERS[char]
# 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))
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_module_count(version: int) -> int:
return 21 + 4 * (version - 1)
func _get_allignment_pattern_positions() -> Array[Vector2i]:
var module_count: int = self.get_module_count()
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 > module_count - 8 && col - 2 < 8 || \
row - 2 < 8 && col > module_count - 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
func generate_image(module_px_size: int = 1, light_module_color: Color = Color.WHITE, dark_module_color: Color = Color.BLACK, quiet_zone_size: int = 4) -> Image:
module_px_size = maxi(1, module_px_size)
quiet_zone_size = maxi(0, quiet_zone_size)
var qr_code: PackedByteArray = self.encode()
var module_count: int = self.get_module_count()
var image_size: int = (module_count + 2 * quiet_zone_size) * module_px_size
var image: Image = Image.create(image_size, image_size, false, Image.FORMAT_RGB8)
image.fill(light_module_color)
for y: int in range(module_count):
for x: int in range(module_count):
var color: Color = Color.PINK
match qr_code[x + y * module_count]:
0:
color = light_module_color
1:
color = dark_module_color
for offset_x: int in range(module_px_size):
for offset_y: int in range(module_px_size):
image.set_pixel((x + quiet_zone_size) * module_px_size + offset_x, (y + quiet_zone_size) * module_px_size + offset_y, color)
return image
func put_numeric(number: String) -> void:
if self.mode != Mode.NUMERIC || number != self._input_data:
self._clear_cache()
self.mode = Mode.NUMERIC
self._input_data = _number_rx.sub(number, "", true)
func put_alphanumeric(text: String) -> void:
if self.mode != Mode.ALPHANUMERIC || text != self._input_data:
self._clear_cache()
self.mode = Mode.ALPHANUMERIC
self._input_data = _alphanumeric_rx.sub(text, "", true)
func put_byte(data: PackedByteArray) -> void:
if self.mode != Mode.BYTE || data != self._input_data:
self._clear_cache()
self.mode = Mode.BYTE
self._input_data = data
func put_kanji(data: String) -> void:
if self.mode != Mode.KANJI || data != self._input_data:
self._clear_cache()
self.mode = Mode.KANJI
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_module_count
func encode() -> PackedByteArray:
if !self._cached_qr.is_empty():
return self._cached_qr.duplicate()
if self.auto_version:
self.version = self.calc_min_version()
var data_stream: BitStream = self._encode_data()
var err_correction: Array = self._error_correction(data_stream)
var structured_data: BitStream = self._structure_data(data_stream, err_correction)
var qr_data: PackedByteArray = self._place_modules(structured_data)
qr_data = self._mask_qr(qr_data)
self._cached_qr = qr_data.duplicate()
return qr_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 _clear_cache() -> void:
self._cached_qr.clear()
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, module_count: int, pos: Vector2i) -> void:
for row: int in range(7):
for col: int in range(7):
data[(pos.x + row) + (pos.y + col) * module_count] = 1
for idx: int in range(5):
data[(pos.x + 1 + idx) + (pos.y + 1) * module_count] = 0
data[(pos.x + 1 + idx) + (pos.y + 5) * module_count] = 0
for idx: int in range(3):
data[(pos.x + 1) + (pos.y + 2 + idx) * module_count] = 0
data[(pos.x + 5) + (pos.y + 2 + idx) * module_count] = 0
# pos is center
# 5 x 5 size
static func _place_align_pattern(data: PackedByteArray, module_count: 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) * module_count] = 1
for idx: int in range(3):
data[(pos.x - 1 + idx) + (pos.y - 1) * module_count] = 0
data[(pos.x - 1 + idx) + (pos.y + 1) * module_count] = 0
data[(pos.x - 1) + (pos.y) * module_count] = 0
data[(pos.x + 1) + (pos.y) * module_count] = 0
static func _place_separators(data: PackedByteArray, module_count: int) -> void:
for idx: int in range(8):
# upper left
data[idx + 7 * module_count] = 0
data[7 + idx * module_count] = 0
# lower left
data[idx + (module_count - 8) * module_count] = 0
data[(module_count - 8) + idx * module_count] = 0
# upper right
data[(module_count - idx - 1) + 7 * module_count] = 0
data[7 + (module_count - idx - 1) * module_count] = 0
static func _place_timing_patterns(data: PackedByteArray, module_count: int) -> void:
for idx: int in range(module_count - 6 * 2):
data[6 + idx + 6 * module_count] = (idx + 1) % 2
data[6 + (6 + idx) * module_count] = (idx + 1) % 2
static func _is_data_module(module_count: int, alignment_pattern_pos: Array[Vector2i], pos: Vector2i) -> bool:
# finder with separation and format information area: upper left finder, upper right finder, lower left finder
# dark module is also included
if (pos.x <= 8 && pos.y <= 8) || (pos.x >= (module_count - 8) && pos.y <= 8) || (pos.x <= 8 && pos.y >= (module_count - 8)):
return false
# timing pattern
if pos.x == 6 || pos.y == 6:
return false
# version information area
# for version >= 7, upper and lower
# this check, will also success if it is in a finder area
if module_count >= 45 && ((pos.x >= module_count - 11 && pos.y <= 5) || (pos.x <= 5 && pos.y >= module_count - 11)):
return false
# check if in alignment pattern
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, module_count: 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 = module_count - 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(module_count):
if upwards:
row = module_count - 1 - row
for offset: int in range(2):
var pos: Vector2i = Vector2i(base_col - offset, row)
if _is_data_module(module_count, alignment_pattern_pos, pos):
data[pos.x + pos.y * module_count] = 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, module_count: int) -> int:
var rating: int = 0
# condition 1
# horizontal
for y: int in range(module_count):
var count: int = 0
var block_value: int = 0
for x: int in range(module_count):
var cur_value: int = data[x + y * module_count]
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(module_count):
var count: int = 0
var block_value: int = 0
for y: int in range(module_count):
var cur_value: int = data[x + y * module_count]
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(module_count - 1):
for y: int in range(module_count - 1):
var val: int = data[x + y * module_count] + data[x + 1 + y * module_count] + data[x + (y + 1) * module_count] + data[x + 1 + (y + 1) * module_count]
if val == 0 || val == 4:
rating += 3
# condition 3
for y: int in range(module_count):
for x: int in range(module_count - 6):
var start_idx: int = x + y * module_count
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 <= (module_count - 10) && data[start_idx + 7] && data[start_idx + 8] && data[start_idx + 9] && data[start_idx + 10]:
rating += 40
for x: int in range(module_count):
for y: int in range(module_count - 6):
if (!data[x + y * module_count]
&& data[x + (y + 1) * module_count]
&& !data[x + (y + 2) * module_count]
&& !data[x + (y + 3) * module_count]
&& !data[x + (y + 4) * module_count]
&& data[x + (y + 5) * module_count]
&& !data[x + (y + 6) * module_count]):
if y >= 4 && data[x + (y - 1) * module_count] && data[x + (y - 2) * module_count] && data[x + (y - 3) * module_count] && data[x + (y - 4) * module_count]:
rating += 40
if y <= (module_count - 11) && data[x + (y + 7) * module_count] && data[x + (y + 8) * module_count] && data[x + (y + 9) * module_count] && data[x + (y + 10) * module_count]:
rating += 40
# condition 4
var dark_mods: int = data.count(0)
var ratio: float = dark_mods / float(module_count * module_count)
var percent: int = int((ratio * 100) - 50)
rating += absi(percent) / 5 * 10
return rating
static func _place_format(qr_data: PackedByteArray, module_count: 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 * module_count] = int(_get_state(code, 14 - idx))
# vertical
qr_data[8 + pos * module_count] = int(_get_state(code, idx))
# lower left finder
for idx: int in range(7):
qr_data[8 + (module_count - 1 - idx) * module_count] = int(_get_state(code, 14 - idx))
# upper right finder
for idx: int in range(8):
qr_data[(module_count - 1 - idx) + 8 * module_count] = 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 module_count: int = _calc_module_count(version)
for idx: int in range(18):
var x: int = idx / 3
var y: int = module_count - 11 + idx % 3
qr_data[x + y * module_count] = int(_get_state(code, idx))
qr_data[y + x * module_count] = 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 module_count: int = self.get_module_count()
qr_data.resize(module_count * module_count)
# place upper left finder
_place_finder(qr_data, module_count, Vector2i(0, 0))
# place lower left finder
_place_finder(qr_data, module_count, Vector2i(0, module_count - 7))
# place upper right finder
_place_finder(qr_data, module_count, Vector2i(module_count - 7, 0))
_place_separators(qr_data, module_count)
var alignment_pattern_pos: Array[Vector2i] = self._get_allignment_pattern_positions()
for pos: Vector2i in alignment_pattern_pos:
_place_align_pattern(qr_data, module_count, pos)
_place_timing_patterns(qr_data, module_count)
# dark module
qr_data[8 + (module_count - 8) * module_count] = 1
# place data
_place_data(qr_data, module_count, alignment_pattern_pos, structured_data)
return qr_data
static func _mask(qr_data: PackedByteArray, module_count: int, alignment_pattern_pos: Array[Vector2i], mask_pattern: int) -> void:
var mask_fn: Callable = _mask_pattern_fns()[mask_pattern]
for x: int in range(module_count):
for y: int in range(module_count):
var pos: Vector2i = Vector2i(x, y)
if _is_data_module(module_count, alignment_pattern_pos, pos):
var idx: int = x + y * module_count
qr_data[idx] = int(mask_fn.call(pos)) ^ qr_data[idx]
# return mask pattern number
func _get_best_qr_mask(masked_qrs: Array[PackedByteArray], module_count: 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], module_count)
if rating < cur_min_value:
min_idx = idx
cur_min_value = rating
return min_idx
func _mask_qr(qr_data: PackedByteArray) -> PackedByteArray:
var module_count: int = self.get_module_count()
var alignment_pattern_pos: Array[Vector2i] = self._get_allignment_pattern_positions()
# apply mask pattern
if !self.auto_mask_pattern:
_mask(qr_data, module_count, alignment_pattern_pos, self.mask_pattern)
_place_format(qr_data, module_count, 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, module_count, 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, module_count, 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, module_count)
self.mask_pattern = best_mask
qr_data = masked_qr[best_mask]
return qr_data
#### DEVEL TOOLS
static func _print_qr(data: PackedByteArray, module_count: int) -> void:
for y: int in range(module_count):
var row: String = ""
for x: int in range(module_count):
var value: int = data[y * module_count + 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()