988f9edcb4fe95935746bfaa53c9b97fa3db1e0d
3 QOI - The "Quite OK Image" format for fast, lossless image compression
5 Dominic Szablewski - https://phoboslab.org
8 -- LICENSE: The MIT License(MIT)
10 Copyright(c) 2021 Dominic Szablewski
12 Permission is hereby granted, free of charge, to any person obtaining a copy of
13 this software and associated documentation files(the "Software"), to deal in
14 the Software without restriction, including without limitation the rights to
15 use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
16 of the Software, and to permit persons to whom the Software is furnished to do
17 so, subject to the following conditions :
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 QOI encodes and decodes images in a lossless format. Compared to stb_image and
32 stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and
33 20% better compression.
38 // Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
39 // library to create the implementation.
41 #define QOI_IMPLEMENTATION
44 // Encode and store an RGBA buffer to the file system. The qoi_desc describes
45 // the input pixel data.
46 qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
50 .colorspace = QOI_SRGB
53 // Load and decode a QOI image from the file system into a 32bbp RGBA buffer.
54 // The qoi_desc struct will be filled with the width, height, number of channels
55 // and colorspace read from the file header.
57 void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
63 This library provides the following functions;
64 - qoi_read -- read and decode a QOI file
65 - qoi_decode -- decode the raw bytes of a QOI image from memory
66 - qoi_write -- encode and write a QOI file
67 - qoi_encode -- encode an rgba buffer into a QOI image in memory
69 See the function declaration below for the signature and more information.
71 If you don't want/need the qoi_read and qoi_write functions, you can define
72 QOI_NO_STDIO before including this library.
74 This library uses malloc() and free(). To supply your own malloc implementation
75 you can define QOI_MALLOC and QOI_FREE before including this library.
77 This library uses memset() to zero-initialize the index. To supply your own
78 implementation you can define QOI_ZEROARR before including this library.
83 A QOI file has a 14 byte header, followed by any number of data "chunks" and an
87 char magic[4]; // magic bytes "qoif"
88 uint32_t width; // image width in pixels (BE)
89 uint32_t height; // image height in pixels (BE)
90 uint8_t channels; // 3 = RGB, 4 = RGBA
91 uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
94 Images are encoded row by row, left to right, top to bottom. The decoder and
95 encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An
96 image is complete when all pixels specified by width * height have been covered.
99 - a run of the previous pixel
100 - an index into an array of previously seen pixels
101 - a difference to the previous pixel value in r,g,b
102 - full r,g,b or r,g,b,a values
104 The color channels are assumed to not be premultiplied with the alpha channel
105 ("un-premultiplied alpha").
107 A running array[64] (zero-initialized) of previously seen pixel values is
108 maintained by the encoder and decoder. Each pixel that is seen by the encoder
109 and decoder is put into this array at the position formed by a hash function of
110 the color value. In the encoder, if the pixel value at the index matches the
111 current pixel, this index position is written to the stream as QOI_OP_INDEX.
112 The hash function for the index is:
114 index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
116 Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
117 bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
118 values encoded in these data bits have the most significant bit on the left.
120 The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
121 presence of an 8-bit tag first.
123 The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
126 The possible chunks are:
129 .- QOI_OP_INDEX ----------.
132 |-------+-----------------|
134 `-------------------------`
136 6-bit index into the color index array: 0..63
138 A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the
139 same index. QOI_OP_RUN should be used instead.
142 .- QOI_OP_DIFF -----------.
145 |-------+-----+-----+-----|
146 | 0 1 | dr | dg | db |
147 `-------------------------`
149 2-bit red channel difference from the previous pixel between -2..1
150 2-bit green channel difference from the previous pixel between -2..1
151 2-bit blue channel difference from the previous pixel between -2..1
153 The difference to the current channel values are using a wraparound operation,
154 so "1 - 2" will result in 255, while "255 + 1" will result in 0.
156 Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
157 0 (b00). 1 is stored as 3 (b11).
159 The alpha value remains unchanged from the previous pixel.
162 .- QOI_OP_LUMA -------------------------------------.
163 | Byte[0] | Byte[1] |
164 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
165 |-------+-----------------+-------------+-----------|
166 | 1 0 | green diff | dr - dg | db - dg |
167 `---------------------------------------------------`
169 6-bit green channel difference from the previous pixel -32..31
170 4-bit red channel difference minus green channel difference -8..7
171 4-bit blue channel difference minus green channel difference -8..7
173 The green channel is used to indicate the general direction of change and is
174 encoded in 6 bits. The red and blue channels (dr and db) base their diffs off
175 of the green channel difference and are encoded in 4 bits. I.e.:
176 dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
177 db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)
179 The difference to the current channel values are using a wraparound operation,
180 so "10 - 13" will result in 253, while "250 + 7" will result in 1.
182 Values are stored as unsigned integers with a bias of 32 for the green channel
183 and a bias of 8 for the red and blue channel.
185 The alpha value remains unchanged from the previous pixel.
188 .- QOI_OP_RUN ------------.
191 |-------+-----------------|
193 `-------------------------`
195 6-bit run-length repeating the previous pixel: 1..62
197 The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64
198 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
202 .- QOI_OP_RGB ------------------------------------------.
203 | Byte[0] | Byte[1] | Byte[2] | Byte[3] |
204 | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
205 |-------------------------+---------+---------+---------|
206 | 1 1 1 1 1 1 1 0 | red | green | blue |
207 `-------------------------------------------------------`
209 8-bit red channel value
210 8-bit green channel value
211 8-bit blue channel value
213 The alpha value remains unchanged from the previous pixel.
216 .- QOI_OP_RGBA ---------------------------------------------------.
217 | Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
218 | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
219 |-------------------------+---------+---------+---------+---------|
220 | 1 1 1 1 1 1 1 1 | red | green | blue | alpha |
221 `-----------------------------------------------------------------`
223 8-bit red channel value
224 8-bit green channel value
225 8-bit blue channel value
226 8-bit alpha channel value
231 /* -----------------------------------------------------------------------------
232 Header - Public functions */
241 /* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
242 It describes either the input format (for qoi_write and qoi_encode), or is
243 filled with the description read from the file header (for qoi_read and
246 The colorspace in this qoi_desc is an enum where
247 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
248 1 = all channels are linear
249 You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
250 informative. It will be saved to the file header, but does not affect
251 how chunks are en-/decoded. */
259 unsigned char channels
;
260 unsigned char colorspace
;
265 /* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
266 system. The qoi_desc struct must be filled with the image width, height,
267 number of channels (3 = RGB, 4 = RGBA) and the colorspace.
269 The function returns 0 on failure (invalid parameters, or fopen or malloc
270 failed) or the number of bytes written on success. */
272 int qoi_write(const char *filename
, const void *data
, const qoi_desc
*desc
);
275 /* Read and decode a QOI image from the file system. If channels is 0, the
276 number of channels from the file header is used. If channels is 3 or 4 the
277 output format will be forced into this number of channels.
279 The function either returns NULL on failure (invalid data, or malloc or fopen
280 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
281 will be filled with the description from the file header.
283 The returned pixel data should be free()d after use. */
285 void *qoi_read(const char *filename
, qoi_desc
*desc
, int channels
);
287 #endif /* QOI_NO_STDIO */
290 /* Encode raw RGB or RGBA pixels into a QOI image in memory.
292 The function either returns NULL on failure (invalid parameters or malloc
293 failed) or a pointer to the encoded data on success. On success the out_len
294 is set to the size in bytes of the encoded data.
296 The returned qoi data should be free()d after use. */
298 void *qoi_encode(const void *data
, const qoi_desc
*desc
, int *out_len
);
301 /* Decode a QOI image from memory.
303 The function either returns NULL on failure (invalid parameters or malloc
304 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
305 is filled with the description from the file header.
307 The returned pixel data should be free()d after use. */
309 void *qoi_decode(const void *data
, int size
, qoi_desc
*desc
, int channels
);
318 /* -----------------------------------------------------------------------------
321 #ifdef QOI_IMPLEMENTATION
326 #define QOI_MALLOC(sz) malloc(sz)
327 #define QOI_FREE(p) free(p)
330 #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
333 #define QOI_OP_INDEX 0x00 /* 00xxxxxx */
334 #define QOI_OP_DIFF 0x40 /* 01xxxxxx */
335 #define QOI_OP_LUMA 0x80 /* 10xxxxxx */
336 #define QOI_OP_RUN 0xc0 /* 11xxxxxx */
337 #define QOI_OP_RGB 0xfe /* 11111110 */
338 #define QOI_OP_RGBA 0xff /* 11111111 */
340 #define QOI_MASK_2 0xc0 /* 11000000 */
342 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
344 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
345 ((unsigned int)'i') << 8 | ((unsigned int)'f'))
346 #define QOI_HEADER_SIZE 14
348 /* 2GB is the max file size that this implementation can safely handle. We guard
349 against anything larger than that, assuming the worst case with 5 bytes per
350 pixel, rounded down to a nice clean value. 400 million pixels ought to be
351 enough for anybody. */
352 #define QOI_PIXELS_MAX ((unsigned int)400000000)
355 struct { unsigned char r
, g
, b
, a
; } rgba
;
359 static const unsigned char qoi_padding
[8] = {0,0,0,0,0,0,0,1};
361 static void qoi_write_32(unsigned char *bytes
, int *p
, unsigned int v
) {
362 bytes
[(*p
)++] = (0xff000000 & v
) >> 24;
363 bytes
[(*p
)++] = (0x00ff0000 & v
) >> 16;
364 bytes
[(*p
)++] = (0x0000ff00 & v
) >> 8;
365 bytes
[(*p
)++] = (0x000000ff & v
);
368 static unsigned int qoi_read_32(const unsigned char *bytes
, int *p
) {
369 unsigned int a
= bytes
[(*p
)++];
370 unsigned int b
= bytes
[(*p
)++];
371 unsigned int c
= bytes
[(*p
)++];
372 unsigned int d
= bytes
[(*p
)++];
373 return a
<< 24 | b
<< 16 | c
<< 8 | d
;
376 void *qoi_encode(const void *data
, const qoi_desc
*desc
, int *out_len
) {
377 int i
, max_size
, p
, run
;
378 int px_len
, px_end
, px_pos
, channels
;
379 unsigned char *bytes
;
380 const unsigned char *pixels
;
381 qoi_rgba_t index
[64];
382 qoi_rgba_t px
, px_prev
;
385 data
== NULL
|| out_len
== NULL
|| desc
== NULL
||
386 desc
->width
== 0 || desc
->height
== 0 ||
387 desc
->channels
< 3 || desc
->channels
> 4 ||
388 desc
->colorspace
> 1 ||
389 desc
->height
>= QOI_PIXELS_MAX
/ desc
->width
395 desc
->width
* desc
->height
* (desc
->channels
+ 1) +
396 QOI_HEADER_SIZE
+ sizeof(qoi_padding
);
399 bytes
= (unsigned char *) QOI_MALLOC(max_size
);
404 qoi_write_32(bytes
, &p
, QOI_MAGIC
);
405 qoi_write_32(bytes
, &p
, desc
->width
);
406 qoi_write_32(bytes
, &p
, desc
->height
);
407 bytes
[p
++] = desc
->channels
;
408 bytes
[p
++] = desc
->colorspace
;
411 pixels
= (const unsigned char *)data
;
419 px_prev
.rgba
.a
= 255;
422 px_len
= desc
->width
* desc
->height
* desc
->channels
;
423 px_end
= px_len
- desc
->channels
;
424 channels
= desc
->channels
;
426 for (px_pos
= 0; px_pos
< px_len
; px_pos
+= channels
) {
428 px
= *(qoi_rgba_t
*)(pixels
+ px_pos
);
431 px
.rgba
.r
= pixels
[px_pos
+ 0];
432 px
.rgba
.g
= pixels
[px_pos
+ 1];
433 px
.rgba
.b
= pixels
[px_pos
+ 2];
436 if (px
.v
== px_prev
.v
) {
438 if (run
== 62 || px_pos
== px_end
) {
439 bytes
[p
++] = QOI_OP_RUN
| (run
- 1);
447 bytes
[p
++] = QOI_OP_RUN
| (run
- 1);
451 index_pos
= QOI_COLOR_HASH(px
) % 64;
453 if (index
[index_pos
].v
== px
.v
) {
454 bytes
[p
++] = QOI_OP_INDEX
| index_pos
;
457 index
[index_pos
] = px
;
459 if (px
.rgba
.a
== px_prev
.rgba
.a
) {
460 signed char vr
= px
.rgba
.r
- px_prev
.rgba
.r
;
461 signed char vg
= px
.rgba
.g
- px_prev
.rgba
.g
;
462 signed char vb
= px
.rgba
.b
- px_prev
.rgba
.b
;
464 signed char vg_r
= vr
- vg
;
465 signed char vg_b
= vb
- vg
;
472 bytes
[p
++] = QOI_OP_DIFF
| (vr
+ 2) << 4 | (vg
+ 2) << 2 | (vb
+ 2);
475 vg_r
> -9 && vg_r
< 8 &&
476 vg
> -33 && vg
< 32 &&
477 vg_b
> -9 && vg_b
< 8
479 bytes
[p
++] = QOI_OP_LUMA
| (vg
+ 32);
480 bytes
[p
++] = (vg_r
+ 8) << 4 | (vg_b
+ 8);
483 bytes
[p
++] = QOI_OP_RGB
;
484 bytes
[p
++] = px
.rgba
.r
;
485 bytes
[p
++] = px
.rgba
.g
;
486 bytes
[p
++] = px
.rgba
.b
;
490 bytes
[p
++] = QOI_OP_RGBA
;
491 bytes
[p
++] = px
.rgba
.r
;
492 bytes
[p
++] = px
.rgba
.g
;
493 bytes
[p
++] = px
.rgba
.b
;
494 bytes
[p
++] = px
.rgba
.a
;
501 for (i
= 0; i
< (int)sizeof(qoi_padding
); i
++) {
502 bytes
[p
++] = qoi_padding
[i
];
509 void *qoi_decode(const void *data
, int size
, qoi_desc
*desc
, int channels
) {
510 const unsigned char *bytes
;
511 unsigned int header_magic
;
512 unsigned char *pixels
;
513 qoi_rgba_t index
[64];
515 int px_len
, chunks_len
, px_pos
;
519 data
== NULL
|| desc
== NULL
||
520 (channels
!= 0 && channels
!= 3 && channels
!= 4) ||
521 size
< QOI_HEADER_SIZE
+ (int)sizeof(qoi_padding
)
526 bytes
= (const unsigned char *)data
;
528 header_magic
= qoi_read_32(bytes
, &p
);
529 desc
->width
= qoi_read_32(bytes
, &p
);
530 desc
->height
= qoi_read_32(bytes
, &p
);
531 desc
->channels
= bytes
[p
++];
532 desc
->colorspace
= bytes
[p
++];
535 desc
->width
== 0 || desc
->height
== 0 ||
536 desc
->channels
< 3 || desc
->channels
> 4 ||
537 desc
->colorspace
> 1 ||
538 header_magic
!= QOI_MAGIC
||
539 desc
->height
>= QOI_PIXELS_MAX
/ desc
->width
545 channels
= desc
->channels
;
548 px_len
= desc
->width
* desc
->height
* channels
;
549 pixels
= (unsigned char *) QOI_MALLOC(px_len
);
560 chunks_len
= size
- (int)sizeof(qoi_padding
);
561 for (px_pos
= 0; px_pos
< px_len
; px_pos
+= channels
) {
565 else if (p
< chunks_len
) {
568 if (b1
== QOI_OP_RGB
) {
569 px
.rgba
.r
= bytes
[p
++];
570 px
.rgba
.g
= bytes
[p
++];
571 px
.rgba
.b
= bytes
[p
++];
573 else if (b1
== QOI_OP_RGBA
) {
574 px
.rgba
.r
= bytes
[p
++];
575 px
.rgba
.g
= bytes
[p
++];
576 px
.rgba
.b
= bytes
[p
++];
577 px
.rgba
.a
= bytes
[p
++];
579 else if ((b1
& QOI_MASK_2
) == QOI_OP_INDEX
) {
582 else if ((b1
& QOI_MASK_2
) == QOI_OP_DIFF
) {
583 px
.rgba
.r
+= ((b1
>> 4) & 0x03) - 2;
584 px
.rgba
.g
+= ((b1
>> 2) & 0x03) - 2;
585 px
.rgba
.b
+= ( b1
& 0x03) - 2;
587 else if ((b1
& QOI_MASK_2
) == QOI_OP_LUMA
) {
589 int vg
= (b1
& 0x3f) - 32;
590 px
.rgba
.r
+= vg
- 8 + ((b2
>> 4) & 0x0f);
592 px
.rgba
.b
+= vg
- 8 + (b2
& 0x0f);
594 else if ((b1
& QOI_MASK_2
) == QOI_OP_RUN
) {
598 index
[QOI_COLOR_HASH(px
) % 64] = px
;
602 *(qoi_rgba_t
*)(pixels
+ px_pos
) = px
;
605 pixels
[px_pos
+ 0] = px
.rgba
.r
;
606 pixels
[px_pos
+ 1] = px
.rgba
.g
;
607 pixels
[px_pos
+ 2] = px
.rgba
.b
;
617 int qoi_write(const char *filename
, const void *data
, const qoi_desc
*desc
) {
618 FILE *f
= fopen(filename
, "wb");
626 encoded
= qoi_encode(data
, desc
, &size
);
632 fwrite(encoded
, 1, size
, f
);
639 void *qoi_read(const char *filename
, qoi_desc
*desc
, int channels
) {
640 FILE *f
= fopen(filename
, "rb");
641 int size
, bytes_read
;
648 fseek(f
, 0, SEEK_END
);
654 fseek(f
, 0, SEEK_SET
);
656 data
= QOI_MALLOC(size
);
662 bytes_read
= fread(data
, 1, size
, f
);
665 pixels
= qoi_decode(data
, bytes_read
, desc
, channels
);
670 #endif /* QOI_NO_STDIO */
671 #endif /* QOI_IMPLEMENTATION */