# QQR
QR code decoder in pure Elixir. Zero dependencies — no NIFs, no ports, no C.
## Installation
```elixir
def deps do
[{:qqr, "~> 0.1.0"}]
end
```
## Usage
### From RGBA pixels
```elixir
case QQR.decode(rgba_binary, width, height) do
{:ok, result} ->
result.text #=> "https://example.com"
result.version #=> 3
result.bytes #=> [104, 116, 116, 112, ...]
result.chunks #=> [%{mode: :byte, text: "https://example.com", bytes: [...]}]
result.location #=> %{top_left_corner: {10.5, 10.5}, ...}
:error ->
# no QR code found
end
```
`rgba_binary` is a binary of RGBA pixels — 4 bytes per pixel, same format as `ImageData` in browsers or what most image libraries produce.
### From a module grid
Skip image processing when you already have a binarized grid:
```elixir
QQR.decode_matrix(bit_matrix)
```
### Inversion
By default both normal and inverted (light-on-dark) images are tried. Pass `inversion: :dont_invert` for ~2× speedup when you know the background is white.
```elixir
QQR.decode(rgba, w, h, inversion: :dont_invert)
```
## Features
- Versions 1–40, all error correction levels (L/M/Q/H)
- Numeric, alphanumeric, and byte data modes
- Kanji mode (raw bytes — Shift-JIS to text conversion not yet implemented)
- ECI segment parsing (designators consumed, encoding not applied)
- Reed-Solomon error correction
- Adaptive binarization, perspective correction
- Dark-background (inverted) and mirror/transposed QR codes
## Benchmarks
Compared against [qrex](https://hex.pm/packages/qrex) (Rust NIF, PNG input). Run with `elixir bench/decode.exs`.
| Input | QQR.decode_matrix | QRex (Rust NIF) | QQR.decode (RGBA) |
|-------|------------------:|----------------:|------------------:|
| Version 1, "Hello" | **30 µs** | 51 µs | 1.5 ms |
| Version 2, URL | **55 µs** | 70 µs | 2.1 ms |
| Version 6, 100 chars | 251 µs | **146 µs** | 5.5 ms |
Grid-only decode (`decode_matrix`) is **1.3–1.7× faster than Rust** for small and medium QR codes. The full RGBA pipeline is slower due to image processing overhead in the binarizer and locator.
## How it works
```
RGBA pixels → Binarizer → Locator → Extractor → Decoder → text
```
| Stage | What it does |
|-------|-------------|
| Binarize | Adaptive threshold — RGBA to 1-bit per module |
| Locate | Find three finder patterns (1:1:3:1:1 ratio scan), alignment pattern, grid size |
| Extract | Perspective transform, sample rectified module grid |
| Decode | Format/version info, unmask, Reed-Solomon error correction, data segment parsing |
GF(256) exp/log tables are compiled into pattern-matched function heads. The `BitMatrix` uses a flat tuple with `:erlang.element/2` for constant-time access. No mutable state — zigzag traversal, Bresenham walks, and polynomial arithmetic are purely functional.
Ported from [jsQR](https://github.com/cozmo/jsQR) with algorithm verification against [quirc](https://github.com/dlbeer/quirc).
## License
[MIT](LICENSE)