cross compile build script
[fishladder.git] / phoboslab / qoi.h
1 /*
2
3 QOI - The "Quite OK Image" format for fast, lossless image compression
4
5 Dominic Szablewski - https://phoboslab.org
6
7
8 -- LICENSE: The MIT License(MIT)
9
10 Copyright(c) 2021 Dominic Szablewski
11
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
26 SOFTWARE.
27
28
29 -- About
30
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.
34
35
36 -- Synopsis
37
38 // Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
39 // library to create the implementation.
40
41 #define QOI_IMPLEMENTATION
42 #include "qoi.h"
43
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){
47 .width = 1920,
48 .height = 1080,
49 .channels = 4,
50 .colorspace = QOI_SRGB
51 });
52
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.
56 qoi_desc desc;
57 void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
58
59
60
61 -- Documentation
62
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
68
69 See the function declaration below for the signature and more information.
70
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.
73
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.
76
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.
79
80
81 -- Data Format
82
83 A QOI file has a 14 byte header, followed by any number of data "chunks" and an
84 8-byte end marker.
85
86 struct qoi_header_t {
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
92 };
93
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.
97
98 Pixels are encoded as
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
103
104 The color channels are assumed to not be premultiplied with the alpha channel
105 ("un-premultiplied alpha").
106
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:
113
114 index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
115
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.
119
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.
122
123 The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
124
125
126 The possible chunks are:
127
128
129 .- QOI_OP_INDEX ----------.
130 | Byte[0] |
131 | 7 6 5 4 3 2 1 0 |
132 |-------+-----------------|
133 | 0 0 | index |
134 `-------------------------`
135 2-bit tag b00
136 6-bit index into the color index array: 0..63
137
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.
140
141
142 .- QOI_OP_DIFF -----------.
143 | Byte[0] |
144 | 7 6 5 4 3 2 1 0 |
145 |-------+-----+-----+-----|
146 | 0 1 | dr | dg | db |
147 `-------------------------`
148 2-bit tag b01
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
152
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.
155
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).
158
159 The alpha value remains unchanged from the previous pixel.
160
161
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 `---------------------------------------------------`
168 2-bit tag b10
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
172
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)
178
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.
181
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.
184
185 The alpha value remains unchanged from the previous pixel.
186
187
188 .- QOI_OP_RUN ------------.
189 | Byte[0] |
190 | 7 6 5 4 3 2 1 0 |
191 |-------+-----------------|
192 | 1 1 | run |
193 `-------------------------`
194 2-bit tag b11
195 6-bit run-length repeating the previous pixel: 1..62
196
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
199 QOI_OP_RGBA tags.
200
201
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 `-------------------------------------------------------`
208 8-bit tag b11111110
209 8-bit red channel value
210 8-bit green channel value
211 8-bit blue channel value
212
213 The alpha value remains unchanged from the previous pixel.
214
215
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 `-----------------------------------------------------------------`
222 8-bit tag b11111111
223 8-bit red channel value
224 8-bit green channel value
225 8-bit blue channel value
226 8-bit alpha channel value
227
228 */
229
230
231 /* -----------------------------------------------------------------------------
232 Header - Public functions */
233
234 #ifndef QOI_H
235 #define QOI_H
236
237 #ifdef __cplusplus
238 extern "C" {
239 #endif
240
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
244 qoi_decode).
245
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. */
252
253 #define QOI_SRGB 0
254 #define QOI_LINEAR 1
255
256 typedef struct {
257 unsigned int width;
258 unsigned int height;
259 unsigned char channels;
260 unsigned char colorspace;
261 } qoi_desc;
262
263 #ifndef QOI_NO_STDIO
264
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.
268
269 The function returns 0 on failure (invalid parameters, or fopen or malloc
270 failed) or the number of bytes written on success. */
271
272 int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
273
274
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.
278
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.
282
283 The returned pixel data should be free()d after use. */
284
285 void *qoi_read(const char *filename, qoi_desc *desc, int channels);
286
287 #endif /* QOI_NO_STDIO */
288
289
290 /* Encode raw RGB or RGBA pixels into a QOI image in memory.
291
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.
295
296 The returned qoi data should be free()d after use. */
297
298 void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
299
300
301 /* Decode a QOI image from memory.
302
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.
306
307 The returned pixel data should be free()d after use. */
308
309 void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels);
310
311
312 #ifdef __cplusplus
313 }
314 #endif
315 #endif /* QOI_H */
316
317
318 /* -----------------------------------------------------------------------------
319 Implementation */
320
321 #ifdef QOI_IMPLEMENTATION
322 #include <stdlib.h>
323 #include <string.h>
324
325 #ifndef QOI_MALLOC
326 #define QOI_MALLOC(sz) malloc(sz)
327 #define QOI_FREE(p) free(p)
328 #endif
329 #ifndef QOI_ZEROARR
330 #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
331 #endif
332
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 */
339
340 #define QOI_MASK_2 0xc0 /* 11000000 */
341
342 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
343 #define QOI_MAGIC \
344 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
345 ((unsigned int)'i') << 8 | ((unsigned int)'f'))
346 #define QOI_HEADER_SIZE 14
347
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)
353
354 typedef union {
355 struct { unsigned char r, g, b, a; } rgba;
356 unsigned int v;
357 } qoi_rgba_t;
358
359 static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
360
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);
366 }
367
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;
374 }
375
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;
383
384 if (
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
390 ) {
391 return NULL;
392 }
393
394 max_size =
395 desc->width * desc->height * (desc->channels + 1) +
396 QOI_HEADER_SIZE + sizeof(qoi_padding);
397
398 p = 0;
399 bytes = (unsigned char *) QOI_MALLOC(max_size);
400 if (!bytes) {
401 return NULL;
402 }
403
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;
409
410
411 pixels = (const unsigned char *)data;
412
413 QOI_ZEROARR(index);
414
415 run = 0;
416 px_prev.rgba.r = 0;
417 px_prev.rgba.g = 0;
418 px_prev.rgba.b = 0;
419 px_prev.rgba.a = 255;
420 px = px_prev;
421
422 px_len = desc->width * desc->height * desc->channels;
423 px_end = px_len - desc->channels;
424 channels = desc->channels;
425
426 for (px_pos = 0; px_pos < px_len; px_pos += channels) {
427 if (channels == 4) {
428 px = *(qoi_rgba_t *)(pixels + px_pos);
429 }
430 else {
431 px.rgba.r = pixels[px_pos + 0];
432 px.rgba.g = pixels[px_pos + 1];
433 px.rgba.b = pixels[px_pos + 2];
434 }
435
436 if (px.v == px_prev.v) {
437 run++;
438 if (run == 62 || px_pos == px_end) {
439 bytes[p++] = QOI_OP_RUN | (run - 1);
440 run = 0;
441 }
442 }
443 else {
444 int index_pos;
445
446 if (run > 0) {
447 bytes[p++] = QOI_OP_RUN | (run - 1);
448 run = 0;
449 }
450
451 index_pos = QOI_COLOR_HASH(px) % 64;
452
453 if (index[index_pos].v == px.v) {
454 bytes[p++] = QOI_OP_INDEX | index_pos;
455 }
456 else {
457 index[index_pos] = px;
458
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;
463
464 signed char vg_r = vr - vg;
465 signed char vg_b = vb - vg;
466
467 if (
468 vr > -3 && vr < 2 &&
469 vg > -3 && vg < 2 &&
470 vb > -3 && vb < 2
471 ) {
472 bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
473 }
474 else if (
475 vg_r > -9 && vg_r < 8 &&
476 vg > -33 && vg < 32 &&
477 vg_b > -9 && vg_b < 8
478 ) {
479 bytes[p++] = QOI_OP_LUMA | (vg + 32);
480 bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
481 }
482 else {
483 bytes[p++] = QOI_OP_RGB;
484 bytes[p++] = px.rgba.r;
485 bytes[p++] = px.rgba.g;
486 bytes[p++] = px.rgba.b;
487 }
488 }
489 else {
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;
495 }
496 }
497 }
498 px_prev = px;
499 }
500
501 for (i = 0; i < (int)sizeof(qoi_padding); i++) {
502 bytes[p++] = qoi_padding[i];
503 }
504
505 *out_len = p;
506 return bytes;
507 }
508
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];
514 qoi_rgba_t px;
515 int px_len, chunks_len, px_pos;
516 int p = 0, run = 0;
517
518 if (
519 data == NULL || desc == NULL ||
520 (channels != 0 && channels != 3 && channels != 4) ||
521 size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
522 ) {
523 return NULL;
524 }
525
526 bytes = (const unsigned char *)data;
527
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++];
533
534 if (
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
540 ) {
541 return NULL;
542 }
543
544 if (channels == 0) {
545 channels = desc->channels;
546 }
547
548 px_len = desc->width * desc->height * channels;
549 pixels = (unsigned char *) QOI_MALLOC(px_len);
550 if (!pixels) {
551 return NULL;
552 }
553
554 QOI_ZEROARR(index);
555 px.rgba.r = 0;
556 px.rgba.g = 0;
557 px.rgba.b = 0;
558 px.rgba.a = 255;
559
560 chunks_len = size - (int)sizeof(qoi_padding);
561 for (px_pos = 0; px_pos < px_len; px_pos += channels) {
562 if (run > 0) {
563 run--;
564 }
565 else if (p < chunks_len) {
566 int b1 = bytes[p++];
567
568 if (b1 == QOI_OP_RGB) {
569 px.rgba.r = bytes[p++];
570 px.rgba.g = bytes[p++];
571 px.rgba.b = bytes[p++];
572 }
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++];
578 }
579 else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
580 px = index[b1];
581 }
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;
586 }
587 else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
588 int b2 = bytes[p++];
589 int vg = (b1 & 0x3f) - 32;
590 px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
591 px.rgba.g += vg;
592 px.rgba.b += vg - 8 + (b2 & 0x0f);
593 }
594 else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
595 run = (b1 & 0x3f);
596 }
597
598 index[QOI_COLOR_HASH(px) % 64] = px;
599 }
600
601 if (channels == 4) {
602 *(qoi_rgba_t*)(pixels + px_pos) = px;
603 }
604 else {
605 pixels[px_pos + 0] = px.rgba.r;
606 pixels[px_pos + 1] = px.rgba.g;
607 pixels[px_pos + 2] = px.rgba.b;
608 }
609 }
610
611 return pixels;
612 }
613
614 #ifndef QOI_NO_STDIO
615 #include <stdio.h>
616
617 int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
618 FILE *f = fopen(filename, "wb");
619 int size;
620 void *encoded;
621
622 if (!f) {
623 return 0;
624 }
625
626 encoded = qoi_encode(data, desc, &size);
627 if (!encoded) {
628 fclose(f);
629 return 0;
630 }
631
632 fwrite(encoded, 1, size, f);
633 fclose(f);
634
635 QOI_FREE(encoded);
636 return size;
637 }
638
639 void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
640 FILE *f = fopen(filename, "rb");
641 int size, bytes_read;
642 void *pixels, *data;
643
644 if (!f) {
645 return NULL;
646 }
647
648 fseek(f, 0, SEEK_END);
649 size = ftell(f);
650 if (size <= 0) {
651 fclose(f);
652 return NULL;
653 }
654 fseek(f, 0, SEEK_SET);
655
656 data = QOI_MALLOC(size);
657 if (!data) {
658 fclose(f);
659 return NULL;
660 }
661
662 bytes_read = fread(data, 1, size, f);
663 fclose(f);
664
665 pixels = qoi_decode(data, bytes_read, desc, channels);
666 QOI_FREE(data);
667 return pixels;
668 }
669
670 #endif /* QOI_NO_STDIO */
671 #endif /* QOI_IMPLEMENTATION */