├── .gitignore ├── qrcode.bmp ├── qr_code_layouts.xlsx ├── bitstream.odin ├── readme.md ├── tests.odin ├── license.md ├── debug.odin ├── reed_solomon ├── field.odin └── poly.odin ├── image.odin ├── types.odin ├── data_encoder.odin ├── qrcode.odin └── versions.odin /.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | *.pdb 3 | *.exe 4 | unused/ 5 | *.txt -------------------------------------------------------------------------------- /qrcode.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kalsprite/qrcode/HEAD/qrcode.bmp -------------------------------------------------------------------------------- /qr_code_layouts.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kalsprite/qrcode/HEAD/qr_code_layouts.xlsx -------------------------------------------------------------------------------- /bitstream.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import "core:fmt" 4 | 5 | Bit_Stream :: struct { 6 | data: [dynamic]byte, 7 | bit_count: int, // Total number of actual bits 8 | } 9 | 10 | create_bit_stream :: proc() -> Bit_Stream { 11 | return Bit_Stream{data = make([dynamic]byte), bit_count = 0} 12 | } 13 | 14 | append_bit :: proc(bs: ^Bit_Stream, bit: bool) { 15 | byte_index := bs.bit_count / 8 16 | bit_index := uint(bs.bit_count % 8) 17 | 18 | if byte_index >= len(bs.data) { 19 | append(&bs.data, 0) 20 | } 21 | if bit { 22 | bs.data[byte_index] |= (1 << (7 - bit_index)) 23 | } 24 | bs.bit_count += 1 25 | } 26 | append_bits :: proc(bs: ^Bit_Stream, value: u32, count: int) { 27 | for i := count - 1; i >= 0; i -= 1 { 28 | bit := (value & (1 << u32(i))) != 0 29 | append_bit(bs, bit) 30 | } 31 | } 32 | 33 | destroy_bit_stream :: proc(bs: ^Bit_Stream) { 34 | delete(bs.data) 35 | } 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # QR-Code Generator 2 | 3 | Basic Implementation of QRCode Spec to ISO 18004:2015, with the limitations listed in `Unsupported Features`. 4 | 5 | Versions 1H and 4H have been tested. Any other version who knows.. (Very likely there are typos in the version_data table. It was manually trascribed) 6 | 7 | ## Unsupported Features 8 | 9 | - Micro Codes 10 | - Kanji 11 | - ECI 12 | - Reflectance Reversal 13 | - Mirror Orientation 14 | - C, K, R table-data is not populated on version 31-40; they wont work until it is filled 15 | - Decoding 16 | 17 | ## Reed-Solomon Encoding 18 | 19 | This is a very basic implementation, and I'd say a low performance implementation. 20 | 21 | ## Useful Utilities 22 | 23 | [QRazyBox](https://merri.cx/qrazybox/) - a nice tool for decoding a qr-code and seeing what it thinks the data is. Used extensively to compare against produced qrcodes to help zero-in on implementation errors. 24 | 25 | [Thonky QRCode Tutorial](https://www.thonky.com/qr-code-tutorial/) - Excellent resource to supplement the ISO, which is not great at explaining itself. 26 | -------------------------------------------------------------------------------- /tests.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import "core:fmt" 4 | import "core:testing" 5 | 6 | @(test) 7 | segmenter :: proc(t: ^testing.T) { 8 | data := "Pa! &abc123 DEFGHIJ" 9 | segments := segment_data(transmute([]u8)data) 10 | testing.expect(t, len(segments) == 2, "Expected Two segments") 11 | testing.expect(t, segments[0].mode == .Byte, "Segment 1 as .Byte") 12 | testing.expect(t, segments[1].mode == .Alphanumeric, "Segment 1 as .Alphanumeric") 13 | delete(segments) 14 | 15 | data = "12345" 16 | segments = segment_data(transmute([]u8)data) 17 | testing.expect(t, len(segments) == 1, "Expected 1 segment") 18 | testing.expect(t, segments[0].mode == .Numeric, "Segment 1 as .Numeric") 19 | delete(segments) 20 | 21 | data = "ABC123" 22 | segments = segment_data(transmute([]u8)data) 23 | testing.expect(t, len(segments) == 1, "Expected 1 segment") 24 | testing.expect(t, segments[0].mode == .Alphanumeric, "Segment 1 as .Alphanumeric") 25 | delete(segments) 26 | 27 | data = "Hello, world!" 28 | segments = segment_data(transmute([]u8)data) 29 | testing.expect(t, len(segments) == 1, "Expected 1 segment") 30 | testing.expect(t, segments[0].mode == .Byte, "Segment 1 as .Byte") 31 | delete(segments) 32 | } 33 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2025 jon-lipstate 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /debug.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import "core:fmt" 4 | import "core:strings" 5 | 6 | make_printable_grid :: proc(reserved: [][]bool) -> [][]rune { 7 | _, grid := make_2d_slice(len(reserved), len(reserved), rune) 8 | 9 | for y in 0 ..< len(reserved) { 10 | for x in 0 ..< len(reserved) { 11 | if reserved[y][x] { 12 | grid[y][x] = 'X' 13 | } else { 14 | grid[y][x] = '_' 15 | } 16 | } 17 | } 18 | return grid 19 | } 20 | 21 | print_grid :: proc(grid: [][]rune) { 22 | sb := strings.builder_make() 23 | for row in grid { 24 | for col in row { 25 | strings.write_rune(&sb, col) 26 | } 27 | strings.write_rune(&sb, '\n') 28 | } 29 | fmt.println(strings.to_string(sb)) 30 | } 31 | 32 | print_version :: proc(v: Version_Data, ecc: Error_Level) { 33 | fmt.printf("Version: %v\n", v.id) 34 | fmt.printf("Size: %v\n", v.size) 35 | fmt.printf("total_codewords: %v\n", v.total_codewords) 36 | fmt.printf("remainder_bits: %v\n", v.remainder_bits) 37 | fmt.printf("alignment_centers: %v\n", v.alignment_centers) 38 | enc := v.encodings[int(ecc)] 39 | fmt.printf("%#v\n", enc) 40 | 41 | } 42 | 43 | // debug helper to see the bounding box of a emplaced byte 44 | print_br_corner :: proc(p: [8][2]int, i: int, current_up: bool) { 45 | min_x := 999 46 | min_y := 999 47 | max_x := 0 48 | max_y := 0 49 | for b in p { 50 | if b.x > max_x {max_x = b.x} 51 | if b.y > max_y {max_y = b.y} 52 | if b.x < min_x {min_x = b.x} 53 | if b.y < min_y {min_y = b.y} 54 | } 55 | fmt.printf( 56 | "Byte %v Range: %v,%v to %v,%v; up: %v\n", 57 | i, 58 | min_x, 59 | min_y, 60 | max_x, 61 | max_y, 62 | current_up, 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /reed_solomon/field.odin: -------------------------------------------------------------------------------- 1 | package reed_solomon 2 | 3 | QR_CODE_POLY :: 0x11D // x^8 + x^4 + x^3 + x^2 + 1 4 | 5 | @(private) 6 | exp_table: [256]byte 7 | 8 | @(private) 9 | log_table: [256]byte 10 | 11 | @(init) 12 | init_gf256 :: proc() { 13 | x: u16 = 1 14 | for &e in exp_table { 15 | e = u8(x) 16 | 17 | x <<= 1 // Multiply by 2 (left shift) 18 | 19 | // If overflow, XOR with the reduction polynomial 20 | if x & 0x100 != 0 { 21 | x ~= QR_CODE_POLY 22 | } 23 | } 24 | // Initialize log table (inverse of exp table) 25 | for i in 0 ..< 255 { 26 | log_table[exp_table[i]] = u8(i) 27 | } 28 | log_table[0] = 0 // set 0 29 | } 30 | 31 | add :: proc(a, b: byte) -> byte { 32 | return a ~ b // Addition in GF(2^8) is just XOR 33 | } 34 | 35 | subtract :: proc(a, b: byte) -> byte { 36 | return a ~ b // Subtraction in GF(2^8) is the same as addition 37 | } 38 | 39 | multiply :: proc(a, b: byte) -> byte { 40 | if a == 0 || b == 0 {return 0} 41 | 42 | log_a := u16(log_table[a]) 43 | log_b := u16(log_table[b]) 44 | return exp_table[(log_a + log_b) % 255] 45 | } 46 | 47 | inverse :: proc(a: byte) -> (byte, bool) { 48 | if a == 0 {return 0, false} 49 | 50 | log_a := u16(log_table[a]) 51 | return exp_table[255 - log_a], true 52 | } 53 | 54 | divide :: proc(a, b: byte) -> (byte, bool) { 55 | if b == 0 {return 0, false} 56 | if a == 0 {return 0, true} 57 | 58 | log_a := u16(log_table[a]) 59 | log_b := u16(log_table[b]) 60 | return exp_table[(log_a + 255 - log_b) % 255], true 61 | } 62 | 63 | power :: proc(a: byte, power: int) -> byte { 64 | if a == 0 {return 0} 65 | 66 | log_a := u16(log_table[a]) 67 | return exp_table[(log_a * u16(power)) % 255] 68 | } -------------------------------------------------------------------------------- /image.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import "core:bytes" 4 | import "core:math" 5 | import "core:os" 6 | 7 | // handrolled bmp generator; TODO: something better 8 | save_qr_code_as_bmp_raw :: proc(qr: ^QR_Code, filename: string, pixels_per_module: int) -> bool { 9 | if qr == nil {return false} 10 | 11 | span := len(qr.modules) 12 | total_span := span + 2 * qr.quiet_zone 13 | actual_size := total_span * pixels_per_module 14 | // BMP rows must be padded to a multiple of 4 bytes 15 | row_size := uint(actual_size * 3 + 3) & ~uint(3) 16 | image_size := row_size * uint(actual_size) 17 | 18 | // BMP file header (14 bytes) + DIB header (40 bytes) + pixel data 19 | file_size := 14 + 40 + image_size 20 | 21 | buffer := make([]byte, file_size) 22 | defer delete(buffer) 23 | 24 | // BMP File Header (14 bytes) 25 | buffer[0] = 'B' // Signature 'B' 26 | buffer[1] = 'M' // Signature 'M' 27 | buffer[2] = byte(file_size & 0xFF) // File size in bytes (lowest byte) 28 | buffer[3] = byte((file_size >> 8) & 0xFF) // Second byte 29 | buffer[4] = byte((file_size >> 16) & 0xFF) // Third byte 30 | buffer[5] = byte((file_size >> 24) & 0xFF) // Fourth byte (highest byte) 31 | // bytes 6-9 reserved (0) 32 | buffer[10] = 54 // Offset to start of pixel data 33 | // bytes 11-13 are 0 34 | 35 | // DIB Header (40 bytes) 36 | buffer[14] = 40 // Header size 37 | // bytes 15-17 are 0 38 | buffer[18] = byte(actual_size) // Width 39 | buffer[19] = byte(actual_size >> 8) 40 | buffer[20] = byte(actual_size >> 16) 41 | buffer[21] = byte(actual_size >> 24) 42 | buffer[22] = byte(actual_size) // Height 43 | buffer[23] = byte(actual_size >> 8) 44 | buffer[24] = byte(actual_size >> 16) 45 | buffer[25] = byte(actual_size >> 24) 46 | buffer[26] = 1 // Color planes 47 | buffer[27] = 0 48 | buffer[28] = 24 // Bits per pixel (24 for RGB) 49 | buffer[29] = 0 50 | // bytes 30-33 are 0 (no compression) 51 | buffer[34] = byte(image_size) // Image size 52 | buffer[35] = byte(image_size >> 8) 53 | buffer[36] = byte(image_size >> 16) 54 | buffer[37] = byte(image_size >> 24) 55 | 56 | // Standard resolution (72 DPI = 2835 pixels/meter) 57 | resolution := 2835 58 | buffer[38] = byte(resolution) 59 | buffer[39] = byte(resolution >> 8) 60 | buffer[40] = byte(resolution >> 16) 61 | buffer[41] = byte(resolution >> 24) 62 | buffer[42] = byte(resolution) 63 | buffer[43] = byte(resolution >> 8) 64 | buffer[44] = byte(resolution >> 16) 65 | buffer[45] = byte(resolution >> 24) 66 | // bytes 46-53 are 0 (color table info) 67 | 68 | // Generate the pixel data 69 | pixel_offset := 54 // Start of pixel data 70 | 71 | for y in 0 ..< actual_size { 72 | // In BMP, rows are stored bottom-to-top 73 | row := actual_size - 1 - y 74 | row_offset := pixel_offset + (row * int(row_size)) 75 | 76 | for x in 0 ..< actual_size { 77 | // Determine which module this pixel belongs to 78 | module_x := x / pixels_per_module - qr.quiet_zone 79 | module_y := y / pixels_per_module - qr.quiet_zone 80 | 81 | // Determine pixel color (black or white) 82 | is_black := false 83 | 84 | // Check if we're in the actual QR code area 85 | if module_x >= 0 && module_x < span && module_y >= 0 && module_y < span { 86 | is_black = qr.modules[module_y][module_x] 87 | } 88 | 89 | // Calculate position in buffer (BGR order in BMP) 90 | pos := row_offset + (x * 3) 91 | 92 | if is_black { 93 | // Black (0, 0, 0) 94 | buffer[pos] = 0 // Blue 95 | buffer[pos + 1] = 0 // Green 96 | buffer[pos + 2] = 0 // Red 97 | } else { 98 | // White (255, 255, 255) 99 | buffer[pos] = 255 // Blue 100 | buffer[pos + 1] = 255 // Green 101 | buffer[pos + 2] = 255 // Red 102 | } 103 | } 104 | } 105 | return os.write_entire_file(filename, buffer) 106 | } 107 | -------------------------------------------------------------------------------- /types.odin: -------------------------------------------------------------------------------- 1 | #+feature dynamic-literals 2 | package qrcode 3 | 4 | QR_Code :: struct { 5 | version: int, // QR code version (1-40) 6 | error_level: Error_Level, // Error correction level 7 | mask: int, // valid 0-7 8 | // module index 0,0 is top left of code 9 | modules: [][]bool, // The QR code matrix (true = black, false = white); always square == size 10 | backing: []bool, // backing memory for modules 11 | quiet_zone: int, // (NOT part of the matrix; to be used by the image coverter) spec-min is 4 modules 12 | } 13 | 14 | // Error_Level represents the error correction level 15 | Error_Level :: enum { 16 | L, // Low - 7% recovery capacity 17 | M, // Medium - 15% recovery capacity 18 | Q, // Quartile - 25% recovery capacity 19 | H, // High - 30% recovery capacity 20 | } 21 | encode_error_level :: proc(lvl: Error_Level) -> u8 { 22 | switch lvl { 23 | case .L: 24 | return 0b01 25 | case .M: 26 | return 0b00 27 | case .Q: 28 | return 0b11 29 | case .H: 30 | return 0b10 31 | } 32 | unreachable() 33 | } 34 | 35 | // EncodingMode represents the data encoding mode 36 | Encoding_Mode :: enum u8 { 37 | Numeric = 0b0001, // 1 38 | Alphanumeric = 0b0010, // 2 39 | Byte = 0b0100, // 4 40 | ECI = 0b0111, // 7 41 | Kanji = 0b1000, // 8 42 | } 43 | 44 | 45 | Segment :: struct { 46 | mode: Encoding_Mode, 47 | offset: int, // offset into parent slice 48 | data: []byte, 49 | eci_kind: ECI_Character_Set, // not used atm 50 | bit_cost: int, // cached 51 | } 52 | // not used atm: 53 | ECI_Character_Set :: enum u32 { 54 | NONE = 0, 55 | // ISO/IEC 646 Character Sets 56 | ISO_646_US = 1, // US ASCII 57 | ISO_646_GB = 2, // British ASCII 58 | ISO_646_FR = 3, // French ASCII 59 | ISO_646_DE = 4, // German ASCII 60 | ISO_646_IT = 5, // Italian ASCII 61 | ISO_646_ES = 6, // Spanish ASCII 62 | ISO_646_PT = 7, // Portuguese ASCII 63 | ISO_646_SE = 8, // Swedish ASCII 64 | 65 | // ISO/IEC 8859 Character Sets 66 | ISO_8859_1 = 9, // Latin-1 (Western European) 67 | ISO_8859_2 = 10, // Latin-2 (Central European) 68 | ISO_8859_3 = 11, // Latin-3 (South European) 69 | ISO_8859_4 = 12, // Latin-4 (North European) 70 | ISO_8859_5 = 13, // Cyrillic 71 | ISO_8859_6 = 14, // Arabic 72 | ISO_8859_7 = 15, // Greek 73 | ISO_8859_8 = 16, // Hebrew 74 | ISO_8859_9 = 17, // Latin-5 (Turkish) 75 | ISO_8859_10 = 18, // Latin-6 (Nordic) 76 | ISO_8859_11 = 19, // Thai 77 | ISO_8859_13 = 21, // Latin-7 (Baltic) 78 | ISO_8859_14 = 22, // Latin-8 (Celtic) 79 | ISO_8859_15 = 23, // Latin-9 80 | ISO_8859_16 = 24, // Latin-10 81 | 82 | // Unicode/UTF Character Sets 83 | UTF_8 = 26, // UTF-8 84 | 85 | // ISO/IEC 10646 Character Sets 86 | ISO_10646_UCS2 = 27, // UCS-2 (Basic Multilingual Plane) 87 | ISO_10646_UTF16 = 28, // UTF-16BE 88 | 89 | // Shift JIS Character Sets 90 | SHIFT_JIS = 20, // Shift JIS (JIS X 0208 Annex 1 + JIS X 0201) 91 | 92 | // Windows Character Sets 93 | WINDOWS_1250 = 29, // Windows-1250 (Central European) 94 | WINDOWS_1251 = 30, // Windows-1251 (Cyrillic) 95 | WINDOWS_1252 = 31, // Windows-1252 (Western European) 96 | WINDOWS_1256 = 32, // Windows-1256 (Arabic) 97 | 98 | // Chinese Character Sets 99 | GB18030 = 33, // GB18030 (Simplified Chinese) 100 | BIG5 = 34, // Big5 (Traditional Chinese) 101 | 102 | // Korean Character Sets 103 | KS_X_1001 = 35, // KS X 1001 (Korean) 104 | 105 | // Private Use 106 | PRIVATE_CUSTOM_1 = 899, 107 | PRIVATE_CUSTOM_2 = 900, 108 | 109 | // Default (when no ECI is specified) 110 | DEFAULT = 0, 111 | } 112 | 113 | Version_Data :: struct { 114 | id: int, 115 | size: int, // Size in modules (e.g. V1: 21x21) 116 | total_codewords: int, 117 | remainder_bits: int, // # of remainder bits to add after data 118 | alignment_centers: []int, 119 | encodings: []Encoding, // typically 4 L, M, Q, H (some micro dont have H) 120 | } 121 | Encoding :: struct { 122 | level: Error_Level, 123 | data_codewords: int, 124 | ec_codewords: int, 125 | blocks: []EC_Block, 126 | } 127 | EC_Block :: struct { 128 | p: int, // mis-decode bytes 129 | no_blocks: int, // Number of blocks 130 | c: int, // total bytes 131 | k: int, // data bytes 132 | r: int, // ecc capacity (1/2 bytes) 133 | } 134 | -------------------------------------------------------------------------------- /reed_solomon/poly.odin: -------------------------------------------------------------------------------- 1 | package reed_solomon 2 | 3 | import "core:fmt" 4 | 5 | Polynomial :: struct { 6 | data: []byte, 7 | orig: []byte, 8 | } 9 | 10 | into_poly :: proc(data: []byte, owned := false) -> (p: Polynomial) { 11 | if len(data) == 0 { 12 | return 13 | } 14 | 15 | // Find first non-zero coefficient 16 | non_zero_offset := 0 17 | for d, i in data { 18 | if d != 0 { 19 | non_zero_offset = i 20 | break 21 | } 22 | } 23 | 24 | // If all data are zero, return zero polynomial 25 | if non_zero_offset == len(data) - 1 { 26 | return 27 | } 28 | 29 | // Start at first non-zero coefficient, which can be an offset of zero 30 | p.data = data[non_zero_offset:] 31 | 32 | // Only set p.orig if data is owned, and not a slice into other memory 33 | if owned { 34 | p.orig = data 35 | } 36 | return 37 | } 38 | 39 | destroy_poly :: proc(p: Polynomial) { 40 | delete(p.orig) 41 | } 42 | 43 | degree :: proc(p: Polynomial) -> (res: int) { 44 | return len(p.data) - 1 if len(p.data) > 0 else 0 45 | } 46 | 47 | get_coefficient :: proc(p: Polynomial, degree: int) -> (coeff: byte, ok: bool) { 48 | if p.data == nil || degree < 0 || degree > len(p.data) - 1 { 49 | return 50 | } 51 | return p.data[len(p.data) - 1 - degree], true 52 | } 53 | 54 | add_poly :: proc(a, b: Polynomial, allocator := context.allocator) -> Polynomial { 55 | context.allocator = allocator 56 | 57 | a_len := len(a.data) 58 | b_len := len(b.data) 59 | 60 | if a_len == 0 {return b} 61 | if b_len == 0 {return a} 62 | 63 | max_len := max(a_len, b_len) 64 | result := make([]byte, max_len) 65 | 66 | for ad, i in a.data { 67 | result[max_len - a_len + i] = ad // Load 'a' into new poly 68 | } 69 | for bd, i in b.data { 70 | result[max_len - b_len + i] ~= bd // XOR for GF(2^8) addition 71 | } 72 | 73 | return into_poly(result, true) // Result is owned 74 | } 75 | 76 | // clones 77 | multiply_scalar :: proc(p: ^Polynomial, scalar: byte, allocator := context.allocator) -> Polynomial { 78 | context.allocator = allocator 79 | 80 | if scalar == 0 || p.data == nil { 81 | return into_poly(nil) 82 | } 83 | 84 | if scalar == 1 { 85 | return into_poly(p.data) 86 | } 87 | 88 | result := make([]byte, len(p.data)) 89 | 90 | for pd, i in p.data { 91 | result[i] = multiply(pd, scalar) 92 | } 93 | 94 | return into_poly(result, true) // owned 95 | } 96 | 97 | // Multiply two polynomials 98 | multiply_poly :: proc(a, b: Polynomial, allocator := context.allocator) -> Polynomial { 99 | context.allocator = allocator 100 | 101 | if len(a.data) == 0 || len(b.data) == 0 { 102 | return into_poly(nil) 103 | } 104 | 105 | // Create a result polynomial with degree = deg(a) + deg(b) 106 | result_len := len(a.data) + len(b.data) - 1 107 | result := make([]byte, result_len) 108 | 109 | // Multiply each term 110 | for ad, i in a.data { 111 | for bd, j in b.data { 112 | term := multiply(ad, bd) 113 | result[i + j] ~= term // XOR for GF(2^8) addition 114 | } 115 | } 116 | 117 | return into_poly(result, true) // Owned 118 | } 119 | 120 | // Divide polynomials: a / b = quotient with remainder 121 | divide_poly :: proc(a, b: Polynomial, return_quotient := false, allocator := context.allocator) -> (quotient, remainder: Polynomial, ok: bool) { 122 | context.allocator = allocator 123 | 124 | if len(b.data) == 0 || b.data[0] == 0 { 125 | fmt.eprintln("Error: Division by zero polynomial") 126 | ok = false 127 | return 128 | } 129 | if a.data == nil { 130 | return into_poly(nil), into_poly(nil), true 131 | } 132 | 133 | // Copy a's data for the remainder 134 | remainder_coeffs := make([]byte, len(a.data)) 135 | copy(remainder_coeffs, a.data) 136 | remainder = into_poly(remainder_coeffs, true) // owned 137 | 138 | // If a's degree is less than b's, quotient is 0 and remainder is a 139 | if degree(remainder) < degree(b) { 140 | return into_poly(nil), remainder, true 141 | } 142 | 143 | // Calculate the quotient's size 144 | quotient_degree := degree(remainder) - degree(b) 145 | quotient_coeffs := make([]byte, quotient_degree + 1) 146 | 147 | // Get the leading coefficient of divisor 148 | normalizer, iok := inverse(b.data[0]) 149 | if !iok { 150 | fmt.eprintln("Error: Cannot invert leading coefficient") 151 | ok = false 152 | return 153 | } 154 | 155 | // Polynomial long division 156 | for remainder_degree := degree(remainder); remainder_degree >= degree(b); { 157 | if remainder.data[0] == 0 { 158 | // Move to next term 159 | remainder.data = remainder.data[1:] 160 | continue 161 | } 162 | 163 | // Calculate quotient term 164 | coef := multiply(remainder.data[0], normalizer) 165 | quotient_pos := remainder_degree - degree(b) 166 | quotient_coeffs[quotient_pos] = coef 167 | 168 | // Subtract b * coef from remainder 169 | for i in 0 ..< len(b.data) { 170 | idx := i 171 | if idx < len(remainder.data) { 172 | remainder.data[idx] ~= multiply(b.data[i], coef) 173 | } 174 | } 175 | 176 | // Update remainder 177 | remainder.data = remainder.data[1:] // Nix terms as we are able to do long-division steps 178 | remainder_degree = degree(remainder) 179 | } 180 | if return_quotient { 181 | quotient = into_poly(quotient_coeffs, true) // Owned 182 | } else { 183 | delete(quotient_coeffs) 184 | } 185 | 186 | return quotient, remainder, true 187 | } 188 | 189 | // Evaluate the polynomial at a point 190 | evaluate :: proc(p: Polynomial, x: byte) -> (res: byte, ok: bool) { 191 | if p.data == nil { 192 | return 193 | } 194 | 195 | // Horner's method for polynomial evaluation 196 | for pd in p.data { 197 | // result = result * x + coefficient 198 | res = add(multiply(res, x), pd) 199 | } 200 | 201 | return res, true 202 | } 203 | 204 | // Generate generator polynomial for Reed-Solomon encoding 205 | generate_generator :: proc(degree: int, allocator := context.allocator) -> Polynomial { 206 | context.allocator = allocator 207 | 208 | // Start with (x + a^0) 209 | g := into_poly([]byte{1, exp_table[0]}) 210 | 211 | // Multiply by (x + a^i) for i from 1 to degree-1 212 | for i in 1 ..< degree { 213 | term := into_poly([]byte{1, exp_table[i]}) 214 | tmp := multiply_poly(g, term, allocator) 215 | 216 | destroy_poly(g) // safe-deletes 217 | g = tmp 218 | } 219 | 220 | return g 221 | } 222 | 223 | // Encode message using Reed-Solomon 224 | encode_rs :: proc(message: []byte, ec_bytes: int, allocator := context.allocator) -> (res: []byte) { 225 | context.allocator = allocator 226 | 227 | if ec_bytes == 0 {return message} 228 | 229 | generator := generate_generator(ec_bytes) 230 | message_poly := into_poly(message) 231 | defer destroy_poly(generator) 232 | 233 | // Multiply by x^(ec_bytes) 234 | x_ec := make([]byte, ec_bytes + 1) 235 | x_ec[0] = 1 // x^(ec_bytes) 236 | x_poly := into_poly(x_ec, true) 237 | defer destroy_poly(x_poly) 238 | extended_message := multiply_poly(message_poly, x_poly) 239 | defer destroy_poly(extended_message) 240 | 241 | _, remainder, div_ok := divide_poly(extended_message, generator) 242 | assert(div_ok) 243 | defer destroy_poly(remainder) 244 | 245 | res = make([]byte, ec_bytes) 246 | 247 | // Fill in the error correction bytes 248 | rem_offset := max(0, len(remainder.data) - ec_bytes) 249 | for i in 0 ..< min(ec_bytes, len(remainder.data)) { 250 | res[i] = remainder.data[rem_offset + i] 251 | } 252 | 253 | return 254 | } 255 | 256 | to_string :: proc(p: ^Polynomial, allocator := context.allocator) -> string { 257 | context.allocator = allocator 258 | 259 | if len(p.data) == 0 {return "0"} 260 | 261 | result := "" 262 | for i := 0; i < len(p.data); i += 1 { 263 | coef := p.data[i] 264 | if coef == 0 {continue} 265 | 266 | degree := len(p.data) - 1 - i 267 | 268 | if len(result) > 0 {result = fmt.aprintf("%s + ", result)} 269 | 270 | if degree == 0 { 271 | result = fmt.aprintf("%s%d", result, coef) 272 | } else if degree == 1 { 273 | if coef == 1 { 274 | result = fmt.aprintf("%sx", result) 275 | } else { 276 | result = fmt.aprintf("%s%dx", result, coef) 277 | } 278 | } else { 279 | if coef == 1 { 280 | result = fmt.aprintf("%sx^%d", result, degree) 281 | } else { 282 | result = fmt.aprintf("%s%dx^%d", result, coef, degree) 283 | } 284 | } 285 | } 286 | return result 287 | } 288 | 289 | import "core:log" 290 | import "core:slice" 291 | import "core:testing" 292 | 293 | Test_Vector :: struct { 294 | message: []byte, // Input 295 | ecc: []byte, // Expected ECC output 296 | } 297 | 298 | test_vectors := []Test_Vector{ 299 | { // 4-byte message with 4 ECC bytes 300 | {0x40, 0xd2, 0x75, 0x47}, // 64, 210, 117, 71 301 | {0x55, 0x7e, 0xb6, 0x3d}, // 85, 126, 182, 61 302 | }, 303 | { // Test case 2: QR code typical message 304 | {0x40, 0xd2, 0x75, 0x47, 0x76, 0x17, 0x32, 0x06, 0x27, 0x26, 0x96, 0xc6, 0xc6, 0x96, 0x70, 0xec}, 305 | {0xbc, 0x2a, 0x90, 0x13, 0x6b, 0xaf, 0xef, 0xfd, 0x4b, 0xe0}, 306 | }, 307 | } 308 | 309 | @(test) 310 | test_reed_solomon :: proc(t: ^testing.T) { 311 | for test, i in test_vectors { 312 | ecc := encode_rs(test.message, len(test.ecc)) 313 | defer delete(ecc) 314 | 315 | passed := slice.equal(test.ecc, ecc) 316 | testing.expectf(t, passed, "Expected %v to equal %v", ecc, test.ecc) 317 | 318 | log.infof("Test case %v %v", i + 1, "passed" if passed else "failed") 319 | log.info("Message: ", test.message) 320 | log.info("Expected ECC:", test.ecc) 321 | log.info("Actual ECC: ", ecc) 322 | } 323 | } -------------------------------------------------------------------------------- /data_encoder.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import "core:fmt" 4 | import "core:unicode/utf8" 5 | ///////////////////////////////////////////////////////////////////////////////////////////// 6 | // Greedy Solver - maybe switch to DP ?? 7 | // TODO: ISO18004:2015 Annex J has a suggested algo; didnt see until was done.. compare encoding-perf to mine 8 | segment_data :: proc(data: []byte) -> []Segment { 9 | if len(data) == 0 { 10 | return nil 11 | } 12 | 13 | Encoded_Index :: struct { 14 | index: int, 15 | mode: Encoding_Mode, 16 | } 17 | 18 | // First pass: Identify mode change indices 19 | mode_changes := make([dynamic]Encoded_Index) 20 | defer delete(mode_changes) 21 | 22 | i := 0 23 | r, size := utf8.decode_rune_in_bytes(data[i:]) 24 | current_mode := determine_mode_for_rune(r) 25 | append(&mode_changes, Encoded_Index{0, current_mode}) 26 | i += size 27 | 28 | for i < len(data) { 29 | r, size = utf8.decode_rune_in_bytes(data[i:]) 30 | 31 | // If it's an invalid UTF-8 sequence, treat it as a single byte 32 | if r == utf8.RUNE_ERROR && size == 1 { 33 | optimal_mode := Encoding_Mode.Byte 34 | 35 | if optimal_mode != current_mode { 36 | append(&mode_changes, Encoded_Index{i, optimal_mode}) 37 | current_mode = optimal_mode 38 | } 39 | 40 | i += 1 41 | } else { 42 | // Valid UTF-8 sequence 43 | optimal_mode := determine_mode_for_rune(r) 44 | 45 | if optimal_mode != current_mode { 46 | append(&mode_changes, Encoded_Index{i, optimal_mode}) 47 | current_mode = optimal_mode 48 | } 49 | 50 | i += size 51 | } 52 | } 53 | /////////////////////////////////////////////////////////////////////////////// 54 | // Second pass: Generate lowest-cost segments 55 | segments := make([dynamic]Segment) 56 | 57 | if len(mode_changes) == 0 { 58 | return segments[:] 59 | } 60 | 61 | for i := 0; i < len(mode_changes); i += 1 { 62 | current_change := mode_changes[i] 63 | current_start := current_change.index 64 | current_mode := current_change.mode 65 | 66 | // Calculate end index 67 | current_end := len(data) 68 | if i + 1 < len(mode_changes) { 69 | current_end = mode_changes[i + 1].index 70 | } 71 | 72 | current_segment := Segment { 73 | mode = current_mode, 74 | offset = current_start, 75 | data = data[current_start:current_end], 76 | } 77 | 78 | current_cost := calculate_segment_bits(current_segment) 79 | current_segment.bit_cost = current_cost 80 | 81 | if len(segments) > 0 { 82 | prev_segment := &segments[len(segments) - 1] 83 | 84 | // If same mode or merging would be cheaper, merge 85 | if prev_segment.mode == current_mode { 86 | prev_segment.data = data[prev_segment.offset:current_end] 87 | // Recalculate the bit cost for the extended segment 88 | prev_segment.bit_cost = calculate_segment_bits(prev_segment^) 89 | continue 90 | } 91 | 92 | // Check if merging would be cheaper 93 | merged_data := data[prev_segment.offset:current_end] 94 | best_mode := determine_best_mode_for_data(merged_data) 95 | 96 | merged_segment := Segment { 97 | mode = best_mode, 98 | offset = prev_segment.offset, 99 | data = merged_data, 100 | } 101 | 102 | merged_cost := calculate_segment_bits(merged_segment) 103 | merged_segment.bit_cost = merged_cost 104 | 105 | separate_cost := prev_segment.bit_cost + current_cost 106 | 107 | if merged_cost < separate_cost { 108 | // Merging is cheaper, update previous segment 109 | prev_segment^ = merged_segment 110 | continue 111 | } 112 | } 113 | 114 | // Add as a new segment 115 | append(&segments, current_segment) 116 | } 117 | 118 | return segments[:] 119 | } 120 | 121 | calculate_segment_bits :: proc(segment: Segment, version := 40) -> int { 122 | // Mode indicator (4 bits) 123 | total_bits := 4 124 | 125 | switch segment.mode { 126 | case .Numeric: 127 | if version >= 1 && version <= 9 { 128 | total_bits += 10 129 | } else if version >= 10 && version <= 26 { 130 | total_bits += 12 131 | } else { 132 | total_bits += 14 133 | } 134 | 135 | // Data bits 136 | digit_count := len(segment.data) 137 | total_bits += (digit_count / 3) * 10 138 | 139 | remainder := digit_count % 3 140 | if remainder == 1 { 141 | total_bits += 4 142 | } else if remainder == 2 { 143 | total_bits += 7 144 | } 145 | 146 | case .Alphanumeric: 147 | if version >= 1 && version <= 9 { 148 | total_bits += 9 149 | } else if version >= 10 && version <= 26 { 150 | total_bits += 11 151 | } else { 152 | total_bits += 13 153 | } 154 | 155 | // Data bits 156 | char_count := len(segment.data) 157 | total_bits += (char_count / 2) * 11 158 | 159 | if char_count % 2 == 1 { 160 | total_bits += 6 161 | } 162 | 163 | case .Byte: 164 | if version >= 1 && version <= 9 { 165 | total_bits += 8 166 | } else { 167 | total_bits += 16 168 | } 169 | 170 | // Data bits 171 | total_bits += len(segment.data) * 8 172 | 173 | case .Kanji: 174 | if version >= 1 && version <= 9 { 175 | total_bits += 8 176 | } else if version >= 10 && version <= 26 { 177 | total_bits += 10 178 | } else { 179 | total_bits += 12 180 | } 181 | 182 | // Data bits (assuming 2 bytes per Kanji character) 183 | total_bits += (len(segment.data) / 2) * 13 184 | 185 | case .ECI: 186 | // ECI indicator (no character count) 187 | // ECI assignment number (8, 16, or 24 bits) 188 | eci_value := u32(segment.eci_kind) 189 | 190 | if eci_value <= 127 { 191 | total_bits += 8 192 | } else if eci_value <= 16383 { 193 | total_bits += 16 194 | } else { 195 | total_bits += 24 196 | } 197 | 198 | // Data bits (typically in Byte mode) 199 | total_bits += len(segment.data) * 8 200 | } 201 | 202 | return total_bits 203 | } 204 | 205 | determine_best_mode_for_data :: proc(data: []byte) -> Encoding_Mode { 206 | is_numeric := true 207 | for b in data { 208 | if b < '0' || b > '9' { 209 | is_numeric = false 210 | break 211 | } 212 | } 213 | 214 | if is_numeric { 215 | return .Numeric 216 | } 217 | 218 | // Check if all characters are alphanumeric 219 | is_alpha := true 220 | for b in data { 221 | if !is_alphanumeric(b) { 222 | is_alpha = false 223 | break 224 | } 225 | } 226 | 227 | if is_alpha { 228 | return .Alphanumeric 229 | } 230 | 231 | // TODO: Kanji/ECI 232 | 233 | return .Byte 234 | } 235 | 236 | is_alphanumeric :: proc(b: byte) -> bool { 237 | if b >= '0' && b <= '9' {return true} 238 | if b >= 'A' && b <= 'Z' {return true} // NOTE: QR-Code does _not_ recognize a-z as alpha 239 | switch b { 240 | case ' ', '$', '%', '*', '+', '-', '.', '/', ':': 241 | return true 242 | } 243 | return false 244 | } 245 | 246 | ///////////////////////////////////////////////////////////////////////////////////////////// 247 | find_minimum_version :: proc( 248 | segments: []Segment, 249 | ecc: Error_Level, 250 | ) -> ( 251 | min_version: int, 252 | ok: bool, 253 | ) { 254 | total_bits := 0 255 | for s in segments { 256 | total_bits += s.bit_cost 257 | } 258 | for i := 1; i <= 40; i += 1 { 259 | min_version = i 260 | version_data := versions[i] 261 | enc := version_data.encodings[int(ecc)] 262 | avail_bits := enc.data_codewords * 8 263 | 264 | if total_bits <= avail_bits { 265 | break 266 | } 267 | if i == 40 { 268 | return -1, false 269 | } 270 | } 271 | starting_min_version := min_version 272 | // Recompute costs to see if we can reduce the version level 273 | if min_version > 1 { 274 | for test_version := min_version - 1; test_version >= 1; test_version -= 1 { 275 | version_data := versions[test_version] 276 | enc := version_data.encodings[int(ecc)] 277 | avail_bits := enc.data_codewords * 8 278 | 279 | recalc_total_bits := 0 280 | for &s in segments { 281 | recalc_total_bits += calculate_segment_bits(s, test_version) 282 | } 283 | 284 | if recalc_total_bits <= avail_bits { 285 | min_version = test_version 286 | } else { 287 | break 288 | } 289 | } 290 | } 291 | // save the new version costs into the segments (TODO: do we even need this once min version is known??) 292 | if starting_min_version != min_version { 293 | for &s in segments { 294 | s.bit_cost = calculate_segment_bits(s, min_version) 295 | } 296 | } 297 | return min_version, true 298 | } 299 | // Ref ISO 18004:2015, Section 7.4, Table 3 300 | get_character_count_bits :: proc(mode: Encoding_Mode, version: int) -> int { 301 | if version >= 1 && version <= 9 { 302 | #partial switch mode { 303 | case .Numeric: 304 | return 10 305 | case .Alphanumeric: 306 | return 9 307 | case .Byte: 308 | return 8 309 | case .Kanji: 310 | return 8 311 | case: 312 | unreachable() 313 | } 314 | } else if version >= 10 && version <= 26 { 315 | #partial switch mode { 316 | case .Numeric: 317 | return 12 318 | case .Alphanumeric: 319 | return 11 320 | case .Byte: 321 | return 16 322 | case .Kanji: 323 | return 10 324 | case: 325 | unreachable() 326 | } 327 | } else if version >= 27 && version <= 40 { 328 | #partial switch mode { 329 | case .Numeric: 330 | return 14 331 | case .Alphanumeric: 332 | return 13 333 | case .Byte: 334 | return 16 335 | case .Kanji: 336 | return 12 337 | case: 338 | unreachable() 339 | } 340 | } 341 | unreachable() 342 | } 343 | 344 | ///////////////////////////////////////////////////////////////////////////////////////////// 345 | encode_segments_to_bitstream :: proc(segments: []Segment, version: int) -> Bit_Stream { 346 | bs := create_bit_stream() 347 | 348 | for segment in segments { 349 | // 1. Append mode indicator (4 bits) 350 | append_bits(&bs, u32(segment.mode), 4) 351 | // fmt.printf("Mode Bits: 0b%b (as enum:%v)\n", u8(segment.mode), segment.mode) 352 | // 2. Append character count indicator 353 | count_bits := get_character_count_bits(segment.mode, version) 354 | append_bits(&bs, u32(len(segment.data)), count_bits) 355 | 356 | // 3. Append data bits based on encoding mode 357 | switch segment.mode { 358 | case .Numeric: 359 | encode_numeric(&bs, segment.data) 360 | case .Alphanumeric: 361 | encode_alphanumeric(&bs, segment.data) 362 | case .Byte: 363 | encode_byte(&bs, segment.data) 364 | case .Kanji: 365 | encode_kanji(&bs, segment.data) 366 | case .ECI: 367 | encode_eci(&bs, segment) 368 | case: 369 | unreachable() 370 | } 371 | } 372 | 373 | return bs 374 | } 375 | 376 | encode_numeric :: proc(bs: ^Bit_Stream, data: []byte) { 377 | // Process groups of 3 digits 378 | i := 0 379 | for i < len(data) { 380 | count := min(3, len(data) - i) 381 | value: u32 = 0 382 | 383 | // Convert digits to value 384 | for j := 0; j < count; j += 1 { 385 | value = value * 10 + u32(data[i + j] - '0') 386 | } 387 | 388 | bit_count := 0 389 | if count == 3 { 390 | bit_count = 10 391 | } else if count == 2 { 392 | bit_count = 7 393 | } else { 394 | bit_count = 4 395 | } 396 | 397 | append_bits(bs, value, bit_count) 398 | i += count 399 | } 400 | } 401 | 402 | encode_alphanumeric :: proc(bs: ^Bit_Stream, data: []byte) { 403 | // Process pairs of characters 404 | i := 0 405 | for i < len(data) { 406 | if i + 1 < len(data) { 407 | // Process a pair 408 | value1 := get_alphanumeric_value(data[i]) 409 | value2 := get_alphanumeric_value(data[i + 1]) 410 | value := value1 * 45 + value2 411 | append_bits(bs, u32(value), 11) 412 | i += 2 413 | } else { 414 | // Process a single character 415 | value := get_alphanumeric_value(data[i]) 416 | append_bits(bs, u32(value), 6) 417 | i += 1 418 | } 419 | } 420 | } 421 | 422 | encode_byte :: proc(bs: ^Bit_Stream, data: []byte) { 423 | // Each byte is encoded as 8 bits 424 | for b in data { 425 | // fmt.printf("Encoding byte: 0x%02X (%c) - bits: %08b\n", b, b, b) 426 | append_bits(bs, u32(b), 8) 427 | 428 | // Debug: Print current bit stream state 429 | // fmt.printf("Bit stream after adding byte: ") 430 | // for i := 0; i < len(bs.data); i += 1 { 431 | // fmt.printf("%08b ", bs.data[i]) 432 | // } 433 | // fmt.printf("(bit count: %d)\n", bs.bit_count) 434 | } 435 | } 436 | 437 | encode_kanji :: proc(bs: ^Bit_Stream, data: []byte) { 438 | unimplemented() 439 | 440 | // // Process pairs of bytes (each Kanji character) 441 | // for i := 0; i < len(data); i += 2 { 442 | // // Combine two bytes into a 16-bit value 443 | // value := (u32(data[i]) << 8) | u32(data[i + 1]) 444 | 445 | // // Convert to QR code Kanji value (implementation depends on your Kanji encoding) 446 | // qr_value := convert_to_qr_kanji(value) 447 | 448 | // // Append 13 bits 449 | // append_bits(bs, qr_value, 13) 450 | // } 451 | } 452 | 453 | encode_eci :: proc(bs: ^Bit_Stream, segment: Segment) { 454 | unimplemented() 455 | // // Append ECI assignment number 456 | // eci_value := u32(segment.eci_kind) 457 | 458 | // if eci_value <= 127 { 459 | // append_bits(bs, eci_value, 8) 460 | // } else if eci_value <= 16383 { 461 | // append_bits(bs, 0x8000 | eci_value, 16) 462 | // } else { 463 | // append_bits(bs, 0xC00000 | eci_value, 24) 464 | // } 465 | 466 | // // Encode the data in byte mode 467 | // encode_byte(bs, segment.data) 468 | } 469 | 470 | get_alphanumeric_value :: proc(char: byte) -> int { 471 | // QR code alphanumeric character set mapping 472 | if char >= '0' && char <= '9' { 473 | return int(char - '0') // 0-9 map to values 0-9 474 | } else if char >= 'A' && char <= 'Z' { 475 | return int(char - 'A') + 10 // A-Z map to values 10-35 476 | } else { 477 | // Special characters 478 | switch char { 479 | case ' ': 480 | return 36 481 | case '$': 482 | return 37 483 | case '%': 484 | return 38 485 | case '*': 486 | return 39 487 | case '+': 488 | return 40 489 | case '-': 490 | return 41 491 | case '.': 492 | return 42 493 | case '/': 494 | return 43 495 | case ':': 496 | return 44 497 | case: 498 | unreachable() 499 | } 500 | } 501 | } 502 | 503 | determine_mode_for_rune :: proc(r: rune) -> Encoding_Mode { 504 | // QR code alphanumeric set: 0-9, A-Z, space, $, %, *, +, -, ., /, : 505 | if r >= '0' && r <= '9' { 506 | return .Numeric 507 | } 508 | switch r { 509 | case 'A' ..= 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':': 510 | return .Alphanumeric 511 | } 512 | 513 | // Check if it's a Kanji character 514 | if is_kanji_rune(r) { 515 | return .Kanji 516 | } 517 | // Default to byte mode 518 | return .Byte 519 | } 520 | 521 | 522 | is_kanji_rune :: proc(r: rune) -> bool { 523 | // TODO: check against the Shift JIS encoding 524 | // For now, we'll just check if it's in the CJK Unified Ideographs block 525 | return r >= 0x4E00 && r <= 0x9FFF 526 | } 527 | -------------------------------------------------------------------------------- /qrcode.odin: -------------------------------------------------------------------------------- 1 | package qrcode 2 | 3 | import rs "./reed_solomon" 4 | import "core:fmt" 5 | import "core:mem" 6 | import "core:strings" 7 | 8 | main :: proc() { 9 | data := "https://odin-lang.org/docs/" 10 | qr, ok := create_code(transmute([]u8)data, .H) 11 | fmt.printf("qrcode: version: %v, mask:%v, ECC: %v\n", qr.version, qr.mask, qr.error_level) 12 | defer destroy_code(&qr) 13 | save_qr_code_as_bmp_raw(&qr, "./qrcode.bmp", 10) 14 | } 15 | /////////////////////////////////////////////////////////////////////////////////////////////// 16 | create_code :: proc( 17 | data: []byte, 18 | ecc := Error_Level.H, 19 | quiet_zone := 4, 20 | ) -> ( 21 | qr: QR_Code, 22 | ok: bool, 23 | ) { 24 | segments := segment_data(data) 25 | defer delete(segments) 26 | min_version, v_ok := find_minimum_version(segments, ecc) 27 | if !v_ok { 28 | return {}, false 29 | } 30 | 31 | version_data := versions[min_version] 32 | 33 | qr = QR_Code { 34 | version = min_version, 35 | error_level = ecc, 36 | quiet_zone = quiet_zone, 37 | mask = -1, // is set later 38 | } 39 | 40 | qr.backing, qr.modules = make_2d_slice(version_data.size, version_data.size, bool) 41 | 42 | 43 | bs := encode_segments_to_bitstream(segments, min_version) 44 | add_padding(&bs, &version_data, ecc) 45 | defer delete(bs.data) 46 | // Generate ECC; interleave the data: 47 | buf := process_and_interleave_stream(bs.data[:], min_version, ecc) 48 | defer delete(buf) 49 | 50 | qr.modules[len(qr.modules) - 8][8] = true // Dark Module 51 | 52 | set_finders(&qr) 53 | set_timing(&qr) 54 | set_alignment_patterns(&qr) 55 | set_version_info(&qr) 56 | 57 | set_data(&qr, buf) 58 | 59 | select_best_mask(&qr) 60 | 61 | set_format_info(&qr, ecc) 62 | 63 | return qr, true 64 | } 65 | destroy_code :: proc(qr: ^QR_Code) { 66 | delete(qr.modules) 67 | delete(qr.backing) 68 | } 69 | 70 | make_2d_slice :: proc( 71 | y, x: int, 72 | $T: typeid, 73 | allocator := context.allocator, 74 | ) -> ( 75 | backing: []T, 76 | res: [][]T, 77 | ) { 78 | assert(x > 0 && y > 0) 79 | context.allocator = allocator 80 | 81 | backing = make([]T, x * y) 82 | res = make([][]T, y) 83 | 84 | for i in 0 ..< y { 85 | res[i] = backing[x * i:][:x] 86 | } 87 | return 88 | } 89 | 90 | add_padding :: proc(bs: ^Bit_Stream, version_data: ^Version_Data, ecc: Error_Level) { 91 | // Get the total capacity in bits for this version and ECC level 92 | enc := version_data.encodings[int(ecc)] 93 | total_capacity_bits := (enc.data_codewords) * 8 94 | 95 | remaining_bits := total_capacity_bits - bs.bit_count 96 | 97 | if remaining_bits >= 4 { 98 | // Add the 4-bit terminator 99 | append_bits(bs, 0, 4) 100 | remaining_bits -= 4 101 | } else if remaining_bits > 0 { 102 | // Add as many terminator bits as we can 103 | append_bits(bs, 0, remaining_bits) 104 | remaining_bits = 0 105 | } 106 | 107 | // 2. Add padding bits to align to byte boundary (0s) 108 | if bs.bit_count % 8 != 0 { 109 | padding_bits := 8 - (bs.bit_count % 8) 110 | append_bits(bs, 0, padding_bits) 111 | remaining_bits -= padding_bits 112 | } 113 | 114 | // 3. Add padding bytes alternating 0xEC and 0x11 115 | padding_bytes_needed := remaining_bits / 8 116 | 117 | for i := 0; i < padding_bytes_needed; i += 1 { 118 | if i % 2 == 0 { 119 | // Even index: add 11101100 (0xEC) 120 | append_bits(bs, 0xEC, 8) 121 | } else { 122 | // Odd index: add 00010001 (0x11) 123 | append_bits(bs, 0x11, 8) 124 | } 125 | } 126 | 127 | assert(bs.bit_count == total_capacity_bits, "Bit stream length doesn't match capacity") 128 | } 129 | 130 | set_finders :: proc(qr: ^QR_Code) { 131 | place_finder(qr, 0, 0) // top-left 132 | place_finder(qr, len(qr.modules) - 7, 0) // top-right 133 | place_finder(qr, 0, len(qr.modules) - 7) // bottom-left 134 | 135 | place_finder :: proc(qr: ^QR_Code, top_left_x, top_left_y: int) { 136 | // Finder pattern is a 7x7 square: 137 | // - Outer 7x7 black square border 138 | // - Middle 5x5 white square 139 | // - Inner 3x3 black square 140 | for dy in 0 ..< 7 { 141 | for dx in 0 ..< 7 { 142 | x, y := top_left_x + dx, top_left_y + dy 143 | // Outer black border (top, bottom, left, right edges) 144 | outer_border := dx == 0 || dx == 6 || dy == 0 || dy == 6 145 | // Inner black square (3x3 at the center) 146 | inner_square := dx >= 2 && dx <= 4 && dy >= 2 && dy <= 4 147 | if outer_border || inner_square { 148 | qr.modules[y][x] = true 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | set_timing :: proc(qr: ^QR_Code) { 156 | // horizontal 157 | for i in 8 ..< len(qr.modules) - 8 { 158 | qr.modules[6][i] = (i % 2 == 0) // Alternating black/white 159 | } 160 | // vertical 161 | for i in 8 ..< len(qr.modules) - 8 { 162 | qr.modules[i][6] = (i % 2 == 0) // Alternating black/white 163 | } 164 | } 165 | 166 | set_alignment_patterns :: proc(qr: ^QR_Code) { 167 | if qr.version < 2 { 168 | return // Version 1 has none 169 | } 170 | centers := versions[qr.version].alignment_centers 171 | 172 | for y in centers { 173 | for x in centers { 174 | // Skip if would overlap with a finder pattern 175 | if (x <= 8 && y <= 8) || 176 | (x >= len(qr.modules) - 8 && y <= 8) || 177 | (x <= 8 && y >= len(qr.modules) - 8) { 178 | continue 179 | } 180 | set_alignment_pattern(qr, x, y) 181 | } 182 | } 183 | 184 | set_alignment_pattern :: proc(qr: ^QR_Code, x, y: int) { 185 | // Center point 186 | qr.modules[y][x] = true 187 | // Outer border (black) 188 | for dy in -2 ..= 2 { 189 | for dx in -2 ..= 2 { 190 | if abs(dx) == 2 || abs(dy) == 2 { 191 | qr.modules[y + dy][x + dx] = true 192 | } 193 | } 194 | } 195 | } 196 | } 197 | 198 | set_version_info :: proc(qr: ^QR_Code) { 199 | if qr.version < 7 { 200 | return // No version info for versions 1-6 201 | } 202 | version_info := get_version_info(qr.version) 203 | 204 | // Version info is placed in two locations: 205 | // 1. Below the top-right finder pattern 206 | // 2. To the right of the bottom-left finder pattern 207 | 208 | // Place version info below top-right finder pattern (3x6 modules) 209 | row, col := 0, len(qr.modules) - 11 210 | for i in 0 ..< 18 { 211 | bit := (version_info >> uint(i)) & 0x01 212 | module_value := bit == 1 213 | // Version info is arranged in a 3x6 block 214 | r := row + (i / 3) 215 | c := col + (i % 3) 216 | qr.modules[r][c] = module_value 217 | } 218 | 219 | // Place version info to the right of bottom-left finder pattern (6x3 modules) 220 | row, col = len(qr.modules) - 11, 0 221 | for i in 0 ..< 18 { 222 | bit := (version_info >> uint(i)) & 0x01 223 | module_value := bit == 1 224 | // Version info is arranged in a 6x3 block 225 | r := row + (i % 3) 226 | c := col + (i / 3) 227 | qr.modules[r][c] = module_value 228 | } 229 | } 230 | 231 | get_version_info :: proc(version: int) -> u32 { 232 | if version < 7 { 233 | return 0 // No version info for versions 1-6 234 | } 235 | // Consists of 6 data bits (the version number) followed by 12 error correction bits 236 | 237 | // The version number (6 bits) 238 | info := u32(version) 239 | 240 | // Calculate BCH error correction code for version information 241 | // The generator polynomial for version info is 0x1F25 242 | generator := u32(0x1F25) 243 | 244 | // Shift data bits left by 12 bits (size of error correction) 245 | data := info << 12 246 | 247 | // Perform polynomial division to get error correction bits 248 | remainder := data 249 | for i: u32 = 0; i < 6; i += 1 { 250 | if remainder & (1 << (17 - i)) != 0 { 251 | remainder ~= generator << (5 - i) 252 | } 253 | } 254 | 255 | return (info << 12) | remainder 256 | } 257 | 258 | // Calculate the format information bits 259 | // Returns a 15-bit code with error correction 260 | get_format_info :: proc(error_level: Error_Level, mask_pattern: int) -> u16 { 261 | ecc_bits := u16(encode_error_level(error_level)) 262 | 263 | // Mask pattern bits (0-7) 264 | mask_bits := u16(mask_pattern & 0x7) 265 | // Combine EC and mask bits 266 | data := (ecc_bits << 3) | mask_bits 267 | 268 | // Apply BCH error correction 269 | // The generator polynomial for format info is x^10 + x^8 + x^5 + x^4 + x^2 + x + 1 (0x537) 270 | generator := u16(0x537) 271 | 272 | // Shift data left by 10 bits (for error correction) 273 | remainder := data << 10 274 | 275 | // Find the remainder for BCH calculation 276 | // We need to check each bit position where data would have a 1 277 | for bit_pos: uint = 14; bit_pos >= 10; bit_pos -= 1 { 278 | if (remainder & (1 << bit_pos)) != 0 { 279 | // XOR with generator polynomial shifted to align with this bit 280 | remainder ~= generator << (bit_pos - 10) 281 | } 282 | } 283 | 284 | format_info := (data << 10) | (remainder & 0x3FF) 285 | 286 | // XOR with mask pattern 101010000010010 (0x5412) to ensure no all-zero format info 287 | format_info ~= 0x5412 288 | 289 | // fmt.printf("Final format info: %015b\n", format_info) 290 | return format_info 291 | } 292 | 293 | set_format_info :: proc(qr: ^QR_Code, error_level: Error_Level) { 294 | assert(qr.mask >= 0 && qr.mask <= 7, "Invalid Mask") 295 | format_info := get_format_info(error_level, qr.mask) 296 | size := len(qr.modules) 297 | 298 | // Spec Diagram: Figure 25 Iso 2015 (P56) 299 | 300 | // Top-left vertical portion (bits 0-5) 301 | for i := 0; i < 6; i += 1 { 302 | bit := (format_info >> uint(i)) & 1 == 1 303 | qr.modules[i][8] = bit 304 | } 305 | // Position of bit 6,7,8 (after timing pattern) 306 | qr.modules[7][8] = (format_info >> uint(6)) & 1 == 1 307 | qr.modules[8][8] = (format_info >> uint(7)) & 1 == 1 308 | qr.modules[8][7] = (format_info >> uint(8)) & 1 == 1 309 | 310 | // Top-left horizontal portion (bits 9-14) 311 | for i := 9; i <= 14; i += 1 { 312 | bit := (format_info >> uint(i)) & 1 == 1 313 | qr.modules[8][14 - i] = bit 314 | } 315 | 316 | // Top-right horizontal portion (bits 0-7) 317 | for i := 0; i <= 7; i += 1 { 318 | bit := (format_info >> uint(i)) & 1 == 1 319 | qr.modules[8][size - 8 + i] = bit 320 | } 321 | // Bottom-left vertical portion (bits 8-14) 322 | for i := 8; i <= 14; i += 1 { 323 | bit := (format_info >> uint(i)) & 1 == 1 324 | qr.modules[size - 15 + i][8] = bit 325 | } 326 | } 327 | /////////////////////////////////////////////////////////////////////////////////////////////// 328 | is_reserved :: #force_inline proc(reserved: [][]bool, row, col: int) -> bool { 329 | fmt.assertf(row >= 0 && row < len(reserved), "Row OOB %v,%v", row, col) 330 | fmt.assertf(col >= 0 && col < len(reserved), "Col OOB %v,%v", row, col) 331 | return reserved[row][col] 332 | } 333 | 334 | build_reserved_table :: proc(qr: ^QR_Code) -> (backing: []bool, reserved: [][]bool) { 335 | size := len(qr.modules) 336 | backing, reserved = make_2d_slice(size, size, bool) 337 | 338 | // Mark finder patterns (including separators) 339 | // 7 for finder, 1 for quiet, 1 for format info = 9 340 | for r := 0; r < 9; r += 1 { 341 | for c := 0; c < 9; c += 1 { 342 | reserved[r][c] = true // Top-left 343 | } 344 | // nothing on TR for col after quiet, so 8 345 | for c := size - 8; c < size; c += 1 { 346 | reserved[r][c] = true // Top-right 347 | } 348 | } 349 | for r := size - 8; r < size; r += 1 { 350 | for c := 0; c < 9; c += 1 { 351 | reserved[r][c] = true // Bottom-left 352 | } 353 | } 354 | 355 | // Mark timing patterns 356 | for i := 0; i < size; i += 1 { 357 | reserved[6][i] = true // Horizontal 358 | reserved[i][6] = true // Vertical 359 | } 360 | 361 | // Mark alignment patterns 362 | if qr.version >= 2 { 363 | centers := versions[qr.version].alignment_centers 364 | for y in centers { 365 | if y <= 6 {continue} // dont write on top of finders (it pokes out into writable space) 366 | for x in centers { 367 | if x <= 6 {continue} // dont write on top of finders (it pokes out into writable space) 368 | // Mark the 5x5 area of each alignment pattern 369 | for dr := -2; dr <= 2; dr += 1 { 370 | for dc := -2; dc <= 2; dc += 1 { 371 | if y + dr >= 0 && y + dr < size && x + dc >= 0 && x + dc < size { 372 | reserved[y + dr][x + dc] = true 373 | } 374 | } 375 | } 376 | } 377 | } 378 | } 379 | 380 | // Mark version info areas (only in versions 7+) 381 | if qr.version >= 7 { 382 | // Version info near top-right finder 383 | for r := 0; r < 6; r += 1 { 384 | for c := size - 11; c < size - 8; c += 1 { 385 | reserved[r][c] = true 386 | } 387 | } 388 | // Version info near bottom-left finder 389 | for r := size - 11; r < size - 8; r += 1 { 390 | for c := 0; c < 6; c += 1 { 391 | reserved[r][c] = true 392 | } 393 | } 394 | } 395 | 396 | // Mark format info areas - CORRECTED 397 | // Horizontal format info (top) 398 | for i := 0; i < 9; i += 1 { 399 | reserved[8][i] = true 400 | } 401 | 402 | // Horizontal format info (right side) 403 | for i := size - 8; i < size; i += 1 { 404 | reserved[8][i] = true 405 | } 406 | 407 | // Vertical format info (left) 408 | for i := 0; i < 9; i += 1 { 409 | reserved[i][8] = true 410 | } 411 | 412 | // Vertical format info (bottom) 413 | for i := size - 7; i < size; i += 1 { 414 | reserved[i][8] = true 415 | } 416 | 417 | // Mark the fixed dark module 418 | reserved[8][size - 8] = true 419 | 420 | return 421 | } 422 | /////////////////////////////////////////////////////////////////////////////////////////////// 423 | // QR code data placement algorithm 424 | set_data :: proc(qr: ^QR_Code, data: []byte) { 425 | size := len(qr.modules) 426 | backing, reserved := build_reserved_table(qr) // this copy is mutated so we cant use in future steps for masks 427 | defer delete(reserved) 428 | defer delete(backing) 429 | col := size - 1 430 | row := size - 1 431 | travel_up := true 432 | for b, i in data { 433 | // fmt.printf("Setting Byte: %v, r: %v, c: %v, up: %v\n", i, row, col, travel_up) 434 | row, col, travel_up = set_byte(qr, reserved, row, col, travel_up, b, i) 435 | } 436 | } 437 | // Upwards Downwards 438 | // 00 01 06 07 <- MSB 439 | // 02 03 04 05 440 | // 04 05 02 03 441 | // 06 07 <- MSB 00 01 442 | 443 | // Upward to Downward: 444 | // Lateral Irregular 445 | // 02 03 04 05 00 01 02 03 446 | // 00 01 06 07 04 05 447 | // 06 07 448 | 449 | // Downward to Upward: (Ref Figure 19; 2M Symbol, cell D25, E9 is same, but goes up a number of rows) 450 | // 07 451 | // 06 05 452 | // 04 03 453 | // 02 01 454 | // 00 455 | 456 | // Upward around feature: 457 | // 00 458 | // 01 02 Downwards; split across feature: 459 | // 04 03 06 07 460 | // 05 06 04 05 461 | // 07 @@ @@ @@ @@ @@ 462 | // 00 @@ @@ 463 | // 01 @@ @@ @@ 464 | // 02 @@ @@ 465 | // 03 @@ @@ @@ @@ @@ 466 | // 04 05 02 03 467 | // 06 07 00 01 468 | 469 | // row, col are the 'first available' positions (data is prepared as MSB-first in the encoder) 470 | set_byte :: proc( 471 | qr: ^QR_Code, 472 | reserved: [][]bool, 473 | start_row, start_col: int, 474 | travel_up: bool, 475 | data_byte: byte, 476 | byte_index: int, 477 | ) -> ( 478 | next_row, next_col: int, 479 | next_up: bool, 480 | ) { 481 | size := len(qr.modules) 482 | bits_placed := 0 483 | positions := [8][2]int{} // [row, col] for each placed bit 484 | 485 | assert(start_col >= 0 && start_col < size, "col out of bounds") 486 | assert(start_row >= 0 && start_row < size, "row out of bounds") 487 | 488 | // Current state 489 | current_up := travel_up 490 | row := start_row 491 | left_col: int 492 | right_col: int 493 | is_rh_col := (size - 1 - start_col) % 2 == 0 494 | if start_col <= 6 {is_rh_col = !is_rh_col} // vertical timing line throws us off by one 495 | if is_rh_col { 496 | left_col = start_col - 1 497 | right_col = start_col 498 | } else { 499 | left_col = start_col 500 | right_col = start_col + 1 501 | } 502 | 503 | iterations := 0 504 | 505 | for bits_placed < 8 { 506 | assert(iterations < size * size, "infinite loop") 507 | iterations += 1 508 | 509 | // Direction Changes: 510 | if row < 0 { 511 | current_up = false 512 | row = 0 513 | left_col -= 2 514 | right_col -= 2 515 | 516 | // vertical timing pattern: 517 | if left_col == 6 { 518 | left_col -= 2 519 | right_col -= 2 520 | } else if right_col == 6 { 521 | left_col -= 1 522 | right_col -= 1 523 | } 524 | } else if row >= size { 525 | current_up = true 526 | row = size - 1 527 | left_col -= 2 528 | right_col -= 2 529 | // vertical timing pattern: 530 | if left_col == 6 { 531 | left_col -= 2 532 | right_col -= 2 533 | } else if right_col == 6 { 534 | left_col -= 1 535 | right_col -= 1 536 | } 537 | } 538 | 539 | if !is_reserved(reserved, row, right_col) { 540 | positions[bits_placed] = [2]int{row, right_col} 541 | reserved[row][right_col] = true // mark used 542 | bits_placed += 1 543 | } 544 | if bits_placed == 8 {break} 545 | if !is_reserved(reserved, row, left_col) { 546 | positions[bits_placed] = [2]int{row, left_col} 547 | reserved[row][left_col] = true // mark used 548 | bits_placed += 1 549 | } 550 | row += current_up ? -1 : 1 551 | } 552 | 553 | for i := 0; i < bits_placed; i += 1 { 554 | r, c := positions[i].x, positions[i].y 555 | // Always read bits MSB first (7 down to 0) 556 | bit_idx := 7 - (i % 8) // Use modulo to handle multiple bytes 557 | bit_value := (data_byte & (1 << uint(bit_idx))) != 0 558 | qr.modules[r][c] = bit_value 559 | } 560 | if row < size && row >= 0 && !is_reserved(reserved, row, left_col) { 561 | // we did not consume the left position 562 | next_row = row 563 | next_col = left_col 564 | next_up = current_up 565 | } else { 566 | if current_up { 567 | next_row = row - 1 568 | if next_row < 0 { 569 | next_up = false 570 | next_row = 0 571 | next_col = left_col - 2 572 | } else { 573 | // Continue upward 574 | next_up = true 575 | next_col = left_col 576 | } 577 | } else { 578 | next_row = row + 1 579 | if next_row >= size { 580 | next_up = true 581 | next_row = size - 1 582 | next_col = left_col - 2 583 | } else { 584 | // Continue downward 585 | next_up = false 586 | next_col = left_col 587 | } 588 | } 589 | } 590 | assert(bits_placed == 8, "failed to set full byte") 591 | // fmt.println("Set Byte at:", positions) 592 | // print_br_corner(positions, byte_index, current_up) 593 | return next_row, next_col, next_up 594 | } 595 | /////////////////////////////////////////////////////////////////////////////////////////////// 596 | // Ref ISO 18004:2015 Fig 21, (P51) 597 | mask_pattern :: proc(mask_num: int, row, col: int) -> bool { 598 | switch mask_num { 599 | case 0: 600 | return (row + col) % 2 == 0 601 | case 1: 602 | return row % 2 == 0 603 | case 2: 604 | return col % 3 == 0 605 | case 3: 606 | return (row + col) % 3 == 0 607 | case 4: 608 | return (row / 2 + col / 3) % 2 == 0 609 | case 5: 610 | return (row * col) % 2 + (row * col) % 3 == 0 611 | case 6: 612 | return ((row * col) % 2 + (row * col) % 3) % 2 == 0 613 | case 7: 614 | return ((row + col) % 2 + (row * col) % 3) % 2 == 0 615 | case: 616 | return false 617 | } 618 | } 619 | select_best_mask :: proc(qr: ^QR_Code) { 620 | backing, reserved := build_reserved_table(qr) 621 | defer delete(reserved) 622 | defer delete(backing) 623 | 624 | size := len(qr.modules) 625 | 626 | masked_data := [8]Masked_Modules{} 627 | best_score: int = 1E9 628 | best_index: int 629 | for i in 0 ..< 8 { 630 | masked_data[i].backing, masked_data[i].modules = make_2d_slice(size, size, bool) 631 | copy(masked_data[i].backing, qr.backing) 632 | apply_mask(masked_data[i], reserved, i) 633 | score := calculate_penalty(masked_data[i]) 634 | if score < best_score { 635 | best_score = score 636 | best_index = i 637 | } 638 | } 639 | qr.mask = best_index 640 | copy(qr.backing, masked_data[best_index].backing) 641 | for i in 0 ..< 8 { 642 | delete(masked_data[i].modules) 643 | delete(masked_data[i].backing) 644 | } 645 | } 646 | Masked_Modules :: struct { 647 | backing: []bool, 648 | modules: [][]bool, 649 | } 650 | apply_mask :: proc(mm: Masked_Modules, reserved: [][]bool, mask_num: int) { 651 | size := len(mm.modules) 652 | for row in 0 ..< size { 653 | for col in 0 ..< size { 654 | if is_reserved(reserved, row, col) {continue} 655 | // XOR the module with the mask pattern 656 | if mask_pattern(mask_num, row, col) { 657 | mm.modules[row][col] = !mm.modules[row][col] 658 | } 659 | } 660 | } 661 | } 662 | // Ref ISO18004:2015 Section 7.8.3 663 | calculate_penalty :: proc(mm: Masked_Modules) -> int { 664 | size := len(mm.modules) 665 | score := 0 666 | // Rule 1: Five or more same-colored modules in a row/column 667 | score += penalty_consecutive_modules(mm) 668 | // Rule 2: Penalty for 2x2 blocks of same-colored modules 669 | score += penalty_2x2_blocks(mm) 670 | // Rule 3: Patterns similar to finder patterns 671 | score += penalty_finder_patterns(mm) 672 | // Rule 4: Balance of dark and light modules 673 | score += penalty_balance(mm) 674 | 675 | return score 676 | } 677 | 678 | // Rule 1: Five or more same-colored modules in a row/column 679 | penalty_consecutive_modules :: proc(mm: Masked_Modules) -> int { 680 | size := len(mm.modules) 681 | score := 0 682 | // Check rows 683 | for row in 0 ..< size { 684 | count := 0 685 | last_module := false 686 | 687 | for col in 0 ..< size { 688 | if mm.modules[row][col] == last_module { 689 | count += 1 690 | } else { 691 | if count >= 5 { 692 | score += count - 2 693 | } 694 | count = 1 695 | last_module = mm.modules[row][col] 696 | } 697 | } 698 | if count >= 5 { 699 | score += count - 2 700 | } 701 | } 702 | // Check columns 703 | for col in 0 ..< size { 704 | count := 0 705 | last_module := false 706 | 707 | for row in 0 ..< size { 708 | if mm.modules[row][col] == last_module { 709 | count += 1 710 | } else { 711 | if count >= 5 { 712 | score += count - 2 713 | } 714 | count = 1 715 | last_module = mm.modules[row][col] 716 | } 717 | } 718 | if count >= 5 { 719 | score += count - 2 720 | } 721 | } 722 | 723 | return score 724 | } 725 | 726 | // Rule 2: Penalty for 2x2 blocks of same-colored modules 727 | penalty_2x2_blocks :: proc(mm: Masked_Modules) -> int { 728 | size := len(mm.modules) 729 | score := 0 730 | 731 | for row in 0 ..< (size - 1) { 732 | for col in 0 ..< (size - 1) { 733 | module := mm.modules[row][col] 734 | if module == mm.modules[row + 1][col] && 735 | module == mm.modules[row][col + 1] && 736 | module == mm.modules[row + 1][col + 1] { 737 | score += 3 738 | } 739 | } 740 | } 741 | 742 | return score 743 | } 744 | 745 | // Rule 3: Patterns similar to finder patterns 746 | penalty_finder_patterns :: proc(mm: Masked_Modules) -> int { 747 | size := len(mm.modules) 748 | score := 0 749 | 750 | // Pattern 1: 1:1:3:1:1 ratio (dark:light:dark:light:dark) 751 | // Check horizontally 752 | for row in 0 ..< size { 753 | for col in 0 ..< (size - 6) { 754 | if (mm.modules[row][col] && 755 | !mm.modules[row][col + 1] && 756 | mm.modules[row][col + 2] && 757 | mm.modules[row][col + 3] && 758 | mm.modules[row][col + 4] && 759 | !mm.modules[row][col + 5] && 760 | mm.modules[row][col + 6]) { 761 | // Check for white space on either side (if possible) 762 | if col >= 4 && 763 | !mm.modules[row][col - 1] && 764 | !mm.modules[row][col - 2] && 765 | !mm.modules[row][col - 3] && 766 | !mm.modules[row][col - 4] { 767 | score += 40 768 | } 769 | if col + 10 < size && 770 | !mm.modules[row][col + 7] && 771 | !mm.modules[row][col + 8] && 772 | !mm.modules[row][col + 9] && 773 | !mm.modules[row][col + 10] { 774 | score += 40 775 | } 776 | } 777 | } 778 | } 779 | 780 | // Check vertically 781 | for col in 0 ..< size { 782 | for row in 0 ..< (size - 6) { 783 | if (mm.modules[row][col] && 784 | !mm.modules[row + 1][col] && 785 | mm.modules[row + 2][col] && 786 | mm.modules[row + 3][col] && 787 | mm.modules[row + 4][col] && 788 | !mm.modules[row + 5][col] && 789 | mm.modules[row + 6][col]) { 790 | // Check for white space on either side (if possible) 791 | if row >= 4 && 792 | !mm.modules[row - 1][col] && 793 | !mm.modules[row - 2][col] && 794 | !mm.modules[row - 3][col] && 795 | !mm.modules[row - 4][col] { 796 | score += 40 797 | } 798 | if row + 10 < size && 799 | !mm.modules[row + 7][col] && 800 | !mm.modules[row + 8][col] && 801 | !mm.modules[row + 9][col] && 802 | !mm.modules[row + 10][col] { 803 | score += 40 804 | } 805 | } 806 | } 807 | } 808 | 809 | return score 810 | } 811 | 812 | // Rule 4: Balance of dark and light modules 813 | penalty_balance :: proc(mm: Masked_Modules) -> int { 814 | size := len(mm.modules) 815 | dark_count := 0 816 | total_count := size * size 817 | 818 | for row in 0 ..< size { 819 | for col in 0 ..< size { 820 | if mm.modules[row][col] { 821 | dark_count += 1 822 | } 823 | } 824 | } 825 | 826 | // Calculate percentage of dark modules (in steps of 5%) 827 | percentage := (dark_count * 100) / total_count 828 | steps_from_50 := (percentage / 5) - 10 // 50% would be step 10 829 | if steps_from_50 < 0 { 830 | steps_from_50 = -steps_from_50 831 | } 832 | 833 | return steps_from_50 * 10 834 | } 835 | /////////////////////////////////////////////////////////////////////////////////////////////// 836 | process_and_interleave_stream :: proc(data: []byte, version: int, ecc: Error_Level) -> []byte { 837 | version_data := versions[version] 838 | enc := version_data.encodings[ecc] 839 | 840 | assert(len(data) == enc.data_codewords, "Data was not correctly padded") 841 | 842 | Block_Size :: struct { 843 | ec_bytes: int, 844 | data_bytes: int, 845 | count: int, 846 | start_idx: int, // Start index in block array 847 | } 848 | 849 | // Count blocks and track block groups 850 | total_blocks := 0 851 | block_groups := make([]Block_Size, len(enc.blocks)) 852 | defer delete(block_groups) 853 | 854 | for block_group, i in enc.blocks { 855 | block_groups[i] = Block_Size { 856 | ec_bytes = block_group.r * 2 + block_group.p, // EC capacity * 2 857 | data_bytes = block_group.k, 858 | count = block_group.no_blocks, 859 | start_idx = total_blocks, 860 | } 861 | total_blocks += block_group.no_blocks 862 | } 863 | 864 | // Create data blocks array 865 | data_blocks := make([][]byte, total_blocks) 866 | defer delete(data_blocks) 867 | 868 | // split up the data-stream into slices per the block sizes 869 | data_index := 0 870 | block_index := 0 871 | 872 | for block_group in enc.blocks { 873 | for j in 0 ..< block_group.no_blocks { 874 | end_index := min(data_index + block_group.k, len(data)) 875 | data_blocks[block_index] = data[data_index:end_index] 876 | data_index += block_group.k 877 | block_index += 1 878 | } 879 | } 880 | 881 | // Generate error correction codes for each block 882 | ec_blocks := make([][]byte, total_blocks) 883 | defer { 884 | for i in 0 ..< len(ec_blocks) { 885 | if ec_blocks[i] != nil { 886 | delete(ec_blocks[i]) 887 | } 888 | } 889 | delete(ec_blocks) 890 | } 891 | 892 | // Calculate total EC bytes 893 | total_ec_bytes := 0 894 | for group in block_groups { 895 | total_ec_bytes += group.ec_bytes * group.count 896 | } 897 | // fmt.println("Block groups:", block_groups) 898 | // fmt.println("Expected EC:", enc.ec_codewords, "Calculated EC:", total_ec_bytes) 899 | assert(enc.ec_codewords == total_ec_bytes) 900 | 901 | // Generate EC codewords for each block using the correct EC size 902 | for group in block_groups { 903 | for i in 0 ..< group.count { 904 | block_idx := group.start_idx + i 905 | ec_blocks[block_idx] = rs.encode_rs(data_blocks[block_idx], group.ec_bytes) 906 | } 907 | } 908 | 909 | // Interleave data and error correction codewords 910 | // ISO Section 7.6; the language is really confusing. You take a byte from each block and interleave them, this scatters the data through the QR code to make error recovery easier (less likely a contiguous section of data is destroyed) 911 | result := make([]byte, len(data) + total_ec_bytes) 912 | 913 | // Find maximum block size 914 | max_block_size := 0 915 | for block in data_blocks { 916 | max_block_size = max(max_block_size, len(block)) 917 | } 918 | 919 | // Interleave data codewords 920 | result_index := 0 921 | for i in 0 ..< max_block_size { 922 | for j in 0 ..< total_blocks { // #ecc blocks == #data blocks 923 | if i < len(data_blocks[j]) { 924 | result[result_index] = data_blocks[j][i] 925 | result_index += 1 926 | } 927 | } 928 | } 929 | 930 | // Find maximum EC block size 931 | max_ec_size := 0 932 | for block in ec_blocks { 933 | max_ec_size = max(max_ec_size, len(block)) 934 | } 935 | 936 | // Interleave error correction codewords 937 | for i in 0 ..< max_ec_size { 938 | for j in 0 ..< total_blocks { 939 | if i < len(ec_blocks[j]) { 940 | result[result_index] = ec_blocks[j][i] 941 | result_index += 1 942 | } 943 | } 944 | } 945 | 946 | return result 947 | } 948 | -------------------------------------------------------------------------------- /versions.odin: -------------------------------------------------------------------------------- 1 | #+feature dynamic-literals 2 | package qrcode 3 | // Ref ISO 18004:2015, Tables 7 & 9, mostly 4 | // Note: Table 9 is really confusing, to get the # of encoding blocks you take the last 2 from the list and work backwards 5 | // They show 5H, its the last two entries; 5Q is the next two 5M is the previous 1 and 5L is the first 6 | // (there are 6 total) 7 | 8 | // TODO: version 31+ i got tired of typing so c,k,r are all empty 9 | 10 | // R needs *=2 for use; 11 | versions := []Version_Data { 12 | {}, // allow for one-indexing 13 | Version_Data { 14 | id = 1, 15 | size = 21, 16 | total_codewords = 26, 17 | remainder_bits = 0, 18 | encodings = { 19 | Encoding { 20 | level = .L, 21 | data_codewords = 19, 22 | ec_codewords = 7, 23 | blocks = {EC_Block{p = 3, no_blocks = 1, c = 26, k = 19, r = 2}}, 24 | }, 25 | Encoding { 26 | level = .M, 27 | data_codewords = 16, 28 | ec_codewords = 10, 29 | blocks = {EC_Block{p = 2, no_blocks = 1, c = 26, k = 16, r = 4}}, 30 | }, 31 | Encoding { 32 | level = .Q, 33 | data_codewords = 13, 34 | ec_codewords = 13, 35 | blocks = {EC_Block{p = 1, no_blocks = 1, c = 26, k = 13, r = 6}}, 36 | }, 37 | Encoding { 38 | level = .H, 39 | data_codewords = 9, 40 | ec_codewords = 17, 41 | blocks = {EC_Block{p = 1, no_blocks = 1, c = 26, k = 9, r = 8}}, 42 | }, 43 | }, 44 | }, 45 | Version_Data { 46 | id = 2, 47 | size = 25, 48 | total_codewords = 44, 49 | remainder_bits = 7, 50 | alignment_centers = {6, 18}, 51 | encodings = { 52 | Encoding { 53 | level = .L, 54 | data_codewords = 34, 55 | ec_codewords = 10, 56 | blocks = {EC_Block{p = 2, no_blocks = 1, c = 44, k = 34, r = 4}}, 57 | }, 58 | Encoding { 59 | level = .M, 60 | data_codewords = 28, 61 | ec_codewords = 16, 62 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 44, k = 28, r = 8}}, 63 | }, 64 | Encoding { 65 | level = .Q, 66 | data_codewords = 22, 67 | ec_codewords = 22, 68 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 44, k = 22, r = 11}}, 69 | }, 70 | Encoding { 71 | level = .H, 72 | data_codewords = 16, 73 | ec_codewords = 28, 74 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 44, k = 16, r = 14}}, 75 | }, 76 | }, 77 | }, 78 | Version_Data { 79 | id = 3, 80 | size = 29, 81 | total_codewords = 70, 82 | remainder_bits = 7, 83 | alignment_centers = {6, 22}, 84 | encodings = { 85 | Encoding { 86 | level = .L, 87 | data_codewords = 55, 88 | ec_codewords = 15, 89 | blocks = {EC_Block{p = 1, no_blocks = 1, c = 70, k = 55, r = 7}}, 90 | }, 91 | Encoding { 92 | level = .M, 93 | data_codewords = 44, 94 | ec_codewords = 26, 95 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 70, k = 44, r = 13}}, 96 | }, 97 | Encoding { 98 | level = .Q, 99 | data_codewords = 34, 100 | ec_codewords = 36, 101 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 35, k = 17, r = 9}}, 102 | }, 103 | Encoding { 104 | level = .H, 105 | data_codewords = 26, 106 | ec_codewords = 44, 107 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 35, k = 13, r = 11}}, 108 | }, 109 | }, 110 | }, 111 | Version_Data { 112 | id = 4, 113 | size = 33, 114 | total_codewords = 100, 115 | remainder_bits = 7, 116 | alignment_centers = {6, 26}, 117 | encodings = { 118 | Encoding { 119 | level = .L, 120 | data_codewords = 80, 121 | ec_codewords = 20, 122 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 100, k = 80, r = 10}}, 123 | }, 124 | Encoding { 125 | level = .M, 126 | data_codewords = 64, 127 | ec_codewords = 36, 128 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 50, k = 32, r = 9}}, 129 | }, 130 | Encoding { 131 | level = .Q, 132 | data_codewords = 48, 133 | ec_codewords = 52, 134 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 50, k = 24, r = 13}}, 135 | }, 136 | Encoding { 137 | level = .H, 138 | data_codewords = 36, 139 | ec_codewords = 64, 140 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 25, k = 9, r = 8}}, 141 | }, 142 | }, 143 | }, 144 | Version_Data { 145 | id = 5, 146 | size = 37, 147 | total_codewords = 134, 148 | remainder_bits = 7, 149 | alignment_centers = {6, 30}, 150 | encodings = { 151 | Encoding { 152 | level = .L, 153 | data_codewords = 108, 154 | ec_codewords = 26, 155 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 134, k = 108, r = 13}}, 156 | }, 157 | Encoding { 158 | level = .M, 159 | data_codewords = 86, 160 | ec_codewords = 48, 161 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 67, k = 43, r = 12}}, 162 | }, 163 | Encoding { 164 | level = .Q, 165 | data_codewords = 62, 166 | ec_codewords = 72, 167 | blocks = { 168 | EC_Block{p = 0, no_blocks = 2, c = 33, k = 15, r = 9}, 169 | EC_Block{p = 0, no_blocks = 2, c = 34, k = 16, r = 9}, 170 | }, 171 | }, 172 | Encoding { 173 | level = .H, 174 | data_codewords = 46, 175 | ec_codewords = 88, 176 | blocks = { 177 | EC_Block{p = 0, no_blocks = 2, c = 33, k = 11, r = 11}, 178 | EC_Block{p = 0, no_blocks = 2, c = 34, k = 12, r = 11}, 179 | }, 180 | }, 181 | }, 182 | }, 183 | Version_Data { 184 | id = 6, 185 | size = 41, 186 | total_codewords = 172, 187 | remainder_bits = 7, 188 | alignment_centers = {6, 34}, 189 | encodings = { 190 | Encoding { 191 | level = .L, 192 | data_codewords = 136, 193 | ec_codewords = 36, 194 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 86, k = 68, r = 9}}, 195 | }, 196 | Encoding { 197 | level = .M, 198 | data_codewords = 108, 199 | ec_codewords = 64, 200 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 43, k = 27, r = 8}}, 201 | }, 202 | Encoding { 203 | level = .Q, 204 | data_codewords = 76, 205 | ec_codewords = 96, 206 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 43, k = 19, r = 12}}, 207 | }, 208 | Encoding { 209 | level = .H, 210 | data_codewords = 60, 211 | ec_codewords = 112, 212 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 43, k = 15, r = 14}}, 213 | }, 214 | }, 215 | }, 216 | Version_Data { 217 | id = 7, 218 | size = 45, 219 | total_codewords = 196, 220 | remainder_bits = 0, 221 | alignment_centers = {6, 22, 38}, 222 | encodings = { 223 | Encoding { 224 | level = .L, 225 | data_codewords = 156, 226 | ec_codewords = 40, 227 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 98, k = 78, r = 10}}, 228 | }, 229 | Encoding { 230 | level = .M, 231 | data_codewords = 124, 232 | ec_codewords = 72, 233 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 49, k = 31, r = 9}}, 234 | }, 235 | Encoding { 236 | level = .Q, 237 | data_codewords = 88, 238 | ec_codewords = 108, 239 | blocks = { 240 | EC_Block{p = 0, no_blocks = 2, c = 32, k = 14, r = 9}, 241 | EC_Block{p = 0, no_blocks = 4, c = 33, k = 15, r = 9}, 242 | }, 243 | }, 244 | Encoding { 245 | level = .H, 246 | data_codewords = 66, 247 | ec_codewords = 130, 248 | blocks = { 249 | EC_Block{p = 0, no_blocks = 4, c = 39, k = 13, r = 13}, 250 | EC_Block{p = 0, no_blocks = 1, c = 40, k = 14, r = 13}, 251 | }, 252 | }, 253 | }, 254 | }, 255 | Version_Data { 256 | id = 8, 257 | size = 49, 258 | total_codewords = 242, 259 | remainder_bits = 0, 260 | alignment_centers = {6, 24, 42}, 261 | encodings = { 262 | Encoding { 263 | level = .L, 264 | data_codewords = 194, 265 | ec_codewords = 48, 266 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 121, k = 97, r = 12}}, 267 | }, 268 | Encoding { 269 | level = .M, 270 | data_codewords = 154, 271 | ec_codewords = 88, 272 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 60, k = 38, r = 11}}, 273 | }, 274 | Encoding { 275 | level = .Q, 276 | data_codewords = 110, 277 | ec_codewords = 132, 278 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 61, k = 39, r = 11}}, 279 | }, 280 | Encoding { 281 | level = .H, 282 | data_codewords = 86, 283 | ec_codewords = 156, 284 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 40, k = 18, r = 11}}, 285 | }, 286 | }, 287 | }, 288 | Version_Data { 289 | id = 9, 290 | size = 53, 291 | total_codewords = 292, 292 | remainder_bits = 0, 293 | alignment_centers = {6, 26, 46}, 294 | encodings = { 295 | Encoding { 296 | level = .L, 297 | data_codewords = 232, 298 | ec_codewords = 60, 299 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 146, k = 116, r = 15}}, 300 | }, 301 | Encoding { 302 | level = .M, 303 | data_codewords = 182, 304 | ec_codewords = 110, 305 | blocks = {EC_Block{p = 0, no_blocks = 3, c = 58, k = 36, r = 11}}, 306 | }, 307 | Encoding { 308 | level = .Q, 309 | data_codewords = 132, 310 | ec_codewords = 160, 311 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 59, k = 37, r = 11}}, 312 | }, 313 | Encoding { 314 | level = .H, 315 | data_codewords = 100, 316 | ec_codewords = 192, 317 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 36, k = 16, r = 10}}, 318 | }, 319 | }, 320 | }, 321 | Version_Data { 322 | id = 10, 323 | size = 57, 324 | total_codewords = 346, 325 | remainder_bits = 0, 326 | alignment_centers = {6, 28, 50}, 327 | encodings = { 328 | Encoding { 329 | level = .L, 330 | data_codewords = 274, 331 | ec_codewords = 72, 332 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 86, k = 68, r = 9}}, 333 | }, 334 | Encoding { 335 | level = .M, 336 | data_codewords = 216, 337 | ec_codewords = 130, 338 | blocks = { 339 | EC_Block{p = 0, no_blocks = 4, c = 69, k = 43, r = 13}, 340 | EC_Block{p = 0, no_blocks = 1, c = 70, k = 44, r = 13}, 341 | }, 342 | }, 343 | Encoding { 344 | level = .Q, 345 | data_codewords = 154, 346 | ec_codewords = 192, 347 | blocks = { 348 | EC_Block{p = 0, no_blocks = 6, c = 43, k = 19, r = 12}, 349 | EC_Block{p = 0, no_blocks = 2, c = 44, k = 20, r = 12}, 350 | }, 351 | }, 352 | Encoding { 353 | level = .H, 354 | data_codewords = 122, 355 | ec_codewords = 224, 356 | blocks = { 357 | EC_Block{p = 0, no_blocks = 6, c = 43, k = 15, r = 14}, 358 | EC_Block{p = 0, no_blocks = 2, c = 44, k = 16, r = 14}, 359 | }, 360 | }, 361 | }, 362 | }, 363 | Version_Data { 364 | id = 11, 365 | size = 61, 366 | total_codewords = 404, 367 | remainder_bits = 0, 368 | alignment_centers = {6, 30, 54}, 369 | encodings = { 370 | Encoding { 371 | level = .L, 372 | data_codewords = 324, 373 | ec_codewords = 80, 374 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 101, k = 81, r = 10}}, 375 | }, 376 | Encoding { 377 | level = .M, 378 | data_codewords = 254, 379 | ec_codewords = 150, 380 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 80, k = 50, r = 15}}, 381 | }, 382 | Encoding { 383 | level = .Q, 384 | data_codewords = 180, 385 | ec_codewords = 224, 386 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 81, k = 51, r = 15}}, 387 | }, 388 | Encoding { 389 | level = .H, 390 | data_codewords = 140, 391 | ec_codewords = 264, 392 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 50, k = 22, r = 14}}, 393 | }, 394 | }, 395 | }, 396 | Version_Data { 397 | id = 12, 398 | size = 65, 399 | total_codewords = 466, 400 | remainder_bits = 0, 401 | alignment_centers = {6, 32, 58}, 402 | encodings = { 403 | Encoding { 404 | level = .L, 405 | data_codewords = 370, 406 | ec_codewords = 96, 407 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 116, k = 92, r = 12}}, 408 | }, 409 | Encoding { 410 | level = .M, 411 | data_codewords = 290, 412 | ec_codewords = 176, 413 | blocks = { 414 | EC_Block{p = 0, no_blocks = 6, c = 58, k = 36, r = 11}, 415 | EC_Block{p = 0, no_blocks = 2, c = 59, k = 36, r = 11}, 416 | }, 417 | }, 418 | Encoding { 419 | level = .Q, 420 | data_codewords = 206, 421 | ec_codewords = 260, 422 | blocks = { 423 | EC_Block{p = 0, no_blocks = 4, c = 46, k = 20, r = 13}, 424 | EC_Block{p = 0, no_blocks = 6, c = 47, k = 21, r = 13}, 425 | }, 426 | }, 427 | Encoding { 428 | level = .H, 429 | data_codewords = 158, 430 | ec_codewords = 308, 431 | blocks = { 432 | EC_Block{p = 0, no_blocks = 7, c = 42, k = 14, r = 14}, 433 | EC_Block{p = 0, no_blocks = 4, c = 43, k = 15, r = 14}, 434 | }, 435 | }, 436 | }, 437 | }, 438 | Version_Data { 439 | id = 13, 440 | size = 69, 441 | total_codewords = 532, 442 | remainder_bits = 0, 443 | alignment_centers = {6, 34, 62}, 444 | encodings = { 445 | Encoding { 446 | level = .L, 447 | data_codewords = 428, 448 | ec_codewords = 104, 449 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 133, k = 107, r = 13}}, 450 | }, 451 | Encoding { 452 | level = .M, 453 | data_codewords = 334, 454 | ec_codewords = 198, 455 | blocks = {EC_Block{p = 0, no_blocks = 8, c = 59, k = 37, r = 11}}, 456 | }, 457 | Encoding { 458 | level = .Q, 459 | data_codewords = 244, 460 | ec_codewords = 288, 461 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 60, k = 38, r = 11}}, 462 | }, 463 | Encoding { 464 | level = .H, 465 | data_codewords = 180, 466 | ec_codewords = 352, 467 | blocks = {EC_Block{p = 0, no_blocks = 8, c = 44, k = 20, r = 12}}, 468 | }, 469 | }, 470 | }, 471 | Version_Data { 472 | id = 14, 473 | size = 73, 474 | total_codewords = 581, 475 | remainder_bits = 3, 476 | alignment_centers = {6, 26, 46, 66}, 477 | encodings = { 478 | Encoding { 479 | level = .L, 480 | data_codewords = 461, 481 | ec_codewords = 120, 482 | blocks = {EC_Block{p = 0, no_blocks = 3, c = 145, k = 115, r = 15}}, 483 | }, 484 | Encoding { 485 | level = .M, 486 | data_codewords = 365, 487 | ec_codewords = 216, 488 | blocks = { 489 | EC_Block{p = 0, no_blocks = 4, c = 64, k = 40, r = 12}, 490 | EC_Block{p = 0, no_blocks = 5, c = 65, k = 41, r = 12}, 491 | }, 492 | }, 493 | Encoding { 494 | level = .Q, 495 | data_codewords = 261, 496 | ec_codewords = 320, 497 | blocks = { 498 | EC_Block{p = 0, no_blocks = 11, c = 36, k = 16, r = 10}, 499 | EC_Block{p = 0, no_blocks = 5, c = 37, k = 17, r = 10}, 500 | }, 501 | }, 502 | Encoding { 503 | level = .H, 504 | data_codewords = 197, 505 | ec_codewords = 384, 506 | blocks = { 507 | EC_Block{p = 0, no_blocks = 11, c = 36, k = 12, r = 12}, 508 | EC_Block{p = 0, no_blocks = 5, c = 37, k = 13, r = 12}, 509 | }, 510 | }, 511 | }, 512 | }, 513 | Version_Data { 514 | id = 15, 515 | size = 77, 516 | total_codewords = 655, 517 | remainder_bits = 3, 518 | alignment_centers = {6, 26, 48, 70}, 519 | encodings = { 520 | Encoding { 521 | level = .L, 522 | data_codewords = 523, 523 | ec_codewords = 132, 524 | blocks = {EC_Block{p = 0, no_blocks = 5, c = 109, k = 75, r = 11}}, 525 | }, 526 | Encoding { 527 | level = .M, 528 | data_codewords = 415, 529 | ec_codewords = 240, 530 | blocks = { 531 | EC_Block{p = 0, no_blocks = 5, c = 65, k = 41, r = 12}, 532 | EC_Block{p = 0, no_blocks = 5, c = 66, k = 42, r = 12}, 533 | }, 534 | }, 535 | Encoding { 536 | level = .Q, 537 | data_codewords = 295, 538 | ec_codewords = 360, 539 | blocks = { 540 | EC_Block{p = 0, no_blocks = 5, c = 54, k = 24, r = 15}, 541 | EC_Block{p = 0, no_blocks = 7, c = 55, k = 25, r = 15}, 542 | }, 543 | }, 544 | Encoding { 545 | level = .H, 546 | data_codewords = 223, 547 | ec_codewords = 432, 548 | blocks = { 549 | EC_Block{p = 0, no_blocks = 11, c = 36, k = 12, r = 12}, 550 | EC_Block{p = 0, no_blocks = 7, c = 37, k = 13, r = 12}, 551 | }, 552 | }, 553 | }, 554 | }, 555 | Version_Data { 556 | id = 16, 557 | size = 81, 558 | total_codewords = 733, 559 | remainder_bits = 3, 560 | alignment_centers = {6, 26, 50, 74}, 561 | encodings = { 562 | Encoding { 563 | level = .L, 564 | data_codewords = 589, 565 | ec_codewords = 144, 566 | blocks = {EC_Block{p = 0, no_blocks = 5, c = 122, k = 98, r = 12}}, 567 | }, 568 | Encoding { 569 | level = .M, 570 | data_codewords = 453, 571 | ec_codewords = 280, 572 | blocks = { 573 | EC_Block{p = 0, no_blocks = 7, c = 73, k = 45, r = 14}, 574 | EC_Block{p = 0, no_blocks = 3, c = 74, k = 46, r = 14}, 575 | }, 576 | }, 577 | Encoding { 578 | level = .Q, 579 | data_codewords = 325, 580 | ec_codewords = 408, 581 | blocks = { 582 | EC_Block{p = 0, no_blocks = 15, c = 43, k = 19, r = 12}, 583 | EC_Block{p = 0, no_blocks = 2, c = 44, k = 20, r = 12}, 584 | }, 585 | }, 586 | Encoding { 587 | level = .H, 588 | data_codewords = 253, 589 | ec_codewords = 480, 590 | blocks = { 591 | EC_Block{p = 0, no_blocks = 3, c = 45, k = 15, r = 15}, 592 | EC_Block{p = 0, no_blocks = 13, c = 46, k = 16, r = 15}, 593 | }, 594 | }, 595 | }, 596 | }, 597 | Version_Data { 598 | id = 17, 599 | size = 85, 600 | total_codewords = 815, 601 | remainder_bits = 3, 602 | alignment_centers = {6, 30, 54, 78}, 603 | encodings = { 604 | Encoding { 605 | level = .L, 606 | data_codewords = 647, 607 | ec_codewords = 168, 608 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 135, k = 107, r = 14}}, 609 | }, 610 | Encoding { 611 | level = .M, 612 | data_codewords = 507, 613 | ec_codewords = 308, 614 | blocks = { 615 | EC_Block{p = 0, no_blocks = 10, c = 74, k = 46, r = 14}, 616 | EC_Block{p = 0, no_blocks = 1, c = 75, k = 47, r = 14}, 617 | }, 618 | }, 619 | Encoding { 620 | level = .Q, 621 | data_codewords = 367, 622 | ec_codewords = 448, 623 | blocks = { 624 | EC_Block{p = 0, no_blocks = 1, c = 50, k = 22, r = 14}, 625 | EC_Block{p = 0, no_blocks = 15, c = 51, k = 23, r = 14}, 626 | }, 627 | }, 628 | Encoding { 629 | level = .H, 630 | data_codewords = 283, 631 | ec_codewords = 532, 632 | blocks = { 633 | EC_Block{p = 0, no_blocks = 2, c = 42, k = 14, r = 14}, 634 | EC_Block{p = 0, no_blocks = 17, c = 43, k = 15, r = 14}, 635 | }, 636 | }, 637 | }, 638 | }, 639 | Version_Data { 640 | id = 18, 641 | size = 89, 642 | total_codewords = 901, 643 | remainder_bits = 3, 644 | alignment_centers = {6, 30, 56, 82}, 645 | encodings = { 646 | Encoding { 647 | level = .L, 648 | data_codewords = 721, 649 | ec_codewords = 180, 650 | blocks = {EC_Block{p = 0, no_blocks = 5, c = 150, k = 120, r = 15}}, 651 | }, 652 | Encoding { 653 | level = .M, 654 | data_codewords = 563, 655 | ec_codewords = 338, 656 | blocks = { 657 | EC_Block{p = 0, no_blocks = 9, c = 69, k = 43, r = 13}, 658 | EC_Block{p = 0, no_blocks = 4, c = 70, k = 44, r = 13}, 659 | }, 660 | }, 661 | Encoding { 662 | level = .Q, 663 | data_codewords = 397, 664 | ec_codewords = 504, 665 | blocks = { 666 | EC_Block{p = 0, no_blocks = 17, c = 50, k = 22, r = 14}, 667 | EC_Block{p = 0, no_blocks = 1, c = 51, k = 23, r = 14}, 668 | }, 669 | }, 670 | Encoding { 671 | level = .H, 672 | data_codewords = 313, 673 | ec_codewords = 588, 674 | blocks = { 675 | EC_Block{p = 0, no_blocks = 2, c = 42, k = 14, r = 14}, 676 | EC_Block{p = 0, no_blocks = 19, c = 43, k = 15, r = 14}, 677 | }, 678 | }, 679 | }, 680 | }, 681 | Version_Data { 682 | id = 19, 683 | size = 93, 684 | total_codewords = 991, 685 | remainder_bits = 3, 686 | alignment_centers = {6, 30, 58, 86}, 687 | encodings = { 688 | Encoding { 689 | level = .L, 690 | data_codewords = 795, 691 | ec_codewords = 196, 692 | blocks = {EC_Block{p = 0, no_blocks = 3, c = 141, k = 113, r = 14}}, 693 | }, 694 | Encoding { 695 | level = .M, 696 | data_codewords = 627, 697 | ec_codewords = 364, 698 | blocks = { 699 | EC_Block{p = 0, no_blocks = 3, c = 70, k = 44, r = 13}, 700 | EC_Block{p = 0, no_blocks = 11, c = 71, k = 45, r = 13}, 701 | }, 702 | }, 703 | Encoding { 704 | level = .Q, 705 | data_codewords = 445, 706 | ec_codewords = 546, 707 | blocks = { 708 | EC_Block{p = 0, no_blocks = 17, c = 47, k = 21, r = 13}, 709 | EC_Block{p = 0, no_blocks = 4, c = 48, k = 22, r = 13}, 710 | }, 711 | }, 712 | Encoding { 713 | level = .H, 714 | data_codewords = 341, 715 | ec_codewords = 650, 716 | blocks = { 717 | EC_Block{p = 0, no_blocks = 9, c = 39, k = 13, r = 13}, 718 | EC_Block{p = 0, no_blocks = 16, c = 40, k = 14, r = 13}, 719 | }, 720 | }, 721 | }, 722 | }, 723 | Version_Data { 724 | id = 20, 725 | size = 97, 726 | total_codewords = 1085, 727 | remainder_bits = 3, 728 | alignment_centers = {6, 34, 62, 90}, 729 | encodings = { 730 | Encoding { 731 | level = .L, 732 | data_codewords = 861, 733 | ec_codewords = 224, 734 | blocks = {EC_Block{p = 0, no_blocks = 3, c = 135, k = 107, r = 14}}, 735 | }, 736 | Encoding { 737 | level = .M, 738 | data_codewords = 669, 739 | ec_codewords = 416, 740 | blocks = { 741 | EC_Block{p = 0, no_blocks = 3, c = 67, k = 41, r = 13}, 742 | EC_Block{p = 0, no_blocks = 13, c = 68, k = 41, r = 13}, 743 | }, 744 | }, 745 | Encoding { 746 | level = .Q, 747 | data_codewords = 485, 748 | ec_codewords = 600, 749 | blocks = { 750 | EC_Block{p = 0, no_blocks = 15, c = 54, k = 24, r = 15}, 751 | EC_Block{p = 0, no_blocks = 5, c = 55, k = 25, r = 15}, 752 | }, 753 | }, 754 | Encoding { 755 | level = .H, 756 | data_codewords = 385, 757 | ec_codewords = 700, 758 | blocks = { 759 | EC_Block{p = 0, no_blocks = 15, c = 43, k = 15, r = 14}, 760 | EC_Block{p = 0, no_blocks = 10, c = 44, k = 16, r = 14}, 761 | }, 762 | }, 763 | }, 764 | }, 765 | Version_Data { 766 | id = 21, 767 | size = 101, 768 | total_codewords = 1156, 769 | remainder_bits = 4, 770 | alignment_centers = {6, 28, 50, 72, 94}, 771 | encodings = { 772 | Encoding { 773 | level = .L, 774 | data_codewords = 932, 775 | ec_codewords = 224, 776 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 144, k = 116, r = 14}}, 777 | }, 778 | Encoding { 779 | level = .M, 780 | data_codewords = 714, 781 | ec_codewords = 442, 782 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 145, k = 117, r = 14}}, 783 | }, 784 | Encoding { 785 | level = .Q, 786 | data_codewords = 512, 787 | ec_codewords = 644, 788 | blocks = {EC_Block{p = 0, no_blocks = 17, c = 68, k = 42, r = 13}}, 789 | }, 790 | Encoding { 791 | level = .H, 792 | data_codewords = 406, 793 | ec_codewords = 750, 794 | blocks = {EC_Block{p = 0, no_blocks = 17, c = 50, k = 22, r = 14}}, 795 | }, 796 | }, 797 | }, 798 | Version_Data { 799 | id = 22, 800 | size = 105, 801 | total_codewords = 1258, 802 | remainder_bits = 4, 803 | alignment_centers = {6, 26, 50, 74, 98}, 804 | encodings = { 805 | Encoding { 806 | level = .L, 807 | data_codewords = 1006, 808 | ec_codewords = 252, 809 | blocks = {EC_Block{p = 0, no_blocks = 2, c = 139, k = 111, r = 14}}, 810 | }, 811 | Encoding { 812 | level = .M, 813 | data_codewords = 782, 814 | ec_codewords = 476, 815 | blocks = {EC_Block{p = 0, no_blocks = 7, c = 140, k = 112, r = 14}}, 816 | }, 817 | Encoding { 818 | level = .Q, 819 | data_codewords = 568, 820 | ec_codewords = 690, 821 | blocks = { 822 | EC_Block{p = 0, no_blocks = 17, c = 74, k = 46, r = 14}, 823 | EC_Block{p = 0, no_blocks = 7, c = 54, k = 24, r = 15}, 824 | }, 825 | }, 826 | Encoding { 827 | level = .H, 828 | data_codewords = 442, 829 | ec_codewords = 816, 830 | blocks = { 831 | EC_Block{p = 0, no_blocks = 16, c = 55, k = 25, r = 15}, 832 | EC_Block{p = 0, no_blocks = 34, c = 37, k = 13, r = 12}, 833 | }, 834 | }, 835 | }, 836 | }, 837 | Version_Data { 838 | id = 23, 839 | size = 109, 840 | total_codewords = 1364, 841 | remainder_bits = 4, 842 | alignment_centers = {6, 30, 54, 78, 102}, 843 | encodings = { 844 | Encoding { 845 | level = .L, 846 | data_codewords = 1094, 847 | ec_codewords = 270, 848 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 151, k = 121, r = 15}}, 849 | }, 850 | Encoding { 851 | level = .M, 852 | data_codewords = 860, 853 | ec_codewords = 504, 854 | blocks = { 855 | EC_Block{p = 0, no_blocks = 4, c = 75, k = 47, r = 14}, 856 | EC_Block{p = 0, no_blocks = 14, c = 76, k = 48, r = 14}, 857 | }, 858 | }, 859 | Encoding { 860 | level = .Q, 861 | data_codewords = 614, 862 | ec_codewords = 750, 863 | blocks = { 864 | EC_Block{p = 0, no_blocks = 11, c = 54, k = 24, r = 15}, 865 | EC_Block{p = 0, no_blocks = 14, c = 55, k = 25, r = 15}, 866 | }, 867 | }, 868 | Encoding { 869 | level = .H, 870 | data_codewords = 464, 871 | ec_codewords = 900, 872 | blocks = { 873 | EC_Block{p = 0, no_blocks = 16, c = 45, k = 15, r = 15}, 874 | EC_Block{p = 0, no_blocks = 14, c = 46, k = 16, r = 15}, 875 | }, 876 | }, 877 | }, 878 | }, 879 | Version_Data { 880 | id = 24, 881 | size = 113, 882 | total_codewords = 1474, 883 | remainder_bits = 4, 884 | alignment_centers = {6, 28, 54, 80, 106}, 885 | encodings = { 886 | Encoding { 887 | level = .L, 888 | data_codewords = 1174, 889 | ec_codewords = 300, 890 | blocks = {EC_Block{p = 0, no_blocks = 6, c = 147, k = 117, r = 15}}, 891 | }, 892 | Encoding { 893 | level = .M, 894 | data_codewords = 914, 895 | ec_codewords = 560, 896 | blocks = { 897 | EC_Block{p = 0, no_blocks = 6, c = 73, k = 45, r = 14}, 898 | EC_Block{p = 0, no_blocks = 14, c = 74, k = 46, r = 14}, 899 | }, 900 | }, 901 | Encoding { 902 | level = .Q, 903 | data_codewords = 664, 904 | ec_codewords = 810, 905 | blocks = { 906 | EC_Block{p = 0, no_blocks = 11, c = 54, k = 24, r = 15}, 907 | EC_Block{p = 0, no_blocks = 16, c = 55, k = 25, r = 15}, 908 | }, 909 | }, 910 | Encoding { 911 | level = .H, 912 | data_codewords = 514, 913 | ec_codewords = 960, 914 | blocks = { 915 | EC_Block{p = 0, no_blocks = 30, c = 46, k = 16, r = 15}, 916 | EC_Block{p = 0, no_blocks = 2, c = 47, k = 17, r = 15}, 917 | }, 918 | }, 919 | }, 920 | }, 921 | Version_Data { 922 | id = 25, 923 | size = 117, 924 | total_codewords = 1588, 925 | remainder_bits = 4, 926 | alignment_centers = {6, 32, 58, 84, 110}, 927 | encodings = { 928 | Encoding { 929 | level = .L, 930 | data_codewords = 1276, 931 | ec_codewords = 312, 932 | blocks = {EC_Block{p = 0, no_blocks = 8, c = 132, k = 106, r = 13}}, 933 | }, 934 | Encoding { 935 | level = .M, 936 | data_codewords = 1000, 937 | ec_codewords = 588, 938 | blocks = { 939 | EC_Block{p = 0, no_blocks = 8, c = 75, k = 47, r = 14}, 940 | EC_Block{p = 0, no_blocks = 13, c = 76, k = 48, r = 14}, 941 | }, 942 | }, 943 | Encoding { 944 | level = .Q, 945 | data_codewords = 718, 946 | ec_codewords = 870, 947 | blocks = { 948 | EC_Block{p = 0, no_blocks = 7, c = 54, k = 25, r = 15}, 949 | EC_Block{p = 0, no_blocks = 22, c = 55, k = 25, r = 15}, 950 | }, 951 | }, 952 | Encoding { 953 | level = .H, 954 | data_codewords = 538, 955 | ec_codewords = 1050, 956 | blocks = { 957 | EC_Block{p = 0, no_blocks = 22, c = 45, k = 15, r = 15}, 958 | EC_Block{p = 0, no_blocks = 13, c = 46, k = 16, r = 15}, 959 | }, 960 | }, 961 | }, 962 | }, 963 | Version_Data { 964 | id = 26, 965 | size = 121, 966 | total_codewords = 1706, 967 | remainder_bits = 4, 968 | alignment_centers = {6, 30, 58, 86, 114}, 969 | encodings = { 970 | Encoding { 971 | level = .L, 972 | data_codewords = 1370, 973 | ec_codewords = 336, 974 | blocks = {EC_Block{p = 0, no_blocks = 10, c = 142, k = 114, r = 14}}, 975 | }, 976 | Encoding { 977 | level = .M, 978 | data_codewords = 1062, 979 | ec_codewords = 644, 980 | blocks = { 981 | EC_Block{p = 0, no_blocks = 19, c = 74, k = 46, r = 14}, 982 | EC_Block{p = 0, no_blocks = 4, c = 75, k = 47, r = 14}, 983 | }, 984 | }, 985 | Encoding { 986 | level = .Q, 987 | data_codewords = 754, 988 | ec_codewords = 952, 989 | blocks = { 990 | EC_Block{p = 0, no_blocks = 28, c = 50, k = 22, r = 14}, 991 | EC_Block{p = 0, no_blocks = 6, c = 51, k = 23, r = 14}, 992 | }, 993 | }, 994 | Encoding { 995 | level = .H, 996 | data_codewords = 596, 997 | ec_codewords = 1110, 998 | blocks = { 999 | EC_Block{p = 0, no_blocks = 33, c = 46, k = 16, r = 15}, 1000 | EC_Block{p = 0, no_blocks = 4, c = 47, k = 17, r = 15}, 1001 | }, 1002 | }, 1003 | }, 1004 | }, 1005 | Version_Data { 1006 | id = 27, 1007 | size = 125, 1008 | total_codewords = 1828, 1009 | remainder_bits = 4, 1010 | alignment_centers = {6, 34, 62, 90, 118}, 1011 | encodings = { 1012 | Encoding { 1013 | level = .L, 1014 | data_codewords = 1468, 1015 | ec_codewords = 360, 1016 | blocks = {EC_Block{p = 0, no_blocks = 8, c = 152, k = 122, r = 15}}, 1017 | }, 1018 | Encoding { 1019 | level = .M, 1020 | data_codewords = 1128, 1021 | ec_codewords = 700, 1022 | blocks = { 1023 | EC_Block{p = 0, no_blocks = 22, c = 73, k = 45, r = 14}, 1024 | EC_Block{p = 0, no_blocks = 3, c = 74, k = 46, r = 14}, 1025 | }, 1026 | }, 1027 | Encoding { 1028 | level = .Q, 1029 | data_codewords = 808, 1030 | ec_codewords = 1020, 1031 | blocks = { 1032 | EC_Block{p = 0, no_blocks = 8, c = 53, k = 23, r = 15}, 1033 | EC_Block{p = 0, no_blocks = 26, c = 54, k = 24, r = 15}, 1034 | }, 1035 | }, 1036 | Encoding { 1037 | level = .H, 1038 | data_codewords = 628, 1039 | ec_codewords = 1200, 1040 | blocks = { 1041 | EC_Block{p = 0, no_blocks = 12, c = 45, k = 15, r = 15}, 1042 | EC_Block{p = 0, no_blocks = 28, c = 46, k = 16, r = 15}, 1043 | }, 1044 | }, 1045 | }, 1046 | }, 1047 | Version_Data { 1048 | id = 28, 1049 | size = 129, 1050 | total_codewords = 1921, 1051 | remainder_bits = 3, 1052 | alignment_centers = {6, 26, 50, 74, 98, 122}, 1053 | encodings = { 1054 | Encoding { 1055 | level = .L, 1056 | data_codewords = 1531, 1057 | ec_codewords = 390, 1058 | blocks = {EC_Block{p = 0, no_blocks = 3, c = 147, k = 117, r = 15}}, 1059 | }, 1060 | Encoding { 1061 | level = .M, 1062 | data_codewords = 1193, 1063 | ec_codewords = 728, 1064 | blocks = { 1065 | EC_Block{p = 0, no_blocks = 3, c = 73, k = 45, r = 14}, 1066 | EC_Block{p = 0, no_blocks = 23, c = 74, k = 46, r = 14}, 1067 | }, 1068 | }, 1069 | Encoding { 1070 | level = .Q, 1071 | data_codewords = 871, 1072 | ec_codewords = 1050, 1073 | blocks = { 1074 | EC_Block{p = 0, no_blocks = 4, c = 54, k = 25, r = 15}, 1075 | EC_Block{p = 0, no_blocks = 31, c = 55, k = 25, r = 15}, 1076 | }, 1077 | }, 1078 | Encoding { 1079 | level = .H, 1080 | data_codewords = 661, 1081 | ec_codewords = 1260, 1082 | blocks = { 1083 | EC_Block{p = 0, no_blocks = 11, c = 45, k = 15, r = 15}, 1084 | EC_Block{p = 0, no_blocks = 31, c = 46, k = 16, r = 15}, 1085 | }, 1086 | }, 1087 | }, 1088 | }, 1089 | Version_Data { 1090 | id = 29, 1091 | size = 133, 1092 | total_codewords = 2051, 1093 | remainder_bits = 3, 1094 | alignment_centers = {6, 30, 54, 78, 102, 126}, 1095 | encodings = { 1096 | Encoding { 1097 | level = .L, 1098 | data_codewords = 1631, 1099 | ec_codewords = 420, 1100 | blocks = {EC_Block{p = 0, no_blocks = 7, c = 146, k = 116, r = 15}}, 1101 | }, 1102 | Encoding { 1103 | level = .M, 1104 | data_codewords = 1267, 1105 | ec_codewords = 784, 1106 | blocks = { 1107 | EC_Block{p = 0, no_blocks = 21, c = 73, k = 45, r = 14}, 1108 | EC_Block{p = 0, no_blocks = 7, c = 74, k = 46, r = 14}, 1109 | }, 1110 | }, 1111 | Encoding { 1112 | level = .Q, 1113 | data_codewords = 911, 1114 | ec_codewords = 1140, 1115 | blocks = { 1116 | EC_Block{p = 0, no_blocks = 1, c = 53, k = 23, r = 15}, 1117 | EC_Block{p = 0, no_blocks = 37, c = 54, k = 24, r = 15}, 1118 | }, 1119 | }, 1120 | Encoding { 1121 | level = .H, 1122 | data_codewords = 701, 1123 | ec_codewords = 1350, 1124 | blocks = { 1125 | EC_Block{p = 0, no_blocks = 19, c = 45, k = 15, r = 15}, 1126 | EC_Block{p = 0, no_blocks = 26, c = 46, k = 16, r = 16}, 1127 | }, 1128 | }, 1129 | }, 1130 | }, 1131 | Version_Data { 1132 | id = 30, 1133 | size = 137, 1134 | total_codewords = 2185, 1135 | remainder_bits = 3, 1136 | alignment_centers = {6, 26, 52, 78, 104, 130}, 1137 | encodings = { 1138 | Encoding { 1139 | level = .L, 1140 | data_codewords = 1735, 1141 | ec_codewords = 450, 1142 | blocks = {EC_Block{p = 0, no_blocks = 5, c = 145, k = 115, r = 15}}, 1143 | }, 1144 | Encoding { 1145 | level = .M, 1146 | data_codewords = 1373, 1147 | ec_codewords = 812, 1148 | blocks = { 1149 | EC_Block{p = 0, no_blocks = 19, c = 75, k = 47, r = 14}, 1150 | EC_Block{p = 0, no_blocks = 10, c = 76, k = 48, r = 14}, 1151 | }, 1152 | }, 1153 | Encoding { 1154 | level = .Q, 1155 | data_codewords = 985, 1156 | ec_codewords = 1200, 1157 | blocks = { 1158 | EC_Block{p = 0, no_blocks = 15, c = 54, k = 24, r = 15}, 1159 | EC_Block{p = 0, no_blocks = 25, c = 55, k = 25, r = 15}, 1160 | }, 1161 | }, 1162 | Encoding { 1163 | level = .H, 1164 | data_codewords = 745, 1165 | ec_codewords = 1440, 1166 | blocks = { 1167 | EC_Block{p = 0, no_blocks = 23, c = 45, k = 15, r = 15}, 1168 | EC_Block{p = 0, no_blocks = 25, c = 46, k = 16, r = 15}, 1169 | }, 1170 | }, 1171 | }, 1172 | }, 1173 | Version_Data { 1174 | id = 31, 1175 | size = 141, 1176 | total_codewords = 2323, 1177 | remainder_bits = 3, 1178 | alignment_centers = {6, 30, 56, 82, 108, 134}, 1179 | encodings = { 1180 | Encoding { 1181 | level = .L, 1182 | data_codewords = 1843, 1183 | ec_codewords = 480, 1184 | blocks = {EC_Block{p = 0, no_blocks = 13, c = 0, k = 0, r = 0}}, 1185 | }, 1186 | Encoding { 1187 | level = .M, 1188 | data_codewords = 1455, 1189 | ec_codewords = 868, 1190 | blocks = { 1191 | EC_Block{p = 0, no_blocks = 2, c = 0, k = 0, r = 0}, 1192 | EC_Block{p = 0, no_blocks = 29, c = 0, k = 0, r = 0}, 1193 | }, 1194 | }, 1195 | Encoding { 1196 | level = .Q, 1197 | data_codewords = 1033, 1198 | ec_codewords = 1290, 1199 | blocks = { 1200 | EC_Block{p = 0, no_blocks = 42, c = 0, k = 0, r = 0}, 1201 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1202 | }, 1203 | }, 1204 | Encoding { 1205 | level = .H, 1206 | data_codewords = 793, 1207 | ec_codewords = 1530, 1208 | blocks = { 1209 | EC_Block{p = 0, no_blocks = 23, c = 0, k = 0, r = 0}, 1210 | EC_Block{p = 0, no_blocks = 28, c = 0, k = 0, r = 0}, 1211 | }, 1212 | }, 1213 | }, 1214 | }, 1215 | Version_Data { 1216 | id = 32, 1217 | size = 145, 1218 | total_codewords = 2465, 1219 | remainder_bits = 3, 1220 | alignment_centers = {6, 34, 60, 86, 112, 138}, 1221 | encodings = { 1222 | Encoding { 1223 | level = .L, 1224 | data_codewords = 1955, 1225 | ec_codewords = 510, 1226 | blocks = {EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}}, 1227 | }, 1228 | Encoding { 1229 | level = .M, 1230 | data_codewords = 1541, 1231 | ec_codewords = 924, 1232 | blocks = { 1233 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1234 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1235 | }, 1236 | }, 1237 | Encoding { 1238 | level = .Q, 1239 | data_codewords = 1115, 1240 | ec_codewords = 1350, 1241 | blocks = { 1242 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1243 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1244 | }, 1245 | }, 1246 | Encoding { 1247 | level = .H, 1248 | data_codewords = 845, 1249 | ec_codewords = 1620, 1250 | blocks = { 1251 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1252 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1253 | }, 1254 | }, 1255 | }, 1256 | }, 1257 | Version_Data { 1258 | id = 33, 1259 | size = 149, 1260 | total_codewords = 2611, 1261 | remainder_bits = 3, 1262 | alignment_centers = {6, 30, 58, 86, 114, 142}, 1263 | encodings = { 1264 | Encoding { 1265 | level = .L, 1266 | data_codewords = 2071, 1267 | ec_codewords = 540, 1268 | blocks = {EC_Block{p = 0, no_blocks = 17, c = 0, k = 0, r = 0}}, 1269 | }, 1270 | Encoding { 1271 | level = .M, 1272 | data_codewords = 1631, 1273 | ec_codewords = 980, 1274 | blocks = { 1275 | EC_Block{p = 0, no_blocks = 14, c = 0, k = 0, r = 0}, 1276 | EC_Block{p = 0, no_blocks = 21, c = 0, k = 0, r = 0}, 1277 | }, 1278 | }, 1279 | Encoding { 1280 | level = .Q, 1281 | data_codewords = 1171, 1282 | ec_codewords = 1440, 1283 | blocks = { 1284 | EC_Block{p = 0, no_blocks = 29, c = 0, k = 0, r = 0}, 1285 | EC_Block{p = 0, no_blocks = 19, c = 0, k = 0, r = 0}, 1286 | }, 1287 | }, 1288 | Encoding { 1289 | level = .H, 1290 | data_codewords = 901, 1291 | ec_codewords = 1710, 1292 | blocks = { 1293 | EC_Block{p = 0, no_blocks = 11, c = 0, k = 0, r = 0}, 1294 | EC_Block{p = 0, no_blocks = 46, c = 0, k = 0, r = 0}, 1295 | }, 1296 | }, 1297 | }, 1298 | }, 1299 | Version_Data { 1300 | id = 34, 1301 | size = 153, 1302 | total_codewords = 2761, 1303 | remainder_bits = 3, 1304 | alignment_centers = {6, 34, 62, 90, 118, 146}, 1305 | encodings = { 1306 | Encoding { 1307 | level = .L, 1308 | data_codewords = 2191, 1309 | ec_codewords = 570, 1310 | blocks = {EC_Block{p = 0, no_blocks = 13, c = 0, k = 0, r = 0}}, 1311 | }, 1312 | Encoding { 1313 | level = .M, 1314 | data_codewords = 1725, 1315 | ec_codewords = 1036, 1316 | blocks = { 1317 | EC_Block{p = 0, no_blocks = 14, c = 0, k = 0, r = 0}, 1318 | EC_Block{p = 0, no_blocks = 23, c = 0, k = 0, r = 0}, 1319 | }, 1320 | }, 1321 | Encoding { 1322 | level = .Q, 1323 | data_codewords = 1231, 1324 | ec_codewords = 1530, 1325 | blocks = { 1326 | EC_Block{p = 0, no_blocks = 44, c = 0, k = 0, r = 0}, 1327 | EC_Block{p = 0, no_blocks = 7, c = 0, k = 0, r = 0}, 1328 | }, 1329 | }, 1330 | Encoding { 1331 | level = .H, 1332 | data_codewords = 961, 1333 | ec_codewords = 1800, 1334 | blocks = { 1335 | EC_Block{p = 0, no_blocks = 59, c = 0, k = 0, r = 0}, 1336 | EC_Block{p = 0, no_blocks = 1, c = 0, k = 0, r = 0}, 1337 | }, 1338 | }, 1339 | }, 1340 | }, 1341 | Version_Data { 1342 | id = 35, 1343 | size = 157, 1344 | total_codewords = 2876, 1345 | remainder_bits = 0, 1346 | alignment_centers = {6, 30, 54, 78, 102, 126, 150}, 1347 | encodings = { 1348 | Encoding { 1349 | level = .L, 1350 | data_codewords = 2306, 1351 | ec_codewords = 570, 1352 | blocks = {EC_Block{p = 0, no_blocks = 12, c = 0, k = 0, r = 0}}, 1353 | }, 1354 | Encoding { 1355 | level = .M, 1356 | data_codewords = 1812, 1357 | ec_codewords = 1064, 1358 | blocks = { 1359 | EC_Block{p = 0, no_blocks = 12, c = 0, k = 0, r = 0}, 1360 | EC_Block{p = 0, no_blocks = 26, c = 0, k = 0, r = 0}, 1361 | }, 1362 | }, 1363 | Encoding { 1364 | level = .Q, 1365 | data_codewords = 1286, 1366 | ec_codewords = 1590, 1367 | blocks = { 1368 | EC_Block{p = 0, no_blocks = 39, c = 0, k = 0, r = 0}, 1369 | EC_Block{p = 0, no_blocks = 14, c = 0, k = 0, r = 0}, 1370 | }, 1371 | }, 1372 | Encoding { 1373 | level = .H, 1374 | data_codewords = 986, 1375 | ec_codewords = 1890, 1376 | blocks = { 1377 | EC_Block{p = 0, no_blocks = 22, c = 0, k = 0, r = 0}, 1378 | EC_Block{p = 0, no_blocks = 41, c = 0, k = 0, r = 0}, 1379 | }, 1380 | }, 1381 | }, 1382 | }, 1383 | Version_Data { 1384 | id = 36, 1385 | size = 161, 1386 | total_codewords = 3034, 1387 | remainder_bits = 0, 1388 | alignment_centers = {6, 24, 50, 76, 102, 128, 154}, 1389 | encodings = { 1390 | Encoding { 1391 | level = .L, 1392 | data_codewords = 2434, 1393 | ec_codewords = 600, 1394 | blocks = {EC_Block{p = 0, no_blocks = 6, c = 0, k = 0, r = 0}}, 1395 | }, 1396 | Encoding { 1397 | level = .M, 1398 | data_codewords = 1914, 1399 | ec_codewords = 1120, 1400 | blocks = { 1401 | EC_Block{p = 0, no_blocks = 6, c = 0, k = 0, r = 0}, 1402 | EC_Block{p = 0, no_blocks = 34, c = 0, k = 0, r = 0}, 1403 | }, 1404 | }, 1405 | Encoding { 1406 | level = .Q, 1407 | data_codewords = 1354, 1408 | ec_codewords = 1680, 1409 | blocks = { 1410 | EC_Block{p = 0, no_blocks = 46, c = 0, k = 0, r = 0}, 1411 | EC_Block{p = 0, no_blocks = 10, c = 0, k = 0, r = 0}, 1412 | }, 1413 | }, 1414 | Encoding { 1415 | level = .H, 1416 | data_codewords = 1054, 1417 | ec_codewords = 1980, 1418 | blocks = { 1419 | EC_Block{p = 0, no_blocks = 2, c = 0, k = 0, r = 0}, 1420 | EC_Block{p = 0, no_blocks = 64, c = 0, k = 0, r = 0}, 1421 | }, 1422 | }, 1423 | }, 1424 | }, 1425 | Version_Data { 1426 | id = 37, 1427 | size = 165, 1428 | total_codewords = 3196, 1429 | remainder_bits = 0, 1430 | alignment_centers = {6, 28, 54, 80, 106, 132, 158}, 1431 | encodings = { 1432 | Encoding { 1433 | level = .L, 1434 | data_codewords = 2566, 1435 | ec_codewords = 630, 1436 | blocks = {EC_Block{p = 0, no_blocks = 17, c = 0, k = 0, r = 0}}, 1437 | }, 1438 | Encoding { 1439 | level = .M, 1440 | data_codewords = 1992, 1441 | ec_codewords = 1204, 1442 | blocks = { 1443 | EC_Block{p = 0, no_blocks = 29, c = 0, k = 0, r = 0}, 1444 | EC_Block{p = 0, no_blocks = 14, c = 0, k = 0, r = 0}, 1445 | }, 1446 | }, 1447 | Encoding { 1448 | level = .Q, 1449 | data_codewords = 1426, 1450 | ec_codewords = 1770, 1451 | blocks = { 1452 | EC_Block{p = 0, no_blocks = 49, c = 0, k = 0, r = 0}, 1453 | EC_Block{p = 0, no_blocks = 10, c = 0, k = 0, r = 0}, 1454 | }, 1455 | }, 1456 | Encoding { 1457 | level = .H, 1458 | data_codewords = 1096, 1459 | ec_codewords = 2100, 1460 | blocks = { 1461 | EC_Block{p = 0, no_blocks = 24, c = 0, k = 0, r = 0}, 1462 | EC_Block{p = 0, no_blocks = 46, c = 0, k = 0, r = 0}, 1463 | }, 1464 | }, 1465 | }, 1466 | }, 1467 | Version_Data { 1468 | id = 38, 1469 | size = 169, 1470 | total_codewords = 3362, 1471 | remainder_bits = 0, 1472 | alignment_centers = {6, 32, 58, 84, 110, 136, 162}, 1473 | encodings = { 1474 | Encoding { 1475 | level = .L, 1476 | data_codewords = 2702, 1477 | ec_codewords = 660, 1478 | blocks = {EC_Block{p = 0, no_blocks = 4, c = 0, k = 0, r = 0}}, 1479 | }, 1480 | Encoding { 1481 | level = .M, 1482 | data_codewords = 2102, 1483 | ec_codewords = 1260, 1484 | blocks = { 1485 | EC_Block{p = 0, no_blocks = 13, c = 0, k = 0, r = 0}, 1486 | EC_Block{p = 0, no_blocks = 32, c = 0, k = 0, r = 0}, 1487 | }, 1488 | }, 1489 | Encoding { 1490 | level = .Q, 1491 | data_codewords = 1502, 1492 | ec_codewords = 1860, 1493 | blocks = { 1494 | EC_Block{p = 0, no_blocks = 48, c = 0, k = 0, r = 0}, 1495 | EC_Block{p = 0, no_blocks = 14, c = 0, k = 0, r = 0}, 1496 | }, 1497 | }, 1498 | Encoding { 1499 | level = .H, 1500 | data_codewords = 1142, 1501 | ec_codewords = 2220, 1502 | blocks = { 1503 | EC_Block{p = 0, no_blocks = 42, c = 0, k = 0, r = 0}, 1504 | EC_Block{p = 0, no_blocks = 32, c = 0, k = 0, r = 0}, 1505 | }, 1506 | }, 1507 | }, 1508 | }, 1509 | Version_Data { 1510 | id = 39, 1511 | size = 173, 1512 | total_codewords = 3532, 1513 | remainder_bits = 0, 1514 | alignment_centers = {6, 26, 54, 82, 110, 138, 166}, 1515 | encodings = { 1516 | Encoding { 1517 | level = .L, 1518 | data_codewords = 2812, 1519 | ec_codewords = 720, 1520 | blocks = {EC_Block{p = 0, no_blocks = 20, c = 0, k = 0, r = 0}}, 1521 | }, 1522 | Encoding { 1523 | level = .M, 1524 | data_codewords = 2216, 1525 | ec_codewords = 1316, 1526 | blocks = { 1527 | EC_Block{p = 0, no_blocks = 40, c = 0, k = 0, r = 0}, 1528 | EC_Block{p = 0, no_blocks = 7, c = 0, k = 0, r = 0}, 1529 | }, 1530 | }, 1531 | Encoding { 1532 | level = .Q, 1533 | data_codewords = 1582, 1534 | ec_codewords = 1950, 1535 | blocks = { 1536 | EC_Block{p = 0, no_blocks = 43, c = 0, k = 0, r = 0}, 1537 | EC_Block{p = 0, no_blocks = 22, c = 0, k = 0, r = 0}, 1538 | }, 1539 | }, 1540 | Encoding { 1541 | level = .H, 1542 | data_codewords = 1222, 1543 | ec_codewords = 2310, 1544 | blocks = { 1545 | EC_Block{p = 0, no_blocks = 10, c = 0, k = 0, r = 0}, 1546 | EC_Block{p = 0, no_blocks = 67, c = 0, k = 0, r = 0}, 1547 | }, 1548 | }, 1549 | }, 1550 | }, 1551 | Version_Data { 1552 | id = 40, 1553 | size = 177, 1554 | total_codewords = 3706, 1555 | remainder_bits = 0, 1556 | alignment_centers = {6, 30, 58, 86, 114, 142, 170}, 1557 | encodings = { 1558 | Encoding { 1559 | level = .L, 1560 | data_codewords = 2956, 1561 | ec_codewords = 750, 1562 | blocks = {EC_Block{p = 0, no_blocks = 19, c = 0, k = 0, r = 0}}, 1563 | }, 1564 | Encoding { 1565 | level = .M, 1566 | data_codewords = 2334, 1567 | ec_codewords = 1372, 1568 | blocks = { 1569 | EC_Block{p = 0, no_blocks = 18, c = 0, k = 0, r = 0}, 1570 | EC_Block{p = 0, no_blocks = 31, c = 0, k = 0, r = 0}, 1571 | }, 1572 | }, 1573 | Encoding { 1574 | level = .Q, 1575 | data_codewords = 1666, 1576 | ec_codewords = 2040, 1577 | blocks = { 1578 | EC_Block{p = 0, no_blocks = 34, c = 0, k = 0, r = 0}, 1579 | EC_Block{p = 0, no_blocks = 34, c = 0, k = 0, r = 0}, 1580 | }, 1581 | }, 1582 | Encoding { 1583 | level = .H, 1584 | data_codewords = 1276, 1585 | ec_codewords = 2430, 1586 | blocks = { 1587 | EC_Block{p = 0, no_blocks = 20, c = 0, k = 0, r = 0}, 1588 | EC_Block{p = 0, no_blocks = 61, c = 0, k = 0, r = 0}, 1589 | }, 1590 | }, 1591 | }, 1592 | }, 1593 | } 1594 | --------------------------------------------------------------------------------