QOI
37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0QOI (“Quite OK Image”) is a lossless format for 8-bit RGB and RGBA images, designed by Dominic Szablewski so that a complete encoder and decoder fits in a single C header. Its selling point is speed: in the author’s benchmark, encoding is about 30 times faster than libpng and decoding about 3 times faster, at the cost of larger files. It suits game assets, textures, screenshot tools and anything that writes lots of images quickly. It is not a delivery format for the web.
| Property | Value |
|---|---|
| Compression | Lossless |
| Bit depth | 8 bits per channel, 3 (RGB) or 4 (RGBA) channels |
| Alpha | Straight alpha in 4-channel files |
| Metadata | None (no ICC, Exif or XMP); header has a sRGB/linear flag only |
| Animation | No |
| Progressive | No |
| Royalty-free | Yes (reference library is MIT-licensed) |
How it compares with PNG
Section titled “How it compares with PNG”The author’s benchmark on 2,879 screenshots, icons, photos and textures (single-threaded, i7-6700K) gives:
| Codec | Average size | Encode | Decode |
|---|---|---|---|
| libpng | 398 KB (24.2% of raw) | 83.8 ms (5.5 MP/s) | 7.0 ms (67 MP/s) |
| stb_image_write | 561 KB (34.2%) | 60.5 ms | 7.0 ms |
| QOI | 463 KB (28.2%) | 2.9 ms (162 MP/s) | 2.1 ms (226 MP/s) |
So QOI is about 16% larger than libpng on average and much faster. An optimiser such as oxipng widens the gap further, and lossless WebP or JPEG XL are smaller still. QOI does best on images with flat runs and smooth gradients; it does poorly on noise and photographs.
Using it
Section titled “Using it”The reference tool converts between PNG and QOI:
qoiconv input.png output.qoiqoiconv output.qoi restored.pngqoiconv has no options; it always writes sRGB QOI and loads PNGs as 8-bit, so 16-bit input is truncated. The same C header, qoi.h, is what most applications embed; ports for many other languages are listed in the repository README.
FFmpeg has a native QOI encoder and decoder:
ffmpeg -i input.png output.qoiffmpeg -i output.qoi restored.pngImageMagick reads and writes QOI (magick input.png output.qoi). Browsers have no native support, so convert to PNG, WebP or AVIF for the web.
How the format works
Section titled “How the format works”A 14-byte header (magic qoif, width, height, channels, colour-space flag) is followed by a stream of byte-aligned chunks and an 8-byte end marker. The encoder keeps the previous pixel and a table of 64 recently seen colours, and emits one of six chunk types:
| Chunk | Size | Meaning |
|---|---|---|
QOI_OP_RUN | 1 byte | Repeat the previous pixel 1–62 times |
QOI_OP_INDEX | 1 byte | Pixel found in the 64-entry colour table (hash of the colour) |
QOI_OP_DIFF | 1 byte | Small per-channel difference from the previous pixel (−2…+1) |
QOI_OP_LUMA | 2 bytes | Green difference (−32…+31) with red and blue differences relative to it |
QOI_OP_RGB | 4 bytes | Full RGB pixel |
QOI_OP_RGBA | 5 bytes | Full RGBA pixel |
There is nothing tunable: no quality or effort setting, so every QOI encoder writes nearly the same file. The one-page specification is the whole format.
Pitfalls
Section titled “Pitfalls”- Colour management: the header’s
colorspaceflag (sRGB with linear alpha, or all linear) does not convert anything and there is no ICC support. Keep the profile elsewhere if it matters. - 16-bit and HDR sources must be reduced to 8 bits first, which loses data. Use PNG, TIFF or JPEG XL instead.
- Size limit: the reference library refuses images over 400 million pixels. The format itself allows far larger images.
Full option reference
Section titled “Full option reference”The reference converter, library and FFmpeg interfaces, option by option. Reading option tables explains the Default column.
Complete reference-converter arguments
Section titled “Complete reference-converter arguments”qoiconv takes two positional operands and no options. Both must end in a lowercase .png or .qoi, and the writer overwrites existing files. Arguments after the second operand are ignored, and a leading flag such as --quality is read as the input path, so it fails.
| Argument | Default | Accepted values and responsibility |
|---|---|---|
INPUT | required | Source path ending in lowercase .png or .qoi; other PNG channel layouts load as RGBA. |
OUTPUT | required | Destination path ending in .png or .qoi; an existing file is overwritten. |
Complete library encode contract
Section titled “Complete library encode contract”qoi_encode(data, desc, out_len) has no presets; the caller supplies every field and frees the returned buffer.
| Parameter / field | Default | Accepted value / responsibility |
|---|---|---|
data | required | Tightly packed row-major RGB or RGBA bytes matching the descriptor. |
desc | required | Pointer to a filled qoi_desc. |
out_len | required | Receives the compressed size in bytes on success. |
desc.width | required | Pixel width, at least 1. |
desc.height | required | Pixel height, at least 1; width times height must stay below 400 million pixels. |
desc.channels | required | 3 for RGB or 4 for RGBA. |
desc.colorspace | required | QOI_SRGB = 0 or QOI_LINEAR = 1; a label stored in the header, not a conversion. |
Invalid descriptors return null, so check the result before using out_len. qoi_write adds a filename, and qoi_read / qoi_decode form the separate decoder contract.
FFmpeg QOI encoder interface
Section titled “FFmpeg QOI encoder interface”FFmpeg 9.0.2’s qoi encoder has no private options. It takes rgb24 or rgba input, and writes the linear flag in the header only when the frames are tagged with a linear transfer characteristic (the flag is signalling only; it does not linearise anything).
ffmpeg -i input.png -c:v qoi -pix_fmt rgba output.qoiffmpeg -i linear.png -vf setparams=color_trc=linear -c:v qoi output.qoiConverting to rgb24 drops the alpha channel.