package draw_qr import draw ".." import "../../qrcode" // ----------------------------------------------------------------------------- // Layer 1 — pure: encoded QR buffer → RGBA pixels + descriptor // ----------------------------------------------------------------------------- // Returns the number of bytes to_texture will write for the given encoded // QR buffer. Equivalent to size*size*4 where size = qrcode.get_size(qrcode_buf). texture_size :: #force_inline proc(qrcode_buf: []u8) -> int { size := qrcode.get_size(qrcode_buf) return size * size * 4 } // Decodes an encoded QR buffer into tightly-packed RGBA pixel data written to // texture_buf. No allocations, no GPU calls. Returns the Texture_Desc the // caller should pass to draw.register_texture alongside texture_buf. // // Returns ok=false when: // - qrcode_buf is invalid (qrcode.get_size returns 0). // - texture_buf is smaller than to_texture_size(qrcode_buf). @(require_results) to_texture :: proc( qrcode_buf: []u8, texture_buf: []u8, dark: draw.Color = draw.BLACK, light: draw.Color = draw.WHITE, ) -> ( desc: draw.Texture_Desc, ok: bool, ) { size := qrcode.get_size(qrcode_buf) if size == 0 do return {}, false if len(texture_buf) < size * size * 4 do return {}, false for y in 0 ..< size { for x in 0 ..< size { i := (y * size + x) * 4 c := dark if qrcode.get_module(qrcode_buf, x, y) else light texture_buf[i + 0] = c[0] texture_buf[i + 1] = c[1] texture_buf[i + 2] = c[2] texture_buf[i + 3] = c[3] } } return draw.Texture_Desc { width = u32(size), height = u32(size), depth_or_layers = 1, type = .D2, format = .R8G8B8A8_UNORM, usage = {.SAMPLER}, mip_levels = 1, kind = .Static, }, true } // ----------------------------------------------------------------------------- // Layer 2 — raw: pre-encoded QR buffer → registered GPU texture // ----------------------------------------------------------------------------- // Allocates pixel buffer via temp_allocator, decodes qrcode_buf into it, and // registers with the GPU. The pixel allocation is freed before return. // // Returns ok=false when: // - qrcode_buf is invalid (qrcode.get_size returns 0). // - temp_allocator fails to allocate the pixel buffer. // - GPU texture registration fails. @(require_results) register_texture_from_raw :: proc( qrcode_buf: []u8, dark: draw.Color = draw.BLACK, light: draw.Color = draw.WHITE, temp_allocator := context.temp_allocator, ) -> ( texture: draw.Texture_Id, ok: bool, ) { tex_size := texture_size(qrcode_buf) if tex_size == 0 do return draw.INVALID_TEXTURE, false pixels, alloc_err := make([]u8, tex_size, temp_allocator) if alloc_err != nil do return draw.INVALID_TEXTURE, false defer delete(pixels, temp_allocator) desc := to_texture(qrcode_buf, pixels, dark, light) or_return return draw.register_texture(desc, pixels) } // ----------------------------------------------------------------------------- // Layer 3 — text → registered GPU texture // ----------------------------------------------------------------------------- // Encodes text as a QR Code and registers the result as an RGBA texture. // // Returns ok=false when: // - temp_allocator fails to allocate. // - The text cannot fit in any version within [min_version, max_version] at the given ECL. // - GPU texture registration fails. @(require_results) register_texture_from_text :: proc( text: string, ecl: qrcode.Ecc = .Low, min_version: int = qrcode.VERSION_MIN, max_version: int = qrcode.VERSION_MAX, mask: Maybe(qrcode.Mask) = nil, boost_ecl: bool = true, dark: draw.Color = draw.BLACK, light: draw.Color = draw.WHITE, temp_allocator := context.temp_allocator, ) -> ( texture: draw.Texture_Id, ok: bool, ) { qrcode_buf, alloc_err := make([]u8, qrcode.buffer_len_for_version(max_version), temp_allocator) if alloc_err != nil do return draw.INVALID_TEXTURE, false defer delete(qrcode_buf, temp_allocator) qrcode.encode_auto( text, qrcode_buf, ecl, min_version, max_version, mask, boost_ecl, temp_allocator, ) or_return return register_texture_from_raw(qrcode_buf, dark, light, temp_allocator) } // ----------------------------------------------------------------------------- // Layer 4 — binary → registered GPU texture // ----------------------------------------------------------------------------- // Encodes arbitrary binary data as a QR Code and registers the result as an RGBA texture. // // Returns ok=false when: // - temp_allocator fails to allocate. // - The payload cannot fit in any version within [min_version, max_version] at the given ECL. // - GPU texture registration fails. @(require_results) register_texture_from_binary :: proc( bin_data: []u8, ecl: qrcode.Ecc = .Low, min_version: int = qrcode.VERSION_MIN, max_version: int = qrcode.VERSION_MAX, mask: Maybe(qrcode.Mask) = nil, boost_ecl: bool = true, dark: draw.Color = draw.BLACK, light: draw.Color = draw.WHITE, temp_allocator := context.temp_allocator, ) -> ( texture: draw.Texture_Id, ok: bool, ) { qrcode_buf, alloc_err := make([]u8, qrcode.buffer_len_for_version(max_version), temp_allocator) if alloc_err != nil do return draw.INVALID_TEXTURE, false defer delete(qrcode_buf, temp_allocator) qrcode.encode_auto( bin_data, qrcode_buf, ecl, min_version, max_version, mask, boost_ecl, temp_allocator, ) or_return return register_texture_from_raw(qrcode_buf, dark, light, temp_allocator) } // ----------------------------------------------------------------------------- // Clay integration helper // ----------------------------------------------------------------------------- // Default fit=.Fit preserves the QR's square aspect; override as needed. clay_image :: #force_inline proc( texture: draw.Texture_Id, tint: draw.Color = draw.WHITE, ) -> draw.Clay_Image_Data { return draw.clay_image_data(texture, fit = .Fit, tint = tint) }