Files
2026-05-14 12:18:42 +02:00

1469 lines
53 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()