diff --git a/draw/draw_qr/draw_qr.odin b/draw/draw_qr/draw_qr.odin index 3567092..91cf532 100644 --- a/draw/draw_qr/draw_qr.odin +++ b/draw/draw_qr/draw_qr.odin @@ -161,6 +161,11 @@ register_texture_from_binary :: proc( return register_texture_from_raw(qrcode_buf, dark, light, temp_allocator) } +register_texture_from :: proc { + register_texture_from_text, + register_texture_from_binary +} + // Default fit=.Fit preserves the QR's square aspect; override as needed. clay_image :: #force_inline proc( texture: draw.Texture_Id, diff --git a/draw/examples/textures.odin b/draw/examples/textures.odin index a89be7d..b49b33a 100644 --- a/draw/examples/textures.odin +++ b/draw/examples/textures.odin @@ -2,7 +2,6 @@ package examples import "../../draw" import "../../draw/draw_qr" -import "core:math" import "core:os" import sdl "vendor:sdl3" @@ -17,9 +16,8 @@ textures :: proc() { FONT_SIZE :: u16(14) LABEL_OFFSET :: f32(8) // gap between item and its label - // ------------------------------------------------------------------------- - // Procedural checkerboard texture (8x8, RGBA8) - // ------------------------------------------------------------------------- + //----- Texture registration ---------------------------------- + checker_size :: 8 checker_pixels: [checker_size * checker_size * 4]u8 for y in 0 ..< checker_size { @@ -47,9 +45,6 @@ textures :: proc() { ) defer draw.unregister_texture(checker_texture) - // ------------------------------------------------------------------------- - // Non-square gradient stripe texture (16x8, RGBA8) for fit mode demos - // ------------------------------------------------------------------------- stripe_w :: 16 stripe_h :: 8 stripe_pixels: [stripe_w * stripe_h * 4]u8 @@ -76,14 +71,13 @@ textures :: proc() { ) defer draw.unregister_texture(stripe_texture) - // ------------------------------------------------------------------------- - // QR code texture (R8_UNORM — see rendering note below) - // ------------------------------------------------------------------------- - qr_texture, _ := draw_qr.register_texture_from_text("https://x.com/miiilato/status/1880241066471051443") + qr_texture, _ := draw_qr.register_texture_from("https://x.com/miiilato/status/1880241066471051443") defer draw.unregister_texture(qr_texture) spin_angle: f32 = 0 + //----- Draw loop ---------------------------------- + for { defer free_all(context.temp_allocator) ev: sdl.Event @@ -97,9 +91,8 @@ textures :: proc() { // Background draw.rectangle(base_layer, {0, 0, 800, 600}, {30, 30, 30, 255}) - // ===================================================================== - // Row 1: Sampler presets (y=30) - // ===================================================================== + //----- Row 1: Sampler presets (y=30) ---------------------------------- + ROW1_Y :: f32(30) ITEM_SIZE :: f32(120) COL1 :: f32(30) @@ -156,9 +149,8 @@ textures :: proc() { color = draw.WHITE, ) - // ===================================================================== - // Row 2: QR code, Rounded, Rotating (y=190) - // ===================================================================== + //----- Row 2: Sampler presets (y=190) ---------------------------------- + ROW2_Y :: f32(190) // QR code (RGBA texture with baked colors, nearest sampling) @@ -214,9 +206,8 @@ textures :: proc() { color = draw.WHITE, ) - // ===================================================================== - // Row 3: Fit modes + Per-corner radii (y=360) - // ===================================================================== + //----- Row 3: Fit modes + Per-corner radii (y=360) ---------------------------------- + ROW3_Y :: f32(360) FIT_SIZE :: f32(120) // square target rect diff --git a/draw/textures.odin b/draw/textures.odin index 64f636d..b9e5b31 100644 --- a/draw/textures.odin +++ b/draw/textures.odin @@ -4,10 +4,6 @@ import "core:log" import "core:mem" import sdl "vendor:sdl3" -// --------------------------------------------------------------------------- -// Texture types -// --------------------------------------------------------------------------- - Texture_Id :: distinct u32 INVALID_TEXTURE :: Texture_Id(0) // Slot 0 is reserved/unused @@ -61,10 +57,6 @@ Texture_Slot :: struct { // GLOB.pending_texture_releases : [dynamic]Texture_Id // GLOB.samplers : [SAMPLER_PRESET_COUNT]^sdl.GPUSampler -// --------------------------------------------------------------------------- -// Clay integration type -// --------------------------------------------------------------------------- - Clay_Image_Data :: struct { texture_id: Texture_Id, fit: Fit_Mode, @@ -75,9 +67,9 @@ clay_image_data :: proc(id: Texture_Id, fit: Fit_Mode = .Stretch, tint: Color = return {texture_id = id, fit = fit, tint = tint} } -// --------------------------------------------------------------------------- -// Registration -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Registration ------------- +// --------------------------------------------------------------------------------------------------------------------- // Register a texture. Draw owns the GPU resource and releases it on unregister. // `data` is tightly-packed row-major bytes matching desc.format. @@ -236,130 +228,9 @@ update_texture_region :: proc(id: Texture_Id, region: Rectangle, data: []u8) { } } -// --------------------------------------------------------------------------- -// Accessors -// --------------------------------------------------------------------------- - -texture_size :: proc(id: Texture_Id) -> [2]u32 { - if id == INVALID_TEXTURE do return {0, 0} - slot := &GLOB.texture_slots[u32(id)] - return {slot.desc.width, slot.desc.height} -} - -texture_format :: proc(id: Texture_Id) -> sdl.GPUTextureFormat { - if id == INVALID_TEXTURE do return .INVALID - return GLOB.texture_slots[u32(id)].desc.format -} - -texture_kind :: proc(id: Texture_Id) -> Texture_Kind { - if id == INVALID_TEXTURE do return .Static - return GLOB.texture_slots[u32(id)].desc.kind -} - -// Internal: get the raw GPU texture pointer for binding during draw. -@(private) -texture_gpu_handle :: proc(id: Texture_Id) -> ^sdl.GPUTexture { - if id == INVALID_TEXTURE do return nil - idx := u32(id) - if idx >= u32(len(GLOB.texture_slots)) do return nil - return GLOB.texture_slots[idx].gpu_texture -} - -// --------------------------------------------------------------------------- -// Deferred release (called from draw.end / clear_global) -// --------------------------------------------------------------------------- - -@(private) -process_pending_texture_releases :: proc() { - device := GLOB.device - for id in GLOB.pending_texture_releases { - idx := u32(id) - if idx >= u32(len(GLOB.texture_slots)) do continue - slot := &GLOB.texture_slots[idx] - if slot.gpu_texture != nil { - sdl.ReleaseGPUTexture(device, slot.gpu_texture) - slot.gpu_texture = nil - } - slot.generation += 1 - append(&GLOB.texture_free_list, idx) - } - clear(&GLOB.pending_texture_releases) -} - -// --------------------------------------------------------------------------- -// Sampler pool -// --------------------------------------------------------------------------- - -@(private) -get_sampler :: proc(preset: Sampler_Preset) -> ^sdl.GPUSampler { - idx := int(preset) - if GLOB.samplers[idx] != nil do return GLOB.samplers[idx] - - // Lazily create - min_filter, mag_filter: sdl.GPUFilter - address_mode: sdl.GPUSamplerAddressMode - - switch preset { - case .Nearest_Clamp: - min_filter = .NEAREST; mag_filter = .NEAREST; address_mode = .CLAMP_TO_EDGE - case .Linear_Clamp: - min_filter = .LINEAR; mag_filter = .LINEAR; address_mode = .CLAMP_TO_EDGE - case .Nearest_Repeat: - min_filter = .NEAREST; mag_filter = .NEAREST; address_mode = .REPEAT - case .Linear_Repeat: - min_filter = .LINEAR; mag_filter = .LINEAR; address_mode = .REPEAT - } - - sampler := sdl.CreateGPUSampler( - GLOB.device, - sdl.GPUSamplerCreateInfo { - min_filter = min_filter, - mag_filter = mag_filter, - mipmap_mode = .LINEAR, - address_mode_u = address_mode, - address_mode_v = address_mode, - address_mode_w = address_mode, - }, - ) - if sampler == nil { - log.errorf("Failed to create sampler preset %v: %s", preset, sdl.GetError()) - return GLOB.pipeline_2d_base.sampler // fallback to existing default sampler - } - - GLOB.samplers[idx] = sampler - return sampler -} - -// Internal: destroy all sampler pool entries. Called from draw.destroy(). -@(private) -destroy_sampler_pool :: proc() { - device := GLOB.device - for &s in GLOB.samplers { - if s != nil { - sdl.ReleaseGPUSampler(device, s) - s = nil - } - } -} - -// Internal: destroy all registered textures. Called from draw.destroy(). -@(private) -destroy_all_textures :: proc() { - device := GLOB.device - for &slot in GLOB.texture_slots { - if slot.gpu_texture != nil { - sdl.ReleaseGPUTexture(device, slot.gpu_texture) - slot.gpu_texture = nil - } - } - delete(GLOB.texture_slots) - delete(GLOB.texture_free_list) - delete(GLOB.pending_texture_releases) -} - -// --------------------------------------------------------------------------- -// Fit mode helper -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Helpers ------------- +// --------------------------------------------------------------------------------------------------------------------- // Compute UV rect, recommended sampler, and inner rect for a given fit mode. // `rect` is the target drawing area; `texture_id` identifies the texture whose @@ -431,3 +302,113 @@ fit_params :: proc( return {0, 0, 1, 1}, .Linear_Clamp, inner_rect } + +texture_size :: proc(id: Texture_Id) -> [2]u32 { + if id == INVALID_TEXTURE do return {0, 0} + slot := &GLOB.texture_slots[u32(id)] + return {slot.desc.width, slot.desc.height} +} + +texture_format :: proc(id: Texture_Id) -> sdl.GPUTextureFormat { + if id == INVALID_TEXTURE do return .INVALID + return GLOB.texture_slots[u32(id)].desc.format +} + +texture_kind :: proc(id: Texture_Id) -> Texture_Kind { + if id == INVALID_TEXTURE do return .Static + return GLOB.texture_slots[u32(id)].desc.kind +} + +// Internal: get the raw GPU texture pointer for binding during draw. +@(private) +texture_gpu_handle :: proc(id: Texture_Id) -> ^sdl.GPUTexture { + if id == INVALID_TEXTURE do return nil + idx := u32(id) + if idx >= u32(len(GLOB.texture_slots)) do return nil + return GLOB.texture_slots[idx].gpu_texture +} + +// Deferred release (called from draw.end / clear_global) +@(private) +process_pending_texture_releases :: proc() { + device := GLOB.device + for id in GLOB.pending_texture_releases { + idx := u32(id) + if idx >= u32(len(GLOB.texture_slots)) do continue + slot := &GLOB.texture_slots[idx] + if slot.gpu_texture != nil { + sdl.ReleaseGPUTexture(device, slot.gpu_texture) + slot.gpu_texture = nil + } + slot.generation += 1 + append(&GLOB.texture_free_list, idx) + } + clear(&GLOB.pending_texture_releases) +} + +@(private) +get_sampler :: proc(preset: Sampler_Preset) -> ^sdl.GPUSampler { + idx := int(preset) + if GLOB.samplers[idx] != nil do return GLOB.samplers[idx] + + // Lazily create + min_filter, mag_filter: sdl.GPUFilter + address_mode: sdl.GPUSamplerAddressMode + + switch preset { + case .Nearest_Clamp: + min_filter = .NEAREST; mag_filter = .NEAREST; address_mode = .CLAMP_TO_EDGE + case .Linear_Clamp: + min_filter = .LINEAR; mag_filter = .LINEAR; address_mode = .CLAMP_TO_EDGE + case .Nearest_Repeat: + min_filter = .NEAREST; mag_filter = .NEAREST; address_mode = .REPEAT + case .Linear_Repeat: + min_filter = .LINEAR; mag_filter = .LINEAR; address_mode = .REPEAT + } + + sampler := sdl.CreateGPUSampler( + GLOB.device, + sdl.GPUSamplerCreateInfo { + min_filter = min_filter, + mag_filter = mag_filter, + mipmap_mode = .LINEAR, + address_mode_u = address_mode, + address_mode_v = address_mode, + address_mode_w = address_mode, + }, + ) + if sampler == nil { + log.errorf("Failed to create sampler preset %v: %s", preset, sdl.GetError()) + return GLOB.pipeline_2d_base.sampler // fallback to existing default sampler + } + + GLOB.samplers[idx] = sampler + return sampler +} + +// Internal: destroy all sampler pool entries. Called from draw.destroy(). +@(private) +destroy_sampler_pool :: proc() { + device := GLOB.device + for &s in GLOB.samplers { + if s != nil { + sdl.ReleaseGPUSampler(device, s) + s = nil + } + } +} + +// Internal: destroy all registered textures. Called from draw.destroy(). +@(private) +destroy_all_textures :: proc() { + device := GLOB.device + for &slot in GLOB.texture_slots { + if slot.gpu_texture != nil { + sdl.ReleaseGPUTexture(device, slot.gpu_texture) + slot.gpu_texture = nil + } + } + delete(GLOB.texture_slots) + delete(GLOB.texture_free_list) + delete(GLOB.pending_texture_releases) +} diff --git a/qrcode/examples/examples.odin b/qrcode/examples/examples.odin index 4db3d59..fabca9a 100644 --- a/qrcode/examples/examples.odin +++ b/qrcode/examples/examples.odin @@ -73,57 +73,32 @@ main :: proc() { } } -// ------------------------------------------------------------------------------------------------- -// Utilities -// ------------------------------------------------------------------------------------------------- - -// Prints the given QR Code to the console. -print_qr :: proc(qrcode: []u8) { - size := qr.get_size(qrcode) - border :: 4 - for y in -border ..< size + border { - for x in -border ..< size + border { - fmt.print("##" if qr.get_module(qrcode, x, y) else " ") - } - fmt.println() - } - fmt.println() -} - -// ------------------------------------------------------------------------------------------------- -// Demo: Basic -// ------------------------------------------------------------------------------------------------- - // Creates a single QR Code, then prints it to the console. basic :: proc() { text :: "Hello, world!" ecl :: qr.Ecc.Low qrcode: [qr.BUFFER_LEN_MAX]u8 - ok := qr.encode(text, qrcode[:], ecl) + ok := qr.encode_auto(text, qrcode[:], ecl) if ok do print_qr(qrcode[:]) } -// ------------------------------------------------------------------------------------------------- -// Demo: Variety -// ------------------------------------------------------------------------------------------------- - // Creates a variety of QR Codes that exercise different features of the library. variety :: proc() { qrcode: [qr.BUFFER_LEN_MAX]u8 { // Numeric mode encoding (3.33 bits per digit) - ok := qr.encode("314159265358979323846264338327950288419716939937510", qrcode[:], qr.Ecc.Medium) + ok := qr.encode_auto("314159265358979323846264338327950288419716939937510", qrcode[:], qr.Ecc.Medium) if ok do print_qr(qrcode[:]) } { // Alphanumeric mode encoding (5.5 bits per character) - ok := qr.encode("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcode[:], qr.Ecc.High) + ok := qr.encode_auto("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcode[:], qr.Ecc.High) if ok do print_qr(qrcode[:]) } { // Unicode text as UTF-8 - ok := qr.encode( + ok := qr.encode_auto( "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81" + "\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", qrcode[:], @@ -133,7 +108,7 @@ variety :: proc() { } { // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - ok := qr.encode( + ok := qr.encode_auto( "Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + @@ -148,10 +123,6 @@ variety :: proc() { } } -// ------------------------------------------------------------------------------------------------- -// Demo: Segment -// ------------------------------------------------------------------------------------------------- - // Creates QR Codes with manually specified segments for better compactness. segment :: proc() { qrcode: [qr.BUFFER_LEN_MAX]u8 @@ -163,7 +134,7 @@ segment :: proc() { // Encode as single text (auto mode selection) { concat :: silver0 + silver1 - ok := qr.encode(concat, qrcode[:], qr.Ecc.Low) + ok := qr.encode_auto(concat, qrcode[:], qr.Ecc.Low) if ok do print_qr(qrcode[:]) } @@ -172,7 +143,7 @@ segment :: proc() { seg_buf0: [qr.BUFFER_LEN_MAX]u8 seg_buf1: [qr.BUFFER_LEN_MAX]u8 segs := [2]qr.Segment{qr.make_alphanumeric(silver0, seg_buf0[:]), qr.make_numeric(silver1, seg_buf1[:])} - ok := qr.encode(segs[:], qr.Ecc.Low, qrcode[:]) + ok := qr.encode_auto(segs[:], qr.Ecc.Low, qrcode[:]) if ok do print_qr(qrcode[:]) } } @@ -185,7 +156,7 @@ segment :: proc() { // Encode as single text (auto mode selection) { concat :: golden0 + golden1 + golden2 - ok := qr.encode(concat, qrcode[:], qr.Ecc.Low) + ok := qr.encode_auto(concat, qrcode[:], qr.Ecc.Low) if ok do print_qr(qrcode[:]) } @@ -201,7 +172,7 @@ segment :: proc() { qr.make_numeric(golden1, seg_buf1[:]), qr.make_alphanumeric(golden2, seg_buf2[:]), } - ok := qr.encode(segs[:], qr.Ecc.Low, qrcode[:]) + ok := qr.encode_auto(segs[:], qr.Ecc.Low, qrcode[:]) if ok do print_qr(qrcode[:]) } } @@ -219,7 +190,7 @@ segment :: proc() { "\xEF\xBD\x84\xEF\xBD\x85\xEF\xBD\x93\xEF" + "\xBD\x95\xE3\x80\x80\xCE\xBA\xCE\xB1\xEF" + "\xBC\x9F" - ok := qr.encode(madoka, qrcode[:], qr.Ecc.Low) + ok := qr.encode_auto(madoka, qrcode[:], qr.Ecc.Low) if ok do print_qr(qrcode[:]) } @@ -254,16 +225,12 @@ segment :: proc() { seg.data = seg_buf[:(seg.bit_length + 7) / 8] segs := [1]qr.Segment{seg} - ok := qr.encode(segs[:], qr.Ecc.Low, qrcode[:]) + ok := qr.encode_auto(segs[:], qr.Ecc.Low, qrcode[:]) if ok do print_qr(qrcode[:]) } } } -// ------------------------------------------------------------------------------------------------- -// Demo: Mask -// ------------------------------------------------------------------------------------------------- - // Creates QR Codes with the same size and contents but different mask patterns. mask :: proc() { qrcode: [qr.BUFFER_LEN_MAX]u8 @@ -271,10 +238,10 @@ mask :: proc() { { // Project Nayuki URL ok: bool - ok = qr.encode("https://www.nayuki.io/", qrcode[:], qr.Ecc.High) + ok = qr.encode_auto("https://www.nayuki.io/", qrcode[:], qr.Ecc.High) if ok do print_qr(qrcode[:]) - ok = qr.encode("https://www.nayuki.io/", qrcode[:], qr.Ecc.High, mask = qr.Mask.M3) + ok = qr.encode_auto("https://www.nayuki.io/", qrcode[:], qr.Ecc.High, mask = qr.Mask.M3) if ok do print_qr(qrcode[:]) } @@ -290,16 +257,29 @@ mask :: proc() { ok: bool - ok = qr.encode(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M0) + ok = qr.encode_auto(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M0) if ok do print_qr(qrcode[:]) - ok = qr.encode(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M1) + ok = qr.encode_auto(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M1) if ok do print_qr(qrcode[:]) - ok = qr.encode(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M5) + ok = qr.encode_auto(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M5) if ok do print_qr(qrcode[:]) - ok = qr.encode(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M7) + ok = qr.encode_auto(text, qrcode[:], qr.Ecc.Medium, mask = qr.Mask.M7) if ok do print_qr(qrcode[:]) } } + +// Prints the given QR Code to the console. +print_qr :: proc(qrcode: []u8) { + size := qr.get_size(qrcode) + border :: 4 + for y in -border ..< size + border { + for x in -border ..< size + border { + fmt.print("##" if qr.get_module(qrcode, x, y) else " ") + } + fmt.println() + } + fmt.println() +} diff --git a/qrcode/generate.odin b/qrcode/generate.odin index 8261021..0bf3b0d 100644 --- a/qrcode/generate.odin +++ b/qrcode/generate.odin @@ -2,10 +2,30 @@ package qrcode import "core:slice" +VERSION_MIN :: 1 +VERSION_MAX :: 40 -// ------------------------------------------------------------------------------------------------- -// Types -// ------------------------------------------------------------------------------------------------- +// The worst-case number of bytes needed to store one QR Code, up to and including version 40. +BUFFER_LEN_MAX :: 3918 // buffer_len_for_version(VERSION_MAX) + +// Returns the number of bytes needed to store any QR Code up to and including the given version. +buffer_len_for_version :: #force_inline proc(n: int) -> int { + size := n * 4 + 17 + return (size * size + 7) / 8 + 1 +} + +@(private) +LENGTH_OVERFLOW :: -1 +@(private) +REED_SOLOMON_DEGREE_MAX :: 30 +@(private) +PENALTY_N1 :: 3 +@(private) +PENALTY_N2 :: 3 +@(private) +PENALTY_N3 :: 40 +@(private) +PENALTY_N4 :: 10 // The error correction level in a QR Code symbol. Ecc :: enum { @@ -44,39 +64,6 @@ Segment :: struct { bit_length: int, } -// ------------------------------------------------------------------------------------------------- -// Constants -// ------------------------------------------------------------------------------------------------- - -VERSION_MIN :: 1 -VERSION_MAX :: 40 - -// The worst-case number of bytes needed to store one QR Code, up to and including version 40. -BUFFER_LEN_MAX :: 3918 // buffer_len_for_version(VERSION_MAX) - -// Returns the number of bytes needed to store any QR Code up to and including the given version. -buffer_len_for_version :: #force_inline proc(n: int) -> int { - size := n * 4 + 17 - return (size * size + 7) / 8 + 1 -} - -// ------------------------------------------------------------------------------------------------- -// Private constants -// ------------------------------------------------------------------------------------------------- - -@(private) -LENGTH_OVERFLOW :: -1 -@(private) -REED_SOLOMON_DEGREE_MAX :: 30 -@(private) -PENALTY_N1 :: 3 -@(private) -PENALTY_N2 :: 3 -@(private) -PENALTY_N3 :: 40 -@(private) -PENALTY_N4 :: 10 - //odinfmt: disable // For generating error correction codes. Index 0 is padding (set to illegal value). @(private) @@ -96,10 +83,9 @@ NUM_ERROR_CORRECTION_BLOCKS := [4][41]i8{ } //odinfmt: enable - -// ------------------------------------------------------------------------------------------------- -// Encode procedures -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Encode Procedures ------------------------ +// --------------------------------------------------------------------------------------------------------------------- // Encodes the given text string to a QR Code, automatically selecting // numeric, alphanumeric, or byte mode based on content. @@ -548,9 +534,10 @@ encode_auto :: proc { encode_segments_advanced_auto, } -// ------------------------------------------------------------------------------------------------- -// Error correction code generation -// ------------------------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------------------------------------------- +// ----- Error Correction Code Generation ------------------------ +// --------------------------------------------------------------------------------------------------------------------- // Appends error correction bytes to each block of data, then interleaves bytes from all blocks. @(private) @@ -618,10 +605,6 @@ get_num_raw_data_modules :: proc(ver: int) -> int { return result } -// ------------------------------------------------------------------------------------------------- -// Reed-Solomon ECC generator -// ------------------------------------------------------------------------------------------------- - @(private) reed_solomon_compute_divisor :: proc(degree: int, result: []u8) { assert(1 <= degree && degree <= REED_SOLOMON_DEGREE_MAX, "reed-solomon degree out of range") @@ -668,9 +651,9 @@ reed_solomon_multiply :: proc(x, y: u8) -> u8 { return z } -// ------------------------------------------------------------------------------------------------- -// Drawing function modules -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Drawing Function Modules ------------------------ +// --------------------------------------------------------------------------------------------------------------------- // Clears the QR Code grid and marks every function module as dark. @(private) @@ -816,9 +799,9 @@ fill_rectangle :: proc(left, top, width, height: int, qrcode: []u8) { } } -// ------------------------------------------------------------------------------------------------- -// Drawing data modules and masking -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Drawing data modules and masking ------------------------ +// --------------------------------------------------------------------------------------------------------------------- @(private) draw_codewords :: proc(data: []u8, data_len: int, qrcode: []u8) { @@ -996,9 +979,9 @@ finder_penalty_add_history :: proc(current_run_length: int, run_history: ^[7]int run_history[0] = current_run_length } -// ------------------------------------------------------------------------------------------------- -// Basic QR Code information -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Basic QR code information ------------------------ +// --------------------------------------------------------------------------------------------------------------------- // Returns the minimum buffer size (in bytes) needed for both temp_buffer and qrcode // to encode the given content at the given ECC level within the given version range. @@ -1158,9 +1141,9 @@ get_bit :: #force_inline proc(x: int, i: uint) -> bool { return ((x >> i) & 1) != 0 } -// ------------------------------------------------------------------------------------------------- -// Segment handling -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Segment Handling ------------------------ +// --------------------------------------------------------------------------------------------------------------------- // Tests whether the given string can be encoded in numeric mode. is_numeric :: proc(text: string) -> bool { @@ -1349,11 +1332,11 @@ make_eci :: proc(assign_val: int, buf: []u8) -> Segment { return result } -// ------------------------------------------------------------------------------------------------- -// Private helpers -// ------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- +// ----- Helpers ------------------------ +// --------------------------------------------------------------------------------------------------------------------- -@(private) +// Internal append_bits_to_buffer :: proc(val: uint, num_bits: int, buffer: []u8, bit_len: ^int) { assert(0 <= num_bits && num_bits <= 16 && val >> uint(num_bits) == 0, "invalid bit count or value overflow") for i := num_bits - 1; i >= 0; i -= 1 { @@ -1362,7 +1345,7 @@ append_bits_to_buffer :: proc(val: uint, num_bits: int, buffer: []u8, bit_len: ^ } } -@(private) +// Internal get_total_bits :: proc(segs: []Segment, version: int) -> int { result := 0 for &seg in segs { @@ -1384,7 +1367,7 @@ get_total_bits :: proc(segs: []Segment, version: int) -> int { return result } -@(private) +// Internal num_char_count_bits :: proc(mode: Mode, version: int) -> int { assert(VERSION_MIN <= version && version <= VERSION_MAX, "version out of bounds") i := (version + 7) / 17 @@ -1406,8 +1389,8 @@ num_char_count_bits :: proc(mode: Mode, version: int) -> int { unreachable() } +// Internal // Returns the index of c in the alphanumeric charset (0-44), or -1 if not found. -@(private) alphanumeric_index :: proc(c: u8) -> int { switch c { case '0' ..= '9': return int(c - '0')