Pure-Zig QOI codec. Byte-identical to phoboslab/qoi. C ABI shim for drop-in qoi.h use.
phoboslab qoibench, full 2848-image corpus, 5 iters. zimage and qoir built for the host CPU.
| decode Mpps | encode Mpps | |
|---|---|---|
| qoi.h | 390 | 306 |
| qoir | 522 | 431 |
| zimage | 577 | 1026 |
| decode Mpps | encode Mpps | |
|---|---|---|
| qoi.h | 181 | 134 |
| qoir | 252 | 210 |
| zimage | 291 | 516 |
zig fetch --save git+https://github.com/sovereignsoft/zimageconst qoi = b.dependency("qoi", .{}).module("qoi");
exe.root_module.addImport("qoi", qoi);const qoi = @import("qoi");
const img = try qoi.decode(allocator, qoi_bytes);
defer allocator.free(img.pixels);
// img.{width, height, channels, colorspace}
// img.pixels: RGBA8, row-major, top-down, width*height*4 bytes
const encoded = try qoi.encode(allocator, rgba_or_rgb_pixels, .{
.width = w,
.height = h,
.channels = .rgba, // .rgb (3 B/px) or .rgba (4 B/px)
.colorspace = .srgb, // .srgb or .linear
});
defer allocator.free(encoded);
const header = try qoi.decodeHeader(qoi_bytes);
// Caller-owned buffers, no allocation:
_ = try qoi.decodeInto(allocator, qoi_bytes, out_buf); // RGBA8
_ = try qoi.decodeIntoRgb(allocator, qoi_bytes, rgb_out); // packed RGB
const written = try qoi.encodeInto(enc_buf, rgba_pixels, header);
// File helpers:
const img2 = try qoi.decodeFile(allocator, init.io, "in.qoi");
try qoi.encodeFile(allocator, init.io, "out.qoi", rgba_pixels, header);pub const Error = error{
InvalidQoiSignature,
InvalidQoiHeader,
UnexpectedEnd,
OutputBufferTooSmall,
InvalidPixelBufferSize,
} || std.mem.Allocator.Error;zig build → zig-out/lib/libqoi.a. Output is libc-malloc'd; free with libc free.
#include "qoi.h"
void *pixels = qoi_read("in.qoi", &desc, 4);
qoi_write("out.qoi", pixels, &desc);
void *encoded = qoi_encode(pixels, &desc, &out_len);
void *decoded = qoi_decode(encoded, out_len, &desc, 4);Caller-buffer variants (no per-call malloc):
int qoi_decode_into(const void *data, int size, qoi_desc *desc, int channels,
void *out_buf, int out_buf_size);
int qoi_encode_into(const void *data, const qoi_desc *desc,
void *out_buf, int out_buf_size);zig build # libqoi.a
zig build test # unit tests + fuzz smoke
zig build qoibench # phoboslab qoibench vs qoi.h + qoirDefault target is cpu_model = .native. -Dcpu=baseline for portable builds.
bash <path>/tests/qoi-corpus/fetch.sh --full
RUSTFLAGS="-C target-cpu=native" cargo build --release \
--manifest-path vendor/qoi_rust_shim/Cargo.toml
zig build qoibench
./zig-out/bin/qoibench 5 <path>/qoi-corpus/images --onlytotals| Platform | Decode | Encode |
|---|---|---|
| aarch64 + ASIMD DotProd (Apple M, Cortex-A75+, Neoverse N1/V1+) | hand-asm | SIMD (UDOT hash) |
| Other aarch64 | scalar | scalar |
| x86_64 + AVX-VNNI (Sapphire Rapids+, Zen 4+) | hand-asm | SIMD (VPDPBUSD hash + AVX2 classify) |
| x86_64 + AVX2 (Haswell..Rocket Lake, Zen 1–3) | hand-asm | SIMD (VPMADDUBSW hash + AVX2 classify) |
| Other x86_64 | scalar | scalar |
Gated comptime on dotprod / avxvnni / avx2.
MIT.
- phoboslab/qoi — spec, reference, bench harness.
- aldanor/qoi-rust — qoir; SWAR hash trick from @zakarumych.