Skip to content

QOI

currentMigrated from docs/images/QOI.mdx at 37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0

QOI (“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.

PropertyValue
CompressionLossless
Bit depth8 bits per channel, 3 (RGB) or 4 (RGBA) channels
AlphaStraight alpha in 4-channel files
MetadataNone (no ICC, Exif or XMP); header has a sRGB/linear flag only
AnimationNo
ProgressiveNo
Royalty-freeYes (reference library is MIT-licensed)

The author’s benchmark on 2,879 screenshots, icons, photos and textures (single-threaded, i7-6700K) gives:

CodecAverage sizeEncodeDecode
libpng398 KB (24.2% of raw)83.8 ms (5.5 MP/s)7.0 ms (67 MP/s)
stb_image_write561 KB (34.2%)60.5 ms7.0 ms
QOI463 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.

The reference tool converts between PNG and QOI:

Terminal window
qoiconv input.png output.qoi
qoiconv output.qoi restored.png

qoiconv 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:

Terminal window
ffmpeg -i input.png output.qoi
ffmpeg -i output.qoi restored.png

ImageMagick reads and writes QOI (magick input.png output.qoi). Browsers have no native support, so convert to PNG, WebP or AVIF for the web.

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:

ChunkSizeMeaning
QOI_OP_RUN1 byteRepeat the previous pixel 1–62 times
QOI_OP_INDEX1 bytePixel found in the 64-entry colour table (hash of the colour)
QOI_OP_DIFF1 byteSmall per-channel difference from the previous pixel (−2…+1)
QOI_OP_LUMA2 bytesGreen difference (−32…+31) with red and blue differences relative to it
QOI_OP_RGB4 bytesFull RGB pixel
QOI_OP_RGBA5 bytesFull 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.

  • Colour management: the header’s colorspace flag (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.

The reference converter, library and FFmpeg interfaces, option by option. Reading option tables explains the Default column.

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.

ArgumentDefaultAccepted values and responsibility
INPUTrequiredSource path ending in lowercase .png or .qoi; other PNG channel layouts load as RGBA.
OUTPUTrequiredDestination path ending in .png or .qoi; an existing file is overwritten.

qoi_encode(data, desc, out_len) has no presets; the caller supplies every field and frees the returned buffer.

Parameter / fieldDefaultAccepted value / responsibility
datarequiredTightly packed row-major RGB or RGBA bytes matching the descriptor.
descrequiredPointer to a filled qoi_desc.
out_lenrequiredReceives the compressed size in bytes on success.
desc.widthrequiredPixel width, at least 1.
desc.heightrequiredPixel height, at least 1; width times height must stay below 400 million pixels.
desc.channelsrequired3 for RGB or 4 for RGBA.
desc.colorspacerequiredQOI_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 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).

Terminal window
ffmpeg -i input.png -c:v qoi -pix_fmt rgba output.qoi
ffmpeg -i linear.png -vf setparams=color_trc=linear -c:v qoi output.qoi

Converting to rgb24 drops the alpha channel.