Skip to content

Latest commit

 

History

612 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jabcode

PkgGoDev MIT license DeepWiki experimental

Pure-Go JAB Code encoder and decoder.

JAB Code is a high-capacity color matrix barcode standardized as ISO/IEC 23634:2022.

This module is experimental. The API may still change, and scanning real-world captures is still being hardened. When you find any errors, please report them as issues.

Status

Single- and multi-symbol encode/decode work, including normative 4- and 8-color ISO modes, docked secondary symbols, diagnostics, and a frame-sequence decoder. Tagged builds add high-color, BSI and historical decoder families. The main active work is print-capture robustness, stream integration, performance, and validation of the ISO target.

Install

go get github.com/srlehn/jabcode

Install the CLI:

go install github.com/srlehn/jabcode/cmd/jabcode@latest

Library

package main

import (
    "bytes"
    "image/png"
    "os"

    "github.com/srlehn/jabcode"
)

func main() {
    img, err := jabcode.NewEncoder(
        jabcode.WithColors(8),
        jabcode.WithModuleSize(12),
    ).Encode([]byte("hello"))
    if err != nil {
        panic(err)
    }

    var buf bytes.Buffer
    if err := png.Encode(&buf, img); err != nil {
        panic(err)
    }
    if err := os.WriteFile("hello.png", buf.Bytes(), 0o644); err != nil {
        panic(err)
    }
}

Decoding accepts any image.Image, so file format support is provided by the decoders registered by the caller.

f, err := os.Open("hello.png")
if err != nil {
    panic(err)
}
defer f.Close()

img, err := png.Decode(f)
if err != nil {
    panic(err)
}

data, err := jabcode.Decode(img)
if err != nil {
    panic(err)
}
_ = data

For ordered, coherent frame sequences, use jabcode.NewStream(). Frames may come from a live camera, network video, or a decoded recording. The stream reuses previous read hypotheses and compatible evidence within a fixed per-frame work budget, and automatically consumes every decoder capability compiled into the build through one integrated detector pipeline. Call Stream.Reset before reusing it for an unrelated coherent sequence.

Decode and Stream accept an already constructed image.Image; they cannot bound storage that a byte-buffer adapter allocated first. Adapters for untrusted camera buffers must validate width, height, stride, and their products and enforce an input-dimension limit before allocating the image. A permanent regular-Go js/wasm gate executes one fixed opaque-byte symbol through the public plan and Stream APIs. It proves the platform and byte contract, not production readiness for repeating loops of changing symbols; that transition and emission work remains separate.

Native applications that only render symbols can import the dependency-light encoder package directly:

import "github.com/srlehn/jabcode/jabenc"

img, err := jabenc.New(
    jabenc.WithColors(8),
    jabenc.WithModuleSize(12),
).Encode([]byte("hello"))

That package does not depend on the read or detect packages, Vulki, or purego. With CGO_ENABLED=0, an encoder-only consumer remains statically linked. The root package keeps the same encoder facade for applications that also decode.

For fixed-size binary transport frames, create one immutable byte-mode plan and use its exact capacity before splitting any data:

plan, err := jabenc.NewOpaquePlan(
    image.Pt(8, 8),
    jabenc.WithColors(8),
    jabenc.WithModuleSize(4),
)
if err != nil {
    panic(err)
}
frameCapacity := plan.Capacity()
img, err := plan.Encode(frame[:frameCapacity])

The plan fixes color count, side versions, ECC level, module size, and output geometry. It rejects empty data and any payload one byte beyond its reported capacity.

Commands

Encode payload bytes from stdin to PNG:

printf hello | jabcode encode --output hello.png

For shell demos, literal text input is also available:

jabcode encode --input "hello" --output hello.png

Decode an image to stdout:

jabcode decode hello.png

Write decoded bytes to a file:

jabcode decode --output payload.bin hello.png

ISO decode output is the reader transmission defined by the standard: it starts with the ]j1, ]j4 or ]j5 symbology identifier, encodes ECI assignments as a backslash plus six digits, and doubles literal data backslashes. Consumers that need application bytes use DecodeMessage or Stream.DecodeMessage. Their Message.Data is decoded directly from the mode stream, while Message.ReaderTransmission retains the standards-facing form and Message.Controls records ECI, FNC1, and ISO/IEC 15434 structure.

jabcode decode registers PNG, JPEG, HEIC, AVIF, TIFF, and WebP decoders (including WebP VP8 and VP8L).

Detector diagnostics for difficult captures write the payload to stdout and the diagnostic report to stderr; annotated diagnostic images go to --diag-out. The diagnostic mode observes the authoritative read once and does not replay a second decode pipeline:

jabcode decode --diag --diag-out ./diag-images capture.png > payload.bin

Multi-symbol encodes use one compact symbol spec per symbol:

jabcode encode --symbols 0:4x4:0,2:4x4:0 --output cascade.png < payload.bin

Compatibility

  • The default encoder targets ISO/IEC 23634:2022 with the normative 4- and 8-color modes. Its Annex F range reduction still lacks an independent wire oracle, so strict-conformance verification is not yet complete.
  • Decoder build tags are additive. Untagged Decode accepts ISO only; jabcode_high_color, jabcode_bsi, and jabcode_legacy add their compiled routes to the same automatic read. The CLI-only --only flag restricts the read to a comma-separated subset of them, for debugging and conformance work.
  • jabcode_high_color adds decoding of the non-standard ISO-derived 16- through 256-color modes. jabcode_non_iso_encode adds the public encoder profile selector with hc and bsi output. Use the corresponding decoder tag as well when the same binary must read what it writes. Physical robustness decreases with color density: capture limits range from camera-grade 16/32 colors to scanner-grade 128 colors, while 256 colors remain pixel-exact only. See WithColors for details.
  • jabcode_legacy adds read-only current and pre-v2.0 C-reference formats, including docked multi-symbol codes. No legacy encoder is exposed.
  • jabcode_bsi adds exact BSI TR-03137 primary and recursively docked-secondary decoding. jabcode_non_iso_encode exposes ProfileBSI and CLI --profile bsi for single- and multi-symbol output. BSI supports its specified 4- through 256-color layouts; the CLI warns above 8 colors because capture robustness still falls as palette density rises.
  • Decoder builds embed the precomputed LDPC pivot catalogs so the first GPU decode does not spend seconds constructing them. This adds roughly 13 MB for ISO and another 13 MB when jabcode_bsi or jabcode_legacy compiles the C-family generator.
  • Decode is intended to return errors, not panic, on malformed or hostile images. Callers should still bound untrusted image dimensions before decoding.
  • Native large resolution-pyramid reads use Vulkan preprocessing automatically when the selected adapter reports a discrete-GPU device type. There is no GPU build tag or required runtime configuration; smaller images, unavailable Vulkan and software implementations such as llvmpipe use the CPU path transparently. GOOS=js builds run the same reader over a WebGPU session when a large enough frame has a browser GPU, and over the CPU path otherwise; Vulki and purego are excluded there. Regular Go js/wasm is the tested browser target. The OS-only build constraint also selects the CPU files for GopherJS without making a GopherJS execution or language compatibility claim. The native GPU path persists a Vulkan pipeline cache under the user cache directory (vulki/pipeline-*.bin); set VULKI_PIPELINE_CACHE=off to disable it or VULKI_PIPELINE_CACHE_PATH to relocate it.

Layout

  • Root package: public Encoder facade, Decode, and Stream.
  • jabenc: dependency-light public encoder API for sender-only consumers.
  • internal/encode: data encoding, matrix placement, masking, and rendering.
  • internal/core: shared pixel buffers, geometry, decoded-symbol types, and status values used by the read path.
  • internal/read, internal/detect, internal/decode: image search, detection, sampling, metadata, palette, ECC, and payload decoding.
  • internal/diag: staged text and image diagnostics over the decoder.
  • internal/ecc, internal/palette, internal/spec, internal/tables: shared format machinery.
  • cmd/: user-facing CLIs.

Development

More detail:

  • ARCHITECTURE.md describes the package boundaries, invariants, robustness extensions, and verification strategy.
  • WIRE_FORMAT.md records the C-reference wire format and known ISO and pre-ISO deltas.

About

JAB Code (color 2D bar code)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages