{
  "schemaVersion": 1,
  "contentVersion": "ca42e61ede0a24f789d7287a0001244ad22f4abf",
  "id": "docs/codecs/images/qoi",
  "collection": "docs",
  "title": "QOI",
  "description": "QOI is a very simple, very fast lossless format for 8-bit RGB and RGBA images, popular in games and tools but not supported by browsers.",
  "canonicalUrl": "https://encode.wiki/codecs/images/qoi/",
  "apiUrl": "https://encode.wiki/api/v1/documents/docs/codecs/images/qoi.json",
  "markdownUrl": "https://encode.wiki/api/v1/markdown/docs/codecs/images/qoi.md",
  "status": "current",
  "tags": [
    "migrated",
    "images"
  ],
  "license": "CC BY-SA 4.0",
  "dates": {
    "lastVerified": "2026-10-08T00:00:00.000Z"
  },
  "authors": [],
  "sources": [
    {
      "title": "QOI 1.0 specification",
      "url": "https://qoiformat.org/qoi-specification.pdf",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "QOI reference implementation README",
      "url": "https://github.com/phoboslab/qoi/blob/ffb2d2cb74a1de60819b21b939f7209aa53e91c1/README.md",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "QOI benchmark results",
      "url": "https://qoiformat.org/benchmark/",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "Pinned reference converter",
      "url": "https://github.com/phoboslab/qoi/blob/ffb2d2cb74a1de60819b21b939f7209aa53e91c1/qoiconv.c",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "Pinned library contract",
      "url": "https://github.com/phoboslab/qoi/blob/ffb2d2cb74a1de60819b21b939f7209aa53e91c1/qoi.h",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "FFmpeg 9.0.2 QOI encoder",
      "url": "https://github.com/FFmpeg/FFmpeg/blob/n9.0.2/libavcodec/qoienc.c",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    }
  ],
  "provenance": {
    "originUrl": "https://github.com/av1-community-contributors/codec-wiki/blob/37c3317fd058b23c71a87679bed5619a98f77752/docs/images/QOI.mdx",
    "originCommit": "37c3317fd058b23c71a87679bed5619a98f77752",
    "migratedAt": "2026-08-21T00:00:00.000Z",
    "modified": true,
    "originPath": "docs/images/QOI.mdx"
  },
  "headings": [
    {
      "depth": 2,
      "text": "How it compares with PNG",
      "anchor": "how-it-compares-with-png"
    },
    {
      "depth": 2,
      "text": "Using it",
      "anchor": "using-it"
    },
    {
      "depth": 2,
      "text": "How the format works",
      "anchor": "how-the-format-works"
    },
    {
      "depth": 2,
      "text": "Pitfalls",
      "anchor": "pitfalls"
    },
    {
      "depth": 2,
      "text": "Full option reference",
      "anchor": "full-option-reference"
    },
    {
      "depth": 2,
      "text": "Complete reference-converter arguments",
      "anchor": "complete-reference-converter-arguments"
    },
    {
      "depth": 2,
      "text": "Complete library encode contract",
      "anchor": "complete-library-encode-contract"
    },
    {
      "depth": 2,
      "text": "FFmpeg QOI encoder interface",
      "anchor": "ffmpeg-qoi-encoder-interface"
    }
  ],
  "sections": [
    {
      "id": "_preamble",
      "title": "Page introduction",
      "depth": 0,
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/_preamble.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/_preamble.md",
      "checksum": "b01d592347009f018dfbcf19ea927982fbcd6ba29742618b53fb42d7bf5f74ef",
      "bytes": 1260
    },
    {
      "id": "how-it-compares-with-png",
      "title": "How it compares with PNG",
      "depth": 2,
      "anchor": "how-it-compares-with-png",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#how-it-compares-with-png",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/how-it-compares-with-png.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/how-it-compares-with-png.md",
      "checksum": "e96248aff106ab68f38de4432b7e746c1f689330b1ffaca6e391b4a7b82d2975",
      "bytes": 768
    },
    {
      "id": "using-it",
      "title": "Using it",
      "depth": 2,
      "anchor": "using-it",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#using-it",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/using-it.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/using-it.md",
      "checksum": "de1dcbfbb2f0387eeda4edf2b361492a825125c29eb5d43ae16118b7ae62bd3a",
      "bytes": 674
    },
    {
      "id": "how-the-format-works",
      "title": "How the format works",
      "depth": 2,
      "anchor": "how-the-format-works",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#how-the-format-works",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/how-the-format-works.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/how-the-format-works.md",
      "checksum": "c082acbbedb17f9d8ae1fd410c7905e31a4c4bbb9023978637b99d75ff4a14ec",
      "bytes": 985
    },
    {
      "id": "pitfalls",
      "title": "Pitfalls",
      "depth": 2,
      "anchor": "pitfalls",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#pitfalls",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/pitfalls.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/pitfalls.md",
      "checksum": "e3159c1b8b48df34e31e9017dd6f75a8d7b09f4eef86395eedf4128b6ecc0b45",
      "bytes": 467
    },
    {
      "id": "full-option-reference",
      "title": "Full option reference",
      "depth": 2,
      "anchor": "full-option-reference",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#full-option-reference",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/full-option-reference.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/full-option-reference.md",
      "checksum": "c70fd57a1ce8ed9ef6aec997d51dcef06e2297fecd2b2a80705a9633aa697081",
      "bytes": 185
    },
    {
      "id": "complete-reference-converter-arguments",
      "title": "Complete reference-converter arguments",
      "depth": 2,
      "anchor": "complete-reference-converter-arguments",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#complete-reference-converter-arguments",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/complete-reference-converter-arguments.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/complete-reference-converter-arguments.md",
      "checksum": "419da9a02c950b1dcea0a74bd37ef10205c5ec22a94a8e0e2b45746b8cd09809",
      "bytes": 764
    },
    {
      "id": "complete-library-encode-contract",
      "title": "Complete library encode contract",
      "depth": 2,
      "anchor": "complete-library-encode-contract",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#complete-library-encode-contract",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/complete-library-encode-contract.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/complete-library-encode-contract.md",
      "checksum": "79d0467a4d11b3780eb876b6026c4cd9bf53a482072c7e67601e4958e37604ac",
      "bytes": 1368
    },
    {
      "id": "ffmpeg-qoi-encoder-interface",
      "title": "FFmpeg QOI encoder interface",
      "depth": 2,
      "anchor": "ffmpeg-qoi-encoder-interface",
      "canonicalUrl": "https://encode.wiki/codecs/images/qoi/#ffmpeg-qoi-encoder-interface",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/ffmpeg-qoi-encoder-interface.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/codecs/images/qoi/ffmpeg-qoi-encoder-interface.md",
      "checksum": "2ebf54291345fbb59b752b3237db5712cc76f0dba50df5e61db3b34b0b5675dd",
      "bytes": 481
    }
  ],
  "outboundReferences": [
    "/codecs/images/jxl/",
    "/codecs/images/png/",
    "/codecs/images/webp/",
    "/reference/encoder-parameters/",
    "https://github.com/phoboslab/qoi",
    "https://qoiformat.org/qoi-specification.pdf"
  ],
  "checksum": "11362a6d89b82f512ae96fe6f34091bd49624083f5331686694b7025a553a2ac",
  "contentFormat": "markdown",
  "markdown": "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.\n\n| Property     | Value                                                               |\n| ------------ | ------------------------------------------------------------------- |\n| Compression  | Lossless                                                            |\n| Bit depth    | 8 bits per channel, 3 (RGB) or 4 (RGBA) channels                    |\n| Alpha        | Straight alpha in 4-channel files                                   |\n| Metadata     | None (no ICC, Exif or XMP); header has a sRGB/linear flag only      |\n| Animation    | No                                                                  |\n| Progressive  | No                                                                  |\n| Royalty-free | Yes (reference library is MIT-licensed)                             |\n\n## How it compares with PNG\n\nThe author's benchmark on 2,879 screenshots, icons, photos and textures (single-threaded, i7-6700K) gives:\n\n| Codec  | Average size | Encode | Decode |\n| ------ | ------------ | ------ | ------ |\n| libpng | 398 KB (24.2% of raw) | 83.8 ms (5.5 MP/s) | 7.0 ms (67 MP/s) |\n| stb_image_write | 561 KB (34.2%) | 60.5 ms | 7.0 ms |\n| QOI    | 463 KB (28.2%) | 2.9 ms (162 MP/s) | 2.1 ms (226 MP/s) |\n\nSo QOI is about 16% **larger** than libpng on average and much faster. An optimiser such as [oxipng](/codecs/images/png/) widens the gap further, and lossless [WebP](/codecs/images/webp/) or [JPEG XL](/codecs/images/jxl/) are smaller still. QOI does best on images with flat runs and smooth gradients; it does poorly on noise and photographs.\n\n## Using it\n\nThe reference tool converts between PNG and QOI:\n\n```sh\nqoiconv input.png output.qoi\nqoiconv output.qoi restored.png\n```\n\n`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`](https://github.com/phoboslab/qoi), is what most applications embed; ports for many other languages are listed in the repository README.\n\nFFmpeg has a native QOI encoder and decoder:\n\n```sh\nffmpeg -i input.png output.qoi\nffmpeg -i output.qoi restored.png\n```\n\nImageMagick reads and writes QOI (`magick input.png output.qoi`). Browsers have no native support, so convert to PNG, WebP or AVIF for the web.\n\n## How the format works\n\nA 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:\n\n| Chunk | Size | Meaning |\n| --- | --- | --- |\n| `QOI_OP_RUN` | 1 byte | Repeat the previous pixel 1–62 times |\n| `QOI_OP_INDEX` | 1 byte | Pixel found in the 64-entry colour table (hash of the colour) |\n| `QOI_OP_DIFF` | 1 byte | Small per-channel difference from the previous pixel (−2…+1) |\n| `QOI_OP_LUMA` | 2 bytes | Green difference (−32…+31) with red and blue differences relative to it |\n| `QOI_OP_RGB` | 4 bytes | Full RGB pixel |\n| `QOI_OP_RGBA` | 5 bytes | Full RGBA pixel |\n\nThere is nothing tunable: no quality or effort setting, so every QOI encoder writes nearly the same file. The [one-page specification](https://qoiformat.org/qoi-specification.pdf) is the whole format.\n\n## Pitfalls\n\n- **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.\n- **16-bit and HDR sources** must be reduced to 8 bits first, which loses data. Use PNG, TIFF or [JPEG XL](/codecs/images/jxl/) instead.\n- **Size limit**: the reference library refuses images over 400 million pixels. The format itself allows far larger images.\n\n## Full option reference\n\nThe reference converter, library and FFmpeg interfaces, option by option. [Reading option tables](/reference/encoder-parameters/) explains the Default column.\n\n## Complete reference-converter arguments\n\n`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.\n\n| Argument | Default  | Accepted values and responsibility                                                    |\n| -------- | -------- | ------------------------------------------------------------------------------------- |\n| `INPUT`  | required | Source path ending in lowercase .png or .qoi; other PNG channel layouts load as RGBA. |\n| `OUTPUT` | required | Destination path ending in .png or .qoi; an existing file is overwritten.             |\n\n## Complete library encode contract\n\n`qoi_encode(data, desc, out_len)` has no presets; the caller supplies every field and frees the returned buffer.\n\n| Parameter / field | Default  | Accepted value / responsibility                                                  |\n| ----------------- | -------- | -------------------------------------------------------------------------------- |\n| `data`            | required | Tightly packed row-major RGB or RGBA bytes matching the descriptor.              |\n| `desc`            | required | Pointer to a filled qoi_desc.                                                    |\n| `out_len`         | required | Receives the compressed size in bytes on success.                                |\n| `desc.width`      | required | Pixel width, at least 1.                                                         |\n| `desc.height`     | required | Pixel height, at least 1; width times height must stay below 400 million pixels. |\n| `desc.channels`   | required | 3 for RGB or 4 for RGBA.                                                         |\n| `desc.colorspace` | required | QOI_SRGB = 0 or QOI_LINEAR = 1; a label stored in the header, not a conversion.  |\n\nInvalid 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.\n\n## FFmpeg QOI encoder interface\n\nFFmpeg 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).\n\n```sh\nffmpeg -i input.png -c:v qoi -pix_fmt rgba output.qoi\nffmpeg -i linear.png -vf setparams=color_trc=linear -c:v qoi output.qoi\n```\n\nConverting to `rgb24` drops the alpha channel.\n"
}
